Просмотр исходного кода

Remove remaining use of legacy_class.

Dehumidifier climate entity is deprecated and the dehumidifier is working as humidifier and fan entities without the features that aren't yet implemented.
So its removal along with test cases that cannot be covered by the generic climate implementation is not much of a loss.

Remove the now unused legacy_class functionality.
Jason Rumney 4 лет назад
Родитель
Сommit
f608e8e671

+ 3 - 5
README.md

@@ -340,11 +340,9 @@ You can find these keys the same way as you would for any Tuya local integration
 
 ## Next steps
 
-1. Remove the need for custom classes for gpph heater and goldair dehumidifier.
-These devices from upstream have some complex logic that currently cannot be represented in the config files.  Find a way to configure this logic so the last of the legacy code can be removed.
-2. This component is mosty unit-tested thanks to the upstream project, but there are a few more to complete. Feel free to use existing specs as inspiration and the Sonar Cloud analysis to see where the gaps are.
-3. Once unit tests are complete, the next task is to complete the Home Assistant quality checklist before considering submission to the HA team for inclusion in standard installations.
-4. Discovery seems possible with the new tinytuya library, though the steps to get a local key will most likely remain manual.  Discovery also returns a productKey, which might help make the device detection more reliable where different devices use the same dps mapping but different names for the presets for example.
+1. This component is mosty unit-tested thanks to the upstream project, but there are a few more to complete. Feel free to use existing specs as inspiration and the Sonar Cloud analysis to see where the gaps are.
+2. Once unit tests are complete, the next task is to complete the Home Assistant quality checklist before considering submission to the HA team for inclusion in standard installations.
+3. Discovery seems possible with the new tinytuya library, though the steps to get a local key will most likely remain manual.  Discovery also returns a productKey, which might help make the device detection more reliable where different devices use the same dps mapping but different names for the presets for example.
 
 Please report any issues and feel free to raise pull requests.
 [Many others](https://github.com/make-all/tuya-local/blob/main/ACKNOWLEDGEMENTS.md) have contributed their help already.

+ 2 - 10
custom_components/tuya_local/climate.py

@@ -25,11 +25,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
         raise ValueError(f"No device config found for {discovery_info}")
     ecfg = cfg.primary_entity
     if ecfg.entity == "climate" and discovery_info.get(ecfg.config_id, False):
-        legacy_class = ecfg.legacy_class
-        if legacy_class is None:
-            data[ecfg.config_id] = TuyaLocalClimate(device, ecfg)
-        else:
-            data[ecfg.config_id] = legacy_class(device)
+        data[ecfg.config_id] = TuyaLocalClimate(device, ecfg)
         climates.append(data[ecfg.config_id])
         if ecfg.deprecated:
             _LOGGER.warning(ecfg.deprecation_message)
@@ -37,11 +33,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
 
     for ecfg in cfg.secondary_entities():
         if ecfg.entity == "climate" and discovery_info.get(ecfg.config_id, False):
-            legacy_class = ecfg.legacy_class
-            if legacy_class is None:
-                data[ecfg.config_id] = TuyaLocalClimate(device, ecfg)
-            else:
-                data[ecfg.config_id] = legacy_class(device)
+            data[ecfg.config_id] = TuyaLocalClimate(device, ecfg)
             climates.append(data[ecfg.config_id])
             if ecfg.deprecated:
                 _LOGGER.warning(ecfg.deprecation_message)

+ 0 - 0
custom_components/tuya_local/dehumidifier/__init__.py


+ 0 - 298
custom_components/tuya_local/dehumidifier/climate.py

@@ -1,298 +0,0 @@
-"""
-Goldair WiFi Dehumidifier device.
-"""
-from homeassistant.components.climate import ClimateEntity
-from homeassistant.components.climate.const import (
-    ATTR_FAN_MODE,
-    ATTR_HUMIDITY,
-    ATTR_HVAC_MODE,
-    ATTR_PRESET_MODE,
-    FAN_HIGH,
-    FAN_LOW,
-    HVAC_MODE_OFF,
-    SUPPORT_FAN_MODE,
-    SUPPORT_PRESET_MODE,
-    SUPPORT_TARGET_HUMIDITY,
-)
-from homeassistant.const import ATTR_TEMPERATURE, STATE_UNAVAILABLE
-
-from ..device import TuyaLocalDevice
-from .const import (
-    ATTR_AIR_CLEAN_ON,
-    ATTR_DEFROSTING,
-    ATTR_ERROR,
-    ATTR_ERROR_CODE,
-    ATTR_TARGET_HUMIDITY,
-    ERROR_CODE_TO_DPS_CODE,
-    ERROR_TANK,
-    FAN_MODE_TO_DPS_MODE,
-    HVAC_MODE_TO_DPS_MODE,
-    PRESET_AIR_CLEAN,
-    PRESET_DRY_CLOTHES,
-    PRESET_HIGH,
-    PRESET_LOW,
-    PRESET_MODE_TO_DPS_MODE,
-    PRESET_NORMAL,
-    PROPERTY_TO_DPS_ID,
-)
-
-SUPPORT_FLAGS = SUPPORT_TARGET_HUMIDITY | SUPPORT_PRESET_MODE | SUPPORT_FAN_MODE
-
-
-class GoldairDehumidifier(ClimateEntity):
-    """Representation of a Goldair WiFi dehumidifier."""
-
-    def __init__(self, device):
-        """Initialize the dehumidifier.
-        Args:
-            name (str): The device's name.
-            device (TuyaLocalDevice): The device API instance."""
-        self._device = device
-
-        self._support_flags = SUPPORT_FLAGS
-
-        self._HUMIDITY_STEP = 5
-        self._HUMIDITY_LIMITS = {"min": 30, "max": 80}
-
-    @property
-    def supported_features(self):
-        """Return the list of supported features."""
-        return self._support_flags
-
-    @property
-    def should_poll(self):
-        """Return the polling state."""
-        return True
-
-    @property
-    def name(self):
-        """Return the name of the climate device."""
-        return self._device.name
-
-    @property
-    def unique_id(self):
-        """Return the unique id for this dehumidifier."""
-        return self._device.unique_id
-
-    @property
-    def device_info(self):
-        """Return device information about this dehumidifier."""
-        return self._device.device_info
-
-    @property
-    def icon(self):
-        """Return the icon to use in the frontend based on the device state."""
-        if self.tank_full_or_missing:
-            return "mdi:cup-water"
-        elif self.defrosting:
-            return "mdi:snowflake-melt"
-        elif (
-            self.hvac_mode is not HVAC_MODE_OFF
-            and self.preset_mode is PRESET_DRY_CLOTHES
-        ):
-            return "mdi:tshirt-crew-outline"
-        elif (
-            self.hvac_mode is not HVAC_MODE_OFF and self.preset_mode is PRESET_AIR_CLEAN
-        ):
-            return "mdi:air-purifier"
-        else:
-            return "mdi:air-humidifier"
-
-    @property
-    def current_humidity(self):
-        """Return the current reading of the humidity sensor."""
-        return self._device.get_property(PROPERTY_TO_DPS_ID[ATTR_HUMIDITY])
-
-    @property
-    def min_humidity(self):
-        """Return the minimum humidity setting."""
-        return self._HUMIDITY_LIMITS["min"]
-
-    @property
-    def max_humidity(self):
-        """Return the maximum humidity setting."""
-        return self._HUMIDITY_LIMITS["max"]
-
-    @property
-    def target_humidity(self):
-        """Return the current target humidity."""
-        if self.preset_mode is PRESET_NORMAL:
-            return self._device.get_property(PROPERTY_TO_DPS_ID[ATTR_TARGET_HUMIDITY])
-        else:
-            return None
-
-    async def async_set_humidity(self, humidity):
-        """Set the device's target humidity."""
-        if self.preset_mode is not PRESET_NORMAL:
-            raise ValueError(
-                "Target humidity can only be changed while in Normal mode."
-            )
-        humidity = int(
-            self._HUMIDITY_STEP * round(float(humidity) / self._HUMIDITY_STEP)
-        )
-        await self._device.async_set_property(
-            PROPERTY_TO_DPS_ID[ATTR_TARGET_HUMIDITY], humidity
-        )
-
-    @property
-    def temperature_unit(self):
-        """Return the unit of measurement."""
-        return self._device.temperature_unit
-
-    @property
-    def min_temp(self):
-        """Return the minimum temperature setting."""
-        return None
-
-    @property
-    def max_temp(self):
-        """Return the maximum temperature setting."""
-        return None
-
-    @property
-    def current_temperature(self):
-        """Return the current temperature."""
-        return self._device.get_property(PROPERTY_TO_DPS_ID[ATTR_TEMPERATURE])
-
-    @property
-    def hvac_mode(self):
-        """Return current HVAC mode, ie Dry or Off."""
-        dps_mode = self._device.get_property(PROPERTY_TO_DPS_ID[ATTR_HVAC_MODE])
-
-        if dps_mode is not None:
-            return TuyaLocalDevice.get_key_for_value(HVAC_MODE_TO_DPS_MODE, dps_mode)
-        else:
-            return STATE_UNAVAILABLE
-
-    @property
-    def hvac_modes(self):
-        """Return the list of available HVAC modes."""
-        return list(HVAC_MODE_TO_DPS_MODE.keys())
-
-    async def async_set_hvac_mode(self, hvac_mode):
-        """Set new HVAC mode."""
-        dps_mode = HVAC_MODE_TO_DPS_MODE[hvac_mode]
-        await self._device.async_set_property(
-            PROPERTY_TO_DPS_ID[ATTR_HVAC_MODE], dps_mode
-        )
-
-    @property
-    def preset_mode(self):
-        """Return current preset mode, ie Normal, Low, High, Dry Clothes, or Air Clean."""
-        air_clean_on = self._device.get_property(PROPERTY_TO_DPS_ID[ATTR_AIR_CLEAN_ON])
-        dps_mode = self._device.get_property(PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE])
-
-        if air_clean_on:
-            return PRESET_AIR_CLEAN
-        elif dps_mode is not None:
-            return TuyaLocalDevice.get_key_for_value(PRESET_MODE_TO_DPS_MODE, dps_mode)
-        else:
-            return None
-
-    @property
-    def preset_modes(self):
-        """Return the list of available preset modes."""
-        return list(PRESET_MODE_TO_DPS_MODE.keys()) + [PRESET_AIR_CLEAN]
-
-    async def async_set_preset_mode(self, preset_mode):
-        """Set new preset mode."""
-        if preset_mode == PRESET_AIR_CLEAN:
-            await self._device.async_set_property(
-                PROPERTY_TO_DPS_ID[ATTR_AIR_CLEAN_ON], True
-            )
-            self._device.anticipate_property_value(
-                PROPERTY_TO_DPS_ID[ATTR_FAN_MODE], FAN_HIGH
-            )
-        else:
-            dps_mode = PRESET_MODE_TO_DPS_MODE[preset_mode]
-            await self._device.async_set_property(
-                PROPERTY_TO_DPS_ID[ATTR_AIR_CLEAN_ON], False
-            )
-            await self._device.async_set_property(
-                PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE], dps_mode
-            )
-            if preset_mode == PRESET_LOW:
-                self._device.anticipate_property_value(
-                    PROPERTY_TO_DPS_ID[ATTR_FAN_MODE], FAN_LOW
-                )
-            elif preset_mode in [PRESET_HIGH, PRESET_DRY_CLOTHES]:
-                self._device.anticipate_property_value(
-                    PROPERTY_TO_DPS_ID[ATTR_FAN_MODE], FAN_HIGH
-                )
-
-    @property
-    def fan_mode(self):
-        """Return the fan mode."""
-        preset = self.preset_mode
-
-        if preset in [PRESET_HIGH, PRESET_DRY_CLOTHES, PRESET_AIR_CLEAN]:
-            return FAN_HIGH
-        elif preset == PRESET_LOW:
-            return FAN_LOW
-        else:
-            dps_mode = self._device.get_property(PROPERTY_TO_DPS_ID[ATTR_FAN_MODE])
-            if dps_mode is not None:
-                return TuyaLocalDevice.get_key_for_value(FAN_MODE_TO_DPS_MODE, dps_mode)
-            else:
-                return None
-
-    @property
-    def fan_modes(self):
-        """List of fan modes."""
-        preset = self.preset_mode
-
-        if preset in [PRESET_HIGH, PRESET_DRY_CLOTHES, PRESET_AIR_CLEAN]:
-            return [FAN_HIGH]
-        elif preset == PRESET_LOW:
-            return [FAN_LOW]
-        elif preset == PRESET_NORMAL:
-            return list(FAN_MODE_TO_DPS_MODE.keys())
-        else:
-            return []
-
-    async def async_set_fan_mode(self, fan_mode):
-        """Set new fan mode."""
-        if self.preset_mode != PRESET_NORMAL:
-            raise ValueError(
-                "Fan mode can only be changed while in Normal preset mode."
-            )
-
-        if fan_mode not in FAN_MODE_TO_DPS_MODE.keys():
-            raise ValueError(f"Invalid fan mode: {fan_mode}")
-
-        dps_mode = FAN_MODE_TO_DPS_MODE[fan_mode]
-        await self._device.async_set_property(
-            PROPERTY_TO_DPS_ID[ATTR_FAN_MODE], dps_mode
-        )
-
-    @property
-    def tank_full_or_missing(self):
-        error = self._device.get_property(PROPERTY_TO_DPS_ID[ATTR_ERROR])
-        return (
-            TuyaLocalDevice.get_key_for_value(ERROR_CODE_TO_DPS_CODE, error)
-            == ERROR_TANK
-        )
-
-    @property
-    def defrosting(self):
-        return self._device.get_property(PROPERTY_TO_DPS_ID[ATTR_DEFROSTING])
-
-    @property
-    def device_state_attributes(self):
-        """Get additional attributes that HA doesn't naturally support."""
-        error_code = self._device.get_property(PROPERTY_TO_DPS_ID[ATTR_ERROR])
-        if isinstance(error_code, int):
-            error = TuyaLocalDevice.get_key_for_value(
-                ERROR_CODE_TO_DPS_CODE, error_code, f"Error {error_code}"
-            )
-        else:
-            error = STATE_UNAVAILABLE
-
-        return {
-            ATTR_ERROR: error,
-            ATTR_ERROR_CODE: error_code,
-            ATTR_DEFROSTING: self.defrosting,
-        }
-
-    async def async_update(self):
-        await self._device.async_refresh()

+ 0 - 52
custom_components/tuya_local/dehumidifier/const.py

@@ -1,52 +0,0 @@
-from homeassistant.components.climate.const import (
-    ATTR_FAN_MODE,
-    ATTR_HUMIDITY,
-    ATTR_HVAC_MODE,
-    ATTR_PRESET_MODE,
-    FAN_HIGH,
-    FAN_LOW,
-    HVAC_MODE_DRY,
-    HVAC_MODE_OFF,
-)
-from homeassistant.const import ATTR_TEMPERATURE
-
-ATTR_TARGET_HUMIDITY = "target_humidity"
-ATTR_AIR_CLEAN_ON = "air_clean_on"
-ATTR_CHILD_LOCK = "child_lock"
-ATTR_ERROR = "error"
-ATTR_ERROR_CODE = "error_code"
-ATTR_DISPLAY_OFF = "display_off"
-ATTR_DEFROSTING = "defrosting"
-
-PRESET_NORMAL = "Normal"
-PRESET_LOW = "Low"
-PRESET_HIGH = "High"
-PRESET_DRY_CLOTHES = "Dry clothes"
-PRESET_AIR_CLEAN = "Air clean"
-
-ERROR_NONE = "OK"
-ERROR_TANK = "Tank full or missing"
-
-PROPERTY_TO_DPS_ID = {
-    ATTR_HVAC_MODE: "1",
-    ATTR_PRESET_MODE: "2",
-    ATTR_TARGET_HUMIDITY: "4",
-    ATTR_AIR_CLEAN_ON: "5",
-    ATTR_FAN_MODE: "6",
-    ATTR_CHILD_LOCK: "7",
-    ATTR_ERROR: "11",
-    ATTR_DISPLAY_OFF: "102",
-    ATTR_TEMPERATURE: "103",
-    ATTR_HUMIDITY: "104",
-    ATTR_DEFROSTING: "105",
-}
-
-HVAC_MODE_TO_DPS_MODE = {HVAC_MODE_OFF: False, HVAC_MODE_DRY: True}
-PRESET_MODE_TO_DPS_MODE = {
-    PRESET_NORMAL: "0",
-    PRESET_LOW: "1",
-    PRESET_HIGH: "2",
-    PRESET_DRY_CLOTHES: "3",
-}
-FAN_MODE_TO_DPS_MODE = {FAN_LOW: "1", FAN_HIGH: "3"}
-ERROR_CODE_TO_DPS_CODE = {ERROR_NONE: 0, ERROR_TANK: 8}

+ 0 - 9
custom_components/tuya_local/devices/README.md

@@ -75,15 +75,6 @@ devices until now and may need to be extended for new devices.  In
 particular, the light and lock entities have only been used for simple
 secondary entities, so only basic functionality is implemented.
 
-### `legacy_class`
-
-//Optional, deprecated.//
-
-The `legacy_class` is a transitional link back to the device specific
-class that contains the implementation of this device. This allows
-a transition for more complex devices that are not yet fully supported
-by the generic implementations. It should not be used for new devices.
-
 ### `deprecated`
 
 //Optional//

+ 0 - 1
custom_components/tuya_local/devices/goldair_dehumidifier.yaml

@@ -104,7 +104,6 @@ secondary_entities:
               - dps_val: "1"
                 invalid: true
   - entity: climate
-    legacy_class: ".dehumidifier.climate.GoldairDehumidifier"
     name: Dehumidifier as Climate
     deprecated: humidifier and fan
     dps:

+ 0 - 8
custom_components/tuya_local/helpers/device_config.py

@@ -160,14 +160,6 @@ class TuyaEntityConfig:
         else:
             return device_uid
 
-    @property
-    def legacy_class(self):
-        """Return the legacy device corresponding to this config."""
-        name = self._config.get("legacy_class")
-        if name is None:
-            return None
-        return locate("custom_components.tuya_local" + name)
-
     @property
     def entity_category(self):
         if self._is_primary:

+ 0 - 0
tests/dehumidifier/__init__.py


+ 0 - 603
tests/dehumidifier/test_climate.py

@@ -1,603 +0,0 @@
-from unittest import IsolatedAsyncioTestCase
-from unittest.mock import AsyncMock, patch
-
-from homeassistant.components.climate.const import (
-    ATTR_FAN_MODE,
-    ATTR_HUMIDITY,
-    ATTR_HVAC_MODE,
-    ATTR_PRESET_MODE,
-    FAN_HIGH,
-    FAN_LOW,
-    HVAC_MODE_DRY,
-    HVAC_MODE_OFF,
-    SUPPORT_FAN_MODE,
-    SUPPORT_PRESET_MODE,
-    SUPPORT_TARGET_HUMIDITY,
-)
-from homeassistant.const import ATTR_TEMPERATURE, STATE_UNAVAILABLE
-
-from custom_components.tuya_local.dehumidifier.climate import GoldairDehumidifier
-from custom_components.tuya_local.dehumidifier.const import (
-    ATTR_AIR_CLEAN_ON,
-    ATTR_DEFROSTING,
-    ATTR_ERROR,
-    ATTR_ERROR_CODE,
-    ATTR_TARGET_HUMIDITY,
-    ERROR_CODE_TO_DPS_CODE,
-    ERROR_TANK,
-    FAN_MODE_TO_DPS_MODE,
-    HVAC_MODE_TO_DPS_MODE,
-    PRESET_AIR_CLEAN,
-    PRESET_DRY_CLOTHES,
-    PRESET_HIGH,
-    PRESET_LOW,
-    PRESET_MODE_TO_DPS_MODE,
-    PRESET_NORMAL,
-    PROPERTY_TO_DPS_ID,
-)
-
-from ..const import DEHUMIDIFIER_PAYLOAD
-from ..helpers import assert_device_properties_set
-
-
-class TestGoldairDehumidifier(IsolatedAsyncioTestCase):
-    def setUp(self):
-        device_patcher = patch("custom_components.tuya_local.device.TuyaLocalDevice")
-        self.addCleanup(device_patcher.stop)
-        self.mock_device = device_patcher.start()
-
-        self.subject = GoldairDehumidifier(self.mock_device())
-
-        self.dps = DEHUMIDIFIER_PAYLOAD.copy()
-        self.subject._device.get_property.side_effect = lambda id: self.dps[id]
-
-    def test_supported_features(self):
-        self.assertEqual(
-            self.subject.supported_features,
-            SUPPORT_TARGET_HUMIDITY | SUPPORT_PRESET_MODE | SUPPORT_FAN_MODE,
-        )
-
-    def test_should_poll(self):
-        self.assertTrue(self.subject.should_poll)
-
-    def test_name_returns_device_name(self):
-        self.assertEqual(self.subject.name, self.subject._device.name)
-
-    def test_unique_id_returns_device_unique_id(self):
-        self.assertEqual(self.subject.unique_id, self.subject._device.unique_id)
-
-    def test_device_info_returns_device_info_from_device(self):
-        self.assertEqual(self.subject.device_info, self.subject._device.device_info)
-
-    def test_icon_is_always_standard_when_off_without_error(self):
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_ERROR]] = None
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_HVAC_MODE]] = False
-
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_AIR_CLEAN_ON]] = False
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]] = PRESET_MODE_TO_DPS_MODE[
-            PRESET_DRY_CLOTHES
-        ]
-        self.assertEqual(self.subject.icon, "mdi:air-humidifier")
-
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_AIR_CLEAN_ON]] = True
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]] = PRESET_MODE_TO_DPS_MODE[
-            PRESET_NORMAL
-        ]
-        self.assertEqual(self.subject.icon, "mdi:air-humidifier")
-
-    def test_icon_is_purifier_when_air_clean_is_active(self):
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_ERROR]] = None
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_HVAC_MODE]] = True
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_AIR_CLEAN_ON]] = True
-
-        self.assertEqual(self.subject.icon, "mdi:air-purifier")
-
-    def test_icon_is_tshirt_when_dry_clothes_is_active(self):
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_ERROR]] = None
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_HVAC_MODE]] = True
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]] = PRESET_MODE_TO_DPS_MODE[
-            PRESET_DRY_CLOTHES
-        ]
-
-        self.assertEqual(self.subject.icon, "mdi:tshirt-crew-outline")
-
-    def test_icon_is_always_melting_snowflake_when_defrosting_and_tank_not_full(self):
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_DEFROSTING]] = True
-
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_HVAC_MODE]] = False
-        self.assertEqual(self.subject.icon, "mdi:snowflake-melt")
-
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_HVAC_MODE]] = True
-        self.assertEqual(self.subject.icon, "mdi:snowflake-melt")
-
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]] = PRESET_MODE_TO_DPS_MODE[
-            PRESET_DRY_CLOTHES
-        ]
-        self.assertEqual(self.subject.icon, "mdi:snowflake-melt")
-
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_AIR_CLEAN_ON]] = True
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]] = PRESET_MODE_TO_DPS_MODE[
-            PRESET_NORMAL
-        ]
-        self.assertEqual(self.subject.icon, "mdi:snowflake-melt")
-
-    def test_icon_is_always_tank_when_tank_full_error_is_present(self):
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_ERROR]] = ERROR_CODE_TO_DPS_CODE[ERROR_TANK]
-
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_HVAC_MODE]] = False
-        self.assertEqual(self.subject.icon, "mdi:cup-water")
-
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_HVAC_MODE]] = True
-        self.assertEqual(self.subject.icon, "mdi:cup-water")
-
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]] = PRESET_MODE_TO_DPS_MODE[
-            PRESET_DRY_CLOTHES
-        ]
-        self.assertEqual(self.subject.icon, "mdi:cup-water")
-
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_AIR_CLEAN_ON]] = True
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]] = PRESET_MODE_TO_DPS_MODE[
-            PRESET_NORMAL
-        ]
-        self.assertEqual(self.subject.icon, "mdi:cup-water")
-
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_DEFROSTING]] = True
-        self.assertEqual(self.subject.icon, "mdi:cup-water")
-
-    def test_current_humidity(self):
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_HUMIDITY]] = 47
-        self.assertEqual(self.subject.current_humidity, 47)
-
-    def test_min_target_humidity(self):
-        self.assertEqual(self.subject.min_humidity, 30)
-
-    def test_max_target_humidity(self):
-        self.assertEqual(self.subject.max_humidity, 80)
-
-    def test_target_humidity_in_normal_preset(self):
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]] = PRESET_MODE_TO_DPS_MODE[
-            PRESET_NORMAL
-        ]
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_TARGET_HUMIDITY]] = 53
-
-        self.assertEqual(self.subject.target_humidity, 53)
-
-    def test_target_humidity_outside_normal_preset(self):
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_TARGET_HUMIDITY]] = 53
-
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]] = PRESET_MODE_TO_DPS_MODE[
-            PRESET_HIGH
-        ]
-        self.assertIs(self.subject.target_humidity, None)
-
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]] = PRESET_MODE_TO_DPS_MODE[
-            PRESET_LOW
-        ]
-        self.assertIs(self.subject.target_humidity, None)
-
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]] = PRESET_MODE_TO_DPS_MODE[
-            PRESET_DRY_CLOTHES
-        ]
-        self.assertIs(self.subject.target_humidity, None)
-
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]] = PRESET_MODE_TO_DPS_MODE[
-            PRESET_NORMAL
-        ]
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_AIR_CLEAN_ON]] = True
-        self.assertIs(self.subject.target_humidity, None)
-
-    async def test_set_target_humidity_in_normal_preset_rounds_up_to_5_percent(self):
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]] = PRESET_MODE_TO_DPS_MODE[
-            PRESET_NORMAL
-        ]
-
-        async with assert_device_properties_set(
-            self.subject._device,
-            {PROPERTY_TO_DPS_ID[ATTR_TARGET_HUMIDITY]: 55},
-        ):
-            await self.subject.async_set_humidity(53)
-
-    async def test_set_target_humidity_in_normal_preset_rounds_down_to_5_percent(self):
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]] = PRESET_MODE_TO_DPS_MODE[
-            PRESET_NORMAL
-        ]
-
-        async with assert_device_properties_set(
-            self.subject._device,
-            {PROPERTY_TO_DPS_ID[ATTR_TARGET_HUMIDITY]: 50},
-        ):
-            await self.subject.async_set_humidity(52)
-
-    async def test_set_target_humidity_raises_error_outside_of_normal_preset(self):
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]] = PRESET_MODE_TO_DPS_MODE[
-            PRESET_LOW
-        ]
-        with self.assertRaisesRegex(
-            ValueError, "Target humidity can only be changed while in Normal mode"
-        ):
-            await self.subject.async_set_humidity(50)
-
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]] = PRESET_MODE_TO_DPS_MODE[
-            PRESET_HIGH
-        ]
-        with self.assertRaisesRegex(
-            ValueError, "Target humidity can only be changed while in Normal mode"
-        ):
-            await self.subject.async_set_humidity(50)
-
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]] = PRESET_MODE_TO_DPS_MODE[
-            PRESET_LOW
-        ]
-        with self.assertRaisesRegex(
-            ValueError, "Target humidity can only be changed while in Normal mode"
-        ):
-            await self.subject.async_set_humidity(50)
-
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]] = PRESET_MODE_TO_DPS_MODE[
-            PRESET_DRY_CLOTHES
-        ]
-        with self.assertRaisesRegex(
-            ValueError, "Target humidity can only be changed while in Normal mode"
-        ):
-            await self.subject.async_set_humidity(50)
-
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]] = PRESET_MODE_TO_DPS_MODE[
-            PRESET_NORMAL
-        ]
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_AIR_CLEAN_ON]] = True
-        with self.assertRaisesRegex(
-            ValueError, "Target humidity can only be changed while in Normal mode"
-        ):
-            await self.subject.async_set_humidity(50)
-
-    def test_temperature_unit_returns_device_temperature_unit(self):
-        self.assertEqual(
-            self.subject.temperature_unit, self.subject._device.temperature_unit
-        )
-
-    def test_minimum_target_temperature(self):
-        self.assertIs(self.subject.min_temp, None)
-
-    def test_maximum_target_temperature(self):
-        self.assertIs(self.subject.max_temp, None)
-
-    def test_current_temperature(self):
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_TEMPERATURE]] = 25
-        self.assertEqual(self.subject.current_temperature, 25)
-
-    def test_hvac_mode(self):
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_HVAC_MODE]] = True
-        self.assertEqual(self.subject.hvac_mode, HVAC_MODE_DRY)
-
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_HVAC_MODE]] = False
-        self.assertEqual(self.subject.hvac_mode, HVAC_MODE_OFF)
-
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_HVAC_MODE]] = None
-        self.assertEqual(self.subject.hvac_mode, STATE_UNAVAILABLE)
-
-    def test_hvac_modes(self):
-        self.assertEqual(self.subject.hvac_modes, [HVAC_MODE_OFF, HVAC_MODE_DRY])
-
-    async def test_turn_on(self):
-        async with assert_device_properties_set(
-            self.subject._device, {PROPERTY_TO_DPS_ID[ATTR_HVAC_MODE]: True}
-        ):
-            await self.subject.async_set_hvac_mode(HVAC_MODE_DRY)
-
-    async def test_turn_off(self):
-        async with assert_device_properties_set(
-            self.subject._device, {PROPERTY_TO_DPS_ID[ATTR_HVAC_MODE]: False}
-        ):
-            await self.subject.async_set_hvac_mode(HVAC_MODE_OFF)
-
-    def test_preset_mode(self):
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]] = PRESET_MODE_TO_DPS_MODE[
-            PRESET_NORMAL
-        ]
-        self.assertEqual(self.subject.preset_mode, PRESET_NORMAL)
-
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]] = PRESET_MODE_TO_DPS_MODE[
-            PRESET_HIGH
-        ]
-        self.assertEqual(self.subject.preset_mode, PRESET_HIGH)
-
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]] = PRESET_MODE_TO_DPS_MODE[
-            PRESET_LOW
-        ]
-        self.assertEqual(self.subject.preset_mode, PRESET_LOW)
-
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]] = PRESET_MODE_TO_DPS_MODE[
-            PRESET_DRY_CLOTHES
-        ]
-        self.assertEqual(self.subject.preset_mode, PRESET_DRY_CLOTHES)
-
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]] = None
-        self.assertEqual(self.subject.preset_mode, None)
-
-    def test_air_clean_is_surfaced_in_preset_mode(self):
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]] = PRESET_MODE_TO_DPS_MODE[
-            PRESET_DRY_CLOTHES
-        ]
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_AIR_CLEAN_ON]] = True
-
-        self.assertEqual(self.subject.preset_mode, PRESET_AIR_CLEAN)
-
-    def test_preset_modes(self):
-        self.assertEqual(
-            self.subject.preset_modes,
-            [
-                PRESET_NORMAL,
-                PRESET_LOW,
-                PRESET_HIGH,
-                PRESET_DRY_CLOTHES,
-                PRESET_AIR_CLEAN,
-            ],
-        )
-
-    async def test_set_test_preset_mode_to_normal(self):
-        async with assert_device_properties_set(
-            self.subject._device,
-            {
-                PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]: PRESET_MODE_TO_DPS_MODE[
-                    PRESET_NORMAL
-                ],
-                PROPERTY_TO_DPS_ID[ATTR_AIR_CLEAN_ON]: False,
-            },
-        ):
-            await self.subject.async_set_preset_mode(PRESET_NORMAL)
-            self.subject._device.anticipate_property_value.assert_not_called()
-
-    async def test_set_test_preset_mode_to_low(self):
-        async with assert_device_properties_set(
-            self.subject._device,
-            {
-                PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]: PRESET_MODE_TO_DPS_MODE[
-                    PRESET_LOW
-                ],
-                PROPERTY_TO_DPS_ID[ATTR_AIR_CLEAN_ON]: False,
-            },
-        ):
-            await self.subject.async_set_preset_mode(PRESET_LOW)
-            self.subject._device.anticipate_property_value.assert_called_once_with(
-                PROPERTY_TO_DPS_ID[ATTR_FAN_MODE], FAN_LOW
-            )
-
-    async def test_set_test_preset_mode_to_high(self):
-        async with assert_device_properties_set(
-            self.subject._device,
-            {
-                PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]: PRESET_MODE_TO_DPS_MODE[
-                    PRESET_HIGH
-                ],
-                PROPERTY_TO_DPS_ID[ATTR_AIR_CLEAN_ON]: False,
-            },
-        ):
-            await self.subject.async_set_preset_mode(PRESET_HIGH)
-            self.subject._device.anticipate_property_value.assert_called_once_with(
-                PROPERTY_TO_DPS_ID[ATTR_FAN_MODE], FAN_HIGH
-            )
-
-    async def test_set_test_preset_mode_to_dry_clothes(self):
-        async with assert_device_properties_set(
-            self.subject._device,
-            {
-                PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]: PRESET_MODE_TO_DPS_MODE[
-                    PRESET_DRY_CLOTHES
-                ],
-                PROPERTY_TO_DPS_ID[ATTR_AIR_CLEAN_ON]: False,
-            },
-        ):
-            await self.subject.async_set_preset_mode(PRESET_DRY_CLOTHES)
-            self.subject._device.anticipate_property_value.assert_called_once_with(
-                PROPERTY_TO_DPS_ID[ATTR_FAN_MODE], FAN_HIGH
-            )
-
-    async def test_set_test_preset_mode_to_air_clean(self):
-        async with assert_device_properties_set(
-            self.subject._device, {PROPERTY_TO_DPS_ID[ATTR_AIR_CLEAN_ON]: True}
-        ):
-            await self.subject.async_set_preset_mode(PRESET_AIR_CLEAN)
-            self.subject._device.anticipate_property_value.assert_called_once_with(
-                PROPERTY_TO_DPS_ID[ATTR_FAN_MODE], FAN_HIGH
-            )
-
-    def test_fan_mode_is_forced_to_high_in_high_dry_clothes_air_clean_presets(self):
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_FAN_MODE]] = FAN_MODE_TO_DPS_MODE[FAN_LOW]
-
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]] = PRESET_MODE_TO_DPS_MODE[
-            PRESET_HIGH
-        ]
-        self.assertEqual(self.subject.fan_mode, FAN_HIGH)
-
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]] = PRESET_MODE_TO_DPS_MODE[
-            PRESET_DRY_CLOTHES
-        ]
-        self.assertEqual(self.subject.fan_mode, FAN_HIGH)
-
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]] = PRESET_MODE_TO_DPS_MODE[
-            PRESET_NORMAL
-        ]
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_AIR_CLEAN_ON]] = True
-        self.assertEqual(self.subject.fan_mode, FAN_HIGH)
-
-    def test_fan_mode_is_forced_to_low_in_low_preset(self):
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_FAN_MODE]] = FAN_MODE_TO_DPS_MODE[FAN_HIGH]
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]] = PRESET_MODE_TO_DPS_MODE[
-            PRESET_LOW
-        ]
-
-        self.assertEqual(self.subject.fan_mode, FAN_LOW)
-
-    def test_fan_mode_reflects_dps_mode_in_normal_preset(self):
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]] = PRESET_MODE_TO_DPS_MODE[
-            PRESET_NORMAL
-        ]
-
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_FAN_MODE]] = FAN_MODE_TO_DPS_MODE[FAN_LOW]
-        self.assertEqual(self.subject.fan_mode, FAN_LOW)
-
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_FAN_MODE]] = FAN_MODE_TO_DPS_MODE[FAN_HIGH]
-        self.assertEqual(self.subject.fan_mode, FAN_HIGH)
-
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_FAN_MODE]] = None
-        self.assertEqual(self.subject.fan_mode, None)
-
-    def test_fan_modes_reflect_preset_mode(self):
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]] = PRESET_MODE_TO_DPS_MODE[
-            PRESET_NORMAL
-        ]
-        self.assertEqual(self.subject.fan_modes, [FAN_LOW, FAN_HIGH])
-
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]] = PRESET_MODE_TO_DPS_MODE[
-            PRESET_LOW
-        ]
-        self.assertEqual(self.subject.fan_modes, [FAN_LOW])
-
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]] = PRESET_MODE_TO_DPS_MODE[
-            PRESET_HIGH
-        ]
-        self.assertEqual(self.subject.fan_modes, [FAN_HIGH])
-
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]] = PRESET_MODE_TO_DPS_MODE[
-            PRESET_DRY_CLOTHES
-        ]
-        self.assertEqual(self.subject.fan_modes, [FAN_HIGH])
-
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]] = PRESET_MODE_TO_DPS_MODE[
-            PRESET_NORMAL
-        ]
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_AIR_CLEAN_ON]] = True
-        self.assertEqual(self.subject.fan_modes, [FAN_HIGH])
-
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]] = None
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_AIR_CLEAN_ON]] = False
-        self.assertEqual(self.subject.fan_modes, [])
-
-    async def test_set_fan_mode_to_low_succeeds_in_normal_preset(self):
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]] = PRESET_MODE_TO_DPS_MODE[
-            PRESET_NORMAL
-        ]
-        async with assert_device_properties_set(
-            self.subject._device,
-            {PROPERTY_TO_DPS_ID[ATTR_FAN_MODE]: FAN_MODE_TO_DPS_MODE[FAN_LOW]},
-        ):
-            await self.subject.async_set_fan_mode(FAN_LOW)
-
-    async def test_set_fan_mode_to_high_succeeds_in_normal_preset(self):
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]] = PRESET_MODE_TO_DPS_MODE[
-            PRESET_NORMAL
-        ]
-        async with assert_device_properties_set(
-            self.subject._device,
-            {PROPERTY_TO_DPS_ID[ATTR_FAN_MODE]: FAN_MODE_TO_DPS_MODE[FAN_HIGH]},
-        ):
-            await self.subject.async_set_fan_mode(FAN_HIGH)
-
-    async def test_set_fan_mode_fails_with_invalid_mode(self):
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]] = PRESET_MODE_TO_DPS_MODE[
-            PRESET_NORMAL
-        ]
-        with self.assertRaisesRegex(ValueError, "Invalid fan mode: something"):
-            await self.subject.async_set_fan_mode("something")
-
-    async def test_set_fan_mode_fails_outside_normal_preset(self):
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]] = PRESET_MODE_TO_DPS_MODE[
-            PRESET_LOW
-        ]
-        with self.assertRaisesRegex(
-            ValueError, "Fan mode can only be changed while in Normal preset mode"
-        ):
-            await self.subject.async_set_fan_mode(FAN_HIGH)
-
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]] = PRESET_MODE_TO_DPS_MODE[
-            PRESET_HIGH
-        ]
-        with self.assertRaisesRegex(
-            ValueError, "Fan mode can only be changed while in Normal preset mode"
-        ):
-            await self.subject.async_set_fan_mode(FAN_HIGH)
-
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]] = PRESET_MODE_TO_DPS_MODE[
-            PRESET_DRY_CLOTHES
-        ]
-        with self.assertRaisesRegex(
-            ValueError, "Fan mode can only be changed while in Normal preset mode"
-        ):
-            await self.subject.async_set_fan_mode(FAN_HIGH)
-
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE]] = PRESET_MODE_TO_DPS_MODE[
-            PRESET_NORMAL
-        ]
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_AIR_CLEAN_ON]] = True
-        with self.assertRaisesRegex(
-            ValueError, "Fan mode can only be changed while in Normal preset mode"
-        ):
-            await self.subject.async_set_fan_mode(FAN_HIGH)
-
-    def test_tank_full_or_missing(self):
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_ERROR]] = None
-        self.assertEqual(self.subject.tank_full_or_missing, False)
-
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_ERROR]] = ERROR_CODE_TO_DPS_CODE[ERROR_TANK]
-        self.assertEqual(self.subject.tank_full_or_missing, True)
-
-    def test_defrosting(self):
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_DEFROSTING]] = False
-        self.assertEqual(self.subject.defrosting, False)
-
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_DEFROSTING]] = True
-        self.assertEqual(self.subject.defrosting, True)
-
-    def test_device_state_attributes(self):
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_ERROR]] = None
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_DEFROSTING]] = False
-        self.assertEqual(
-            self.subject.device_state_attributes,
-            {
-                ATTR_ERROR_CODE: None,
-                ATTR_ERROR: STATE_UNAVAILABLE,
-                ATTR_DEFROSTING: False,
-            },
-        )
-
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_ERROR]] = ERROR_CODE_TO_DPS_CODE[ERROR_TANK]
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_DEFROSTING]] = False
-        self.assertEqual(
-            self.subject.device_state_attributes,
-            {
-                ATTR_ERROR: ERROR_TANK,
-                ATTR_ERROR_CODE: ERROR_CODE_TO_DPS_CODE[ERROR_TANK],
-                ATTR_DEFROSTING: False,
-            },
-        )
-
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_ERROR]] = None
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_DEFROSTING]] = True
-        self.assertEqual(
-            self.subject.device_state_attributes,
-            {
-                ATTR_ERROR_CODE: None,
-                ATTR_ERROR: STATE_UNAVAILABLE,
-                ATTR_DEFROSTING: True,
-            },
-        )
-
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_ERROR]] = ERROR_CODE_TO_DPS_CODE[ERROR_TANK]
-        self.dps[PROPERTY_TO_DPS_ID[ATTR_DEFROSTING]] = True
-        self.assertEqual(
-            self.subject.device_state_attributes,
-            {
-                ATTR_ERROR: ERROR_TANK,
-                ATTR_ERROR_CODE: ERROR_CODE_TO_DPS_CODE[ERROR_TANK],
-                ATTR_DEFROSTING: True,
-            },
-        )
-
-    async def test_update(self):
-        result = AsyncMock()
-        self.subject._device.async_refresh.return_value = result()
-
-        await self.subject.async_update()
-
-        self.subject._device.async_refresh.assert_called_once()
-        result.assert_awaited()

+ 0 - 112
tests/devices/test_goldair_dehumidifier.py

@@ -1,4 +1,3 @@
-from unittest import skip
 from unittest.mock import ANY
 
 from homeassistant.components.binary_sensor import (
@@ -211,10 +210,6 @@ class TestGoldairDehumidifier(
         self.dps[PRESET_DPS] = PRESET_DRY_CLOTHES
         self.assertIs(self.climate.target_humidity, None)
 
-        # self.dps[PRESET_DPS] = PRESET_NORMAL
-        # self.dps[AIRCLEAN_DPS] = True
-        # self.assertIs(self.climate.target_humidity, None)
-
     async def test_set_target_humidity_in_normal_preset_rounds_up_to_5_percent(self):
         self.dps[PRESET_DPS] = PRESET_NORMAL
         async with assert_device_properties_set(
@@ -273,13 +268,6 @@ class TestGoldairDehumidifier(
         ):
             await self.climate.async_set_humidity(50)
 
-        # self.dps[PRESET_DPS] = PRESET_NORMAL
-        # self.dps[AIRCLEAN_DPS] = True
-        # with self.assertRaisesRegex(
-        #     AttributeError, "humidity cannot be set at this time"
-        # ):
-        #     await self.climate.async_set_humidity(50)
-
     def test_temperature_unit_returns_device_temperature_unit(self):
         self.assertEqual(
             self.climate.temperature_unit,
@@ -382,11 +370,6 @@ class TestGoldairDehumidifier(
         ):
             await self.climate.async_set_preset_mode("Low")
 
-            # No anticipation of device behaviour in generic implementation
-            # self.climate._device.anticipate_property_value.assert_called_once_with(
-            #     FANMODE_DPS, "1"
-            # )
-
     async def test_set_preset_mode_to_high(self):
         async with assert_device_properties_set(
             self.climate._device,
@@ -397,11 +380,6 @@ class TestGoldairDehumidifier(
         ):
             await self.climate.async_set_preset_mode("High")
 
-            # No anticipation of device behaviour in generic implementation
-            # self.climate._device.anticipate_property_value.assert_called_once_with(
-            #     FANMODE_DPS, "3"
-            # )
-
     async def test_set_preset_mode_to_dry_clothes(self):
         async with assert_device_properties_set(
             self.climate._device,
@@ -412,46 +390,12 @@ class TestGoldairDehumidifier(
         ):
             await self.climate.async_set_preset_mode("Dry clothes")
 
-            # No anticipation of device behaviour in generic implementation
-            # self.climate._device.anticipate_property_value.assert_called_once_with(
-            #     FANMODE_DPS, "3"
-            # )
-
     async def test_set_preset_mode_to_air_clean(self):
         async with assert_device_properties_set(
             self.climate._device, {AIRCLEAN_DPS: True, PRESET_DPS: ANY}
         ):
             await self.climate.async_set_preset_mode("Air clean")
 
-            # No anticipation of device behaviour in generic implementation
-            # self.climate._device.anticipate_property_value.assert_called_once_with(
-            #     FANMODE_DPS, "1"
-            # )
-
-    @skip("Conditions not included in config")
-    def test_fan_mode_is_forced_to_high_in_high_dry_clothes_air_clean_presets(self):
-        self.dps[FANMODE_DPS] = "1"
-        self.dps[PRESET_DPS] = PRESET_HIGH
-        self.assertEqual(self.climate.fan_mode, FAN_HIGH)
-        self.assertEqual(self.fan.percentage, 100)
-
-        self.dps[PRESET_DPS] = PRESET_DRY_CLOTHES
-        self.assertEqual(self.climate.fan_mode, FAN_HIGH)
-        self.assertEqual(self.fan.percentage, 100)
-
-        self.dps[PRESET_DPS] = PRESET_NORMAL
-        self.dps[AIRCLEAN_DPS] = True
-        self.assertEqual(self.climate.fan_mode, FAN_HIGH)
-        self.assertEqual(self.climate.percentage, 100)
-
-    @skip("Conditions not included in config")
-    def test_fan_mode_is_forced_to_low_in_low_preset(self):
-        self.dps[FANMODE_DPS] = "3"
-        self.dps[PRESET_DPS] = PRESET_LOW
-
-        self.assertEqual(self.climate.fan_mode, FAN_LOW)
-        self.assertEqual(self.fan.percentage, 50)
-
     def test_fan_mode_reflects_dps_mode_in_normal_preset(self):
         self.dps[PRESET_DPS] = PRESET_NORMAL
         self.dps[FANMODE_DPS] = "1"
@@ -466,29 +410,6 @@ class TestGoldairDehumidifier(
         self.assertEqual(self.climate.fan_mode, None)
         self.assertEqual(self.fan.percentage, None)
 
-    @skip("Conditions not included in config")
-    def test_fan_modes_reflect_preset_mode(self):
-        self.dps[PRESET_DPS] = PRESET_NORMAL
-        self.assertCountEqual(self.climate.fan_modes, [FAN_LOW, FAN_HIGH])
-        self.assertEqual(self.fan.speed_count, 2)
-
-        self.dps[PRESET_DPS] = PRESET_LOW
-        self.assertEqual(self.climate.fan_modes, [FAN_LOW])
-        self.assertEqual(self.fan.speed_count, 0)
-
-        self.dps[PRESET_DPS] = PRESET_HIGH
-        self.assertEqual(self.climate.fan_modes, [FAN_HIGH])
-        self.assertEqual(self.fan.speed_count, 0)
-
-        self.dps[PRESET_DPS] = PRESET_DRY_CLOTHES
-        self.assertEqual(self.climate.fan_modes, [FAN_HIGH])
-        self.assertEqual(self.fan.speed_count, 0)
-
-        # self.dps[PRESET_DPS] = PRESET_NORMAL
-        # self.dps[AIRCLEAN_DPS] = True
-        # self.assertEqual(self.climate.fan_modes, [FAN_HIGH])
-        # self.assertEqual(self.fan.speed_count, 0)
-
     async def test_set_fan_mode_to_low_succeeds_in_normal_preset(self):
         self.dps[PRESET_DPS] = PRESET_NORMAL
         async with assert_device_properties_set(
@@ -529,39 +450,6 @@ class TestGoldairDehumidifier(
         ):
             await self.fan.async_set_percentage(30)
 
-    @skip("Restriction to listed options not supported yet")
-    async def test_set_fan_mode_fails_with_invalid_mode(self):
-        self.dps[PRESET_DPS] = PRESET_NORMAL
-        with self.assertRaisesRegex(ValueError, "Invalid fan mode: something"):
-            await self.climate.async_set_fan_mode("something")
-
-    @skip("Conditions not yet supported for setting")
-    async def test_set_fan_mode_fails_outside_normal_preset(self):
-        self.dps[PRESET_DPS] = PRESET_LOW
-        with self.assertRaisesRegex(
-            AttributeError, "fan_mode cannot be set at this time"
-        ):
-            await self.climate.async_set_fan_mode(FAN_HIGH)
-
-        self.dps[PRESET_DPS] = PRESET_HIGH
-        with self.assertRaisesRegex(
-            AttributeError, "fan_mode cannot be set at this time"
-        ):
-            await self.climate.async_set_fan_mode(FAN_HIGH)
-
-        self.dps[PRESET_DPS] = PRESET_DRY_CLOTHES
-        with self.assertRaisesRegex(
-            AttributeError, "fan_mode cannot be set at this time"
-        ):
-            await self.climate.async_set_fan_mode(FAN_HIGH)
-
-        # self.dps[PRESET_DPS] = PRESET_NORMAL
-        # self.dps[AIRCLEAN_DPS] = True
-        # with self.assertRaisesRegex(
-        #     ValueError, "Fan mode can only be changed while in Normal preset mode"
-        # ):
-        #     await self.climate.async_set_fan_mode(FAN_HIGH)
-
     def test_device_state_attributes(self):
         self.dps[ERROR_DPS] = None
         self.dps[DEFROST_DPS] = False

+ 2 - 3
tests/test_climate.py

@@ -8,7 +8,6 @@ from custom_components.tuya_local.const import (
     CONF_TYPE,
     DOMAIN,
 )
-from custom_components.tuya_local.dehumidifier.climate import GoldairDehumidifier
 from custom_components.tuya_local.generic.climate import TuyaLocalClimate
 from custom_components.tuya_local.climate import async_setup_entry
 
@@ -35,7 +34,7 @@ async def test_init_entry(hass):
 
 
 async def test_init_entry_as_secondary(hass):
-    """Test initialisation when fan is a secondary entity"""
+    """Test initialisation when climate is a secondary entity"""
     entry = MockConfigEntry(
         domain=DOMAIN,
         data={
@@ -57,7 +56,7 @@ async def test_init_entry_as_secondary(hass):
     await async_setup_entry(hass, entry, m_add_entities)
     assert (
         type(hass.data[DOMAIN]["dummy"]["climate_dehumidifier_as_climate"])
-        == GoldairDehumidifier
+        == TuyaLocalClimate
     )
     m_add_entities.assert_called_once()
 

+ 0 - 65
tests/test_device_config.py

@@ -77,68 +77,3 @@ class TestDeviceConfig(IsolatedAsyncioTestCase):
         cfg = get_config("kogan_switch")
         voltage = cfg.primary_entity.find_dps("voltage_v")
         self.assertIsNone(voltage.values(mock_device))
-
-    # Test detection of all devices.
-
-    def _test_detect(self, payload, dev_type, legacy_class):
-        """Test that payload is detected as the correct type and class."""
-        matched = False
-        false_matches = []
-        quality = 0
-        for cfg in possible_matches(payload):
-            self.assertTrue(cfg.matches(payload))
-            if cfg.legacy_type == dev_type:
-                self.assertFalse(matched)
-                matched = True
-                quality = cfg.match_quality(payload)
-                if legacy_class is not None:
-                    cfg_class = cfg.primary_entity.legacy_class
-                    if cfg_class is None:
-                        for e in cfg.secondary_entities():
-                            cfg_class = e.legacy_class
-                            if cfg_class is not None:
-                                break
-
-                    self.assertEqual(
-                        cfg_class.__name__,
-                        legacy_class,
-                    )
-            else:
-                false_matches.append(cfg)
-
-        self.assertTrue(matched)
-        if quality < 100:
-            warn(f"{dev_type} detected with imperfect quality {quality}%")
-
-        best_q = 0
-        for cfg in false_matches:
-            q = cfg.match_quality(payload)
-            if q > best_q:
-                best_q = q
-
-        self.assertGreater(quality, best_q)
-
-        # Ensure the same correct config is returned when looked up by type
-        cfg = get_config(dev_type)
-        if legacy_class is not None:
-            cfg_class = cfg.primary_entity.legacy_class
-            if cfg_class is None:
-                for e in cfg.secondary_entities():
-                    cfg_class = e.legacy_class
-                    if cfg_class is not None:
-                        break
-            self.assertEqual(
-                cfg_class.__name__,
-                legacy_class,
-            )
-
-    def test_goldair_dehumidifier_detection(self):
-        """Test that Goldair dehumidifier can be detected from its sample payload."""
-        self._test_detect(
-            DEHUMIDIFIER_PAYLOAD,
-            "dehumidifier",
-            "GoldairDehumidifier",
-        )
-
-    # Non-legacy devices endup being the same as the tests in test_device.py, so
-    # skip them.