#!/usr/bin/env python3 # -*- coding: utf-8 -*- from typing import Union import wx import wx.lib.scrolledpanel as scrolled from tinge import Tinge, HueBridge, HueGroup, HueLight class Hui(wx.Frame): """This is the Hue GUI class Args: wx (Frame): Parent class """ def __init__(self, *args, **kw): """Constructor """ super().__init__(*args, **kw) self.m_on_icon: str = '☼' self.m_off_icon: str = '☾' self.m_tinge: Tinge = Tinge() self.m_bridges: list[HueBridge] = self.m_tinge.get_bridges() 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.SetupScrolling() # and create a sizer to manage the layout of child widgets self.sizer: wx.BoxSizer = wx.BoxSizer(wx.VERTICAL) 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 """ self.SetTitle('Tinge - All Bridges') if self.m_bridges: for bridge in self.m_bridges: 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) else: btn: wx.Button = wx.Button(self.pnl, label="Press Hue Bridge button, and then press here") self.sizer.Add(btn, 0, wx.EXPAND) self.Bind(wx.EVT_BUTTON, lambda event: self.discover_new_bridges(), btn) @redraw def add_groups(self, groups: list[HueGroup]): """This will add the groups to the sizer, when coming down from a bridge, or up from a light Args: groups (list[HueGroup]): The groups to display """ self.SetTitle("Tinge - {}".format(self.cur_bridge.m_name)) bridge_btn: wx.Button = wx.Button(self.pnl, label="All Bridges") self.sizer.Add(bridge_btn, 0, wx.EXPAND) self.Bind(wx.EVT_BUTTON, lambda event: self.add_bridges(), bridge_btn) for group in groups: inner_sizer: wx.BoxSizer = wx.BoxSizer(orient=wx.HORIZONTAL) groupid: int = group.get_id() icon: str = self.m_off_icon if group.is_any_on(): 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) label: str = str(group) 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.sizer.Add(inner_sizer, 0, wx.EXPAND) @redraw def add_lights(self, lights: list[HueLight]): """This will add the lights from a group to the sizer Args: lights (list[HueLight]): The lights to display """ self.SetTitle("Tinge - {}".format(self.cur_group)) 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) for light in lights: inner_sizer = wx.BoxSizer(orient=wx.HORIZONTAL) lightid: int = light.get_id() icon: str = self.m_off_icon if light.is_on(): 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, 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) inner_sizer.Add(light_btn, 4, wx.EXPAND) self.Bind(wx.EVT_BUTTON, lambda event, mlightid=lightid: self.goto_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): """Call back for light button Args: lightid (int): The light id of the light to display """ light: HueLight = self.cur_bridge.get_light_by_id(lightid) self.SetTitle("Tinge - {}".format(light)) is_on: bool = light.is_on() 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) # Toggle icon: str = self.m_off_icon if is_on: icon = self.m_on_icon 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), toggle_btn) # Slider for brightness if is_on: if light.get_brightness() > 0: 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) self.sizer.Add(b_slider, 0, wx.EXPAND) 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") self.sizer.Add(c_label, 0, wx.EXPAND) 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) # 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) self.sizer.Add(d_slider, 0, wx.EXPAND) self.Bind(wx.EVT_SCROLL, 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") self.sizer.Add(e_label, 0, wx.EXPAND) 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) def set_brightness(self, event: wx.ScrollEvent, lightid: int): """Call back for brightness slider Args: event (wx.ScrollEvent): The scroll event to react to lightid (int): The light id of the light to adjust brightness of """ bri: int = event.GetPosition() 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 Args: event (wx.ScrollEvent): The scroll event to react to lightid (int): The light id of the light to adjust colortemp of """ ct: int = event.GetPosition() light: HueLight = self.cur_bridge.get_light_by_id(lightid) light.set_ct(ct) def set_hue(self, event, lightid): """Call back for colortemp slider Args: event (wx.ScrollEvent): The scroll event to react to lightid (int): The light id of the light to adjust hue of """ hue: int = event.GetPosition() light: HueLight = self.cur_bridge.get_light_by_id(lightid) light.set_hue(hue) def set_saturation(self, event, lightid): """Call back for colortemp slider Args: event (wx.ScrollEvent): The scroll event to react to lightid (int): The light id of the light to adjust hue of """ sat: int = event.GetPosition() light: HueLight = self.cur_bridge.get_light_by_id(lightid) light.set_hue(sat) if __name__ == "__main__": app = wx.App() frm = Hui(None, title="Tinge") frm.Show() app.MainLoop()