Beispiel #1
0
    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()
Beispiel #2
0
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]
Beispiel #3
0
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)