Prechádzať zdrojové kódy

WIP: Surface error state and fix UI strings

Fixes #25 by adding an attribute to the Dehumidifier component allowing
you to set up a template sensor based on that attribute's value.
Nik Rolls 6 rokov pred
rodič
commit
6560ab3e32

+ 5 - 3
README.md

@@ -2,7 +2,7 @@ Home Assistant Goldair WiFi Climate component
 =============================================
 
 The `goldair_climate` component integrates 
-[Goldair WiFi-enabled heaters](http://www.goldair.co.nz/product-catalogue/heating/wifi-heaters), WiFi-enabled [dehumidifiers](http://www.goldair.co.nz/product-catalogue/heating/dehumidifiers), and WiFi-enabled fans](http://www.goldair.co.nz/product-catalogue/cooling/pedestal-fans/40cm-dc-quiet-fan-with-wifi-and-remote-gcpf315) into Home Assistant, enabling control of setting the following parameters via the UI and the following services:
+[Goldair WiFi-enabled heaters](http://www.goldair.co.nz/product-catalogue/heating/wifi-heaters), WiFi-enabled [dehumidifiers](http://www.goldair.co.nz/product-catalogue/heating/dehumidifiers), and [WiFi-enabled fans](http://www.goldair.co.nz/product-catalogue/cooling/pedestal-fans/40cm-dc-quiet-fan-with-wifi-and-remote-gcpf315) into Home Assistant, enabling control of setting the following parameters via the UI and the following services:
 
 **Heaters**
 * **power** (on/off)
@@ -17,7 +17,7 @@ Current temperature is also displayed.
 * **mode** (Normal, Low, High, Dry clothes, Air clean)
 * **target humidity** (`30`-`80`%)
 
-Current temperature is displayed, and current humidity is available as a property.
+Current temperature is displayed, and current humidity is available as a property. The "tank full" state is available via the **error** attribute, and if you want to you can easily surface this to a top-level entity using a [template sensor](https://www.home-assistant.io/integrations/template/).
 
 **Fans**
 * **power** (on/off)
@@ -54,7 +54,9 @@ Alternatively you can copy the contents of this repository's `custom_components`
 
 Configuration
 -------------
-Add the following lines to your `configuration.yaml` file:
+You can easily configure your devices using the Integrations UI at `Home Assistant > Configuration > Integrations > +`. This is the preferred method as things will be unlikely to break as this integration is upgraded. You will need to provide your device's IP address, device ID and local key; the last two can be found using [the instructions below](#finding-your-device-id-and-local-key).
+
+If you would rather configure using yaml, add the following lines to your `configuration.yaml` file (but bear in mind that if the configuration options change your configuration may break until you update it to match the changes):
 
 ```yaml
 # Example configuration.yaml entry

+ 1 - 0
custom_components/goldair_climate/.translations

@@ -0,0 +1 @@
+translations

+ 13 - 13
custom_components/goldair_climate/__init__.py

@@ -27,19 +27,19 @@ DOMAIN = 'goldair_climate'
 DATA_GOLDAIR_CLIMATE = 'data_goldair_climate'
 
 INDIVIDUAL_CONFIG_SCHEMA_TEMPLATE = [
-    {'key': CONF_NAME, 'type': cv.string, 'required': True},
-    {'key': CONF_HOST, 'type': cv.string, 'required': True},
-    {'key': CONF_DEVICE_ID, 'type': cv.string, 'required': True, 'fixed': True},
-    {'key': CONF_LOCAL_KEY, 'type': cv.string, 'required': True},
+    {'key': CONF_NAME, 'type': str, 'required': True},
+    {'key': CONF_HOST, 'type': str, 'required': True},
+    {'key': CONF_DEVICE_ID, 'type': str, 'required': True, 'fixed': True},
+    {'key': CONF_LOCAL_KEY, 'type': str, 'required': True},
     {
         'key': CONF_TYPE,
         'type': vol.In([CONF_TYPE_HEATER, CONF_TYPE_DEHUMIDIFIER, CONF_TYPE_FAN]),
         'required': True,
         'fixed': True
     },
-    {'key': CONF_CLIMATE, 'type': cv.boolean, 'required': False, 'default': True},
-    {'key': CONF_DISPLAY_LIGHT, 'type': cv.boolean, 'required': False, 'default': False},
-    {'key': CONF_CHILD_LOCK, 'type': cv.boolean, 'required': False, 'default': False}
+    {'key': CONF_CLIMATE, 'type': bool, 'required': False, 'default': True},
+    {'key': CONF_DISPLAY_LIGHT, 'type': bool, 'required': False, 'default': False},
+    {'key': CONF_CHILD_LOCK, 'type': bool, 'required': False, 'default': False}
 ]
 
 
@@ -50,14 +50,14 @@ def individual_config_schema(defaults={}, exclude_fixed=False):
         if exclude_fixed and prop.get('fixed'):
             continue
 
+        options = {}
+
         default = defaults.get(prop['key'], prop.get('default'))
         if default is not None:
-            key = vol.Required(prop['key'], default=default) if prop['required'] else vol.Optional(prop['key'],
-                                                                                                   default=default)
-            output[key] = prop['type']
-        else:
-            key = vol.Required(prop['key']) if prop['required'] else vol.Optional(prop['key'])
-            output[key] = prop['type']
+            options['default'] = default
+
+        key = vol.Required(prop['key'], **options) if prop['required'] else vol.Optional(prop['key'], **options)
+        output[key] = prop['type']
 
     return output
 

+ 3 - 15
custom_components/goldair_climate/config_flow.py

@@ -8,18 +8,6 @@ from . import (DOMAIN, individual_config_schema)
 from .const import (CONF_DEVICE_ID, CONF_TYPE)
 
 
-def cv_schema_to_config_schema(schema):
-    config_schema = {}
-    for key, value in schema.items():
-        if value == cv.string:
-            config_schema[key] = str
-        elif value == cv.boolean:
-            config_schema[key] = bool
-        else:
-            config_schema[key] = value
-    return vol.Schema(config_schema)
-
-
 class ConfigFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
     VERSION = 1
     CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_POLL
@@ -34,7 +22,7 @@ class ConfigFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
 
         return self.async_show_form(
             step_id='user',
-            data_schema=cv_schema_to_config_schema(individual_config_schema())
+            data_schema=vol.Schema(individual_config_schema())
         )
 
     @staticmethod
@@ -57,6 +45,6 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
 
         config = {**self.config_entry.data, **self.config_entry.options}
         return self.async_show_form(
-            step_id='init',
-            data_schema=cv_schema_to_config_schema(individual_config_schema(defaults=config, exclude_fixed=True))
+            step_id='user',
+            data_schema=vol.Schema(individual_config_schema(defaults=config, exclude_fixed=True))
         )

+ 11 - 13
custom_components/goldair_climate/dehumidifier/climate.py

@@ -1,18 +1,16 @@
 """
 Goldair WiFi Dehumidifier device.
 """
-from homeassistant.const import (
-    ATTR_TEMPERATURE, TEMP_CELSIUS, STATE_UNAVAILABLE
-)
+from homeassistant.const import (ATTR_TEMPERATURE, 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_LOW, FAN_HIGH, SUPPORT_TARGET_HUMIDITY,
     SUPPORT_PRESET_MODE, SUPPORT_FAN_MODE
 )
 from .const import (
-    ATTR_TARGET_HUMIDITY, ATTR_AIR_CLEAN_ON, ATTR_FAULT, PRESET_NORMAL, PRESET_LOW, PRESET_HIGH, PRESET_DRY_CLOTHES,
-    PRESET_AIR_CLEAN, FAULT_NONE, PROPERTY_TO_DPS_ID, HVAC_MODE_TO_DPS_MODE, PRESET_MODE_TO_DPS_MODE,
-    FAN_MODE_TO_DPS_MODE, FAULT_CODE_TO_DPS_CODE
+    ATTR_TARGET_HUMIDITY, ATTR_AIR_CLEAN_ON, ATTR_ERROR, PRESET_NORMAL, PRESET_LOW, PRESET_HIGH, PRESET_DRY_CLOTHES,
+    PRESET_AIR_CLEAN, PROPERTY_TO_DPS_ID, HVAC_MODE_TO_DPS_MODE, PRESET_MODE_TO_DPS_MODE, FAN_MODE_TO_DPS_MODE,
+    ERROR_CODE_TO_DPS_CODE
 )
 from ..device import GoldairTuyaDevice
 
@@ -193,13 +191,13 @@ class GoldairDehumidifier(ClimateDevice):
         await self._device.async_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 device_state_attributes(self):
+        """Get additional attributes that HA doesn't naturally support."""
+        error = self._device.get_property(PROPERTY_TO_DPS_ID[ATTR_ERROR])
+        if error:
+            error = GoldairTuyaDevice.get_key_for_value(ERROR_CODE_TO_DPS_CODE, error, error)
+
+        return {ATTR_ERROR: error or None}
 
     async def async_update(self):
         await self._device.async_refresh()

+ 7 - 7
custom_components/goldair_climate/dehumidifier/const.py

@@ -6,7 +6,7 @@ from homeassistant.components.climate.const import (
 ATTR_TARGET_HUMIDITY = 'target_humidity'
 ATTR_AIR_CLEAN_ON = 'air_clean_on'
 ATTR_CHILD_LOCK = 'child_lock'
-ATTR_FAULT = 'fault'
+ATTR_ERROR = 'error'
 ATTR_DISPLAY_ON = 'display_on'
 
 PRESET_NORMAL = 'Normal'
@@ -15,8 +15,8 @@ PRESET_HIGH = 'High'
 PRESET_DRY_CLOTHES = 'Dry clothes'
 PRESET_AIR_CLEAN = 'Air clean'
 
-FAULT_NONE = 'No fault'
-FAULT_TANK = 'Tank full or missing'
+ERROR_NONE = 'No error'
+ERROR_TANK = 'Tank full or missing'
 
 PROPERTY_TO_DPS_ID = {
     ATTR_HVAC_MODE: '1',
@@ -25,7 +25,7 @@ PROPERTY_TO_DPS_ID = {
     ATTR_AIR_CLEAN_ON: '5',
     ATTR_FAN_MODE: '6',
     ATTR_CHILD_LOCK: '7',
-    ATTR_FAULT: '11',
+    ATTR_ERROR: '11',
     ATTR_DISPLAY_ON: '102',
     ATTR_TEMPERATURE: '103',
     ATTR_HUMIDITY: '104'
@@ -45,7 +45,7 @@ FAN_MODE_TO_DPS_MODE = {
     FAN_LOW: '1',
     FAN_HIGH: '3'
 }
-FAULT_CODE_TO_DPS_CODE = {
-    FAULT_NONE: 0,
-    FAULT_TANK: 8
+ERROR_CODE_TO_DPS_CODE = {
+    ERROR_NONE: 0,
+    ERROR_TANK: 8
 }

+ 1 - 1
custom_components/goldair_climate/dehumidifier/light.py

@@ -53,7 +53,7 @@ class GoldairDehumidifierLedDisplayLight(Light):
         dps_display_on = self._device.get_property(PROPERTY_TO_DPS_ID[ATTR_DISPLAY_ON])
 
         if dps_hvac_mode != HVAC_MODE_TO_DPS_MODE[HVAC_MODE_OFF]:
-            await self.turn_on() if not dps_display_on else await self.turn_off()
+            await (self.turn_on() if not dps_display_on else self.turn_off())
 
     async def async_update(self):
         await self._device.async_refresh()

+ 1 - 3
custom_components/goldair_climate/dehumidifier/lock.py

@@ -4,9 +4,7 @@ Platform to control the child lock on Goldair WiFi-connected dehumidifiers.
 from homeassistant.components.lock import (STATE_LOCKED, STATE_UNLOCKED, LockDevice)
 from homeassistant.const import STATE_UNAVAILABLE
 from ..device import GoldairTuyaDevice
-from .const import (
-    ATTR_CHILD_LOCK, PROPERTY_TO_DPS_ID
-)
+from .const import (ATTR_CHILD_LOCK, PROPERTY_TO_DPS_ID)
 
 
 class GoldairDehumidifierChildLock(LockDevice):

+ 2 - 2
custom_components/goldair_climate/device.py

@@ -193,7 +193,7 @@ class GoldairTuyaDevice(object):
         self._api.set_version(new_version)
 
     @staticmethod
-    def get_key_for_value(obj, value):
+    def get_key_for_value(obj, value, fallback=None):
         keys = list(obj.keys())
         values = list(obj.values())
-        return keys[values.index(value)]
+        return keys[values.index(value)] or fallback

+ 1 - 1
custom_components/goldair_climate/fan/light.py

@@ -53,7 +53,7 @@ class GoldairFanLedDisplayLight(Light):
         dps_display_on = self._device.get_property(PROPERTY_TO_DPS_ID[ATTR_DISPLAY_ON])
 
         if dps_hvac_mode != HVAC_MODE_TO_DPS_MODE[HVAC_MODE_OFF]:
-            await self.async_turn_on() if not dps_display_on else await self.async_turn_off()
+            await (self.async_turn_on() if not dps_display_on else self.async_turn_off())
 
     async def async_update(self):
         await self._device.async_refresh()

+ 9 - 4
custom_components/goldair_climate/heater/climate.py

@@ -1,9 +1,7 @@
 """
 Goldair WiFi Heater device.
 """
-from homeassistant.const import (
-    ATTR_TEMPERATURE, TEMP_CELSIUS, STATE_UNAVAILABLE
-)
+from homeassistant.const import (ATTR_TEMPERATURE, STATE_UNAVAILABLE)
 from homeassistant.components.climate import ClimateDevice
 from homeassistant.components.climate.const import (
     ATTR_HVAC_MODE, ATTR_PRESET_MODE,
@@ -11,7 +9,7 @@ from homeassistant.components.climate.const import (
 )
 from ..device import GoldairTuyaDevice
 from .const import (
-    ATTR_TARGET_TEMPERATURE, ATTR_POWER_MODE_AUTO, ATTR_POWER_MODE_USER, ATTR_POWER_LEVEL, ATTR_POWER_MODE,
+    ATTR_TARGET_TEMPERATURE, ATTR_ERROR, ATTR_POWER_MODE_AUTO, ATTR_POWER_MODE_USER, ATTR_POWER_LEVEL, ATTR_POWER_MODE,
     ATTR_ECO_TARGET_TEMPERATURE, STATE_COMFORT, STATE_ECO, STATE_ANTI_FREEZE, PROPERTY_TO_DPS_ID, HVAC_MODE_TO_DPS_MODE,
     PRESET_MODE_TO_DPS_MODE, POWER_LEVEL_TO_DPS_LEVEL
 )
@@ -188,5 +186,12 @@ class GoldairHeater(ClimateDevice):
         dps_level = POWER_LEVEL_TO_DPS_LEVEL[new_level]
         await self._device.async_set_property(PROPERTY_TO_DPS_ID[ATTR_POWER_LEVEL], dps_level)
 
+    @property
+    def device_state_attributes(self):
+        """Get additional attributes that HA doesn't naturally support."""
+        error = self._device.get_property(PROPERTY_TO_DPS_ID[ATTR_ERROR])
+
+        return {ATTR_ERROR: error or None}
+
     async def async_update(self):
         await self._device.async_refresh()

+ 2 - 2
custom_components/goldair_climate/heater/const.py

@@ -5,7 +5,7 @@ from homeassistant.components.climate.const import (
 
 ATTR_TARGET_TEMPERATURE = 'target_temperature'
 ATTR_CHILD_LOCK = 'child_lock'
-ATTR_FAULT = 'fault'
+ATTR_ERROR = 'error'
 ATTR_POWER_MODE_AUTO = 'auto'
 ATTR_POWER_MODE_USER = 'user'
 ATTR_POWER_LEVEL = 'power_level'
@@ -23,7 +23,7 @@ PROPERTY_TO_DPS_ID = {
     ATTR_TEMPERATURE: '3',
     ATTR_PRESET_MODE: '4',
     ATTR_CHILD_LOCK: '6',
-    ATTR_FAULT: '12',
+    ATTR_ERROR: '12',
     ATTR_POWER_LEVEL: '101',
     ATTR_DISPLAY_ON: '104',
     ATTR_POWER_MODE: '105',

+ 1 - 1
custom_components/goldair_climate/heater/light.py

@@ -53,7 +53,7 @@ class GoldairHeaterLedDisplayLight(Light):
         dps_display_on = self._device.get_property(PROPERTY_TO_DPS_ID[ATTR_DISPLAY_ON])
 
         if dps_hvac_mode != HVAC_MODE_TO_DPS_MODE[HVAC_MODE_OFF]:
-            await self.async_turn_on() if not dps_display_on else await self.async_turn_off()
+            await (self.async_turn_on() if not dps_display_on else self.async_turn_off())
 
     async def async_update(self):
         await self._device.async_refresh()

+ 21 - 8
custom_components/goldair_climate/heater/lock.py

@@ -1,9 +1,9 @@
 {
-  "title": "Goldair Climate",
   "config": {
+    "title": "Goldair Climate",
     "step": {
       "user": {
-        "title": "Configure a Goldair climate device",
+        "title": "Configure your Goldair climate device",
         "description": "Please enter your device information.",
         "data": {
           "name": "Name",
@@ -11,16 +11,29 @@
           "device_id": "Device ID (uuid)",
           "local_key": "Local key",
           "type": "Device type",
-          "heater": "Heater",
-          "dehumidifier": "Dehumidifier",
-          "fan": "Fan",
-          "display_light": "Add LED display as light entity",
-          "child_lock": "Add child lock as lock entity"
+          "climate": "Add a climate entity",
+          "display_light": "Add LED display as a light entity",
+          "child_lock": "Add child lock as a lock entity"
         }
       }
     },
     "abort": {
-      "already_configured": "[%key:common.config_flow.abort.already_configured%]"
+      "already_configured": "A device with that ID has already been added."
+    }
+  },
+  "options": {
+    "step": {
+      "user": {
+        "title": "Configure your Goldair climate device",
+        "data": {
+          "name": "Name",
+          "host": "IP address or hostname",
+          "local_key": "Local key",
+          "climate": "Add a climate entity",
+          "display_light": "Add LED display as light entity",
+          "child_lock": "Add child lock as lock entity"
+        }
+      }
     }
   }
 }

+ 39 - 0
custom_components/goldair_climate/translations/en.json

@@ -0,0 +1,39 @@
+{
+  "config": {
+    "title": "Goldair Climate",
+    "step": {
+      "user": {
+        "title": "Configure your Goldair climate device",
+        "description": "Please enter your device information.",
+        "data": {
+          "name": "Name",
+          "host": "IP address or hostname",
+          "device_id": "Device ID (uuid)",
+          "local_key": "Local key",
+          "type": "Device type",
+          "climate": "Add a climate entity",
+          "display_light": "Add LED display as a light entity",
+          "child_lock": "Add child lock as a lock entity"
+        }
+      }
+    },
+    "abort": {
+      "already_configured": "[%key:common.config_flow.abort.already_configured%]"
+    }
+  },
+  "options": {
+    "step": {
+      "user": {
+        "title": "Configure your Goldair climate device",
+        "data": {
+          "name": "Name",
+          "host": "IP address or hostname",
+          "local_key": "Local key",
+          "climate": "Add a climate entity",
+          "display_light": "Add LED display as light entity",
+          "child_lock": "Add child lock as lock entity"
+        }
+      }
+    }
+  }
+}