From 1926b4af3c23f8a063c33b833360860ecfd59f45 Mon Sep 17 00:00:00 2001 From: mib1185 Date: Sun, 11 Aug 2024 16:06:58 +0000 Subject: [PATCH 1/4] add wait_device_txbusy method --- .../devicetypes/fritzhomedeviceblind.py | 12 ++-- .../devicetypes/fritzhomedevicelevel.py | 8 +-- .../devicetypes/fritzhomedevicelightbulb.py | 24 +++---- .../devicetypes/fritzhomedeviceswitch.py | 12 ++-- .../devicetypes/fritzhomedevicethermostat.py | 16 ++--- pyfritzhome/fritzhome.py | 72 ++++++++++++++----- 6 files changed, 89 insertions(+), 55 deletions(-) diff --git a/pyfritzhome/devicetypes/fritzhomedeviceblind.py b/pyfritzhome/devicetypes/fritzhomedeviceblind.py index 5f728b2..fc11d62 100644 --- a/pyfritzhome/devicetypes/fritzhomedeviceblind.py +++ b/pyfritzhome/devicetypes/fritzhomedeviceblind.py @@ -38,14 +38,14 @@ def _update_blind_from_node(self, node): except Exception: pass - def set_blind_open(self): + def set_blind_open(self, wait=False): """Open the blind.""" - self._fritz.set_blind_open(self.ain) + self._fritz.set_blind_open(self.ain, wait) - def set_blind_close(self): + def set_blind_close(self, wait=False): """Close the blind.""" - self._fritz.set_blind_close(self.ain) + self._fritz.set_blind_close(self.ain, wait) - def set_blind_stop(self): + def set_blind_stop(self, wait=False): """Stop the blind.""" - self._fritz.set_blind_stop(self.ain) + self._fritz.set_blind_stop(self.ain, wait) diff --git a/pyfritzhome/devicetypes/fritzhomedevicelevel.py b/pyfritzhome/devicetypes/fritzhomedevicelevel.py index 5b08955..3e8fcf5 100644 --- a/pyfritzhome/devicetypes/fritzhomedevicelevel.py +++ b/pyfritzhome/devicetypes/fritzhomedevicelevel.py @@ -48,10 +48,10 @@ def get_level_percentage(self): """Get the level in percentage.""" return self.levelpercentage - def set_level(self, level): + def set_level(self, level, wait=False): """Set the level.""" - self._fritz.set_level(self.ain, level) + self._fritz.set_level(self.ain, level, wait) - def set_level_percentage(self, levelpercentage): + def set_level_percentage(self, levelpercentage, wait=False): """Set the level in percentage.""" - self._fritz.set_level_percentage(self.ain, levelpercentage) + self._fritz.set_level_percentage(self.ain, levelpercentage, wait) diff --git a/pyfritzhome/devicetypes/fritzhomedevicelightbulb.py b/pyfritzhome/devicetypes/fritzhomedevicelightbulb.py index 8d085d5..2dbe4aa 100644 --- a/pyfritzhome/devicetypes/fritzhomedevicelightbulb.py +++ b/pyfritzhome/devicetypes/fritzhomedevicelightbulb.py @@ -91,20 +91,20 @@ def _update_lightbulb_from_node(self, node): # reset values after color mode changed self.color_temp = None - def set_state_off(self): + def set_state_off(self, wait=False): """Switch light bulb off.""" self.state = True - self._fritz.set_state_off(self.ain) + self._fritz.set_state_off(self.ain, wait) - def set_state_on(self): + def set_state_on(self, wait=False): """Switch light bulb on.""" self.state = True - self._fritz.set_state_on(self.ain) + self._fritz.set_state_on(self.ain, wait) - def set_state_toggle(self): + def set_state_toggle(self, wait=False): """Toogle light bulb state.""" self.state = True - self._fritz.set_state_toggle(self.ain) + self._fritz.set_state_toggle(self.ain, wait) def get_colors(self): """Get the supported colors.""" @@ -113,15 +113,15 @@ def get_colors(self): else: return {} - def set_color(self, hsv, duration=0): + def set_color(self, hsv, duration=0, wait=False): """Set HSV color.""" if self.has_color: - self._fritz.set_color(self.ain, hsv, duration, True) + self._fritz.set_color(self.ain, hsv, duration, True, wait) - def set_unmapped_color(self, hsv, duration=0): + def set_unmapped_color(self, hsv, duration=0, wait=False): """Set unmapped HSV color (Free color selection).""" if self.has_color: - self._fritz.set_color(self.ain, hsv, duration, False) + self._fritz.set_color(self.ain, hsv, duration, False, wait) def get_color_temps(self): """Get the supported color temperatures energy.""" @@ -130,7 +130,7 @@ def get_color_temps(self): else: return [] - def set_color_temp(self, temperature, duration=0): + def set_color_temp(self, temperature, duration=0, wait=False): """Set white color temperature.""" if self.has_color: - self._fritz.set_color_temp(self.ain, temperature, duration) + self._fritz.set_color_temp(self.ain, temperature, duration, wait) diff --git a/pyfritzhome/devicetypes/fritzhomedeviceswitch.py b/pyfritzhome/devicetypes/fritzhomedeviceswitch.py index 9df1fb0..92f1de3 100644 --- a/pyfritzhome/devicetypes/fritzhomedeviceswitch.py +++ b/pyfritzhome/devicetypes/fritzhomedeviceswitch.py @@ -68,14 +68,14 @@ def get_switch_state(self): """Get the switch state.""" return self._fritz.get_switch_state(self.ain) - def set_switch_state_on(self): + def set_switch_state_on(self, wait=False): """Set the switch state to on.""" - return self._fritz.set_switch_state_on(self.ain) + return self._fritz.set_switch_state_on(self.ain, wait) - def set_switch_state_off(self): + def set_switch_state_off(self, wait=False): """Set the switch state to off.""" - return self._fritz.set_switch_state_off(self.ain) + return self._fritz.set_switch_state_off(self.ain, wait) - def set_switch_state_toggle(self): + def set_switch_state_toggle(self, wait=False): """Toggle the switch state.""" - return self._fritz.set_switch_state_toggle(self.ain) + return self._fritz.set_switch_state_toggle(self.ain, wait) diff --git a/pyfritzhome/devicetypes/fritzhomedevicethermostat.py b/pyfritzhome/devicetypes/fritzhomedevicethermostat.py index 189a4f4..29c8ebd 100644 --- a/pyfritzhome/devicetypes/fritzhomedevicethermostat.py +++ b/pyfritzhome/devicetypes/fritzhomedevicethermostat.py @@ -132,17 +132,17 @@ def get_target_temperature(self): """Get the thermostate target temperature.""" return self._fritz.get_target_temperature(self.ain) - def set_target_temperature(self, temperature): + def set_target_temperature(self, temperature, wait=False): """Set the thermostate target temperature.""" - return self._fritz.set_target_temperature(self.ain, temperature) + return self._fritz.set_target_temperature(self.ain, temperature, wait) - def set_window_open(self, seconds): + def set_window_open(self, seconds, wait=False): """Set the thermostate to window open.""" - return self._fritz.set_window_open(self.ain, seconds) + return self._fritz.set_window_open(self.ain, seconds, wait) - def set_boost_mode(self, seconds): + def set_boost_mode(self, seconds, wait=False): """Set the thermostate into boost mode.""" - return self._fritz.set_boost_mode(self.ain, seconds) + return self._fritz.set_boost_mode(self.ain, seconds, wait) def get_comfort_temperature(self): """Get the thermostate comfort temperature.""" @@ -164,7 +164,7 @@ def get_hkr_state(self): except KeyError: return "manual" - def set_hkr_state(self, state): + def set_hkr_state(self, state, wait=False): """Set the state of the thermostat. Possible values for state are: 'on', 'off', 'comfort', 'eco'. @@ -179,4 +179,4 @@ def set_hkr_state(self, state): except KeyError: return - self.set_target_temperature(value) + self.set_target_temperature(value, wait) diff --git a/pyfritzhome/fritzhome.py b/pyfritzhome/fritzhome.py index d57420b..bf68e18 100644 --- a/pyfritzhome/fritzhome.py +++ b/pyfritzhome/fritzhome.py @@ -197,6 +197,17 @@ def _get_listinfo_elements(self, entity_type): _LOGGER.debug(dom) return dom.findall("*") + def wait_device_txbusy(self, ain, retries=10): + """Wait for device to finish command execution.""" + for _ in range(retries): + plain = self.get_device_infos(ain) + dom = ElementTree.fromstring(plain) + txbusy = dom.findall("txbusy") + if txbusy[0].text == "0": + return True + time.sleep(0.1) + return False + def get_device_elements(self): """Get the DOM elements for the device list.""" return self._get_listinfo_elements("device") @@ -223,6 +234,10 @@ def get_device_by_ain(self, ain): """Return a device specified by the AIN.""" return self.get_devices_as_dict()[ain] + def get_device_infos(self, ain): + """Get the device infos.""" + return self._aha_request("getdeviceinfos", ain=ain) + def get_device_present(self, ain): """Get the device presence.""" return self._aha_request("getswitchpresent", ain=ain, rf=bool) @@ -235,17 +250,23 @@ def get_switch_state(self, ain): """Get the switch state.""" return self._aha_request("getswitchstate", ain=ain, rf=bool) - def set_switch_state_on(self, ain): + def set_switch_state_on(self, ain, wait=False): """Set the switch to on state.""" - return self._aha_request("setswitchon", ain=ain, rf=bool) + result = self._aha_request("setswitchon", ain=ain, rf=bool) + wait and self.wait_device_txbusy(ain) + return result - def set_switch_state_off(self, ain): + def set_switch_state_off(self, ain, wait=False): """Set the switch to off state.""" - return self._aha_request("setswitchoff", ain=ain, rf=bool) + result = self._aha_request("setswitchoff", ain=ain, rf=bool) + wait and self.wait_device_txbusy(ain) + return result - def set_switch_state_toggle(self, ain): + def set_switch_state_toggle(self, ain, wait=False): """Toggle the switch state.""" - return self._aha_request("setswitchtoggle", ain=ain, rf=bool) + result = self._aha_request("setswitchtoggle", ain=ain, rf=bool) + wait and self.wait_device_txbusy(ain) + return result def get_switch_power(self, ain): """Get the switch power consumption.""" @@ -267,7 +288,7 @@ def get_target_temperature(self, ain): """Get the thermostate target temperature.""" return self._get_temperature(ain, "gethkrtsoll") - def set_target_temperature(self, ain, temperature): + def set_target_temperature(self, ain, temperature, wait=False): """Set the thermostate target temperature.""" temp = int(16 + ((float(temperature) - 8) * 2)) @@ -277,20 +298,23 @@ def set_target_temperature(self, ain, temperature): temp = 254 self._aha_request("sethkrtsoll", ain=ain, param={"param": temp}) + wait and self.wait_device_txbusy(ain) - def set_window_open(self, ain, seconds): + def set_window_open(self, ain, seconds, wait=False): """Set the thermostate target temperature.""" endtimestamp = int(time.time() + seconds) self._aha_request( "sethkrwindowopen", ain=ain, param={"endtimestamp": endtimestamp} ) + wait and self.wait_device_txbusy(ain) - def set_boost_mode(self, ain, seconds): + def set_boost_mode(self, ain, seconds, wait=False): """Set the thermostate to boost mode.""" endtimestamp = int(time.time() + seconds) self._aha_request("sethkrboost", ain=ain, param={"endtimestamp": endtimestamp}) + wait and self.wait_device_txbusy(ain) def get_comfort_temperature(self, ain): """Get the thermostate comfort temperature.""" @@ -307,19 +331,22 @@ def get_device_statistics(self, ain): # Lightbulb-related commands - def set_state_off(self, ain): + def set_state_off(self, ain, wait=False): """Set the switch/actuator/lightbulb to on state.""" self._aha_request("setsimpleonoff", ain=ain, param={"onoff": 0}) + wait and self.wait_device_txbusy(ain) - def set_state_on(self, ain): + def set_state_on(self, ain, wait=False): """Set the switch/actuator/lightbulb to on state.""" self._aha_request("setsimpleonoff", ain=ain, param={"onoff": 1}) + wait and self.wait_device_txbusy(ain) - def set_state_toggle(self, ain): + def set_state_toggle(self, ain, wait=False): """Toggle the switch/actuator/lightbulb state.""" self._aha_request("setsimpleonoff", ain=ain, param={"onoff": 2}) + wait and self.wait_device_txbusy(ain) - def set_level(self, ain, level): + def set_level(self, ain, level, wait=False): """Set level/brightness/height in interval [0,255].""" if level < 0: level = 0 # 0% @@ -327,8 +354,9 @@ def set_level(self, ain, level): level = 255 # 100 % self._aha_request("setlevel", ain=ain, param={"level": int(level)}) + wait and self.wait_device_txbusy(ain) - def set_level_percentage(self, ain, level): + def set_level_percentage(self, ain, level, wait=False): """Set level/brightness/height in interval [0,100].""" if level < 0: level = 0 @@ -336,6 +364,7 @@ def set_level_percentage(self, ain, level): level = 100 self._aha_request("setlevelpercentage", ain=ain, param={"level": int(level)}) + wait and self.wait_device_txbusy(ain) def _get_colordefaults(self, ain): plain = self._aha_request("getcolordefaults", ain=ain) @@ -353,7 +382,7 @@ def get_colors(self, ain): colors[name] = values return colors - def set_color(self, ain, hsv, duration=0, mapped=True): + def set_color(self, ain, hsv, duration=0, mapped=True, wait=False): """Set hue and saturation. hsv: HUE colorspace element obtained from get_colors() @@ -369,6 +398,7 @@ def set_color(self, ain, hsv, duration=0, mapped=True): else: # undocumented API method for free color selection self._aha_request("setunmappedcolor", ain=ain, param=params) + wait and self.wait_device_txbusy(ain) def get_color_temps(self, ain): """Get temperatures supported by this lightbulb.""" @@ -378,7 +408,7 @@ def get_color_temps(self, ain): temperatures.append(temp.get("value")) return temperatures - def set_color_temp(self, ain, temperature, duration=0): + def set_color_temp(self, ain, temperature, duration=0, wait=False): """Set color temperature. temperature: temperature element obtained from get_temperatures() @@ -386,23 +416,27 @@ def set_color_temp(self, ain, temperature, duration=0): """ params = {"temperature": int(temperature), "duration": int(duration) * 10} self._aha_request("setcolortemperature", ain=ain, param=params) + wait and self.wait_device_txbusy(ain) # blinds # states: open, close, stop def _set_blind_state(self, ain, state): self._aha_request("setblind", ain=ain, param={"target": state}) - def set_blind_open(self, ain): + def set_blind_open(self, ain, wait=False): """Set the blind state to open.""" self._set_blind_state(ain, "open") + wait and self.wait_device_txbusy(ain) - def set_blind_close(self, ain): + def set_blind_close(self, ain, wait=False): """Set the blind state to close.""" self._set_blind_state(ain, "close") + wait and self.wait_device_txbusy(ain) - def set_blind_stop(self, ain): + def set_blind_stop(self, ain, wait=False): """Set the blind state to stop.""" self._set_blind_state(ain, "stop") + wait and self.wait_device_txbusy(ain) # Template-related commands From 9c54db0cc629308aae3aaf1404ad592617f6e61b Mon Sep 17 00:00:00 2001 From: mib1185 Date: Sun, 18 Aug 2024 20:22:45 +0000 Subject: [PATCH 2/4] add tests --- tests/responses/base/device_not_txbusy.xml | 24 ++++++++++++++++++++++ tests/responses/base/device_txbusy.xml | 24 ++++++++++++++++++++++ tests/test_fritzhome.py | 16 +++++++++++++++ 3 files changed, 64 insertions(+) create mode 100644 tests/responses/base/device_not_txbusy.xml create mode 100644 tests/responses/base/device_txbusy.xml diff --git a/tests/responses/base/device_not_txbusy.xml b/tests/responses/base/device_not_txbusy.xml new file mode 100644 index 0000000..3b7fc48 --- /dev/null +++ b/tests/responses/base/device_not_txbusy.xml @@ -0,0 +1,24 @@ + + + 0 + 0 + Kitchen + + + + + + + + + + + + 0 + 0 + + 0 + 255 + + + \ No newline at end of file diff --git a/tests/responses/base/device_txbusy.xml b/tests/responses/base/device_txbusy.xml new file mode 100644 index 0000000..5d7e4e5 --- /dev/null +++ b/tests/responses/base/device_txbusy.xml @@ -0,0 +1,24 @@ + + + 0 + 1 + Kitchen + + + + + + + + + + + + 0 + 0 + + 0 + 255 + + + \ No newline at end of file diff --git a/tests/test_fritzhome.py b/tests/test_fritzhome.py index 7233870..4f87976 100644 --- a/tests/test_fritzhome.py +++ b/tests/test_fritzhome.py @@ -240,3 +240,19 @@ def test_set_boost_mode(self): "endtimestamp": 1000 + 25, }, ) + + def test_wait_tx_busy(self): + self.mock.side_effect = [ + Helper.response("base/device_txbusy"), + Helper.response("base/device_not_txbusy"), + ] + + assert self.fritz.wait_device_txbusy("11960 0089208") + + def test_wait_tx_busy_failed(self): + self.mock.side_effect = [ + Helper.response("base/device_txbusy"), + Helper.response("base/device_txbusy"), + ] + + assert not self.fritz.wait_device_txbusy("11960 0089208", 1) From efed73bbadc27c40d8e73cff0733beb3f792638c Mon Sep 17 00:00:00 2001 From: mib1185 Date: Sun, 18 Aug 2024 20:27:06 +0000 Subject: [PATCH 3/4] increase codeclimate argument-count threshold to 5 --- .codeclimate.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.codeclimate.yml b/.codeclimate.yml index feded49..c0db768 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -1,5 +1,8 @@ version: "2" checks: + argument-count: + config: + threshold: 5 method-complexity: config: threshold: 13 From caa54770d86fe708e2317109533030f9834143f0 Mon Sep 17 00:00:00 2001 From: Michael <35783820+mib1185@users.noreply.github.com> Date: Sun, 27 Oct 2024 20:20:38 +0100 Subject: [PATCH 4/4] increase sleep from 0.1 to 0.2 --- pyfritzhome/fritzhome.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyfritzhome/fritzhome.py b/pyfritzhome/fritzhome.py index bf68e18..43e1b51 100644 --- a/pyfritzhome/fritzhome.py +++ b/pyfritzhome/fritzhome.py @@ -205,7 +205,7 @@ def wait_device_txbusy(self, ain, retries=10): txbusy = dom.findall("txbusy") if txbusy[0].text == "0": return True - time.sleep(0.1) + time.sleep(0.2) return False def get_device_elements(self):