Преглед изворни кода

WIP: Support device registry

Nik Rolls пре 5 година
родитељ
комит
a333a449f3

+ 0 - 0
custom_components/__init__.py


+ 32 - 67
custom_components/goldair_climate/__init__.py

@@ -9,87 +9,47 @@ import logging
 
 import homeassistant.helpers.config_validation as cv
 import voluptuous as vol
+from homeassistant.config_entries import ConfigEntry
 from homeassistant.const import CONF_HOST, CONF_NAME
+from homeassistant.core import HomeAssistant
 from homeassistant.helpers.discovery import async_load_platform
 
-from .const import (CONF_CHILD_LOCK, CONF_CLIMATE, CONF_DEVICE_ID,
+from .configuration import individual_config_schema
+from .const import (DOMAIN, CONF_CHILD_LOCK, CONF_CLIMATE, CONF_DEVICE_ID,
                     CONF_DISPLAY_LIGHT, CONF_LOCAL_KEY, CONF_TYPE,
-                    CONF_TYPE_DEHUMIDIFIER, CONF_TYPE_FAN, CONF_TYPE_HEATER)
+                    CONF_TYPE_DEHUMIDIFIER, CONF_TYPE_FAN, CONF_TYPE_HEATER, SCAN_INTERVAL)
 from .device import GoldairTuyaDevice
 
 _LOGGER = logging.getLogger(__name__)
 
 VERSION = "0.0.8"
 
-DOMAIN = "goldair_climate"
-DATA_GOLDAIR_CLIMATE = "data_goldair_climate"
-
-INDIVIDUAL_CONFIG_SCHEMA_TEMPLATE = [
-    {"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": 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},
-]
-
-
-def individual_config_schema(defaults={}, exclude_fixed=False):
-    output = {}
-
-    for prop in INDIVIDUAL_CONFIG_SCHEMA_TEMPLATE:
-        if exclude_fixed and prop.get("fixed"):
-            continue
-
-        options = {}
-
-        default = defaults.get(prop["key"], prop.get("default"))
-        if default is not None:
-            options["default"] = default
-
-        key = (
-            vol.Required(prop["key"], **options)
-            if prop["required"]
-            else vol.Optional(prop["key"], **options)
-        )
-        output[key] = prop["type"]
-
-    return output
-
-
 CONFIG_SCHEMA = vol.Schema(
     {DOMAIN: vol.All(cv.ensure_list, [vol.Schema(individual_config_schema())])},
     extra=vol.ALLOW_EXTRA,
 )
 
 
-async def async_setup(hass, config):
+async def async_setup(hass: HomeAssistant, config: dict):
     hass.data[DOMAIN] = {}
 
     for device_config in config.get(DOMAIN, []):
         setup_device(hass, device_config)
 
         discovery_info = {
-            CONF_DEVICE_ID: device_config.get(CONF_DEVICE_ID),
-            CONF_TYPE: device_config.get(CONF_TYPE),
+            CONF_DEVICE_ID: device_config[CONF_DEVICE_ID],
+            CONF_TYPE: device_config[CONF_TYPE],
         }
 
-        if device_config.get(CONF_CLIMATE) == True:
+        if device_config[CONF_CLIMATE] == True:
             hass.async_create_task(
                 async_load_platform(hass, "climate", DOMAIN, discovery_info, config)
             )
-        if device_config.get(CONF_DISPLAY_LIGHT) == True:
+        if device_config[CONF_DISPLAY_LIGHT] == True:
             hass.async_create_task(
                 async_load_platform(hass, "light", DOMAIN, discovery_info, config)
             )
-        if device_config.get(CONF_CHILD_LOCK) == True:
+        if device_config[CONF_CHILD_LOCK] == True:
             hass.async_create_task(
                 async_load_platform(hass, "lock", DOMAIN, discovery_info, config)
             )
@@ -97,8 +57,8 @@ async def async_setup(hass, config):
     return True
 
 
-async def async_setup_entry(hass, entry):
-    config = {**entry.data, **entry.options}
+async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
+    config = {**entry.data, **entry.options, 'name': entry.title}
     setup_device(hass, config)
 
     if config[CONF_CLIMATE] == True:
@@ -119,35 +79,40 @@ async def async_setup_entry(hass, entry):
     return True
 
 
-async def async_unload_entry(hass, entry):
+async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
     config = entry.data
-    delete_device(hass, config)
+    data = hass.data[DOMAIN][config[CONF_DEVICE_ID]]
 
-    if config[CONF_CLIMATE] == True:
+    if CONF_CLIMATE in data:
         await hass.config_entries.async_forward_entry_unload(entry, "climate")
-    if config[CONF_DISPLAY_LIGHT] == True:
+    if CONF_DISPLAY_LIGHT in data:
         await hass.config_entries.async_forward_entry_unload(entry, "light")
-    if config[CONF_CHILD_LOCK] == True:
+    if CONF_CHILD_LOCK in data:
         await hass.config_entries.async_forward_entry_unload(entry, "lock")
 
+    delete_device(hass, config)
+    del hass.data[DOMAIN][config[CONF_DEVICE_ID]]
+
     return True
 
 
-async def async_update_entry(hass, entry):
+async def async_update_entry(hass: HomeAssistant, entry: ConfigEntry):
     await async_unload_entry(hass, entry)
     await async_setup_entry(hass, entry)
 
 
-def setup_device(hass, config):
+def setup_device(hass: HomeAssistant, config: dict):
     device = GoldairTuyaDevice(
-        config.get(CONF_NAME),
-        config.get(CONF_DEVICE_ID),
-        config.get(CONF_HOST),
-        config.get(CONF_LOCAL_KEY),
+        config[CONF_NAME],
+        config[CONF_DEVICE_ID],
+        config[CONF_HOST],
+        config[CONF_LOCAL_KEY],
         hass,
     )
-    hass.data[DOMAIN][config.get(CONF_DEVICE_ID)] = device
+    hass.data[DOMAIN][config[CONF_DEVICE_ID]] = {
+        'device': device
+    }
 
 
-def delete_device(hass, config):
-    del hass.data[DOMAIN][config[CONF_DEVICE_ID]]
+def delete_device(hass: HomeAssistant, config: dict):
+    del hass.data[DOMAIN][config[CONF_DEVICE_ID]]['device']

+ 8 - 5
custom_components/goldair_climate/climate.py

@@ -3,7 +3,7 @@ Setup for different kinds of Goldair climate devices
 """
 from . import DOMAIN
 from .const import (CONF_DEVICE_ID, CONF_TYPE, CONF_TYPE_DEHUMIDIFIER,
-                    CONF_TYPE_FAN, CONF_TYPE_HEATER)
+                    CONF_TYPE_FAN, CONF_TYPE_HEATER, CONF_CLIMATE)
 from .dehumidifier.climate import GoldairDehumidifier
 from .fan.climate import GoldairFan
 from .heater.climate import GoldairHeater
@@ -11,13 +11,16 @@ from .heater.climate import GoldairHeater
 
 async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
     """Set up the Goldair climate device according to its type."""
-    device = hass.data[DOMAIN][discovery_info[CONF_DEVICE_ID]]
+    data = hass.data[DOMAIN][discovery_info[CONF_DEVICE_ID]]
+    device = data['device']
     if discovery_info[CONF_TYPE] == CONF_TYPE_HEATER:
-        async_add_entities([GoldairHeater(device)])
+        data[CONF_CLIMATE] = GoldairHeater(device)
     elif discovery_info[CONF_TYPE] == CONF_TYPE_DEHUMIDIFIER:
-        async_add_entities([GoldairDehumidifier(device)])
+        data[CONF_CLIMATE] = GoldairDehumidifier(device)
     elif discovery_info[CONF_TYPE] == CONF_TYPE_FAN:
-        async_add_entities([GoldairFan(device)])
+        data[CONF_CLIMATE] = GoldairFan(device)
+
+    async_add_entities([data[CONF_CLIMATE]])
 
 
 async def async_setup_entry(hass, config_entry, async_add_entities):

+ 9 - 5
custom_components/goldair_climate/config_flow.py

@@ -1,11 +1,10 @@
-import homeassistant.helpers.config_validation as cv
 import voluptuous as vol
 from homeassistant import config_entries
-from homeassistant.const import CONF_HOST, CONF_NAME
+from homeassistant.const import CONF_NAME
 from homeassistant.core import callback
 
 from . import DOMAIN, individual_config_schema
-from .const import CONF_DEVICE_ID, CONF_TYPE
+from .const import CONF_DEVICE_ID
 
 
 class ConfigFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
@@ -16,7 +15,9 @@ class ConfigFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
         if user_input is not None:
             await self.async_set_unique_id(user_input[CONF_DEVICE_ID])
             self._abort_if_unique_id_configured()
-            return self.async_create_entry(title=user_input[CONF_NAME], data=user_input)
+            title = user_input[CONF_NAME]
+            del user_input[CONF_NAME]
+            return self.async_create_entry(title=title, data=user_input)
 
         return self.async_show_form(
             step_id="user", data_schema=vol.Schema(individual_config_schema())
@@ -34,6 +35,9 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
         self.config_entry = config_entry
 
     async def async_step_init(self, user_input=None):
+        return await self.async_step_user(user_input)
+
+    async def async_step_user(self, user_input=None):
         """Manage the options."""
         if user_input is not None:
             return self.async_create_entry(title=user_input[CONF_NAME], data=user_input)
@@ -42,6 +46,6 @@ class OptionsFlowHandler(config_entries.OptionsFlow):
         return self.async_show_form(
             step_id="user",
             data_schema=vol.Schema(
-                individual_config_schema(defaults=config, exclude_fixed=True)
+                individual_config_schema(defaults=config, options_only=True)
             ),
         )

+ 44 - 0
custom_components/goldair_climate/configuration.py

@@ -0,0 +1,44 @@
+import voluptuous as vol
+from homeassistant.const import CONF_NAME, CONF_HOST
+
+from .const import (CONF_DEVICE_ID, CONF_LOCAL_KEY, CONF_TYPE, CONF_TYPE_HEATER,
+                    CONF_TYPE_DEHUMIDIFIER, CONF_TYPE_FAN, CONF_CLIMATE, CONF_DISPLAY_LIGHT, CONF_CHILD_LOCK)
+
+INDIVIDUAL_CONFIG_SCHEMA_TEMPLATE = [
+    {"key": CONF_NAME, "type": str, "required": True, "option": False},
+    {"key": CONF_HOST, "type": str, "required": True, "option": True},
+    {"key": CONF_DEVICE_ID, "type": str, "required": True, "option": False},
+    {"key": CONF_LOCAL_KEY, "type": str, "required": True, "option": True},
+    {
+        "key": CONF_TYPE,
+        "type": vol.In([CONF_TYPE_HEATER, CONF_TYPE_DEHUMIDIFIER, CONF_TYPE_FAN]),
+        "required": True,
+        "option": False,
+    },
+    {"key": CONF_CLIMATE, "type": bool, "required": False, "default": True, "option": True},
+    {"key": CONF_DISPLAY_LIGHT, "type": bool, "required": False, "default": False, "option": True},
+    {"key": CONF_CHILD_LOCK, "type": bool, "required": False, "default": False, "option": True},
+]
+
+
+def individual_config_schema(defaults={}, options_only=False):
+    output = {}
+
+    for prop in INDIVIDUAL_CONFIG_SCHEMA_TEMPLATE:
+        if options_only and not prop.get("option"):
+            continue
+
+        options = {}
+
+        default = defaults.get(prop["key"], prop.get("default"))
+        if default is not None:
+            options["default"] = default
+
+        key = (
+            vol.Required(prop["key"], **options)
+            if prop["required"]
+            else vol.Optional(prop["key"], **options)
+        )
+        output[key] = prop["type"]
+
+    return output

+ 5 - 0
custom_components/goldair_climate/const.py

@@ -1,3 +1,7 @@
+from datetime import timedelta
+
+DOMAIN = "goldair_climate"
+
 CONF_DEVICE_ID = "device_id"
 CONF_LOCAL_KEY = "local_key"
 CONF_TYPE = "type"
@@ -9,3 +13,4 @@ CONF_DISPLAY_LIGHT = "display_light"
 CONF_CHILD_LOCK = "child_lock"
 
 API_PROTOCOL_VERSIONS = [3.3, 3.1]
+SCAN_INTERVAL = timedelta(seconds=30)

+ 10 - 0
custom_components/goldair_climate/dehumidifier/climate.py

@@ -65,6 +65,16 @@ class GoldairDehumidifier(ClimateDevice):
         """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 current_humidity(self):
         """Return the current reading of the humidity sensor."""

+ 10 - 0
custom_components/goldair_climate/dehumidifier/light.py

@@ -28,6 +28,16 @@ class GoldairDehumidifierLedDisplayLight(Light):
         """Return the name of the light."""
         return self._device.name
 
+    @property
+    def unique_id(self):
+        """Return the unique id for this dehumidifier LED display."""
+        return self._device.unique_id
+
+    @property
+    def device_info(self):
+        """Return device information about this dehumidifier LED display."""
+        return self._device.device_info
+
     @property
     def is_on(self):
         """Return the current state."""

+ 10 - 0
custom_components/goldair_climate/dehumidifier/lock.py

@@ -27,6 +27,16 @@ class GoldairDehumidifierChildLock(LockDevice):
         """Return the name of the lock."""
         return self._device.name
 
+    @property
+    def unique_id(self):
+        """Return the unique id for this dehumidifier child lock."""
+        return self._device.unique_id
+
+    @property
+    def device_info(self):
+        """Return device information about this dehumidifier child lock."""
+        return self._device.device_info
+
     @property
     def state(self):
         """Return the current state."""

+ 15 - 1
custom_components/goldair_climate/device.py

@@ -9,7 +9,7 @@ from time import time
 
 from homeassistant.const import TEMP_CELSIUS
 
-from .const import API_PROTOCOL_VERSIONS
+from .const import DOMAIN, API_PROTOCOL_VERSIONS
 
 _LOGGER = logging.getLogger(__name__)
 
@@ -51,6 +51,20 @@ class GoldairTuyaDevice(object):
     def name(self):
         return self._name
 
+    @property
+    def unique_id(self):
+        """Return the unique id for this device (the dev_id)."""
+        return self._api.id
+
+    @property
+    def device_info(self):
+        """Return the device information for this device."""
+        return {
+            "identifiers": {(DOMAIN, self.unique_id)},
+            "name": self.name,
+            "manufacturer": "Goldair"
+        }
+
     @property
     def temperature_unit(self):
         return self._TEMPERATURE_UNIT

+ 10 - 0
custom_components/goldair_climate/fan/climate.py

@@ -52,6 +52,16 @@ class GoldairFan(ClimateDevice):
         """Return the name of the climate device."""
         return self._device.name
 
+    @property
+    def unique_id(self):
+        """Return the unique id for this fan."""
+        return self._device.unique_id
+
+    @property
+    def device_info(self):
+        """Return device information about this fan."""
+        return self._device.device_info
+
     @property
     def temperature_unit(self):
         """This is not used but required by Home Assistant."""

+ 10 - 0
custom_components/goldair_climate/fan/light.py

@@ -28,6 +28,16 @@ class GoldairFanLedDisplayLight(Light):
         """Return the name of the light."""
         return self._device.name
 
+    @property
+    def unique_id(self):
+        """Return the unique id for this fan LED display."""
+        return self._device.unique_id
+
+    @property
+    def device_info(self):
+        """Return device information about this LED display."""
+        return self._device.device_info
+
     @property
     def is_on(self):
         """Return the current state."""

+ 10 - 0
custom_components/goldair_climate/heater/climate.py

@@ -65,6 +65,16 @@ class GoldairHeater(ClimateDevice):
         """Return the name of the climate device."""
         return self._device.name
 
+    @property
+    def unique_id(self):
+        """Return the unique id for this heater."""
+        return self._device.unique_id
+
+    @property
+    def device_info(self):
+        """Return device information about this heater."""
+        return self._device.device_info
+
     @property
     def temperature_unit(self):
         """Return the unit of measurement."""

+ 10 - 0
custom_components/goldair_climate/heater/light.py

@@ -28,6 +28,16 @@ class GoldairHeaterLedDisplayLight(Light):
         """Return the name of the light."""
         return self._device.name
 
+    @property
+    def unique_id(self):
+        """Return the unique id for this heater LED display."""
+        return self._device.unique_id
+
+    @property
+    def device_info(self):
+        """Return device information about this heater LED display."""
+        return self._device.device_info
+
     @property
     def is_on(self):
         """Return the current state."""

+ 10 - 0
custom_components/goldair_climate/heater/lock.py

@@ -27,6 +27,16 @@ class GoldairHeaterChildLock(LockDevice):
         """Return the name of the lock."""
         return self._device.name
 
+    @property
+    def unique_id(self):
+        """Return the unique id for this heater child lock."""
+        return self._device.unique_id
+
+    @property
+    def device_info(self):
+        """Return device information about this heater child lock."""
+        return self._device.device_info
+
     @property
     def state(self):
         """Return the current state."""

+ 8 - 5
custom_components/goldair_climate/light.py

@@ -3,7 +3,7 @@ Setup for different kinds of Goldair climate devices
 """
 from . import DOMAIN
 from .const import (CONF_DEVICE_ID, CONF_TYPE, CONF_TYPE_DEHUMIDIFIER,
-                    CONF_TYPE_FAN, CONF_TYPE_HEATER)
+                    CONF_TYPE_FAN, CONF_TYPE_HEATER, CONF_DISPLAY_LIGHT)
 from .dehumidifier.light import GoldairDehumidifierLedDisplayLight
 from .fan.light import GoldairFanLedDisplayLight
 from .heater.light import GoldairHeaterLedDisplayLight
@@ -11,13 +11,16 @@ from .heater.light import GoldairHeaterLedDisplayLight
 
 async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
     """Set up the Goldair climate device according to its type."""
-    device = hass.data[DOMAIN][discovery_info[CONF_DEVICE_ID]]
+    data = hass.data[DOMAIN][discovery_info[CONF_DEVICE_ID]]
+    device = data['device']
     if discovery_info[CONF_TYPE] == CONF_TYPE_HEATER:
-        async_add_entities([GoldairHeaterLedDisplayLight(device)])
+        data[CONF_DISPLAY_LIGHT] = GoldairHeaterLedDisplayLight(device)
     elif discovery_info[CONF_TYPE] == CONF_TYPE_DEHUMIDIFIER:
-        async_add_entities([GoldairDehumidifierLedDisplayLight(device)])
+        data[CONF_DISPLAY_LIGHT] = GoldairDehumidifierLedDisplayLight(device)
     elif discovery_info[CONF_TYPE] == CONF_TYPE_FAN:
-        async_add_entities([GoldairFanLedDisplayLight(device)])
+        data[CONF_DISPLAY_LIGHT] = GoldairFanLedDisplayLight(device)
+
+    async_add_entities([data[CONF_DISPLAY_LIGHT]])
 
 
 async def async_setup_entry(hass, config_entry, async_add_entities):

+ 10 - 6
custom_components/goldair_climate/lock.py

@@ -3,20 +3,24 @@ Setup for different kinds of Goldair climate devices
 """
 from . import DOMAIN
 from .const import (CONF_DEVICE_ID, CONF_TYPE, CONF_TYPE_DEHUMIDIFIER,
-                    CONF_TYPE_FAN, CONF_TYPE_HEATER)
+                    CONF_TYPE_FAN, CONF_TYPE_HEATER, CONF_CHILD_LOCK)
 from .dehumidifier.lock import GoldairDehumidifierChildLock
 from .heater.lock import GoldairHeaterChildLock
 
 
-async def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
+async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
     """Set up the Goldair climate device according to its type."""
-    device = hass.data[DOMAIN][discovery_info[CONF_DEVICE_ID]]
+    data = hass.data[DOMAIN][discovery_info[CONF_DEVICE_ID]]
+    device = data['device']
     if discovery_info[CONF_TYPE] == CONF_TYPE_HEATER:
-        async_add_devices([GoldairHeaterChildLock(device)])
+        data[CONF_CHILD_LOCK] = GoldairHeaterChildLock(device)
     if discovery_info[CONF_TYPE] == CONF_TYPE_DEHUMIDIFIER:
-        async_add_devices([GoldairDehumidifierChildLock(device)])
+        data[CONF_CHILD_LOCK] = 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 CONF_CHILD_LOCK in data:
+        async_add_entities([data[CONF_CHILD_LOCK]])
 
 
 async def async_setup_entry(hass, config_entry, async_add_entities):

+ 2 - 2
custom_components/goldair_climate/strings.json

@@ -4,7 +4,7 @@
     "step": {
       "user": {
         "title": "Configure your Goldair climate device",
-        "description": "Please enter your device information.",
+        "description": "[Follow these instructions to find your local key.](https://github.com/nikrolls/homeassistant-goldair-climate#finding-your-device-id-and-local-key)",
         "data": {
           "name": "Name",
           "host": "IP address or hostname",
@@ -25,8 +25,8 @@
     "step": {
       "user": {
         "title": "Configure your Goldair climate device",
+        "description": "[Follow these instructions to find your local key.](https://github.com/nikrolls/homeassistant-goldair-climate#finding-your-device-id-and-local-key)",
         "data": {
-          "name": "Name",
           "host": "IP address or hostname",
           "local_key": "Local key",
           "climate": "Add a climate entity",

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

@@ -1,39 +0,0 @@
-{
-  "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"
-        }
-      }
-    }
-  }
-}

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

@@ -0,0 +1 @@
+../strings.json