Browse Source

Implement rename and delete

This commit introduces the ability to rename or delete a light.
There is also a special icon for unreachable lights now.
tags/0.0.1
Micke Nordin 5 months ago
parent
commit
7cfe8049fc
Signed by: Micke Nordin <hej@mic.ke> GPG Key ID: 014B273D614BE877

+ 12
- 7
README.md View File

@@ -21,10 +21,15 @@ cd tinge
21 21
 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.
22 22
 
23 23
 ## Screenshots
24
-![Discover Bridge View](https://code.smolnet.org/micke/tinge/raw/branch/master/screenshots/20210506_16h18m26s_grim.png)
25
-![All Bridge View](https://code.smolnet.org/micke/tinge/raw/branch/master/screenshots/20210506_14h48m15s_grim.png)
26
-![All Group View](https://code.smolnet.org/micke/tinge/raw/branch/master/screenshots/20210506_14h48m24s_grim.png)
27
-![Single Group View, with Bed Light Off](https://code.smolnet.org/micke/tinge/raw/branch/master/screenshots/20210506_14h48m30s_grim.png)
28
-![Single Light Off](https://code.smolnet.org/micke/tinge/raw/branch/master/screenshots/20210506_14h48m34s_grim.png)
29
-![Single Light On](https://code.smolnet.org/micke/tinge/raw/branch/master/screenshots/20210506_14h48m37s_grim.png)
30
-![Single Group View, with Bed Light On](https://code.smolnet.org/micke/tinge/raw/branch/master/screenshots/20210506_14h48m48s_grim.png)
24
+![Discover Bridge View](https://code.smolnet.org/micke/tinge/raw/branch/master/screenshots/scrot1.png)
25
+![All Bridge View](https://code.smolnet.org/micke/tinge/raw/branch/master/screenshots/scrot2.png)
26
+![All Group View](https://code.smolnet.org/micke/tinge/raw/branch/master/screenshots/scrot3.png)
27
+![Single Group View, with Some Lights Off, and Roof light unreachable](https://code.smolnet.org/micke/tinge/raw/branch/master/screenshots/scrot4.png)
28
+![Single Light View, with Bed Lamp Off](https://code.smolnet.org/micke/tinge/raw/branch/master/screenshots/scrot5.png)
29
+![Single Light View, with Bed Lamp On](https://code.smolnet.org/micke/tinge/raw/branch/master/screenshots/scrot6.png)
30
+![Single Group View, with Bed Lamp On](https://code.smolnet.org/micke/tinge/raw/branch/master/screenshots/scrot7.png)
31
+![Rename Bed lamp](https://code.smolnet.org/micke/tinge/raw/branch/master/screenshots/scrot8.png)
32
+![Single Light View, with Bed Lamp renamed to Table lamp](https://code.smolnet.org/micke/tinge/raw/branch/master/screenshots/scrot9.png)
33
+![Single Group View, with Bed Lamp renamed to Table lamp](https://code.smolnet.org/micke/tinge/raw/branch/master/screenshots/scrot10.png)
34
+![Single Light View, with Table lamp Delete modal](https://code.smolnet.org/micke/tinge/raw/branch/master/screenshots/scrot11.png)
35
+![Single Group View, with Table lamp Deleted](https://code.smolnet.org/micke/tinge/raw/branch/master/screenshots/scrot12.png)

+ 69
- 1
main.py View File

@@ -38,6 +38,7 @@ class Hui(wx.Frame):
38 38
         super().__init__(*args, **kw)
39 39
         self.m_on_icon: str = '☼'
40 40
         self.m_off_icon: str = '☾'
41
+        self.m_unreachable_icon: str = '⚠'
41 42
         self.m_tinge: Tinge = Tinge()
42 43
         self.m_bridges: list[HueBridge] = self.m_tinge.get_bridges()
43 44
         self.cur_bridge: Union[None, HueBridge] = None
@@ -114,6 +115,8 @@ class Hui(wx.Frame):
114 115
             icon: str = self.m_off_icon
115 116
             if light.is_on():
116 117
                 icon = self.m_on_icon
118
+            elif not light.is_reachable():
119
+                icon = self.m_unreachable_icon
117 120
             toggle_btn: wx.Button = wx.Button(self.pnl, label=icon)
118 121
             inner_sizer.Add(toggle_btn, 1, wx.EXPAND)
119 122
             self.Bind(wx.EVT_BUTTON,
@@ -145,6 +148,8 @@ class Hui(wx.Frame):
145 148
         icon: str = self.m_off_icon
146 149
         if is_on:
147 150
             icon = self.m_on_icon
151
+        elif not light.is_reachable():
152
+            icon = self.m_unreachable_icon
148 153
         toggle_btn: wx.Button = wx.Button(self.pnl, label=icon)
149 154
         self.sizer.Add(toggle_btn, 0, wx.EXPAND)
150 155
         self.Bind(wx.EVT_BUTTON,
@@ -187,6 +192,30 @@ class Hui(wx.Frame):
187 192
                 self.sizer.Add(e_slider, 0, wx.EXPAND)
188 193
                 self.Bind(wx.EVT_SCROLL,
189 194
                           lambda event: self.set_saturation(event, light.get_id()), e_slider)
195
+        rename_btn: wx.Button = wx.Button(self.pnl, label="Rename")
196
+        self.sizer.Add(rename_btn, 0, wx.EXPAND)
197
+        self.Bind(wx.EVT_BUTTON,
198
+                  lambda event, mlightid=lightid: self.rename_light_and_goto_light(mlightid),
199
+                  rename_btn)
200
+        delete_btn: wx.Button = wx.Button(self.pnl, label="Delete")
201
+        self.sizer.Add(delete_btn, 0, wx.EXPAND)
202
+        self.Bind(wx.EVT_BUTTON,
203
+                  lambda event, mlightid=lightid: self.delete_light_and_goto_group(mlightid),
204
+                  delete_btn)
205
+
206
+    def delete_light_and_goto_group(self, lightid):
207
+        """Combo call back for delete and goto group
208
+
209
+        Args:
210
+            lightid (int): The light id of the light to delete
211
+        """
212
+        if self.get_ok_cancel_answer_from_modal("Are you sure you want to delete this light?"):
213
+            light: HueLight = self.cur_bridge.get_light_by_id(lightid)
214
+            light.delete()
215
+            self.cur_bridge.remove_light(light)
216
+            self.add_lights(self.cur_group.get_lights())
217
+        else:
218
+            self.add_single_light(lightid)
190 219
 
191 220
     def discover_new_bridges(self):
192 221
         """Call back for button that is displayed if no bridges were found
@@ -194,6 +223,34 @@ class Hui(wx.Frame):
194 223
         self.m_tinge.discover_new_bridges()
195 224
         self.add_bridges()
196 225
 
226
+    def get_ok_cancel_answer_from_modal(self, message: str) -> bool:
227
+        """Display a message dialog and return ok or cancel
228
+
229
+        Args:
230
+            message (str): The message to display
231
+
232
+        Returns:
233
+            bool: The response from the user
234
+        """
235
+        with wx.MessageDialog(self.pnl, message, style=wx.OK | wx.CANCEL | wx.CANCEL_DEFAULT) as dlg:
236
+            return dlg.ShowModal() == wx.ID_OK
237
+
238
+    def get_text_answer_from_modal(self, message: str, cap: str, val: str = "") -> str:
239
+        """Display a text entry and return the content
240
+
241
+        Args:
242
+            message (str): The message to display
243
+            cap (str): The caption to display
244
+            val (str, optional): The default value to display, defaults to the empty string
245
+
246
+        Returns:
247
+            str: The response from the user
248
+        """
249
+        with wx.TextEntryDialog(self.pnl, message, caption=cap, value=val) as dlg:
250
+            dlg.ShowModal()
251
+            answer: str = dlg.GetValue()
252
+            return answer
253
+
197 254
     def goto_bridge(self, bridge: HueBridge):
198 255
         """Call back for a bridge button
199 256
 
@@ -217,6 +274,17 @@ class Hui(wx.Frame):
217 274
         self.cur_group = group
218 275
         self.add_lights(group.get_lights())
219 276
 
277
+    def rename_light_and_goto_light(self, lightid):
278
+        """Combo call back to rename a light and display that light again
279
+
280
+        Args:
281
+            lightid ([type]): The light id of the light to rename/display
282
+        """
283
+        newname: str = self.get_text_answer_from_modal("Set new name", "New name:")
284
+        if newname:
285
+            self.cur_bridge.get_light_by_id(lightid).rename(newname)
286
+        self.add_single_light(lightid)
287
+
220 288
     def set_brightness(self, event: wx.ScrollEvent, lightid: int):
221 289
         """Call back for brightness slider
222 290
 
@@ -283,7 +351,7 @@ class Hui(wx.Frame):
283 351
         """Combo call back for toggle and goto group
284 352
 
285 353
         Args:
286
-            lightid (int): The light id oof the light to toggle
354
+            lightid (int): The light id of the light to toggle
287 355
             lights (list[HueLight]): The lights to display after toggle
288 356
         """
289 357
         self.cur_bridge.get_light_by_id(lightid).toggle()

BIN
screenshots/20210506_14h48m24s_grim.png View File


BIN
screenshots/20210506_14h48m30s_grim.png View File


BIN
screenshots/20210506_14h48m34s_grim.png View File


BIN
screenshots/20210506_14h48m37s_grim.png View File


BIN
screenshots/20210506_14h48m48s_grim.png View File


screenshots/20210506_16h18m26s_grim.png → screenshots/scrot1.png View File


BIN
screenshots/scrot10.png View File


BIN
screenshots/scrot11.png View File


BIN
screenshots/scrot12.png View File


screenshots/20210506_14h48m15s_grim.png → screenshots/scrot2.png View File


BIN
screenshots/scrot3.png View File


BIN
screenshots/scrot4.png View File


BIN
screenshots/scrot5.png View File


BIN
screenshots/scrot6.png View File


BIN
screenshots/scrot7.png View File


BIN
screenshots/scrot8.png View File


BIN
screenshots/scrot9.png View File


+ 11
- 0
tinge/HueBridge/__init__.py View File

@@ -190,3 +190,14 @@ class HueBridge:
190 190
             str: The username
191 191
         """
192 192
         return self.m_username
193
+
194
+    def remove_light(self, light: HueLight):
195
+        """Remove a light from all groups and self
196
+
197
+        Args:
198
+            light (HueLight): The light to remove
199
+
200
+        """
201
+        for group in self.m_groups:
202
+            group.remove_light(light)
203
+        self.m_lights.remove(light)

+ 6
- 0
tinge/HueGroup/__init__.py View File

@@ -142,6 +142,12 @@ class HueGroup:
142 142
                 on = True
143 143
         return on
144 144
 
145
+    def remove_light(self, light: HueLight):
146
+        """Remove a light
147
+        """
148
+        if light in self.m_lights:
149
+            self.m_lights.remove(light)
150
+
145 151
     def toggle(self):
146 152
         """Toggle all lights of the group
147 153
         """

+ 21
- 0
tinge/HueLight/__init__.py View File

@@ -297,6 +297,14 @@ class HueLight:
297 297
         """
298 298
         return self.get_sat() != -1
299 299
 
300
+    def delete(self):
301
+        """Delete the light
302
+
303
+        """
304
+        method: str = "DELETE"
305
+        path = "{}/lights/{}".format(self.m_parent_bridge_user, self.get_id())
306
+        make_request(self.m_parent_bridge_ip, path, method)
307
+
300 308
     def get_brightness(self) -> int:
301 309
         """Get currrent brightness
302 310
 
@@ -362,6 +370,18 @@ class HueLight:
362 370
         """
363 371
         return self.get_state().is_reachable()
364 372
 
373
+    def rename(self, name):
374
+        """Set the name of the light
375
+
376
+        Args:
377
+            name (str): The new name for the light 
378
+        """
379
+        method: str = "PUT"
380
+        payload = '{{"name":"{0}"}}'.format(name)
381
+        path = "{}/lights/{}".format(self.m_parent_bridge_user, self.get_id())
382
+        make_request(self.m_parent_bridge_ip, path, method, payload)
383
+        self.update_state()
384
+
365 385
     def set_brightness(self, bri: int):
366 386
         """Set the brightness of the light
367 387
 
@@ -431,3 +451,4 @@ class HueLight:
431 451
         path: str = "{}/lights/{}".format(self.m_parent_bridge_user, self.m_id)
432 452
         response = make_request(self.m_parent_bridge_ip, path)
433 453
         self.m_state = HueLight.State(json.loads(response.text)['state'])
454
+        self.m_name = json.loads(response.text)['name']

Loading…
Cancel
Save