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

Add support for Kogan Heaters

These are simpler heaters than the Goldair ones, with only two presets for High and Low, and no control of panel lighting, child lock etc.
Jason Rumney 5 лет назад
Родитель
Сommit
200f637431

+ 15 - 3
README.md

@@ -43,6 +43,18 @@ Please note, this component has currently only been tested with the Goldair GPPH
 
 ---
 
+## Kogan Heater support
+
+Although these are not Goldair devices, they are based on the same Tuya platform, and sold in the same Australia/New Zealand market.
+Kogan heaters support the following parameters and services:
+* **power** (on/off)
+* **mode** (low/high)
+* **target temperature** (`16`-`30` in °C)
+
+Current temperature is also displayed.
+
+Kogan heater support is tested with the Kogan SmarterHome 1500W Smart Panel Heater.  If you have another type of Kogan SmarterHome heater, it may or may not work with the same configuration.
+
 Installation
 ------------
 The preferred installation method is via [HACS](https://hacs.xyz/). Once you have HACS set up, simply follow the [instructions for adding a custom repository](https://hacs.xyz/docs/navigation/settings#custom-repositories) and then the integration will be available to install like any other.
@@ -87,7 +99,7 @@ goldair_climate:
                                               [as per the instructions below](#finding-your-device-id-and-local-key).
 
 #### type
-    *(string) (Required)* The type of Goldair device: currently `heater`, `dehumidifier` or `fan`.
+    *(string) (Required)* The type of Goldair device: currently `heater`, `dehumidifier`, `fan` or `kogan_heater`.
 
 #### climate
     *(boolean) (Optional)* Whether to surface this appliance as a climate device.
@@ -95,12 +107,12 @@ goldair_climate:
     *Default value: true* 
 
 #### display_light
-    *(boolean) (Optional)* Whether to surface this appliance's LED display control as a light.
+    *(boolean) (Optional)* Whether to surface this appliance's LED display control as a light (not supported for Kogan Heaters).
 
     *Default value: false* 
 
 #### child_lock
-    *(boolean) (Optional)* Whether to surface this appliances's child lock as a lock device (not supported for fans).
+    *(boolean) (Optional)* Whether to surface this appliances's child lock as a lock device (not supported for fans or Kogan Heaters).
 
     *Default value: false* 
 

+ 2 - 1
custom_components/goldair_climate/__init__.py

@@ -30,6 +30,7 @@ CONF_TYPE = 'type'
 CONF_TYPE_HEATER = 'heater'
 CONF_TYPE_DEHUMIDIFIER = 'dehumidifier'
 CONF_TYPE_FAN = 'fan'
+CONF_TYPE_KOGAN_HEATER = 'kogan_heater'
 CONF_CLIMATE = 'climate'
 CONF_DISPLAY_LIGHT = 'display_light'
 CONF_CHILD_LOCK = 'child_lock'
@@ -40,7 +41,7 @@ PLATFORM_SCHEMA = vol.Schema({
     vol.Required(CONF_HOST): cv.string,
     vol.Required(CONF_DEVICE_ID): cv.string,
     vol.Required(CONF_LOCAL_KEY): cv.string,
-    vol.Required(CONF_TYPE): vol.In([CONF_TYPE_HEATER, CONF_TYPE_DEHUMIDIFIER, CONF_TYPE_FAN]),
+    vol.Required(CONF_TYPE): vol.In([CONF_TYPE_HEATER, CONF_TYPE_DEHUMIDIFIER, CONF_TYPE_FAN, CONF_TYPE_KOGAN_HEATER]),
     vol.Optional(CONF_CLIMATE, default=True): cv.boolean,
     vol.Optional(CONF_DISPLAY_LIGHT, default=False): cv.boolean,
     vol.Optional(CONF_CHILD_LOCK, default=False): cv.boolean,

+ 3 - 1
custom_components/goldair_climate/binary_sensor.py

@@ -3,7 +3,7 @@ Setup for different kinds of Goldair climate devices
 """
 from homeassistant.const import CONF_HOST
 from custom_components.goldair_climate import (
-    DOMAIN, CONF_TYPE, CONF_TYPE_HEATER, CONF_TYPE_DEHUMIDIFIER, CONF_TYPE_FAN
+    DOMAIN, CONF_TYPE, CONF_TYPE_HEATER, CONF_TYPE_DEHUMIDIFIER, CONF_TYPE_FAN, CONF_TYPE_KOGAN_HEATER
 )
 from custom_components.goldair_climate.dehumidifier.binary_sensor import GoldairDehumidifierTankFullBinarySensor
 
@@ -16,3 +16,5 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
         raise ValueError('Goldair heaters do not support tank full sensors.')
     if discovery_info[CONF_TYPE] == CONF_TYPE_FAN:
         raise ValueError('Goldair fans do not support tank full sensors.')
+    if discovery_info[CONF_TYPE] == CONF_TYPE_KOGAN_HEATER:
+        raise ValueError('Kogan heaters do not support tank full sensors.')

+ 4 - 2
custom_components/goldair_climate/climate.py

@@ -3,12 +3,12 @@ Setup for different kinds of Goldair climate devices
 """
 from homeassistant.const import CONF_HOST
 from custom_components.goldair_climate import (
-    DOMAIN, CONF_TYPE, CONF_TYPE_HEATER, CONF_TYPE_DEHUMIDIFIER, CONF_TYPE_FAN
+    DOMAIN, CONF_TYPE, CONF_TYPE_HEATER, CONF_TYPE_DEHUMIDIFIER, CONF_TYPE_FAN, CONF_TYPE_KOGAN_HEATER
 )
 from custom_components.goldair_climate.heater.climate import GoldairHeater
 from custom_components.goldair_climate.dehumidifier.climate import GoldairDehumidifier
 from custom_components.goldair_climate.fan.climate import GoldairFan
-
+from custom_components.goldair_climate.kogan_heater.climate import KoganHeater
 
 def setup_platform(hass, config, add_devices, discovery_info=None):
     """Set up the Goldair climate device according to its type."""
@@ -19,3 +19,5 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
         add_devices([GoldairDehumidifier(device)])
     elif discovery_info[CONF_TYPE] == CONF_TYPE_FAN:
         add_devices([GoldairFan(device)])
+    elif discovery_info[CONF_TYPE] == CONF_TYPE_KOGAN_HEATER:
+        add_devices([KoganHeater(device)])

+ 0 - 0
custom_components/goldair_climate/kogan_heater/__init__.py


+ 171 - 0
custom_components/goldair_climate/kogan_heater/climate.py

@@ -0,0 +1,171 @@
+"""
+Kogan WiFi Heater device.
+
+dps:
+  2 = current temperature (integer)
+  3 = target temperature (integer)
+  4 = preset_mode (string Low/High)
+  6 = timer state (boolean) [not supported - use HA based timers]
+  7 = hvac_mode (boolean)
+  8 = timer (integer) [not supported - use HA based timers]
+"""
+
+from homeassistant.const import (
+    ATTR_TEMPERATURE, TEMP_CELSIUS, STATE_UNAVAILABLE
+)
+from homeassistant.components.climate import ClimateDevice
+from homeassistant.components.climate.const import (
+    ATTR_HVAC_MODE, ATTR_PRESET_MODE,
+    HVAC_MODE_OFF, HVAC_MODE_HEAT,
+    SUPPORT_TARGET_TEMPERATURE, SUPPORT_PRESET_MODE
+)
+from custom_components.goldair_climate import GoldairTuyaDevice
+
+ATTR_TARGET_TEMPERATURE = 'target_temperature'
+
+PRESET_LOW = 'LOW'
+PRESET_HIGH = 'HIGH'
+
+PROPERTY_TO_DPS_ID = {
+    ATTR_HVAC_MODE: '7',
+    ATTR_TARGET_TEMPERATURE: '3',
+    ATTR_TEMPERATURE: '2',
+    ATTR_PRESET_MODE: '4',
+}
+
+HVAC_MODE_TO_DPS_MODE = {
+    HVAC_MODE_OFF: False,
+    HVAC_MODE_HEAT: True
+}
+
+PRESET_MODE_TO_DPS_MODE = {
+    PRESET_LOW: 'Low',
+    PRESET_HIGH: 'High'
+}
+
+SUPPORT_FLAGS = SUPPORT_TARGET_TEMPERATURE | SUPPORT_PRESET_MODE
+
+
+class KoganHeater(ClimateDevice):
+    """Representation of a Kogan WiFi heater."""
+
+    def __init__(self, device):
+        """Initialize the heater.
+        Args:
+            name (str): The device's name.
+            device (GoldairTuyaDevice): The device API instance."""
+        self._device = device
+
+        self._support_flags = SUPPORT_FLAGS
+
+        self._TEMPERATURE_STEP = 1
+        self._TEMPERATURE_LIMITS = {
+            'min': 16,
+            'max': 30
+        }
+
+    @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 temperature_unit(self):
+        """Return the unit of measurement."""
+        return self._device.temperature_unit
+
+    @property
+    def target_temperature(self):
+        """Return the temperature we try to reach."""
+        return self._device.get_property(PROPERTY_TO_DPS_ID[ATTR_TARGET_TEMPERATURE])
+
+    @property
+    def target_temperature_step(self):
+        """Return the supported step of target temperature."""
+        return self._TEMPERATURE_STEP
+
+    @property
+    def min_temp(self):
+        """Return the minimum temperature."""
+        return self._TEMPERATURE_LIMITS['min']
+
+    @property
+    def max_temp(self):
+        """Return the maximum temperature."""
+        return self._TEMPERATURE_LIMITS['max']
+
+    def set_temperature(self, **kwargs):
+        """Set new target temperatures."""
+        if kwargs.get(ATTR_PRESET_MODE) is not None:
+            self.set_preset_mode(kwargs.get(ATTR_PRESET_MODE))
+        if kwargs.get(ATTR_TEMPERATURE) is not None:
+            self.set_target_temperature(kwargs.get(ATTR_TEMPERATURE))
+
+    def set_target_temperature(self, target_temperature):
+        target_temperature = int(round(target_temperature))
+
+        limits = self._TEMPERATURE_LIMITS
+        if not limits['min'] <= target_temperature <= limits['max']:
+            raise ValueError(
+                f'Target temperature ({target_temperature}) must be between '
+                f'{limits["min"]} and {limits["max"]}'
+            )
+
+        self._device.set_property(PROPERTY_TO_DPS_ID[ATTR_TARGET_TEMPERATURE], target_temperature)
+
+    @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 Heat 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 Low or High."""
+        dps_mode = self._device.get_property(PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE])
+        if 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())
+
+    def set_preset_mode(self, preset_mode):
+        """Set new preset mode."""
+        dps_mode = PRESET_MODE_TO_DPS_MODE[preset_mode]
+        self._device.set_property(PROPERTY_TO_DPS_ID[ATTR_PRESET_MODE], dps_mode)
+
+    def update(self):
+        self._device.refresh()

+ 3 - 1
custom_components/goldair_climate/light.py

@@ -3,7 +3,7 @@ Setup for different kinds of Goldair climate devices
 """
 from homeassistant.const import CONF_HOST
 from custom_components.goldair_climate import (
-    DOMAIN, CONF_TYPE, CONF_TYPE_HEATER, CONF_TYPE_DEHUMIDIFIER, CONF_TYPE_FAN
+    DOMAIN, CONF_TYPE, CONF_TYPE_HEATER, CONF_TYPE_DEHUMIDIFIER, CONF_TYPE_FAN, CONF_TYPE_KOGAN_HEATER
 )
 from custom_components.goldair_climate.heater.light import GoldairHeaterLedDisplayLight
 from custom_components.goldair_climate.dehumidifier.light import GoldairDehumidifierLedDisplayLight
@@ -19,3 +19,5 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
         add_devices([GoldairDehumidifierLedDisplayLight(device)])
     elif discovery_info[CONF_TYPE] == CONF_TYPE_FAN:
         add_devices([GoldairFanLedDisplayLight(device)])
+    elif discovery_info[CONF_TYPE] == CONF_TYPE_KOGAN_HEATER:
+        raise ValueError('Kogan heaters do not support panel lighting control')

+ 4 - 2
custom_components/goldair_climate/lock.py

@@ -3,7 +3,7 @@ Setup for different kinds of Goldair climate devices
 """
 from homeassistant.const import CONF_HOST
 from custom_components.goldair_climate import (
-    DOMAIN, CONF_TYPE, CONF_TYPE_HEATER, CONF_TYPE_DEHUMIDIFIER, CONF_TYPE_FAN
+    DOMAIN, CONF_TYPE, CONF_TYPE_HEATER, CONF_TYPE_DEHUMIDIFIER, CONF_TYPE_FAN, CONF_TYPE_KOGAN_HEATER
 )
 from custom_components.goldair_climate.heater.lock import GoldairHeaterChildLock
 from custom_components.goldair_climate.dehumidifier.lock import GoldairDehumidifierChildLock
@@ -17,4 +17,6 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
     if discovery_info[CONF_TYPE] == CONF_TYPE_DEHUMIDIFIER:
         add_devices([GoldairDehumidifierChildLock(device)])
     if discovery_info[CONF_TYPE] == CONF_TYPE_FAN:
-        raise ValueError('Goldair fains do not support Child Lock.')
+        raise ValueError('Goldair fans do not support Child Lock.')
+    if discovery_info[CONF_TYPE] == CONF_TYPE_KOGAN_HEATER:
+        raise ValueError('Kogan Heaters do not support Child Lock.')