diff --git a/README.md b/README.md index 0114321..af7fc53 100644 --- a/README.md +++ b/README.md @@ -21,10 +21,15 @@ cd tinge Requires a recent Python3 and pip3, most likely python >= 3.7. It is only tested with 3.9 on PostmarketOS on the PinePhone and on Debian Bullseye though. ## Screenshots -![Discover Bridge View](https://code.smolnet.org/micke/tinge/raw/branch/master/screenshots/20210506_16h18m26s_grim.png) -![All Bridge View](https://code.smolnet.org/micke/tinge/raw/branch/master/screenshots/20210506_14h48m15s_grim.png) -![All Group View](https://code.smolnet.org/micke/tinge/raw/branch/master/screenshots/20210506_14h48m24s_grim.png) -![Single Group View, with Bed Light Off](https://code.smolnet.org/micke/tinge/raw/branch/master/screenshots/20210506_14h48m30s_grim.png) -![Single Light Off](https://code.smolnet.org/micke/tinge/raw/branch/master/screenshots/20210506_14h48m34s_grim.png) -![Single Light On](https://code.smolnet.org/micke/tinge/raw/branch/master/screenshots/20210506_14h48m37s_grim.png) -![Single Group View, with Bed Light On](https://code.smolnet.org/micke/tinge/raw/branch/master/screenshots/20210506_14h48m48s_grim.png) +![Discover Bridge View](https://code.smolnet.org/micke/tinge/raw/branch/master/screenshots/scrot1.png) +![All Bridge View](https://code.smolnet.org/micke/tinge/raw/branch/master/screenshots/scrot2.png) +![All Group View](https://code.smolnet.org/micke/tinge/raw/branch/master/screenshots/scrot3.png) +![Single Group View, with Some Lights Off, and Roof light unreachable](https://code.smolnet.org/micke/tinge/raw/branch/master/screenshots/scrot4.png) +![Single Light View, with Bed Lamp Off](https://code.smolnet.org/micke/tinge/raw/branch/master/screenshots/scrot5.png) +![Single Light View, with Bed Lamp On](https://code.smolnet.org/micke/tinge/raw/branch/master/screenshots/scrot6.png) +![Single Group View, with Bed Lamp On](https://code.smolnet.org/micke/tinge/raw/branch/master/screenshots/scrot7.png) +![Rename Bed lamp](https://code.smolnet.org/micke/tinge/raw/branch/master/screenshots/scrot8.png) +![Single Light View, with Bed Lamp renamed to Table lamp](https://code.smolnet.org/micke/tinge/raw/branch/master/screenshots/scrot9.png) +![Single Group View, with Bed Lamp renamed to Table lamp](https://code.smolnet.org/micke/tinge/raw/branch/master/screenshots/scrot10.png) +![Single Light View, with Table lamp Delete modal](https://code.smolnet.org/micke/tinge/raw/branch/master/screenshots/scrot11.png) +![Single Group View, with Table lamp Deleted](https://code.smolnet.org/micke/tinge/raw/branch/master/screenshots/scrot12.png) diff --git a/main.py b/main.py index 4f5748a..c9baac9 100644 --- a/main.py +++ b/main.py @@ -38,6 +38,7 @@ class Hui(wx.Frame): super().__init__(*args, **kw) self.m_on_icon: str = '☼' self.m_off_icon: str = '☾' + self.m_unreachable_icon: str = '⚠' self.m_tinge: Tinge = Tinge() self.m_bridges: list[HueBridge] = self.m_tinge.get_bridges() self.cur_bridge: Union[None, HueBridge] = None @@ -114,6 +115,8 @@ class Hui(wx.Frame): icon: str = self.m_off_icon if light.is_on(): icon = self.m_on_icon + elif not light.is_reachable(): + icon = self.m_unreachable_icon toggle_btn: wx.Button = wx.Button(self.pnl, label=icon) inner_sizer.Add(toggle_btn, 1, wx.EXPAND) self.Bind(wx.EVT_BUTTON, @@ -145,6 +148,8 @@ class Hui(wx.Frame): icon: str = self.m_off_icon if is_on: icon = self.m_on_icon + elif not light.is_reachable(): + icon = self.m_unreachable_icon toggle_btn: wx.Button = wx.Button(self.pnl, label=icon) self.sizer.Add(toggle_btn, 0, wx.EXPAND) self.Bind(wx.EVT_BUTTON, @@ -187,6 +192,30 @@ class Hui(wx.Frame): self.sizer.Add(e_slider, 0, wx.EXPAND) 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), + 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), + delete_btn) + + def delete_light_and_goto_group(self, lightid): + """Combo call back for delete and goto group + + 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?"): + light: HueLight = self.cur_bridge.get_light_by_id(lightid) + light.delete() + self.cur_bridge.remove_light(light) + self.add_lights(self.cur_group.get_lights()) + else: + self.add_single_light(lightid) def discover_new_bridges(self): """Call back for button that is displayed if no bridges were found @@ -194,6 +223,34 @@ class Hui(wx.Frame): self.m_tinge.discover_new_bridges() self.add_bridges() + def get_ok_cancel_answer_from_modal(self, message: str) -> bool: + """Display a message dialog and return ok or cancel + + Args: + message (str): The message to display + + Returns: + bool: The response from the user + """ + 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: + """Display a text entry and return the content + + Args: + message (str): The message to display + cap (str): The caption to display + val (str, optional): The default value to display, defaults to the empty string + + Returns: + str: The response from the user + """ + with wx.TextEntryDialog(self.pnl, message, caption=cap, value=val) as dlg: + dlg.ShowModal() + answer: str = dlg.GetValue() + return answer + def goto_bridge(self, bridge: HueBridge): """Call back for a bridge button @@ -217,6 +274,17 @@ class Hui(wx.Frame): self.cur_group = group self.add_lights(group.get_lights()) + def rename_light_and_goto_light(self, lightid): + """Combo call back to rename a light and display that light again + + 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:") + if newname: + self.cur_bridge.get_light_by_id(lightid).rename(newname) + self.add_single_light(lightid) + def set_brightness(self, event: wx.ScrollEvent, lightid: int): """Call back for brightness slider @@ -283,7 +351,7 @@ class Hui(wx.Frame): """Combo call back for toggle and goto group Args: - lightid (int): The light id oof the light to toggle + lightid (int): The light id of the light to toggle lights (list[HueLight]): The lights to display after toggle """ self.cur_bridge.get_light_by_id(lightid).toggle() diff --git a/screenshots/20210506_14h48m24s_grim.png b/screenshots/20210506_14h48m24s_grim.png deleted file mode 100644 index adeae4a..0000000 Binary files a/screenshots/20210506_14h48m24s_grim.png and /dev/null differ diff --git a/screenshots/20210506_14h48m30s_grim.png b/screenshots/20210506_14h48m30s_grim.png deleted file mode 100644 index 176496d..0000000 Binary files a/screenshots/20210506_14h48m30s_grim.png and /dev/null differ diff --git a/screenshots/20210506_14h48m34s_grim.png b/screenshots/20210506_14h48m34s_grim.png deleted file mode 100644 index e2d66c9..0000000 Binary files a/screenshots/20210506_14h48m34s_grim.png and /dev/null differ diff --git a/screenshots/20210506_14h48m37s_grim.png b/screenshots/20210506_14h48m37s_grim.png deleted file mode 100644 index 2d344e4..0000000 Binary files a/screenshots/20210506_14h48m37s_grim.png and /dev/null differ diff --git a/screenshots/20210506_14h48m48s_grim.png b/screenshots/20210506_14h48m48s_grim.png deleted file mode 100644 index c78a3d5..0000000 Binary files a/screenshots/20210506_14h48m48s_grim.png and /dev/null differ diff --git a/screenshots/20210506_16h18m26s_grim.png b/screenshots/scrot1.png similarity index 100% rename from screenshots/20210506_16h18m26s_grim.png rename to screenshots/scrot1.png diff --git a/screenshots/scrot10.png b/screenshots/scrot10.png new file mode 100644 index 0000000..4d3e918 Binary files /dev/null and b/screenshots/scrot10.png differ diff --git a/screenshots/scrot11.png b/screenshots/scrot11.png new file mode 100644 index 0000000..1c298e1 Binary files /dev/null and b/screenshots/scrot11.png differ diff --git a/screenshots/scrot12.png b/screenshots/scrot12.png new file mode 100644 index 0000000..e1d75b7 Binary files /dev/null and b/screenshots/scrot12.png differ diff --git a/screenshots/20210506_14h48m15s_grim.png b/screenshots/scrot2.png similarity index 100% rename from screenshots/20210506_14h48m15s_grim.png rename to screenshots/scrot2.png diff --git a/screenshots/scrot3.png b/screenshots/scrot3.png new file mode 100644 index 0000000..d675530 Binary files /dev/null and b/screenshots/scrot3.png differ diff --git a/screenshots/scrot4.png b/screenshots/scrot4.png new file mode 100644 index 0000000..a2938c1 Binary files /dev/null and b/screenshots/scrot4.png differ diff --git a/screenshots/scrot5.png b/screenshots/scrot5.png new file mode 100644 index 0000000..32b7922 Binary files /dev/null and b/screenshots/scrot5.png differ diff --git a/screenshots/scrot6.png b/screenshots/scrot6.png new file mode 100644 index 0000000..05ca208 Binary files /dev/null and b/screenshots/scrot6.png differ diff --git a/screenshots/scrot7.png b/screenshots/scrot7.png new file mode 100644 index 0000000..f8d1ce2 Binary files /dev/null and b/screenshots/scrot7.png differ diff --git a/screenshots/scrot8.png b/screenshots/scrot8.png new file mode 100644 index 0000000..b608b28 Binary files /dev/null and b/screenshots/scrot8.png differ diff --git a/screenshots/scrot9.png b/screenshots/scrot9.png new file mode 100644 index 0000000..9301db7 Binary files /dev/null and b/screenshots/scrot9.png differ diff --git a/tinge/HueBridge/__init__.py b/tinge/HueBridge/__init__.py index ae5af4a..8ed397c 100644 --- a/tinge/HueBridge/__init__.py +++ b/tinge/HueBridge/__init__.py @@ -190,3 +190,14 @@ class HueBridge: str: The username """ return self.m_username + + def remove_light(self, light: HueLight): + """Remove a light from all groups and self + + Args: + light (HueLight): The light to remove + + """ + for group in self.m_groups: + group.remove_light(light) + self.m_lights.remove(light) diff --git a/tinge/HueGroup/__init__.py b/tinge/HueGroup/__init__.py index e240520..96bce0d 100644 --- a/tinge/HueGroup/__init__.py +++ b/tinge/HueGroup/__init__.py @@ -142,6 +142,12 @@ class HueGroup: on = True return on + def remove_light(self, light: HueLight): + """Remove a light + """ + if light in self.m_lights: + self.m_lights.remove(light) + def toggle(self): """Toggle all lights of the group """ diff --git a/tinge/HueLight/__init__.py b/tinge/HueLight/__init__.py index 2c54923..b89898a 100644 --- a/tinge/HueLight/__init__.py +++ b/tinge/HueLight/__init__.py @@ -297,6 +297,14 @@ class HueLight: """ return self.get_sat() != -1 + def delete(self): + """Delete the light + + """ + method: str = "DELETE" + path = "{}/lights/{}".format(self.m_parent_bridge_user, self.get_id()) + make_request(self.m_parent_bridge_ip, path, method) + def get_brightness(self) -> int: """Get currrent brightness @@ -362,6 +370,18 @@ class HueLight: """ return self.get_state().is_reachable() + def rename(self, name): + """Set the name of the light + + Args: + name (str): The new name for the light + """ + method: str = "PUT" + payload = '{{"name":"{0}"}}'.format(name) + path = "{}/lights/{}".format(self.m_parent_bridge_user, self.get_id()) + make_request(self.m_parent_bridge_ip, path, method, payload) + self.update_state() + def set_brightness(self, bri: int): """Set the brightness of the light @@ -431,3 +451,4 @@ 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']) + self.m_name = json.loads(response.text)['name']