diff --git a/main.py b/main.py index 3a1729e..b582a5d 100644 --- a/main.py +++ b/main.py @@ -5,7 +5,7 @@ from typing import Union import wx 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): @@ -108,6 +108,29 @@ class Hui(wx.Frame): lambda event, mgroupid=groupid: self.goto_group(mgroupid), group_btn) 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 def add_lights(self, lights: list[HueLight]): """This will add the lights from a group to the sizer @@ -245,6 +268,9 @@ class Hui(wx.Frame): found_any = True self.m_tinge.write_all_bridges_to_conf() self.add_bridges() + else: + found_any = self.add_manual_discovery_dialog() + self.add_bridges() return found_any def get_ok_cancel_answer_from_modal(self, message: str) -> bool: diff --git a/tinge/HueUtils/__init__.py b/tinge/HueUtils/__init__.py index 7822e45..227c25e 100644 --- a/tinge/HueUtils/__init__.py +++ b/tinge/HueUtils/__init__.py @@ -47,6 +47,25 @@ def is_valid_config(filename: str) -> bool: 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", body: str = '') -> Union[None, requests.Response]: """Helper function to make an API call to the Hue API on the bridge diff --git a/tinge/__init__.py b/tinge/__init__.py index 2f5c522..ffc5df8 100644 --- a/tinge/__init__.py +++ b/tinge/__init__.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- +import http import os from typing import Union @@ -8,7 +9,7 @@ import toml from upnpy import UPnP 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 @@ -39,7 +40,12 @@ class Tinge: """Use UPnP to discover bridges on the current network """ 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() seen_ips: list[str] = list() if not discovered_devices: @@ -54,21 +60,7 @@ class Tinge: discovered = True # If not we try to do a request against the api and see if we get an answer we can understand else: - try: - 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 - + discovered = is_bridge(device.host) if discovered: bridge = {'ipaddress': device.host, 'name': device.get_friendly_name()} if bridge not in discovered_bridges: