diff --git a/homeassistant/components/ihc/__init__.py b/homeassistant/components/ihc/__init__.py index 9a3ae9f8d6c..34b65c5b791 100644 --- a/homeassistant/components/ihc/__init__.py +++ b/homeassistant/components/ihc/__init__.py @@ -10,13 +10,18 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.typing import ConfigType from .auto_setup import autosetup_ihc_products -from .const import CONF_AUTOSETUP, CONF_INFO, DOMAIN, IHC_CONTROLLER +from .const import ( + CONF_AUTOSETUP, + CONF_INFO, + DOMAIN, + IHC_CONTROLLER, + IHC_CONTROLLER_INDEX, +) from .manual_setup import IHC_SCHEMA, get_manual_configuration from .service_functions import setup_service_functions _LOGGER = logging.getLogger(__name__) -IHC_INFO = "info" CONFIG_SCHEMA = vol.Schema( {DOMAIN: vol.Schema(vol.All(cv.ensure_list, [IHC_SCHEMA]))}, extra=vol.ALLOW_EXTRA @@ -29,7 +34,6 @@ def setup(hass: HomeAssistant, config: ConfigType) -> bool: for index, controller_conf in enumerate(conf): if not ihc_setup(hass, config, controller_conf, index): return False - return True @@ -37,7 +41,7 @@ def ihc_setup( hass: HomeAssistant, config: ConfigType, controller_conf: ConfigType, - controller_id: int, + controller_index: int, ) -> bool: """Set up the IHC integration.""" url = controller_conf[CONF_URL] @@ -48,20 +52,20 @@ def ihc_setup( if not ihc_controller.authenticate(): _LOGGER.error("Unable to authenticate on IHC controller") return False - + controller_id: str = ihc_controller.client.get_system_info()["serial_number"] + # Store controller configuration + hass.data.setdefault(DOMAIN, {}) + hass.data[DOMAIN][controller_id] = { + IHC_CONTROLLER: ihc_controller, + CONF_INFO: controller_conf[CONF_INFO], + IHC_CONTROLLER_INDEX: controller_index, + } if controller_conf[CONF_AUTOSETUP] and not autosetup_ihc_products( hass, config, ihc_controller, controller_id ): return False - # Manual configuration get_manual_configuration(hass, config, controller_conf, controller_id) - # Store controller configuration - ihc_key = f"ihc{controller_id}" - hass.data[ihc_key] = { - IHC_CONTROLLER: ihc_controller, - IHC_INFO: controller_conf[CONF_INFO], - } # We only want to register the service functions once for the first controller - if controller_id == 0: + if controller_index == 0: setup_service_functions(hass) return True diff --git a/homeassistant/components/ihc/auto_setup.py b/homeassistant/components/ihc/auto_setup.py index f782cd590c0..ae271108848 100644 --- a/homeassistant/components/ihc/auto_setup.py +++ b/homeassistant/components/ihc/auto_setup.py @@ -120,19 +120,25 @@ def get_discovery_info(platform_setup, groups, controller_id): for product_cfg in platform_setup: products = group.findall(product_cfg[CONF_XPATH]) for product in products: + product_id = int(product.attrib["id"].strip("_"), 0) nodes = product.findall(product_cfg[CONF_NODE]) for node in nodes: if "setting" in node.attrib and node.attrib["setting"] == "yes": continue ihc_id = int(node.attrib["id"].strip("_"), 0) name = f"{groupname}_{ihc_id}" + # make the model number look a bit nicer - strip leading _ + model = product.get("product_identifier", "").lstrip("_") device = { "ihc_id": ihc_id, "ctrl_id": controller_id, "product": { + "id": product_id, "name": product.get("name") or "", "note": product.get("note") or "", "position": product.get("position") or "", + "model": model, + "group": groupname, }, "product_cfg": product_cfg, } diff --git a/homeassistant/components/ihc/binary_sensor.py b/homeassistant/components/ihc/binary_sensor.py index 7a981fc1b63..48035d27a4d 100644 --- a/homeassistant/components/ihc/binary_sensor.py +++ b/homeassistant/components/ihc/binary_sensor.py @@ -1,14 +1,15 @@ """Support for IHC binary sensors.""" from __future__ import annotations +from ihcsdk.ihccontroller import IHCController + from homeassistant.components.binary_sensor import BinarySensorEntity from homeassistant.const import CONF_TYPE from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from . import IHC_CONTROLLER, IHC_INFO -from .const import CONF_INVERTING +from .const import CONF_INVERTING, DOMAIN, IHC_CONTROLLER from .ihcdevice import IHCDevice @@ -27,16 +28,13 @@ def setup_platform( product_cfg = device["product_cfg"] product = device["product"] # Find controller that corresponds with device id - ctrl_id = device["ctrl_id"] - ihc_key = f"ihc{ctrl_id}" - info = hass.data[ihc_key][IHC_INFO] - ihc_controller = hass.data[ihc_key][IHC_CONTROLLER] - + controller_id = device["ctrl_id"] + ihc_controller: IHCController = hass.data[DOMAIN][controller_id][IHC_CONTROLLER] sensor = IHCBinarySensor( ihc_controller, + controller_id, name, ihc_id, - info, product_cfg.get(CONF_TYPE), product_cfg[CONF_INVERTING], product, @@ -54,16 +52,16 @@ class IHCBinarySensor(IHCDevice, BinarySensorEntity): def __init__( self, - ihc_controller, - name, + ihc_controller: IHCController, + controller_id: str, + name: str, ihc_id: int, - info: bool, sensor_type: str, inverting: bool, product=None, ) -> None: """Initialize the IHC binary sensor.""" - super().__init__(ihc_controller, name, ihc_id, info, product) + super().__init__(ihc_controller, controller_id, name, ihc_id, product) self._state = None self._sensor_type = sensor_type self.inverting = inverting diff --git a/homeassistant/components/ihc/const.py b/homeassistant/components/ihc/const.py index 2f3651c7bb7..c86e77870c8 100644 --- a/homeassistant/components/ihc/const.py +++ b/homeassistant/components/ihc/const.py @@ -25,6 +25,7 @@ CONF_XPATH = "xpath" DOMAIN = "ihc" IHC_CONTROLLER = "controller" +IHC_CONTROLLER_INDEX = "controller_index" IHC_PLATFORMS = ( Platform.BINARY_SENSOR, Platform.LIGHT, diff --git a/homeassistant/components/ihc/ihcdevice.py b/homeassistant/components/ihc/ihcdevice.py index e351d2f38ea..31887c51397 100644 --- a/homeassistant/components/ihc/ihcdevice.py +++ b/homeassistant/components/ihc/ihcdevice.py @@ -1,6 +1,14 @@ """Implementation of a base class for all IHC devices.""" +import logging + +from ihcsdk.ihccontroller import IHCController + from homeassistant.helpers.entity import Entity +from .const import CONF_INFO, DOMAIN + +_LOGGER = logging.getLogger(__name__) + class IHCDevice(Entity): """Base class for all IHC devices. @@ -11,17 +19,33 @@ class IHCDevice(Entity): """ def __init__( - self, ihc_controller, name, ihc_id: int, info: bool, product=None + self, + ihc_controller: IHCController, + controller_id: str, + name: str, + ihc_id: int, + product=None, ) -> None: """Initialize IHC attributes.""" self.ihc_controller = ihc_controller self._name = name self.ihc_id = ihc_id - self.info = info + self.controller_id = controller_id + self.device_id = None + self.suggested_area = None if product: self.ihc_name = product["name"] self.ihc_note = product["note"] self.ihc_position = product["position"] + self.suggested_area = product["group"] if "group" in product else None + if "id" in product: + product_id = product["id"] + self.device_id = f"{controller_id}_{product_id }" + # this will name the device the same way as the IHC visual application: Product name + position + self.device_name = product["name"] + if self.ihc_position: + self.device_name += f" ({self.ihc_position})" + self.device_model = product["model"] else: self.ihc_name = "" self.ihc_note = "" @@ -29,6 +53,7 @@ class IHCDevice(Entity): async def async_added_to_hass(self): """Add callback for IHC changes.""" + _LOGGER.debug("Adding IHC entity notify event: %s", self.ihc_id) self.ihc_controller.add_notify_event(self.ihc_id, self.on_ihc_change, True) @property @@ -41,17 +66,26 @@ class IHCDevice(Entity): """Return the device name.""" return self._name + @property + def unique_id(self): + """Return a unique ID.""" + return f"{self.controller_id}-{self.ihc_id}" + @property def extra_state_attributes(self): """Return the state attributes.""" - if not self.info: + if not self.hass.data[DOMAIN][self.controller_id][CONF_INFO]: return {} - return { + attributes = { "ihc_id": self.ihc_id, "ihc_name": self.ihc_name, "ihc_note": self.ihc_note, "ihc_position": self.ihc_position, } + if len(self.hass.data[DOMAIN]) > 1: + # We only want to show the controller id if we have more than one + attributes["ihc_controller"] = self.controller_id + return attributes def on_ihc_change(self, ihc_id, value): """Handle IHC resource change. diff --git a/homeassistant/components/ihc/light.py b/homeassistant/components/ihc/light.py index b6269865072..b86f9fb3c8a 100644 --- a/homeassistant/components/ihc/light.py +++ b/homeassistant/components/ihc/light.py @@ -1,6 +1,8 @@ """Support for IHC lights.""" from __future__ import annotations +from ihcsdk.ihccontroller import IHCController + from homeassistant.components.light import ( ATTR_BRIGHTNESS, SUPPORT_BRIGHTNESS, @@ -10,8 +12,7 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from . import IHC_CONTROLLER, IHC_INFO -from .const import CONF_DIMMABLE, CONF_OFF_ID, CONF_ON_ID +from .const import CONF_DIMMABLE, CONF_OFF_ID, CONF_ON_ID, DOMAIN, IHC_CONTROLLER from .ihcdevice import IHCDevice from .util import async_pulse, async_set_bool, async_set_int @@ -31,15 +32,20 @@ def setup_platform( product_cfg = device["product_cfg"] product = device["product"] # Find controller that corresponds with device id - ctrl_id = device["ctrl_id"] - ihc_key = f"ihc{ctrl_id}" - info = hass.data[ihc_key][IHC_INFO] - ihc_controller = hass.data[ihc_key][IHC_CONTROLLER] + controller_id = device["ctrl_id"] + ihc_controller: IHCController = hass.data[DOMAIN][controller_id][IHC_CONTROLLER] ihc_off_id = product_cfg.get(CONF_OFF_ID) ihc_on_id = product_cfg.get(CONF_ON_ID) dimmable = product_cfg[CONF_DIMMABLE] light = IhcLight( - ihc_controller, name, ihc_id, ihc_off_id, ihc_on_id, info, dimmable, product + ihc_controller, + controller_id, + name, + ihc_id, + ihc_off_id, + ihc_on_id, + dimmable, + product, ) devices.append(light) add_entities(devices) @@ -55,17 +61,17 @@ class IhcLight(IHCDevice, LightEntity): def __init__( self, - ihc_controller, - name, + ihc_controller: IHCController, + controller_id: str, + name: str, ihc_id: int, ihc_off_id: int, ihc_on_id: int, - info: bool, dimmable=False, product=None, ) -> None: """Initialize the light.""" - super().__init__(ihc_controller, name, ihc_id, info, product) + super().__init__(ihc_controller, controller_id, name, ihc_id, product) self._ihc_off_id = ihc_off_id self._ihc_on_id = ihc_on_id self._brightness = 0 diff --git a/homeassistant/components/ihc/manual_setup.py b/homeassistant/components/ihc/manual_setup.py index a68230c9900..297997281c6 100644 --- a/homeassistant/components/ihc/manual_setup.py +++ b/homeassistant/components/ihc/manual_setup.py @@ -111,7 +111,7 @@ def get_manual_configuration( hass: HomeAssistant, config: ConfigType, controller_conf: ConfigType, - controller_id: int, + controller_id: str, ) -> None: """Get manual configuration for IHC devices.""" for platform in IHC_PLATFORMS: diff --git a/homeassistant/components/ihc/sensor.py b/homeassistant/components/ihc/sensor.py index d9dcab431d0..d3c38687caa 100644 --- a/homeassistant/components/ihc/sensor.py +++ b/homeassistant/components/ihc/sensor.py @@ -1,6 +1,8 @@ """Support for IHC sensors.""" from __future__ import annotations +from ihcsdk.ihccontroller import IHCController + from homeassistant.components.sensor import SensorDeviceClass, SensorEntity from homeassistant.const import CONF_UNIT_OF_MEASUREMENT from homeassistant.core import HomeAssistant @@ -8,7 +10,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.util.unit_system import TEMPERATURE_UNITS -from . import IHC_CONTROLLER, IHC_INFO +from .const import DOMAIN, IHC_CONTROLLER from .ihcdevice import IHCDevice @@ -27,12 +29,10 @@ def setup_platform( product_cfg = device["product_cfg"] product = device["product"] # Find controller that corresponds with device id - ctrl_id = device["ctrl_id"] - ihc_key = f"ihc{ctrl_id}" - info = hass.data[ihc_key][IHC_INFO] - ihc_controller = hass.data[ihc_key][IHC_CONTROLLER] + controller_id = device["ctrl_id"] + ihc_controller: IHCController = hass.data[DOMAIN][controller_id][IHC_CONTROLLER] unit = product_cfg[CONF_UNIT_OF_MEASUREMENT] - sensor = IHCSensor(ihc_controller, name, ihc_id, info, unit, product) + sensor = IHCSensor(ihc_controller, controller_id, name, ihc_id, unit, product) devices.append(sensor) add_entities(devices) @@ -41,10 +41,16 @@ class IHCSensor(IHCDevice, SensorEntity): """Implementation of the IHC sensor.""" def __init__( - self, ihc_controller, name, ihc_id: int, info: bool, unit, product=None + self, + ihc_controller: IHCController, + controller_id: str, + name: str, + ihc_id: int, + unit: str, + product=None, ) -> None: """Initialize the IHC sensor.""" - super().__init__(ihc_controller, name, ihc_id, info, product) + super().__init__(ihc_controller, controller_id, name, ihc_id, product) self._state = None self._unit_of_measurement = unit diff --git a/homeassistant/components/ihc/service_functions.py b/homeassistant/components/ihc/service_functions.py index 6136c8ccd46..3d7008ee38b 100644 --- a/homeassistant/components/ihc/service_functions.py +++ b/homeassistant/components/ihc/service_functions.py @@ -1,6 +1,4 @@ """Support for IHC devices.""" -import logging - import voluptuous as vol from homeassistant.core import HomeAssistant @@ -12,6 +10,7 @@ from .const import ( ATTR_VALUE, DOMAIN, IHC_CONTROLLER, + IHC_CONTROLLER_INDEX, SERVICE_PULSE, SERVICE_SET_RUNTIME_VALUE_BOOL, SERVICE_SET_RUNTIME_VALUE_FLOAT, @@ -19,9 +18,6 @@ from .const import ( ) from .util import async_pulse, async_set_bool, async_set_float, async_set_int -_LOGGER = logging.getLogger(__name__) - - SET_RUNTIME_VALUE_BOOL_SCHEMA = vol.Schema( { vol.Required(ATTR_IHC_ID): cv.positive_int, @@ -54,13 +50,17 @@ PULSE_SCHEMA = vol.Schema( ) -def setup_service_functions(hass: HomeAssistant): +def setup_service_functions(hass: HomeAssistant) -> None: """Set up the IHC service functions.""" def _get_controller(call): - controller_id = call.data[ATTR_CONTROLLER_ID] - ihc_key = f"ihc{controller_id}" - return hass.data[ihc_key][IHC_CONTROLLER] + controller_index = call.data[ATTR_CONTROLLER_ID] + for controller_id in hass.data[DOMAIN]: + controller_conf = hass.data[DOMAIN][controller_id] + if controller_conf[IHC_CONTROLLER_INDEX] == controller_index: + return controller_conf[IHC_CONTROLLER] + # if not found the controller_index is ouf of range + raise ValueError("The controller index is out of range") async def async_set_runtime_value_bool(call): """Set a IHC runtime bool value service function.""" diff --git a/homeassistant/components/ihc/switch.py b/homeassistant/components/ihc/switch.py index fb1e2f1642d..e33d3b6bb5e 100644 --- a/homeassistant/components/ihc/switch.py +++ b/homeassistant/components/ihc/switch.py @@ -1,13 +1,14 @@ """Support for IHC switches.""" from __future__ import annotations +from ihcsdk.ihccontroller import IHCController + from homeassistant.components.switch import SwitchEntity from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from . import IHC_CONTROLLER, IHC_INFO -from .const import CONF_OFF_ID, CONF_ON_ID +from .const import CONF_OFF_ID, CONF_ON_ID, DOMAIN, IHC_CONTROLLER from .ihcdevice import IHCDevice from .util import async_pulse, async_set_bool @@ -27,15 +28,13 @@ def setup_platform( product_cfg = device["product_cfg"] product = device["product"] # Find controller that corresponds with device id - ctrl_id = device["ctrl_id"] - ihc_key = f"ihc{ctrl_id}" - info = hass.data[ihc_key][IHC_INFO] - ihc_controller = hass.data[ihc_key][IHC_CONTROLLER] + controller_id = device["ctrl_id"] + ihc_controller: IHCController = hass.data[DOMAIN][controller_id][IHC_CONTROLLER] ihc_off_id = product_cfg.get(CONF_OFF_ID) ihc_on_id = product_cfg.get(CONF_ON_ID) switch = IHCSwitch( - ihc_controller, name, ihc_id, ihc_off_id, ihc_on_id, info, product + ihc_controller, controller_id, name, ihc_id, ihc_off_id, ihc_on_id, product ) devices.append(switch) add_entities(devices) @@ -46,16 +45,16 @@ class IHCSwitch(IHCDevice, SwitchEntity): def __init__( self, - ihc_controller, + ihc_controller: IHCController, + controller_id: str, name: str, ihc_id: int, ihc_off_id: int, ihc_on_id: int, - info: bool, product=None, ) -> None: """Initialize the IHC switch.""" - super().__init__(ihc_controller, name, ihc_id, product) + super().__init__(ihc_controller, controller_id, name, ihc_id, product) self._ihc_off_id = ihc_off_id self._ihc_on_id = ihc_on_id self._state = False