Fix for #5 Manually add bridge

This patch intruduces more robust handling of situations where
a bridge can not be discovered by means of UPnP.

A modal now pops up, which allows you to manually enter an
ipaddress if no bridge can be discovered.
Micke Nordin 3 years ago committed by Micke Nordin
parent 0a0e616c2d
commit c7934e973b
Signed by: micke
GPG Key ID: 014B273D614BE877

@ -5,7 +5,7 @@ from typing import Union
import wx import wx
import wx.lib.scrolledpanel as scrolled import wx.lib.scrolledpanel as scrolled
from tinge import Tinge, HueBridge, HueGroup, HueLight, HueUtils from tinge import Tinge, HueBridge, HueGroup, HueLight, HueUtils, is_bridge
class Hui(wx.Frame): class Hui(wx.Frame):
@ -108,6 +108,29 @@ class Hui(wx.Frame):
lambda event, mgroupid=groupid: self.goto_group(mgroupid), group_btn) lambda event, mgroupid=groupid: self.goto_group(mgroupid), group_btn)
self.sizer.Add(inner_sizer, 0, wx.EXPAND) self.sizer.Add(inner_sizer, 0, wx.EXPAND)
def add_manual_discovery_dialog(self) -> bool:
self.sizer.Clear(delete_windows=True)
found_any: bool = False
text_entry: wx.TextEntryDialog = wx.TextEntryDialog(self.pnl, "Manually enter IP address of bridge:",
caption="Auto discovery failure")
warn_label: wx.StaticText = wx.StaticText(self.pnl, label="Waiting for Button Press on Bridge")
if text_entry.ShowModal() == wx.ID_OK:
ipaddress: str = text_entry.GetValue()
if is_bridge(ipaddress):
self.sizer.Add(warn_label, 0, wx.ALIGN_CENTER)
self.sizer.Layout()
user_or_error = HueUtils.connect(ipaddress)
while user_or_error.is_error():
user_or_error = HueUtils.connect(ipaddress)
self.m_tinge.append_bridge(HueBridge(ipaddress, user_or_error.get_user(), ipaddress))
found_any = True
self.m_tinge.write_all_bridges_to_conf()
else:
label = "Supplied IP Address did not match a Bridge.",
failure_msg: wx.GenericMessageDialog = wx.GenericMessageDialog(self.pnl, label, caption="Try again!")
failure_msg.ShowModal()
return found_any
@redraw @redraw
def add_lights(self, lights: list[HueLight]): def add_lights(self, lights: list[HueLight]):
"""This will add the lights from a group to the sizer """This will add the lights from a group to the sizer
@ -245,6 +268,9 @@ class Hui(wx.Frame):
found_any = True found_any = True
self.m_tinge.write_all_bridges_to_conf() self.m_tinge.write_all_bridges_to_conf()
self.add_bridges() self.add_bridges()
else:
found_any = self.add_manual_discovery_dialog()
self.add_bridges()
return found_any return found_any
def get_ok_cancel_answer_from_modal(self, message: str) -> bool: def get_ok_cancel_answer_from_modal(self, message: str) -> bool:

@ -47,6 +47,25 @@ def is_valid_config(filename: str) -> bool:
return os.path.exists(filename) and os.path.getsize(filename) > 0 return os.path.exists(filename) and os.path.getsize(filename) > 0
def is_bridge(ipaddress: str):
try:
response = make_request(ipaddress, "1234/lights")
discovered = False
if response:
resp = response.json()[0]
if 'error' in resp.keys():
m_keys = resp['error'].keys()
if 'description' in m_keys and 'address' in m_keys and 'type' in m_keys:
# This is kinda ugly but line is too long with and statement
if resp['error']['description'] == "unauthorized user":
if resp['error']['address'] == "/lights":
if resp['error']['type'] == 1:
discovered = True
except simplejson.errors.JSONDecodeError:
pass
return discovered
def make_request(ipaddress: str, path: str, method: str = "GET", def make_request(ipaddress: str, path: str, method: str = "GET",
body: str = '') -> Union[None, requests.Response]: body: str = '') -> Union[None, requests.Response]:
"""Helper function to make an API call to the Hue API on the bridge """Helper function to make an API call to the Hue API on the bridge

@ -1,5 +1,6 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import http
import os import os
from typing import Union from typing import Union
@ -8,7 +9,7 @@ import toml
from upnpy import UPnP from upnpy import UPnP
from .HueBridge import HueBridge from .HueBridge import HueBridge
from .HueUtils import connect, is_valid_config, make_request from .HueUtils import connect, is_valid_config, make_request, is_bridge
from .UserOrError import UserOrError from .UserOrError import UserOrError
@ -39,7 +40,12 @@ class Tinge:
"""Use UPnP to discover bridges on the current network """Use UPnP to discover bridges on the current network
""" """
upnp: UPnP = UPnP() upnp: UPnP = UPnP()
discovered_devices = upnp.discover() discovered_devices = list()
try:
discovered_devices = upnp.discover()
except http.client.BadStatusLine:
print("UPnP discovery failed")
return None
discovered_bridges: list[dict] = list() discovered_bridges: list[dict] = list()
seen_ips: list[str] = list() seen_ips: list[str] = list()
if not discovered_devices: if not discovered_devices:
@ -54,21 +60,7 @@ class Tinge:
discovered = True discovered = True
# If not we try to do a request against the api and see if we get an answer we can understand # If not we try to do a request against the api and see if we get an answer we can understand
else: else:
try: discovered = is_bridge(device.host)
response = make_request(device.host, "1234/lights")
if response:
resp = response.json()[0]
if 'error' in resp.keys():
m_keys = resp['error'].keys()
if 'description' in m_keys and 'address' in m_keys and 'type' in m_keys:
# This is kinda ugly but line is too long with and statement
if resp['error']['description'] == "unauthorized user":
if resp['error']['address'] == "/lights":
if resp['error']['type'] == 1:
discovered = True
except simplejson.errors.JSONDecodeError:
pass
if discovered: if discovered:
bridge = {'ipaddress': device.host, 'name': device.get_friendly_name()} bridge = {'ipaddress': device.host, 'name': device.get_friendly_name()}
if bridge not in discovered_bridges: if bridge not in discovered_bridges:

Loading…
Cancel
Save