diff --git a/main.py b/main.py index cdb5127..4f5748a 100644 --- a/main.py +++ b/main.py @@ -15,6 +15,23 @@ class Hui(wx.Frame): wx (Frame): Parent class """ + def redraw(*args): + """Decorator used for redrawing the widgets in the sizer + + Returns: + function: The decorated function + """ + func = args[0] + + def wrapper(self, *wrapper_args): + """The wrapper function for the decorator + """ + self.sizer.Clear(delete_windows=True) + func(self, *wrapper_args) + self.sizer.Layout() + + return wrapper + def __init__(self, *args, **kw): """Constructor """ @@ -33,23 +50,6 @@ class Hui(wx.Frame): self.pnl.SetSizer(self.sizer) self.add_bridges() - def redraw(*args): - """Decorator used for redrawing the widgets in the sizer - - Returns: - function: The decorated function - """ - func = args[0] - - def wrapper(self, *wrapper_args): - """The wrapper function for the decorator - """ - self.sizer.Clear(delete_windows=True) - func(self, *wrapper_args) - self.sizer.Layout() - - return wrapper - @redraw def add_bridges(self): """Add bridges to sizer, the entry point of the program @@ -123,59 +123,11 @@ class Hui(wx.Frame): 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.goto_light(mlightid), light_btn) + lambda event, mlightid=lightid: self.add_single_light(mlightid), light_btn) self.sizer.Add(inner_sizer, 0, wx.EXPAND) - def goto_bridge(self, bridge: HueBridge): - """Call back for a bridge button - - Args: - bridge (HueBridge): The bridge to display - """ - self.cur_bridge = bridge - groups: list[HueGroup] = bridge.get_groups() - if groups: - self.add_groups(groups) - else: - self.add_lights(bridge.get_lights()) - - def discover_new_bridges(self): - """Call back for button that is displayed if no bridges were found - """ - self.m_tinge.discover_new_bridges() - self.add_bridges() - - def toggle_group(self, groupid: int): - """Toggle the lights of a group - - Args: - groupid (int): The group id of the group to toggle - """ - self.cur_bridge.get_group_by_id(groupid).toggle() - self.add_groups(self.cur_bridge.get_groups()) - - def goto_group(self, groupid: int): - """Call back for group button - - Args: - groupid (int): The group id of the group to display - """ - group = self.cur_bridge.get_group_by_id(groupid) - self.cur_group = group - self.add_lights(group.get_lights()) - - def toggle_light_and_goto_group(self, lightid: int, lights: list[HueLight]): - """Combo call back for toggle and goto group - - Args: - lightid (int): The light id oof the light to toggle - lights (list[HueLight]): The lights to display after toggle - """ - self.cur_bridge.get_light_by_id(lightid).toggle() - self.add_lights(lights) - @redraw - def goto_light(self, lightid: int): + def add_single_light(self, lightid: int): """Call back for light button Args: @@ -201,7 +153,7 @@ class Hui(wx.Frame): # Slider for brightness if is_on: - if light.get_brightness() > 0: + if light.can_set_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, @@ -236,6 +188,35 @@ class Hui(wx.Frame): self.Bind(wx.EVT_SCROLL, lambda event: self.set_saturation(event, light.get_id()), e_slider) + def discover_new_bridges(self): + """Call back for button that is displayed if no bridges were found + """ + self.m_tinge.discover_new_bridges() + self.add_bridges() + + def goto_bridge(self, bridge: HueBridge): + """Call back for a bridge button + + Args: + bridge (HueBridge): The bridge to display + """ + self.cur_bridge = bridge + groups: list[HueGroup] = bridge.get_groups() + if groups: + self.add_groups(groups) + else: + self.add_lights(bridge.get_lights()) + + def goto_group(self, groupid: int): + """Call back for group button + + Args: + groupid (int): The group id of the group to display + """ + group = self.cur_bridge.get_group_by_id(groupid) + self.cur_group = group + self.add_lights(group.get_lights()) + def set_brightness(self, event: wx.ScrollEvent, lightid: int): """Call back for brightness slider @@ -247,15 +228,6 @@ class Hui(wx.Frame): light: HueLight = self.cur_bridge.get_light_by_id(lightid) light.set_brightness(bri) - def toggle_light_and_goto_light(self, lightid): - """Combo call back to toggle a light and display that light again - - Args: - lightid ([type]): The light id of the light to toggle/display - """ - self.cur_bridge.get_light_by_id(lightid).toggle() - self.goto_light(lightid) - def set_colortemp(self, event, lightid): """Call back for colortemp slider @@ -268,7 +240,7 @@ class Hui(wx.Frame): light.set_ct(ct) def set_hue(self, event, lightid): - """Call back for colortemp slider + """Call back for hue slider Args: event (wx.ScrollEvent): The scroll event to react to @@ -279,15 +251,43 @@ class Hui(wx.Frame): light.set_hue(hue) def set_saturation(self, event, lightid): - """Call back for colortemp slider + """Call back for saturation slider Args: event (wx.ScrollEvent): The scroll event to react to - lightid (int): The light id of the light to adjust hue of + lightid (int): The light id of the light to adjust saturation of """ sat: int = event.GetPosition() light: HueLight = self.cur_bridge.get_light_by_id(lightid) - light.set_hue(sat) + light.set_sat(sat) + + def toggle_group(self, groupid: int): + """Toggle the lights of a group + + Args: + groupid (int): The group id of the group to toggle + """ + self.cur_bridge.get_group_by_id(groupid).toggle() + self.add_groups(self.cur_bridge.get_groups()) + + def toggle_light_and_goto_light(self, lightid): + """Combo call back to toggle a light and display that light again + + Args: + lightid ([type]): The light id of the light to toggle/display + """ + 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]): + """Combo call back for toggle and goto group + + Args: + lightid (int): The light id oof the light to toggle + lights (list[HueLight]): The lights to display after toggle + """ + self.cur_bridge.get_light_by_id(lightid).toggle() + self.add_lights(lights) if __name__ == "__main__": diff --git a/tinge/HueBridge/__init__.py b/tinge/HueBridge/__init__.py index 57e6543..ae5af4a 100644 --- a/tinge/HueBridge/__init__.py +++ b/tinge/HueBridge/__init__.py @@ -37,21 +37,21 @@ class HueBridge: """ return self.m_name - def discover_groups(self) -> list[HueGroup]: - """Get groups from Hue Api + 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 Returns: - list[HueGroup]: discovered groups + bool: True if the request was ok, otherwise False """ - path: str = "{}/groups".format(self.m_username) + path: str = "{}/lights/new".format(self.m_username) response = make_request(self.m_ipaddress, path) - groups: list[HueGroup] = list() for key, value in json.loads(response.text).items(): - 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())) - return groups + 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())) + return response.ok def create_group(self, lights: list[HueLight], name: str, group_type: str = "LightGroup", group_class: str = "Other") -> bool: @@ -84,6 +84,22 @@ class HueBridge: else: return False + def discover_groups(self) -> list[HueGroup]: + """Get groups from Hue Api + + Returns: + list[HueGroup]: discovered groups + """ + path: str = "{}/groups".format(self.m_username) + response = make_request(self.m_ipaddress, path) + groups: list[HueGroup] = list() + for key, value in json.loads(response.text).items(): + 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())) + return groups + def discover_lights(self) -> list[HueLight]: """Get lights from Hue API @@ -117,21 +133,18 @@ class HueBridge: response = make_request(self.m_ipaddress, path, method, body) return 'success' in response.json()[0].keys() - 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 + def get_group_by_id(self, group_id: int) -> HueGroup: + """Get a specific group + + Args: + group_id (int): The group id of the group to get Returns: - bool: True if the request was ok, otherwise False + HueGroup: The group """ - path: str = "{}/lights/new".format(self.m_username) - response = make_request(self.m_ipaddress, path) - for key, value in json.loads(response.text).items(): - 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())) - return response.ok + for group in self.m_groups: + if group.get_id() == group_id: + return group def get_groups(self) -> list[HueGroup]: """Return all the groups of this bridge @@ -141,13 +154,13 @@ class HueBridge: """ return self.m_groups - def get_lights(self) -> list[HueLight]: - """Get all the lights owned by this bridge + def get_ipaddress(self) -> str: + """Get the ip address of this bridge Returns: - list[HueLight]: A flat list of all lights owned by this bridge + str: The ip address """ - return self.m_lights + return self.m_ipaddress def get_light_by_id(self, light_id: int) -> HueLight: """Get a specific light @@ -162,26 +175,13 @@ class HueBridge: if light.get_id() == light_id: return light - def get_group_by_id(self, group_id: int) -> HueGroup: - """Get a specific group - - Args: - group_id (int): The group id of the group to get - - Returns: - HueGroup: The group - """ - for group in self.m_groups: - if group.get_id() == group_id: - return group - - def get_ipaddress(self) -> str: - """Get the ip address of this bridge + def get_lights(self) -> list[HueLight]: + """Get all the lights owned by this bridge Returns: - str: The ip address + list[HueLight]: A flat list of all lights owned by this bridge """ - return self.m_ipaddress + return self.m_lights def get_user(self) -> str: """A user, or username, is more like a password and is needed to authenticate with the Hue API diff --git a/tinge/HueLight/__init__.py b/tinge/HueLight/__init__.py index 1e2cd19..2c54923 100644 --- a/tinge/HueLight/__init__.py +++ b/tinge/HueLight/__init__.py @@ -264,6 +264,15 @@ class HueLight: """ return self.m_name + def can_set_brightness(self): + """Check if we can set brightness for this light + Docs says min brightness is 1, so 0 (suspected default for uncapable light ) or -1 (our default) means not able + + Returns: + bool: True if we can, otherwise False + """ + return self.get_brightness() > 0 + def can_set_ct(self) -> bool: """Check if we can set a color temp for this light @@ -288,13 +297,29 @@ class HueLight: """ return self.get_sat() != -1 - def get_state(self): - """Get the state object for this light + def get_brightness(self) -> int: + """Get currrent brightness Returns: - HueLight.State: The current state of affairs + int: 0-254 """ - return self.m_state + return self.get_state().get_brightness() + + def get_ct(self) -> int: + """Get current colortemp + + Returns: + int: -1,153-500 + """ + return self.get_state().get_ct() + + def get_hue(self) -> int: + """Get current hue + + Returns: + int: -1-65535 + """ + return self.get_state().get_hue() def get_id(self) -> int: """Get the id of this light @@ -304,6 +329,22 @@ class HueLight: """ return self.m_id + def get_sat(self): + """Get current saturation + + Returns: + int: -1-254 + """ + return self.get_state().get_sat() + + def get_state(self): + """Get the state object for this light + + Returns: + HueLight.State: The current state of affairs + """ + return self.m_state + def is_on(self) -> bool: """Is it on though? @@ -321,21 +362,6 @@ class HueLight: """ return self.get_state().is_reachable() - def set_state(self, state: str) -> requests.Response: - """A helper method to set the state of the light - - Args: - state (str): The state of the light - - Returns: - requests.Response: The response from the Hue API - """ - path: str = "{}/lights/{}/state".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() - return response - def set_brightness(self, bri: int): """Set the brightness of the light @@ -375,6 +401,21 @@ class HueLight: state = '{{"sat":{0}}}'.format(sat) self.set_state(state) + def set_state(self, state: str) -> requests.Response: + """A helper method to set the state of the light + + Args: + state (str): The state of the light + + Returns: + requests.Response: The response from the Hue API + """ + path: str = "{}/lights/{}/state".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() + return response + def toggle(self): """Toggle light """ @@ -390,35 +431,3 @@ class HueLight: path: str = "{}/lights/{}".format(self.m_parent_bridge_user, self.m_id) response = make_request(self.m_parent_bridge_ip, path) self.m_state = HueLight.State(json.loads(response.text)['state']) - - def get_brightness(self) -> int: - """Get currrent brightness - - Returns: - int: 0-254 - """ - return self.get_state().get_brightness() - - def get_ct(self) -> int: - """Get current colortemp - - Returns: - int: -1,153-500 - """ - return self.get_state().get_ct() - - def get_hue(self) -> int: - """Get current hue - - Returns: - int: -1-65535 - """ - return self.get_state().get_hue() - - def get_sat(self): - """Get current saturation - - Returns: - int: -1-254 - """ - return self.get_state().get_sat()