diff --git a/scripts/tinge b/scripts/tinge index 1efa55d..5e14fd9 100755 --- a/scripts/tinge +++ b/scripts/tinge @@ -5,7 +5,7 @@ from typing import Union import wx import wx.lib.scrolledpanel as scrolled -from tinge import Tinge, HueBridge, HueGroup, HueLight, HueUtils, is_bridge +from tinge import HueBridge, HueGroup, HueLight, HueUtils, Tinge, is_bridge class Hui(wx.Frame): @@ -14,7 +14,6 @@ class Hui(wx.Frame): Args: wx (Frame): Parent class """ - def redraw(*args): """Decorator used for redrawing the widgets in the sizer @@ -43,7 +42,8 @@ class Hui(wx.Frame): self.cur_bridge: Union[None, HueBridge] = None self.cur_group: Union[None, HueGroup] = None # create a panel in the frame - self.pnl: scrolled.ScrolledPanel = scrolled.ScrolledPanel(self, -1, style=wx.VSCROLL) + self.pnl: scrolled.ScrolledPanel = scrolled.ScrolledPanel( + self, -1, style=wx.VSCROLL) self.pnl.SetupScrolling() # and create a sizer to manage the layout of child widgets self.sizer: wx.BoxSizer = wx.BoxSizer(wx.VERTICAL) @@ -66,9 +66,12 @@ class Hui(wx.Frame): btn: wx.Button = wx.Button(self.pnl, label=str(bridge)) self.sizer.Add(btn, 0, wx.EXPAND) self.Bind(wx.EVT_BUTTON, - lambda event, mbridge=bridge: self.goto_bridge(mbridge), btn) + lambda event, mbridge=bridge: self.goto_bridge( + mbridge), + btn) else: - label = "{} {} ({})".format(self.m_unreachable_icon, str(bridge), "unreachable") + label = "{} {} ({})".format(self.m_unreachable_icon, + str(bridge), "unreachable") btn: wx.Button = wx.Button(self.pnl, label=label) self.sizer.Add(btn, 0, wx.EXPAND) @@ -76,8 +79,8 @@ class Hui(wx.Frame): label = "Discover bridge" btn: wx.Button = wx.Button(self.pnl, label=label) self.sizer.Add(btn, 0, wx.EXPAND) - self.Bind(wx.EVT_BUTTON, - lambda event: self.discover_new_bridges(), btn) + self.Bind(wx.EVT_BUTTON, lambda event: self.discover_new_bridges(), + btn) @redraw def add_groups(self, groups: list[HueGroup]): @@ -91,10 +94,11 @@ class Hui(wx.Frame): has_unattached: bool = len(self.cur_bridge.unattached_lights) > 0 self.sizer.Add(bridge_btn, 0, wx.EXPAND) - self.Bind(wx.EVT_BUTTON, - lambda event: self.add_bridges(), bridge_btn) + self.Bind(wx.EVT_BUTTON, lambda event: self.add_bridges(), bridge_btn) if has_unattached: - group_label: wx.StaticText = wx.StaticText(self.pnl, label=" ⚯ Groups ⚯ ", style=wx.ALIGN_CENTER) + group_label: wx.StaticText = wx.StaticText(self.pnl, + label=" ⚯ Groups ⚯ ", + style=wx.ALIGN_CENTER) self.sizer.Add(group_label, 0, wx.EXPAND) for group in groups: inner_sizer: wx.BoxSizer = wx.BoxSizer(orient=wx.HORIZONTAL) @@ -104,17 +108,25 @@ class Hui(wx.Frame): icon = self.m_on_icon toggle_btn: wx.Button = wx.Button(self.pnl, label=icon) inner_sizer.Add(toggle_btn, 1, wx.EXPAND) - self.Bind(wx.EVT_BUTTON, - lambda event, mgroupid=groupid: self.toggle_group(mgroupid), toggle_btn) + self.Bind( + wx.EVT_BUTTON, + lambda event, mgroupid=groupid: self.toggle_group(mgroupid), + toggle_btn) label: str = "{}".format(str(group)) - group_btn: wx.Button = wx.Button(self.pnl, label=label, style=wx.BU_LEFT) + group_btn: wx.Button = wx.Button(self.pnl, + label=label, + style=wx.BU_LEFT) inner_sizer.Add(group_btn, 4, wx.EXPAND) - self.Bind(wx.EVT_BUTTON, - lambda event, mgroupid=groupid: self.goto_group(mgroupid), group_btn) + self.Bind( + wx.EVT_BUTTON, + lambda event, mgroupid=groupid: self.goto_group(mgroupid), + group_btn) self.sizer.Add(inner_sizer, 0, wx.EXPAND) if has_unattached: - unattached_label: wx.StaticText = wx.StaticText(self.pnl, label=" ⚬ Unattached lights ⚬ ", - style=wx.ALIGN_CENTER) + unattached_label: wx.StaticText = wx.StaticText( + self.pnl, + label=" ⚬ Unattached lights ⚬ ", + style=wx.ALIGN_CENTER) self.sizer.Add(unattached_label, 0, wx.EXPAND) for light in self.cur_bridge.unattached_lights: inner_sizer = wx.BoxSizer(orient=wx.HORIZONTAL) @@ -127,21 +139,29 @@ class Hui(wx.Frame): toggle_btn: wx.Button = wx.Button(self.pnl, label=icon) inner_sizer.Add(toggle_btn, 1, wx.EXPAND) self.Bind(wx.EVT_BUTTON, - lambda event, mlightid=lightid: self.toggle_light_and_goto_group(mlightid, lights), + lambda event, mlightid=lightid: self. + toggle_light_and_goto_group(mlightid, lights), toggle_btn) label: str = "{}".format(light) - light_btn: wx.Button = wx.Button(self.pnl, label=label, style=wx.BU_LEFT) + light_btn: wx.Button = wx.Button(self.pnl, + label=label, + style=wx.BU_LEFT) inner_sizer.Add(light_btn, 4, wx.EXPAND) self.Bind(wx.EVT_BUTTON, - lambda event, mlightid=lightid: self.add_single_light(mlightid, True), light_btn) + lambda event, mlightid=lightid: self. + add_single_light(mlightid, True), + light_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") + 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): @@ -150,12 +170,14 @@ class Hui(wx.Frame): 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)) + 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: wx.GenericMessageDialog = wx.GenericMessageDialog( + self.pnl, label, caption="Try again!") failure_msg.ShowModal() return found_any @@ -170,7 +192,8 @@ class Hui(wx.Frame): group_btn: wx.Button = wx.Button(self.pnl, label=str(self.cur_bridge)) self.sizer.Add(group_btn, 0, wx.EXPAND) self.Bind(wx.EVT_BUTTON, - lambda event: self.add_groups(self.cur_bridge.get_groups()), group_btn) + lambda event: self.add_groups(self.cur_bridge.get_groups()), + group_btn) for light in lights: inner_sizer = wx.BoxSizer(orient=wx.HORIZONTAL) lightid: int = light.get_id() @@ -182,13 +205,18 @@ class Hui(wx.Frame): toggle_btn: wx.Button = wx.Button(self.pnl, label=icon) inner_sizer.Add(toggle_btn, 1, wx.EXPAND) self.Bind(wx.EVT_BUTTON, - lambda event, mlightid=lightid: self.toggle_light_and_goto_group(mlightid, lights), + lambda event, mlightid=lightid: self. + toggle_light_and_goto_group(mlightid, lights), toggle_btn) label: str = "{}".format(light) - light_btn: wx.Button = wx.Button(self.pnl, label=label, style=wx.BU_LEFT) + light_btn: wx.Button = wx.Button(self.pnl, + label=label, + style=wx.BU_LEFT) inner_sizer.Add(light_btn, 4, wx.EXPAND) self.Bind(wx.EVT_BUTTON, - lambda event, mlightid=lightid: self.add_single_light(mlightid), light_btn) + lambda event, mlightid=lightid: self.add_single_light( + mlightid), + light_btn) self.sizer.Add(inner_sizer, 0, wx.EXPAND) @redraw @@ -203,16 +231,20 @@ class Hui(wx.Frame): self.SetTitle("Tinge - {}".format(light)) is_on: bool = light.is_on() if unattached: - group_btn: wx.Button = wx.Button(self.pnl, label=str(self.cur_bridge)) + group_btn: wx.Button = wx.Button(self.pnl, + label=str(self.cur_bridge)) self.sizer.Add(group_btn, 0, wx.EXPAND) - self.Bind(wx.EVT_BUTTON, - lambda event: self.add_groups(self.cur_bridge.get_groups()), group_btn) + self.Bind( + wx.EVT_BUTTON, + lambda event: self.add_groups(self.cur_bridge.get_groups()), + group_btn) else: group: HueGroup = self.cur_group group_btn: wx.Button = wx.Button(self.pnl, label=str(group)) self.sizer.Add(group_btn, 0, wx.EXPAND) self.Bind(wx.EVT_BUTTON, - lambda event: self.goto_group(self.cur_group.get_id()), group_btn) + lambda event: self.goto_group(self.cur_group.get_id()), + group_btn) # Toggle icon: str = self.m_off_icon if is_on: @@ -222,54 +254,79 @@ class Hui(wx.Frame): toggle_btn: wx.Button = wx.Button(self.pnl, label=icon) self.sizer.Add(toggle_btn, 0, wx.EXPAND) self.Bind(wx.EVT_BUTTON, - lambda event, mlightid=lightid: self.toggle_light_and_goto_light(mlightid), + lambda event, mlightid=lightid: self. + toggle_light_and_goto_light(mlightid), toggle_btn) # Slider for brightness if is_on: if light.can_set_brightness(): - b_label: wx.StaticText = wx.StaticText(self.pnl, label="Brightness") + b_label: wx.StaticText = wx.StaticText(self.pnl, + label="Brightness") self.sizer.Add(b_label, 0, wx.EXPAND) - b_slider: wx.Slider = wx.Slider(self.pnl, value=light.get_state().get_brightness(), minValue=1, - maxValue=254) + b_slider: wx.Slider = wx.Slider( + self.pnl, + value=light.get_state().get_brightness(), + minValue=1, + maxValue=254) self.sizer.Add(b_slider, 0, wx.EXPAND) - self.Bind(wx.EVT_SCROLL, - lambda event: self.set_brightness(event, light.get_id()), b_slider) + self.Bind( + wx.EVT_SCROLL, + lambda event: self.set_brightness(event, light.get_id()), + b_slider) # Slider for colortemp if light.can_set_ct(): - c_label: wx.StaticText = wx.StaticText(self.pnl, label="Color Temperature") + c_label: wx.StaticText = wx.StaticText( + self.pnl, label="Color Temperature") self.sizer.Add(c_label, 0, wx.EXPAND) - c_slider: wx.Slider = wx.Slider(self.pnl, value=light.get_ct(), minValue=153, maxValue=500) + c_slider: wx.Slider = wx.Slider(self.pnl, + value=light.get_ct(), + minValue=153, + maxValue=500) self.sizer.Add(c_slider, 0, wx.EXPAND) - self.Bind(wx.EVT_SCROLL, - lambda event: self.set_colortemp(event, light.get_id()), c_slider) + self.Bind( + wx.EVT_SCROLL, + lambda event: self.set_colortemp(event, light.get_id()), + c_slider) # Slider for hue if light.can_set_hue(): d_label: wx.StaticText = wx.StaticText(self.pnl, label="Hue") self.sizer.Add(d_label, 0, wx.EXPAND) - d_slider: wx.Slider = wx.Slider(self.pnl, value=light.get_hue(), minValue=0, maxValue=65535) + d_slider: wx.Slider = wx.Slider(self.pnl, + value=light.get_hue(), + minValue=0, + maxValue=65535) self.sizer.Add(d_slider, 0, wx.EXPAND) self.Bind(wx.EVT_SCROLL, - lambda event: self.set_hue(event, light.get_id()), d_slider) + lambda event: self.set_hue(event, light.get_id()), + d_slider) # Slider for saturation if light.can_set_sat(): - e_label: wx.StaticText = wx.StaticText(self.pnl, label="Saturation") + e_label: wx.StaticText = wx.StaticText(self.pnl, + label="Saturation") self.sizer.Add(e_label, 0, wx.EXPAND) - e_slider: wx.Slider = wx.Slider(self.pnl, value=light.get_sat(), minValue=0, maxValue=254) + e_slider: wx.Slider = wx.Slider(self.pnl, + value=light.get_sat(), + minValue=0, + maxValue=254) self.sizer.Add(e_slider, 0, wx.EXPAND) - self.Bind(wx.EVT_SCROLL, - lambda event: self.set_saturation(event, light.get_id()), e_slider) + self.Bind( + wx.EVT_SCROLL, + lambda event: self.set_saturation(event, light.get_id()), + e_slider) rename_btn: wx.Button = wx.Button(self.pnl, label="Rename") self.sizer.Add(rename_btn, 0, wx.EXPAND) self.Bind(wx.EVT_BUTTON, - lambda event, mlightid=lightid: self.rename_light_and_goto_light(mlightid, unattached), + lambda event, mlightid=lightid: self. + rename_light_and_goto_light(mlightid, unattached), rename_btn) delete_btn: wx.Button = wx.Button(self.pnl, label="Delete") self.sizer.Add(delete_btn, 0, wx.EXPAND) self.Bind(wx.EVT_BUTTON, - lambda event, mlightid=lightid: self.delete_light_and_goto_group(mlightid), + lambda event, mlightid=lightid: self. + delete_light_and_goto_group(mlightid), delete_btn) def delete_light_and_goto_group(self, lightid): @@ -278,7 +335,8 @@ class Hui(wx.Frame): Args: lightid (int): The light id of the light to delete """ - if self.get_ok_cancel_answer_from_modal("Are you sure you want to delete this light?"): + if self.get_ok_cancel_answer_from_modal( + "Are you sure you want to delete this light?"): light: HueLight = self.cur_bridge.get_light_by_id(lightid) light.delete() self.cur_bridge.remove_light(light) @@ -299,7 +357,9 @@ class Hui(wx.Frame): user_or_error = HueUtils.connect(bridge['ipaddress']) while user_or_error.is_error(): user_or_error = HueUtils.connect(bridge['ipaddress']) - self.m_tinge.append_bridge(HueBridge(bridge['ipaddress'], user_or_error.get_user(), bridge['name'])) + self.m_tinge.append_bridge( + HueBridge(bridge['ipaddress'], user_or_error.get_user(), + bridge['name'])) found_any = True self.m_tinge.write_all_bridges_to_conf() self.add_bridges() @@ -317,10 +377,16 @@ class Hui(wx.Frame): Returns: bool: The response from the user """ - with wx.MessageDialog(self.pnl, message, style=wx.OK | wx.CANCEL | wx.CANCEL_DEFAULT) as dlg: + with wx.MessageDialog(self.pnl, + message, + style=wx.OK | wx.CANCEL + | wx.CANCEL_DEFAULT) as dlg: return dlg.ShowModal() == wx.ID_OK - def get_text_answer_from_modal(self, message: str, cap: str, val: str = "") -> str: + def get_text_answer_from_modal(self, + message: str, + cap: str, + val: str = "") -> str: """Display a text entry and return the content Args: @@ -331,7 +397,8 @@ class Hui(wx.Frame): Returns: str: The response from the user """ - with wx.TextEntryDialog(self.pnl, message, caption=cap, value=val) as dlg: + with wx.TextEntryDialog(self.pnl, message, caption=cap, + value=val) as dlg: dlg.ShowModal() answer: str = dlg.GetValue() return answer @@ -365,7 +432,8 @@ class Hui(wx.Frame): Args: lightid ([type]): The light id of the light to rename/display """ - newname: str = self.get_text_answer_from_modal("Set new name", "New name:") + newname: str = self.get_text_answer_from_modal("Set new name", + "New name:") if newname: self.cur_bridge.get_light_by_id(lightid).rename(newname) self.add_single_light(lightid, unattached) @@ -432,7 +500,8 @@ class Hui(wx.Frame): self.cur_bridge.get_light_by_id(lightid).toggle() self.add_single_light(lightid) - def toggle_light_and_goto_group(self, lightid: int, lights: list[HueLight]): + def toggle_light_and_goto_group(self, lightid: int, + lights: list[HueLight]): """Combo call back for toggle and goto group Args: diff --git a/src/tinge/HueBridge/__init__.py b/src/tinge/HueBridge/__init__.py index 888900a..153d761 100644 --- a/src/tinge/HueBridge/__init__.py +++ b/src/tinge/HueBridge/__init__.py @@ -12,13 +12,15 @@ class HueBridge: """This class represents a Hue Bridge """ - def __init__(self, ipaddress: str, username: str, name: str = "", is_reachable: bool = True): + def __init__(self, ipaddress: str, username: str, + name: str = "", is_reachable: bool = True): """ Constructor - + Args: ipaddress (str): The ip address of the bridge username (str): The username for this app for this bridge - name (str, optional): A human readable name for this bridge. Is set to ipaddress, if not supplied. + name (str, optional): A human readable name for this bridge. + Is set to ipaddress, if not supplied. """ self.m_ipaddress: str = ipaddress self.m_username: str = username @@ -58,8 +60,8 @@ class HueBridge: return self.m_name def append_new_lights(self) -> bool: - """If any new lights were discovered in discover_new_lights(), they can be appended to this bridges - list of lights with this function + """If any new lights were discovered in discover_new_lights(), + they can be appended to this bridges list of lights with this function Returns: bool: True if the request was ok, otherwise False @@ -70,36 +72,44 @@ class HueBridge: if key != 'lastscan': path: str = "{}/lights/{}".format(self.m_username, key) response = make_request(self.m_ipaddress, path) - self.m_lights.append(HueLight(int(key), response.json(), self.get_ipaddress(), self.get_user())) + self.m_lights.append(HueLight(int(key), response.json(), + self.get_ipaddress(), self.get_user())) return response.ok - def create_group(self, lights: list[HueLight], name: str, group_type: str = "LightGroup", + def create_group(self, lights: list[HueLight], name: str, + group_type: str = "LightGroup", group_class: str = "Other") -> bool: """Create a group from a list of lights Args: lights (list[HueLight]): a list of lights to group name (str): The name of the new group - group_type (str, optional): The group type can be “LightGroup”, “Room” or either “Luminaire” or - “LightSource” if a Multisource Luminaire is present in the system. - Defaults to "LightGroup". - group_class (str, optional): Category of Room Types. Defaults to "Other". + group_type (str, optional): The group type can be “LightGroup”, + “Room” or either “Luminaire” or + “LightSource” if a Multisource + Luminaire is present in the system. + Defaults to "LightGroup". + group_class (str, optional): Category of Room Types. + Defaults to "Other". Returns: bool: True if creation was a success, otherwise False """ path = "{}/groups".format(self.get_user()) method = "POST" - data: dict = {'lights': [], 'name': name, 'type': group_type, 'class': group_class} + data: dict = {'lights': [], 'name': name, + 'type': group_type, 'class': group_class} for light in lights: data['lights'].append(str(light.get_id())) - response = make_request(self.get_ipaddress(), path, method, json.dumps(data)) + response = make_request(self.get_ipaddress(), + path, method, json.dumps(data)) r_json = response.json() if 'success' in r_json.keys(): new_id = r_json['success']['id'] new_path = "{}/groups/{}".format(self.get_user(), new_id) new_group = make_request(self.get_ipaddress(), new_path).json() - self.m_groups.append(HueGroup(int(new_id), lights, new_group, self.get_ipaddress(), self.get_user())) + self.m_groups.append(HueGroup(int(new_id), lights, new_group, + self.get_ipaddress(), self.get_user())) return True else: return False @@ -117,7 +127,8 @@ class HueBridge: lights: list[HueLight] = list() for light in value['lights']: lights.append(self.get_light_by_id(int(light))) - groups.append(HueGroup(int(key), lights, value, self.get_ipaddress(), self.get_user())) + groups.append(HueGroup(int(key), lights, value, + self.get_ipaddress(), self.get_user())) return groups def discover_lights(self) -> list[HueLight]: @@ -130,7 +141,8 @@ class HueBridge: response = make_request(self.m_ipaddress, path) lights: list[HueLight] = list() for key, value in json.loads(response.text).items(): - lights.append(HueLight(int(key), value, self.get_ipaddress(), self.get_user())) + lights.append(HueLight(int(key), + value, self.get_ipaddress(), self.get_user())) return lights def discover_new_lights(self, light_ids: Union[None, list[int]] = None) -> bool: @@ -211,7 +223,8 @@ class HueBridge: if lightid != "lastscan": print(lightid) if not self.get_light_by_id(int(lightid)): - lightpath: str = "{}/lights/{}".format(self.m_username, int(lightid)) + lightpath: str = "{}/lights/{}".format( + self.m_username, int(lightid)) lightresponse = make_request(self.m_ipaddress, lightpath) newlights.append( HueLight(int(lightid), lightresponse.json(), self.get_ipaddress(), self.get_user())) diff --git a/src/tinge/HueGroup/__init__.py b/src/tinge/HueGroup/__init__.py index 6c3457d..bdab490 100644 --- a/src/tinge/HueGroup/__init__.py +++ b/src/tinge/HueGroup/__init__.py @@ -19,7 +19,8 @@ class HueGroup: """Constructor Args: - data_slice (dict): The part of the data structure that concerns this Action + data_slice (dict): The part of the data structure that + concerns this Action """ keys = data_slice.keys() self.m_on: bool = data_slice['on'] @@ -52,14 +53,15 @@ class HueGroup: class State: """A hueGroup.State represents the collective state of the group - + """ def __init__(self, data_slice: dict): """Constructor Args: - data_slice (dict): The part of the data structure that concerns this State + data_slice (dict): The part of the data structure + that concerns this State """ self.m_all_on: bool = data_slice['all_on'] self.m_any_on: bool = data_slice['any_on'] @@ -199,7 +201,8 @@ class HueGroup: Returns: requests.Response: The API response """ - path: str = "{}/groups/{}/action".format(self.m_parent_bridge_user, self.m_id) + path: str = "{}/groups/{}/action".format( + self.m_parent_bridge_user, self.m_id) method: str = "PUT" response = make_request(self.m_parent_bridge_ip, path, method, state) self.update_state()