From c7732f174cc197a76a66fce1e175f6400313d38f Mon Sep 17 00:00:00 2001 From: parlia_desktop Date: Sun, 19 Feb 2023 16:48:04 +0100 Subject: [PATCH 1/5] fix telekom hanfun lightbulbs which dont support color --- pyfritzhome/cli.py | 8 +- .../devicetypes/fritzhomedevicefeatures.py | 5 +- .../devicetypes/fritzhomedevicelightbulb.py | 135 ++++++++++-------- 3 files changed, 87 insertions(+), 61 deletions(-) diff --git a/pyfritzhome/cli.py b/pyfritzhome/cli.py index 59628ee..3aae0c5 100644 --- a/pyfritzhome/cli.py +++ b/pyfritzhome/cli.py @@ -63,9 +63,11 @@ def list_all(fritz, args): if device.has_lightbulb: print(" Light bulb:") print(" state=%s" % ("Off" if device.state == 0 else "On")) - print(" level=%s" % device.level) - print(" hue=%s" % device.hue) - print(" saturation=%s" % device.saturation) + if device.has_level: + print(" level=%s" % device.level) + if device.has_color: + print(" hue=%s" % device.hue) + print(" saturation=%s" % device.saturation) if device.has_blind: print(" Blind:") print(" level=%s" % device.level) diff --git a/pyfritzhome/devicetypes/fritzhomedevicefeatures.py b/pyfritzhome/devicetypes/fritzhomedevicefeatures.py index 8b0a0f4..cfc1e30 100644 --- a/pyfritzhome/devicetypes/fritzhomedevicefeatures.py +++ b/pyfritzhome/devicetypes/fritzhomedevicefeatures.py @@ -5,6 +5,7 @@ class FritzhomeDeviceFeatures(IntFlag): """The feature list class.""" + LIGHTBULB = 0x0004 ALARM = 0x0010 UNKNOWN = 0x0020 BUTTON = 0x0020 @@ -16,6 +17,6 @@ class FritzhomeDeviceFeatures(IntFlag): MICROPHONE = 0x0800 HANFUN = 0x2000 SWITCHABLE = 0x8000 - DIMMABLE = 0x10000 - LIGHTBULB = 0x20000 + LEVEL = 0x10000 + COLOR = 0x20000 BLIND = 0x40000 diff --git a/pyfritzhome/devicetypes/fritzhomedevicelightbulb.py b/pyfritzhome/devicetypes/fritzhomedevicelightbulb.py index 8bccbd9..432df24 100644 --- a/pyfritzhome/devicetypes/fritzhomedevicelightbulb.py +++ b/pyfritzhome/devicetypes/fritzhomedevicelightbulb.py @@ -36,6 +36,16 @@ def has_lightbulb(self): """Check if the device has LightBulb function.""" return self._has_feature(FritzhomeDeviceFeatures.LIGHTBULB) + @property + def has_level(self): + """Check if the device has LightBulb function.""" + return self._has_feature(FritzhomeDeviceFeatures.LEVEL) + + @property + def has_color(self): + """Check if the device has LightBulb function.""" + return self._has_feature(FritzhomeDeviceFeatures.COLOR) + def _update_lightbulb_from_node(self, node): state_element = node.find("simpleonoff") try: @@ -44,54 +54,56 @@ def _update_lightbulb_from_node(self, node): except ValueError: pass - level_element = node.find("levelcontrol") - try: - self.level = self.get_node_value_as_int(level_element, "level") - - self.level_percentage = int(self.level / 2.55) - except ValueError: - pass - - colorcontrol_element = node.find("colorcontrol") - try: - self.color_mode = colorcontrol_element.attrib.get("current_mode") - - self.supported_color_mode = colorcontrol_element.attrib.get( - "supported_modes" - ) - - except ValueError: - pass - - try: - self.hue = self.get_node_value_as_int(colorcontrol_element, "hue") - - self.saturation = self.get_node_value_as_int( - colorcontrol_element, "saturation" - ) - - self.unmapped_hue = self.get_node_value_as_int( - colorcontrol_element, "unmapped_hue" - ) - - self.unmapped_saturation = self.get_node_value_as_int( - colorcontrol_element, "unmapped_saturation" - ) - except ValueError: - # reset values after color mode changed - self.hue = None - self.saturation = None - self.unmapped_hue = None - self.unmapped_saturation = None - - try: - self.color_temp = self.get_node_value_as_int( - colorcontrol_element, "temperature" - ) - - except ValueError: - # reset values after color mode changed - self.color_temp = None + if self.has_level: + level_element = node.find("levelcontrol") + try: + self.level = self.get_node_value_as_int(level_element, "level") + + self.level_percentage = int(self.level / 2.55) + except ValueError: + pass + + if self.has_color: + colorcontrol_element = node.find("colorcontrol") + try: + self.color_mode = colorcontrol_element.attrib.get("current_mode") + + self.supported_color_mode = colorcontrol_element.attrib.get( + "supported_modes" + ) + + except ValueError: + pass + + try: + self.hue = self.get_node_value_as_int(colorcontrol_element, "hue") + + self.saturation = self.get_node_value_as_int( + colorcontrol_element, "saturation" + ) + + self.unmapped_hue = self.get_node_value_as_int( + colorcontrol_element, "unmapped_hue" + ) + + self.unmapped_saturation = self.get_node_value_as_int( + colorcontrol_element, "unmapped_saturation" + ) + except ValueError: + # reset values after color mode changed + self.hue = None + self.saturation = None + self.unmapped_hue = None + self.unmapped_saturation = None + + try: + self.color_temp = self.get_node_value_as_int( + colorcontrol_element, "temperature" + ) + + except ValueError: + # reset values after color mode changed + self.color_temp = None def set_state_off(self): """Switch light bulb off.""" @@ -109,29 +121,40 @@ def set_state_toggle(self): self._fritz.set_state_toggle(self.ain) def set_level(self, level): - """Set HSV color.""" - self._fritz.set_level(self.ain, level) + """Set level.""" + if self.has_level: + self._fritz.set_level(self.ain, level) def set_level_percentage(self, level): """Set HSV color in percent.""" - self._fritz.set_level_percentage(self.ain, level) + if self.has_level: + self._fritz.set_level_percentage(self.ain, level) def get_colors(self): """Get the supported colors.""" - return self._fritz.get_colors(self.ain) + if self.has_color: + return self._fritz.get_colors(self.ain) + else: + return {} def set_color(self, hsv, duration=0): """Set HSV color.""" - self._fritz.set_color(self.ain, hsv, duration, True) + if self.has_color: + self._fritz.set_color(self.ain, hsv, duration, True) def set_unmapped_color(self, hsv, duration=0): """Set unmapped HSV color (Free color selection).""" - self._fritz.set_color(self.ain, hsv, duration, False) + if self.has_color: + self._fritz.set_color(self.ain, hsv, duration, False) def get_color_temps(self): """Get the supported color temperatures energy.""" - return self._fritz.get_color_temps(self.ain) + if self.has_color: + return self._fritz.get_color_temps(self.ain) + else: + return [] def set_color_temp(self, temperature, duration=0): """Set white color temperature.""" - self._fritz.set_color_temp(self.ain, temperature, duration) + if self.has_color: + self._fritz.set_color_temp(self.ain, temperature, duration) From d9defd61321f0626b8374ca9460ea09d453c0e77 Mon Sep 17 00:00:00 2001 From: parlia_desktop Date: Sun, 19 Feb 2023 16:48:26 +0100 Subject: [PATCH 2/5] fix telekom hanfun swtiches --- pyfritzhome/cli.py | 2 +- .../devicetypes/fritzhomedeviceswitch.py | 46 ++++++++++++------- 2 files changed, 31 insertions(+), 17 deletions(-) diff --git a/pyfritzhome/cli.py b/pyfritzhome/cli.py index 3aae0c5..81e6bb7 100644 --- a/pyfritzhome/cli.py +++ b/pyfritzhome/cli.py @@ -37,7 +37,7 @@ def list_all(fritz, args): if device.has_switch: print(" Switch:") print(" switch_state=%s" % device.switch_state) - if device.has_switch: + if device.has_powermeter: print(" Powermeter:") print(" power=%s" % device.power) print(" energy=%s" % device.energy) diff --git a/pyfritzhome/devicetypes/fritzhomedeviceswitch.py b/pyfritzhome/devicetypes/fritzhomedeviceswitch.py index 66c4165..6ea7412 100644 --- a/pyfritzhome/devicetypes/fritzhomedeviceswitch.py +++ b/pyfritzhome/devicetypes/fritzhomedeviceswitch.py @@ -28,25 +28,39 @@ def _update_from_node(self, node): @property def has_switch(self): """Check if the device has switch function.""" - return self._has_feature(FritzhomeDeviceFeatures.SWITCH) + if self._has_feature(FritzhomeDeviceFeatures.SWITCH): + # for AVM plugs like FRITZ!DECT 200 and FRITZ!DECT 210 + return True + if self._has_feature(FritzhomeDeviceFeatures.SWITCHABLE) and not self._has_feature( + FritzhomeDeviceFeatures.LIGHTBULB): + # for HAN-FUN plugs + return True + return False def _update_switch_from_node(self, node): - val = node.find("switch") - try: - self.switch_state = self.get_node_value_as_int_as_bool(val, "state") - except Exception: - self.switch_state = None - self.switch_mode = self.get_node_value(val, "mode") - try: - self.lock = self.get_node_value_as_int_as_bool(val, "lock") - except Exception: - self.lock = None + if self._has_feature(FritzhomeDeviceFeatures.SWITCH): + val = node.find("switch") + try: + self.switch_state = self.get_node_value_as_int_as_bool(val, "state") + except Exception: + self.switch_state = None + self.switch_mode = self.get_node_value(val, "mode") + try: + self.lock = self.get_node_value_as_int_as_bool(val, "lock") + except Exception: + self.lock = None - # optional value - try: - self.device_lock = self.get_node_value_as_int_as_bool(val, "devicelock") - except Exception: - pass + # optional value + try: + self.device_lock = self.get_node_value_as_int_as_bool(val, "devicelock") + except Exception: + pass + else: + val = node.find("simpleonoff") + try: + self.switch_state = self.get_node_value_as_int_as_bool(val, "state") + except Exception: + self.switch_state = None def get_switch_state(self): """Get the switch state.""" From 5f12453e2136fcfb754126b54eeef83f880cee2e Mon Sep 17 00:00:00 2001 From: parlia-laptop Date: Fri, 3 Mar 2023 20:56:44 +0100 Subject: [PATCH 3/5] fix linter and unit test --- .../devicetypes/fritzhomedeviceswitch.py | 4 ++-- tests/test_fritzhomedevicelightbulb.py | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/pyfritzhome/devicetypes/fritzhomedeviceswitch.py b/pyfritzhome/devicetypes/fritzhomedeviceswitch.py index 6ea7412..17f015c 100644 --- a/pyfritzhome/devicetypes/fritzhomedeviceswitch.py +++ b/pyfritzhome/devicetypes/fritzhomedeviceswitch.py @@ -31,8 +31,8 @@ def has_switch(self): if self._has_feature(FritzhomeDeviceFeatures.SWITCH): # for AVM plugs like FRITZ!DECT 200 and FRITZ!DECT 210 return True - if self._has_feature(FritzhomeDeviceFeatures.SWITCHABLE) and not self._has_feature( - FritzhomeDeviceFeatures.LIGHTBULB): + if (self._has_feature(FritzhomeDeviceFeatures.SWITCHABLE) + and not self._has_feature(FritzhomeDeviceFeatures.LIGHTBULB)): # for HAN-FUN plugs return True return False diff --git a/tests/test_fritzhomedevicelightbulb.py b/tests/test_fritzhomedevicelightbulb.py index 64b9dc2..fb87879 100644 --- a/tests/test_fritzhomedevicelightbulb.py +++ b/tests/test_fritzhomedevicelightbulb.py @@ -38,6 +38,25 @@ def test_device_init(self): assert device.color_temp is None assert device.name == "FRITZ!DECT 500 Büro" + def test_device_init_non_color_bulb(self): + self.mock.side_effect = [ + Helper.response("lightbulb/device_Telekom_Magenta_NonColorBulb") + ] + + self.fritz.update_devices() + device = self.fritz.get_device_by_ain("12701 0072784") + + assert device.ain == "12701 0072784" + assert device.fw_version == "34.09.15.16.018" + assert device.present # Lightbulb has power and is connected + + # Get sub-device + device = self.fritz.get_device_by_ain("12701 0072784-1") + assert device.has_lightbulb + assert device.has_level + assert device.state # Lightbulb is switched on + assert device.name == "Telekom White Dimmable Bulb" + def test_device_init_color_temp_mode(self): self.mock.side_effect = [ Helper.response("lightbulb/device_FritzDECT500_34_12_16_color_temp_mode") From fe78d19931f5fb1de930fbe414a4990e01d050e6 Mon Sep 17 00:00:00 2001 From: parlia-laptop Date: Fri, 3 Mar 2023 22:17:20 +0100 Subject: [PATCH 4/5] add test for telekom magenta devices --- .../device_Telekom_Magenta_NonColorBulb.xml | 25 +++++++++++++++++++ tests/responses/switch/device_list.xml | 18 +++++++++++++ tests/test_fritzhomedevicelightbulb.py | 2 ++ 3 files changed, 45 insertions(+) create mode 100644 tests/responses/lightbulb/device_Telekom_Magenta_NonColorBulb.xml diff --git a/tests/responses/lightbulb/device_Telekom_Magenta_NonColorBulb.xml b/tests/responses/lightbulb/device_Telekom_Magenta_NonColorBulb.xml new file mode 100644 index 0000000..a53df92 --- /dev/null +++ b/tests/responses/lightbulb/device_Telekom_Magenta_NonColorBulb.xml @@ -0,0 +1,25 @@ + + + + 1 + 0 + Telekom Lampe + + + 1 + 0 + Telekom White Dimmable Bulb + + 1 + + + 255 + 100 + + + 401 + 265 + 512,513 + + + diff --git a/tests/responses/switch/device_list.xml b/tests/responses/switch/device_list.xml index 300acb1..786522d 100644 --- a/tests/responses/switch/device_list.xml +++ b/tests/responses/switch/device_list.xml @@ -18,4 +18,22 @@ 0 + + 1 + 0 + Telekom Steckdose + + + 1 + 0 + Telekom Steckdose + + 1 + + + 402 + 262 + 512 + + \ No newline at end of file diff --git a/tests/test_fritzhomedevicelightbulb.py b/tests/test_fritzhomedevicelightbulb.py index fb87879..ff46dfc 100644 --- a/tests/test_fritzhomedevicelightbulb.py +++ b/tests/test_fritzhomedevicelightbulb.py @@ -30,6 +30,8 @@ def test_device_init(self): # Get sub-device device = self.fritz.get_device_by_ain("12345-1") assert device.has_lightbulb + assert device.has_level + assert device.has_color assert device.state # Lightbulb is switched on assert device.color_mode == "1" assert device.supported_color_mode == "5" From fcad7916d6e260bccb67031202b8deb4477a5ec1 Mon Sep 17 00:00:00 2001 From: parlia-laptop Date: Fri, 3 Mar 2023 22:27:15 +0100 Subject: [PATCH 5/5] add tested devices to readme add more devices --- README.rst | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 37ed83e..7e6d5c8 100644 --- a/README.rst +++ b/README.rst @@ -13,8 +13,10 @@ Tested Devices * `Panasonic KX-HNS101` * `Magenta Smarthome Tür-/Fensterkontakt optisch`_ * `RADEMACHER RolloTron DECT 1213`_ - - +* `Magenta Smarthome Zwischenstecker außen`_ +* `Magenta SmartHome LED-Lampe E27 Warmweiß` +* `Magenta SmartHome Zwischenstecker innen` +* `Magenta Smarthome Tür-/Fensterkontakt magnetisch` fritzhome CLI tool ------------------ @@ -118,3 +120,4 @@ References .. _FRITZ!Box 6490 Cable: https://avm.de/produkte/fritzbox/fritzbox-6490-cable/ .. _Magenta Smarthome Tür-/Fensterkontakt optisch: https://www.smarthome.de/geraete/smarthome-tuer-fensterkontakt-optisch-weiss .. _RADEMACHER RolloTron DECT 1213: https://www.rademacher.de/shop/rollladen-sonnenschutz/elektrischer-gurtwickler/rollotron-dect-1213 +.. _Magenta Smarthome Zwischenstecker außen: https://www.smarthome.de/geraete/smarthome-zwischenstecker-aussen-schwarz \ No newline at end of file