Prepare for Ihc config flow (#64852)
* Extracting group and extra info from ihc products * Make suggested area not optional * Revert back to assignment expression := * Make auto setup show device info for all platforms * Change code comment to # * Add return typing * Remove device_info key without value * get_manual_configuration typings for everything * Adding IHCController typings * Remove "ihc" from unique id * Remove device_info * Separator in unique id * Return typing on ihc_setup Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Update homeassistant/components/ihc/ihcdevice.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Update homeassistant/components/ihc/ihcdevice.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Raise ValueError instead of logging an error * Update homeassistant/components/ihc/service_functions.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Catch up with dev Co-authored-by: Martin Hjelmare <marhje52@gmail.com>pull/65664/head
parent
43671da7cf
commit
a644baf3cd
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -25,6 +25,7 @@ CONF_XPATH = "xpath"
|
|||
DOMAIN = "ihc"
|
||||
|
||||
IHC_CONTROLLER = "controller"
|
||||
IHC_CONTROLLER_INDEX = "controller_index"
|
||||
IHC_PLATFORMS = (
|
||||
Platform.BINARY_SENSOR,
|
||||
Platform.LIGHT,
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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."""
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue