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.
19
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
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
|
70
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()
|
||||
|
|
Before Width: | Height: | Size: 77 KiB |
Before Width: | Height: | Size: 79 KiB |
Before Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 79 KiB |
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
BIN
screenshots/scrot10.png
Normal file
After Width: | Height: | Size: 64 KiB |
BIN
screenshots/scrot11.png
Normal file
After Width: | Height: | Size: 60 KiB |
BIN
screenshots/scrot12.png
Normal file
After Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
BIN
screenshots/scrot3.png
Normal file
After Width: | Height: | Size: 73 KiB |
BIN
screenshots/scrot4.png
Normal file
After Width: | Height: | Size: 64 KiB |
BIN
screenshots/scrot5.png
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
screenshots/scrot6.png
Normal file
After Width: | Height: | Size: 46 KiB |
BIN
screenshots/scrot7.png
Normal file
After Width: | Height: | Size: 64 KiB |
BIN
screenshots/scrot8.png
Normal file
After Width: | Height: | Size: 78 KiB |
BIN
screenshots/scrot9.png
Normal file
After Width: | Height: | Size: 46 KiB |
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
"""
|
||||
|
|
|
@ -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']
|
||||
|
|