|
|
@@ -0,0 +1,250 @@
|
|
|
+"""
|
|
|
+Goldair WiFi Heater device.
|
|
|
+"""
|
|
|
+from homeassistant.const import (
|
|
|
+ ATTR_TEMPERATURE, TEMP_CELSIUS, STATE_UNAVAILABLE
|
|
|
+)
|
|
|
+from homeassistant.components.climate import ClimateDevice
|
|
|
+from homeassistant.components.climate.const import (
|
|
|
+ ATTR_FAN_MODE, ATTR_HUMIDITY, ATTR_HVAC_MODE, ATTR_PRESET_MODE,
|
|
|
+ FAN_OFF, FAN_LOW, FAN_HIGH,
|
|
|
+ HVAC_MODE_OFF, HVAC_MODE_DRY,
|
|
|
+ SUPPORT_TARGET_HUMIDITY, SUPPORT_PRESET_MODE, SUPPORT_FAN_MODE
|
|
|
+)
|
|
|
+from custom_components.goldair_climate import GoldairTuyaDevice
|
|
|
+
|
|
|
+ATTR_ON = 'on'
|
|
|
+ATTR_TARGET_HUMIDITY = 'target_humidity'
|
|
|
+ATTR_AIR_CLEAN_ON = 'air_clean_on'
|
|
|
+ATTR_CHILD_LOCK = 'child_lock'
|
|
|
+ATTR_FAULT = 'fault'
|
|
|
+ATTR_DISPLAY_ON = 'display_on'
|
|
|
+
|
|
|
+PRESET_NORMAL = 'Normal'
|
|
|
+PRESET_LOW = 'Low'
|
|
|
+PRESET_HIGH = 'High'
|
|
|
+PRESET_DRY_CLOTHES = 'Dry clothes'
|
|
|
+PRESET_AIR_CLEAN = 'Air clean'
|
|
|
+
|
|
|
+FAULT_NONE = 'No fault'
|
|
|
+FAULT_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_FAULT: '11',
|
|
|
+ ATTR_DISPLAY_ON: '102',
|
|
|
+ ATTR_TEMPERATURE: '103',
|
|
|
+ ATTR_HUMIDITY: '104'
|
|
|
+}
|
|
|
+
|
|
|
+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'
|
|
|
+}
|
|
|
+FAULT_CODE_TO_DPS_CODE = {
|
|
|
+ FAULT_NONE: 0,
|
|
|
+ FAULT_TANK: 8
|
|
|
+}
|
|
|
+
|
|
|
+SUPPORT_FLAGS = SUPPORT_TARGET_HUMIDITY | SUPPORT_PRESET_MODE | SUPPORT_FAN_MODE
|
|
|
+
|
|
|
+
|
|
|
+class GoldairDehumidifier(ClimateDevice):
|
|
|
+ """Representation of a Goldair WiFi dehumidifier."""
|
|
|
+
|
|
|
+ def __init__(self, device):
|
|
|
+ """Initialize the dehumidifier.
|
|
|
+ Args:
|
|
|
+ name (str): The device's name.
|
|
|
+ device (GoldairTuyaDevice): 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 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."""
|
|
|
+ return self._device.get_property(PROPERTY_TO_DPS_ID[ATTR_TARGET_HUMIDITY])
|
|
|
+
|
|
|
+ def set_humidity(self, humidity):
|
|
|
+ """Set the device's target humidity."""
|
|
|
+ if self.preset_mode in [PRESET_AIR_CLEAN, PRESET_DRY_CLOTHES]:
|
|
|
+ raise ValueError('Humidity can only be changed while in Normal, Low or High preset modes.')
|
|
|
+ humidity = int(self._HUMIDITY_STEP * round(float(humidity) / self._HUMIDITY_STEP))
|
|
|
+ self._device.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 GoldairTuyaDevice.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())
|
|
|
+
|
|
|
+ def set_hvac_mode(self, hvac_mode):
|
|
|
+ """Set new HVAC mode."""
|
|
|
+ dps_mode = HVAC_MODE_TO_DPS_MODE[hvac_mode]
|
|
|
+ self._device.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 GoldairTuyaDevice.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]
|
|
|
+
|
|
|
+ def set_preset_mode(self, preset_mode):
|
|
|
+ """Set new preset mode."""
|
|
|
+ if preset_mode == PRESET_AIR_CLEAN:
|
|
|
+ self._device.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]
|
|
|
+ self._device.set_property(PROPERTY_TO_DPS_ID[ATTR_AIR_CLEAN_ON], False)
|
|
|
+ self._device.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 GoldairTuyaDevice.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 []
|
|
|
+
|
|
|
+ def 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]
|
|
|
+ self._device.set_property(PROPERTY_TO_DPS_ID[ATTR_FAN_MODE], dps_mode)
|
|
|
+
|
|
|
+ @property
|
|
|
+ def fault(self):
|
|
|
+ """Get the current fault status."""
|
|
|
+ fault = self._device.get_property(PROPERTY_TO_DPS_ID[ATTR_FAULT])
|
|
|
+ if fault is None or fault == FAULT_NONE:
|
|
|
+ return None
|
|
|
+ else:
|
|
|
+ return GoldairTuyaDevice.get_key_for_value(FAULT_CODE_TO_DPS_CODE, fault)
|
|
|
+
|
|
|
+ def update(self):
|
|
|
+ self._device.refresh()
|