async def refresh(self): gateway = Gateway() logging.info("Refreshing group with id: {}".format(self._group.id)) self._group = await self._api(gateway.get_group(int(self._group.id))) for aMember in self._members: await aMember.refresh()
class MainWindow(QWidget): def __init__(self, appctx): super().__init__() self.appctx = appctx self.api = None self.settings = Settings() self.gateway = Gateway() self.timers: Dict[str, QTimer] = {} self.init_ui() def init_ui(self): self.vbox = QVBoxLayout() self.hbox = QHBoxLayout() # Set layout self.setLayout(self.vbox) # Group list vbox3 = QVBoxLayout() group_frame = QGroupBox("Device Groups") group_frame.setLayout(QVBoxLayout()) self.group_list = QListWidget() self.group_list.itemPressed.connect(self.group_selected) group_frame.layout().addWidget(self.group_list) vbox3.addWidget(group_frame) # Sliders self.group_toggle = QCheckBox("Power") self.group_toggle.setEnabled(False) vbox3.addWidget(self.group_toggle) vbox3.addWidget(QLabel("Brightness")) self.group_brightness_slider = QSlider(Qt.Orientation.Horizontal) self.group_brightness_slider.setEnabled(False) self.group_brightness_slider.sliderMoved.connect( self.group_brightness_changed) vbox3.addWidget(self.group_brightness_slider) vbox3.addWidget(QLabel("Color Temperature")) self.group_color_slider = QSlider(Qt.Orientation.Horizontal) self.group_color_slider.setEnabled(False) self.group_color_slider.sliderMoved.connect(self.group_color_changed) vbox3.addWidget(self.group_color_slider) self.hbox.addLayout(vbox3) # moods mood_frame = QGroupBox("Moods") mood_frame.setLayout(QVBoxLayout()) self.mood_list = QListWidget() self.mood_list.itemPressed.connect(self.mood_selected) mood_frame.layout().addWidget(self.mood_list) self.hbox.addWidget(mood_frame) # Devices in group vbox2 = QVBoxLayout() device_frame = QGroupBox("Devices in Group") device_frame.setLayout(QVBoxLayout()) self.device_list = QListWidget() self.device_list.setEnabled(False) self.device_list.itemPressed.connect(self.device_selected) device_frame.layout().addWidget(self.device_list) vbox2.addWidget(device_frame) # Sliders self.device_toggle = QCheckBox("Power") self.device_toggle.setEnabled(False) vbox2.addWidget(self.device_toggle) vbox2.addWidget(QLabel("Brightness")) self.brightness_slider = QSlider(Qt.Orientation.Horizontal) self.brightness_slider.setEnabled(False) self.brightness_slider.sliderMoved.connect(self.brightness_changed) vbox2.addWidget(self.brightness_slider) vbox2.addWidget(QLabel("Color Temperature")) self.color_slider = QSlider(Qt.Orientation.Horizontal) self.color_slider.setEnabled(False) self.color_slider.sliderMoved.connect(self.color_changed) vbox2.addWidget(self.color_slider) self.hbox.addLayout(vbox2) self.vbox.addLayout(self.hbox) # Settings button icon = QIcon(resource_path('icons/settings.png')) self.settings_button = QPushButton(icon, "Settings") self.settings_button.pressed.connect(self.settings_pressed) self.vbox.addWidget(self.settings_button) self.setWindowTitle('TradfriGUI') self.re_init() def re_init(self): if self.settings.gateway_ip is None or self.settings.gateway_ip == '': self.settings_pressed() self.api = get_api(self.settings) self.device_list.clear() self.group_list.clear() if self.api is None: return groups = self.api(self.gateway.get_groups()) if len(groups) == 0: self.group_list.setEnabled(False) # TODO: load devices directly for group in groups: item = self.api(group) list_item = QListWidgetItem(item.name, self.group_list) setattr(list_item, 'api_item', item) def group_selected(self): current_item = self.group_list.currentItem() item = getattr(current_item, 'api_item', None) if item is None: return # refresh from gateway item = self.api(self.gateway.get_group(item.id)) # load moods self.mood_list.clear() moods = self.api(item.moods()) for m in moods: mood = self.api(m) list_item = QListWidgetItem(mood.name, self.mood_list) setattr(list_item, 'api_item', mood) # load devices devices = item.members() self.device_list.clear() # determine shared state and add devices to list state = False color_temp = False min_color = 10000 max_color = 0 color = [] brightness = [] for d in devices: device = self.api(d) if device.has_light_control: if device.light_control.lights[0].state: state = True if device.light_control.can_set_dimmer: if device.light_control.lights[0].state: brightness.append( device.light_control.lights[0].dimmer) else: brightness.append(0) if device.light_control.can_set_temp: color_temp = True min_color = min(min_color, device.light_control.min_mireds) max_color = max(max_color, device.light_control.max_mireds) color.append(device.light_control.lights[0].color_temp) list_item = QListWidgetItem(device.name, self.device_list) setattr(list_item, 'api_item', device) if len(brightness) > 0: brightness = int(sum(brightness) / len(brightness)) else: brightness = 0 if len(color) > 0: color = int(sum(color) / len(color)) else: color = min_color # enable device list and controls self.device_list.setEnabled(True) self.group_brightness_slider.setEnabled(True) self.group_brightness_slider.setMinimum(0) self.group_brightness_slider.setMaximum(254) self.group_brightness_slider.setSingleStep(16) self.group_brightness_slider.setValue(brightness) if color_temp: self.group_color_slider.setEnabled(True) self.group_color_slider.setMinimum(min_color) self.group_color_slider.setMaximum(max_color) self.group_color_slider.setSingleStep( int((max_color - min_color) / 10)) self.group_color_slider.setValue(color) else: self.group_color_slider.setEnabled(False) self.group_toggle.setEnabled(True) try: self.group_toggle.stateChanged.disconnect(self.group_toggled) except RuntimeError: pass # Disconnect failed because nothing was connected self.group_toggle.setCheckState( Qt.CheckState.Checked if state else Qt.CheckState.Unchecked) self.group_toggle.stateChanged.connect(self.group_toggled) self.brightness_slider.setEnabled(False) self.color_slider.setEnabled(False) self.device_toggle.setEnabled(False) def device_selected(self): current_item = self.device_list.currentItem() item = getattr(current_item, 'api_item', None) if item is None: return # refresh from gateway item = self.api(self.gateway.get_device(item.id)) # enable appropriate controls if item.has_light_control: ctrl = item.light_control if ctrl.can_set_dimmer: self.brightness_slider.setEnabled(True) self.brightness_slider.setMinimum(0) self.brightness_slider.setMaximum(254) self.brightness_slider.setSingleStep(16) self.brightness_slider.setValue(ctrl.lights[0].dimmer) else: self.brightness_slider.setEnabled(False) if ctrl.can_set_temp: self.color_slider.setEnabled(True) self.color_slider.setMinimum(ctrl.min_mireds) self.color_slider.setMaximum(ctrl.max_mireds) self.color_slider.setSingleStep( int((ctrl.max_mireds - ctrl.min_mireds) / 10)) self.color_slider.setValue(ctrl.lights[0].color_temp) else: self.color_slider.setEnabled(False) self.device_toggle.setEnabled(True) try: self.device_toggle.stateChanged.disconnect(self.device_toggled) except RuntimeError: pass # disconnect failed because nothing was connected self.device_toggle.setCheckState( Qt.CheckState.Checked if ctrl.lights[0].state else Qt. CheckState.Unchecked) self.device_toggle.stateChanged.connect(self.device_toggled) else: self.brightness_slider.setEnabled(False) self.color_slider.setEnabled(False) self.device_toggle.setEnabled(False) def mood_selected(self): current_group = self.group_list.currentItem() group = getattr(current_group, 'api_item', None) if group is None: return # refresh from gateway group = self.api(self.gateway.get_group(group.id)) current_mood = self.mood_list.currentItem() mood = getattr(current_mood, 'api_item', None) if mood is None: return self.api(group.activate_mood(mood.id)) def group_brightness_changed(self): current_item = self.group_list.currentItem() if current_item is None: return item = getattr(current_item, 'api_item', None) command = item.set_dimmer(self.group_brightness_slider.value(), transition_time=2) self.queue_command('group_brightness', command) def group_color_changed(self): current_item = self.group_list.currentItem() if current_item is None: return item = getattr(current_item, 'api_item', None) command = item.set_color_temp(self.group_color_slider.value(), transition_time=2) self.queue_command('group_color', command) def brightness_changed(self): current_item = self.device_list.currentItem() if current_item is None: return item = getattr(current_item, 'api_item', None) command = item.light_control.set_dimmer(self.brightness_slider.value(), transition_time=2) self.queue_command('device_brightness_{}'.format(item.id), command) def color_changed(self): current_item = self.device_list.currentItem() if current_item is None: return item = getattr(current_item, 'api_item', None) command = item.light_control.set_color_temp(self.color_slider.value(), transition_time=2) self.queue_command('device_color_{}'.format(item.id), command) def device_toggled(self): current_item = self.device_list.currentItem() if current_item is None: return item = getattr(current_item, 'api_item', None) command = item.light_control.set_state( self.device_toggle.checkState() == Qt.CheckState.Checked) self.api(command) def group_toggled(self): current_item = self.group_list.currentItem() if current_item is None: return item = getattr(current_item, 'api_item', None) command = item.set_state( self.group_toggle.checkState() == Qt.CheckState.Checked) self.api(command) def settings_pressed(self): config = ConfigWindow(self.appctx, self) config.setWindowModality(Qt.ApplicationModal) config.exec_() # reload settings self.settings = config.settings # re-initialize window self.re_init() def queue_command(self, name, command): timer = self.timers.get(name, None) if timer is None: timer = QTimer() timer.setInterval(200) timer.setSingleShot(True) timer.timeout.connect(self.timeout) timer.start() setattr(timer, 'command', command) self.timers[name] = timer def timeout(self): remove = [] for key, item in self.timers.items(): if item.isActive() == False: cmd = getattr(item, 'command') self.api(cmd) remove.append(key) for key in remove: del self.timers[key]
class TradfriManager(metaclass=Singleton): api_factory = None api = None gateway = None enabled = False initialized = False last_init = 0 def __init__(self): self.tradfri_state = TradfriState() self.observing_end = 0 self.observing = False self.observe_thread = None def init(self): if self.initialized: Logger().write(LogVerbosity.Info, "already init") return if sys.platform != "linux" and sys.platform != "linux2": Logger().write(LogVerbosity.Info, "Lighting: Not initializing, no coap client available on windows") self.initialized = True self.tradfri_state.update_group(DeviceGroup(1, "Test group", True, 128, 6)) self.tradfri_state.update_group(DeviceGroup(2, "Test group 2", False, 18, 6)) return if current_time() - self.last_init < 5000: Logger().write(LogVerbosity.Info, "Tried initialization less than 5s ago") return Logger().write(LogVerbosity.All, "Start LightManager init") self.enabled = True if not self.initialized: ip = Settings.get_string("tradfri_hub_ip") identity = Database().get_stat_string("LightingId") key = Database().get_stat_string("LightingKey") if identity is None or key is None: Logger().write(LogVerbosity.Info, "Lighting: No identity/key found, going to generate new") # We don't have all information to connect, reset and start from scratch Database().remove_stat("LightingId") Database().remove_stat("LightingKey") key = None identity = uuid.uuid4().hex Database().update_stat("LightingId", identity) # Generate and save a new id self.api_factory = APIFactory(host=ip, psk_id=identity) else: self.api_factory = APIFactory(host=ip, psk_id=identity, psk=key) self.api = self.api_factory.request self.gateway = Gateway() if key is None: try: security_code = SecureSettings.get_string("tradfri_hub_code") # the code at the bottom of the hub key = self.api_factory.generate_psk(security_code) Database().update_stat("LightingKey", key) # Save the new key Logger().write(LogVerbosity.Info, "Lighting: New key retrieved") except Exception as e: Logger().write_error(e, "Unhandled exception") return else: Logger().write(LogVerbosity.Info, "Lighting: Previously saved key found") try: self.initialized = True groups = self.get_device_groups() for group in groups: self.tradfri_state.update_group(group) except Exception as e: Logger().write(LogVerbosity.Info, "Failed to init tradfri, clearing previous key to try generate new") self.initialized = False Database().remove_stat("LightingId") Database().remove_stat("LightingKey") Logger().write_error(e, "Failed to get groups from hub") def start_observing(self): Logger().write(LogVerbosity.Debug, "Start observing light data") self.observing = True if self.observing_end > current_time(): Logger().write(LogVerbosity.All, "Still observing, not starting again") return # still observing, the check observing thread will renew if not self.check_state(): return self.observing_end = current_time() + 30000 groups_commands = self.api(self.gateway.get_groups()) result = self.api(groups_commands) for group in result: self.observe_group(group) def observe_group(self, group): Logger().write(LogVerbosity.All, "Starting observe for group " + group.name) self.observe_thread = CustomThread(lambda: self.api(group.observe( self.tradfri_state.update_group, lambda x: self.check_observe(group), duration=30)), "Light group observer", []) self.observe_thread.start() def check_observe(self, group): if self.observing: # Restart observing since it timed out Logger().write(LogVerbosity.Debug, "Restarting observing for group " + str(group.name)) self.observe_group(group) def stop_observing(self): Logger().write(LogVerbosity.Debug, "Stop observing light data") self.observing = False def get_devices(self): if not self.check_state(): return [] Logger().write(LogVerbosity.All, "Get devices") devices_commands = self.api(self.gateway.get_devices()) devices = self.api(devices_commands) return [d for d in [self.parse_device(x) for x in devices] if d is not None] def set_state(self, device_id, state): if not self.check_state(): return Logger().write(LogVerbosity.All, "Set device state") device = self.api(self.gateway.get_device(device_id)) if device.has_light_control: self.api(device.light_control.set_state(state)) else: self.api(device.socket_control.set_state(state)) def set_light_warmth(self, device_id, warmth): if not self.check_state(): return Logger().write(LogVerbosity.All, "Set light warmth") device = self.api(self.gateway.get_device(device_id)) self.api(device.light_control.set_color_temp(warmth)) def set_light_dimmer(self, device_id, amount): if not self.check_state(): return Logger().write(LogVerbosity.All, "Set light dimmer") device = self.api(self.gateway.get_device(device_id)) self.api(device.light_control.set_dimmer(amount)) def set_device_name(self, device_id, name): if not self.check_state(): return Logger().write(LogVerbosity.All, "Set device name") device = self.api(self.gateway.get_device(device_id)) self.api(device.set_name(name)) def get_device_groups(self): if not self.check_state(): return [] Logger().write(LogVerbosity.All, "Get device groups") groups_commands = self.api(self.gateway.get_groups()) result = self.api(groups_commands) Logger().write(LogVerbosity.All, "Get device groups: " + str([x.raw for x in result])) return [DeviceGroup(group.id, group.name, group.state, group.dimmer, len(group.member_ids)) for group in result] def get_devices_in_group(self, group): if not self.check_state(): return [] Logger().write(LogVerbosity.All, "Get lights in group") group = self.api(self.gateway.get_group(group)) members = group.member_ids result = [self.api(self.gateway.get_device(x)) for x in members] return [d for d in [self.parse_device(x) for x in result] if d is not None] def set_group_name(self, group, name): if not self.check_state(): return Logger().write(LogVerbosity.All, "Set group name") group = self.api(self.gateway.get_group(group)) self.api(group.set_name(name)) def set_group_state(self, group, state): if not self.check_state(): return Logger().write(LogVerbosity.All, "Set group state") group = self.api(self.gateway.get_group(group)) self.api(group.set_state(state)) def set_group_dimmer(self, group, dimmer): if not self.check_state(): return Logger().write(LogVerbosity.All, "Set group dimmer") group = self.api(self.gateway.get_group(group)) self.api(group.set_dimmer(dimmer)) def check_state(self): if not self.enabled: return False # not available if not self.initialized: self.init() if not self.initialized: return False # init failed return True @staticmethod def parse_device(data): if data.has_light_control: lights = [] for light in data.light_control.lights: lights.append(LightDevice( light.state, light.dimmer, light.color_temp, light.hex_color)) return LightControl(data.id, data.name, data.application_type, data.last_seen.timestamp(), data.reachable, data.light_control.can_set_dimmer, data.light_control.can_set_temp, data.light_control.can_set_color, lights) elif data.has_socket_control: sockets = [] for socket in data.socket_control.sockets: sockets.append(SocketDevice(socket.state)) return SocketControl(data.id, data.name, data.application_type, data.last_seen.timestamp(), data.reachable, sockets)