diff --git a/libdyson/dyson_pure_cool.py b/libdyson/dyson_pure_cool.py index 34c98c0..e637a1d 100644 --- a/libdyson/dyson_pure_cool.py +++ b/libdyson/dyson_pure_cool.py @@ -120,7 +120,9 @@ class DysonPureCool(DysonPureCoolBase): @property def oscillation(self) -> bool: """Return oscillation status.""" - return self._get_field_value(self._status, "oson") == "OION" + # Seems some devices use OION/OIOF while others uses ON/OFF + # https://github.com/shenxn/ha-dyson/issues/22 + return self._get_field_value(self._status, "oson") in ["OION", "ON"] @property def oscillation_angle_low(self) -> int: @@ -152,8 +154,13 @@ def enable_oscillation( "angle_high must be either equal to angle_low or at least 30 larger than angle_low" ) + current_oscillation_raw = self._get_field_value(self._status, "oson") + if current_oscillation_raw in ["OION", "OIOF"]: + oson = "OION" + else: + oson = "ON" self._set_configuration( - oson="OION", + oson=oson, fpwr="ON", ancp="CUST", osal=f"{angle_low:04d}", @@ -162,4 +169,9 @@ def enable_oscillation( def disable_oscillation(self) -> None: """Turn off oscillation.""" - self._set_configuration(oson="OIOF") + current_oscillation_raw = self._get_field_value(self._status, "oson") + if current_oscillation_raw in ["OION", "OIOF"]: + oson = "OIOF" + else: + oson = "OFF" + self._set_configuration(oson=oson) diff --git a/tests/test_fan_device.py b/tests/test_fan_device.py index a94d5ca..cb4aead 100644 --- a/tests/test_fan_device.py +++ b/tests/test_fan_device.py @@ -1,6 +1,7 @@ """Tests for DysonFanDevice.""" import json +from typing import Optional from unittest.mock import MagicMock import pytest @@ -104,9 +105,12 @@ def assert_command( command: str, command_args: list, msg_data: dict, + new_status: Optional[dict] = None, ): """Test commands of fan device.""" device.connect(HOST) + if new_status is not None: + mqtt_client.state_change(new_status) func = getattr(device, command) func(*command_args) assert len(mqtt_client.commands) == 1 diff --git a/tests/test_pure_cool.py b/tests/test_pure_cool.py index 02024a7..f60e37d 100644 --- a/tests/test_pure_cool.py +++ b/tests/test_pure_cool.py @@ -120,6 +120,21 @@ def test_properties(mqtt_client: MockedMQTT): assert device.carbon_filter_life is None assert device.hepa_filter_life == 80 + new_status = { + "product-state": { + "oson": ["ON", "OFF"], + }, + } + mqtt_client.state_change(new_status) + assert device.oscillation is False + new_status = { + "product-state": { + "oson": ["OFF", "ON"], + }, + } + mqtt_client.state_change(new_status) + assert device.oscillation is True + mqtt_client._environmental_data = { "data": { "tact": "2977", @@ -204,6 +219,39 @@ def test_command( ) +def test_oscillation_ON_OFF(mqtt_client: MockedMQTT): + """Test enable and disable oscillation with ON/OFF instead of OION/OIOFF.""" + new_status = { + "product-state": { + "oson": ["OIOF", "ON"], + "osal": ["0063", "0063"], + "osau": ["0243", "0243"], + }, + } + assert_command( + DysonPureCool(SERIAL, CREDENTIAL, DEVICE_TYPE), + mqtt_client, + "enable_oscillation", + [], + { + "oson": "ON", + "fpwr": "ON", + "ancp": "CUST", + "osal": "0063", + "osau": "0243", + }, + new_status, + ) + assert_command( + DysonPureCool(SERIAL, CREDENTIAL, DEVICE_TYPE), + mqtt_client, + "disable_oscillation", + [], + {"oson": "OFF"}, + new_status, + ) + + @pytest.mark.parametrize( "angle_low,angle_high", [