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.
This commit is contained in:
parent
0a0e616c2d
commit
b23acb58d5
3 changed files with 55 additions and 18 deletions
28
main.py
28
main.py
|
@ -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…
Add table
Reference in a new issue