Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
------------------
Expand Down Expand Up @@ -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
10 changes: 6 additions & 4 deletions pyfritzhome/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand Down
5 changes: 3 additions & 2 deletions pyfritzhome/devicetypes/fritzhomedevicefeatures.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
class FritzhomeDeviceFeatures(IntFlag):
"""The feature list class."""

LIGHTBULB = 0x0004
ALARM = 0x0010
UNKNOWN = 0x0020
BUTTON = 0x0020
Expand All @@ -16,6 +17,6 @@ class FritzhomeDeviceFeatures(IntFlag):
MICROPHONE = 0x0800
HANFUN = 0x2000
SWITCHABLE = 0x8000
DIMMABLE = 0x10000
LIGHTBULB = 0x20000
LEVEL = 0x10000
COLOR = 0x20000
BLIND = 0x40000
135 changes: 79 additions & 56 deletions pyfritzhome/devicetypes/fritzhomedevicelightbulb.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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."""
Expand All @@ -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)
46 changes: 30 additions & 16 deletions pyfritzhome/devicetypes/fritzhomedeviceswitch.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""
Expand Down
25 changes: 25 additions & 0 deletions tests/responses/lightbulb/device_Telekom_Magenta_NonColorBulb.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<devicelist version="1" fwversion="7.51">
<device identifier="12701 0072784" id="401" functionbitmask="1" fwversion="34.09.15.16.018" manufacturer="0x319d" productname="HAN-FUN">
<present>1</present>
<txbusy>0</txbusy>
<name>Telekom Lampe</name>
</device>
<device identifier="12701 0072784-1" id="2001" functionbitmask="106500" fwversion="0.0" manufacturer="0x319d" productname="HAN-FUN">
<present>1</present>
<txbusy>0</txbusy>
<name>Telekom White Dimmable Bulb</name>
<simpleonoff>
<state>1</state>
</simpleonoff>
<levelcontrol>
<level>255</level>
<levelpercentage>100</levelpercentage>
</levelcontrol>
<etsiunitinfo>
<etsideviceid>401</etsideviceid>
<unittype>265</unittype>
<interfaces>512,513</interfaces>
</etsiunitinfo>
</device>
</devicelist>
18 changes: 18 additions & 0 deletions tests/responses/switch/device_list.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,22 @@
<offset>0</offset>
</temperature>
</device>
<device identifier="11324 0716524" id="402" functionbitmask="1" fwversion="30.17.04.02.018" manufacturer="0x2c3c" productname="HAN-FUN">
<present>1</present>
<txbusy>0</txbusy>
<name>Telekom Steckdose</name>
</device>
<device identifier="11324 0716524-1" id="2002" functionbitmask="40960" fwversion="0.0" manufacturer="0x2c3c" productname="HAN-FUN">
<present>1</present>
<txbusy>0</txbusy>
<name>Telekom Steckdose</name>
<simpleonoff>
<state>1</state>
</simpleonoff>
<etsiunitinfo>
<etsideviceid>402</etsideviceid>
<unittype>262</unittype>
<interfaces>512</interfaces>
</etsiunitinfo>
</device>
</devicelist>
21 changes: 21 additions & 0 deletions tests/test_fritzhomedevicelightbulb.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -38,6 +40,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")
Expand Down