Add support for multiple IHC controllers ()

* Added support for secondary IHC controller

Most IHC systems only have one controller but the system can be setup with a linked secondary controller.
I have updated the code to have it support both primary and secondary controller. Existing configuration is not impacted and secondary controller can be setup the same way, with similar settings nested under 'secondary' in the configuration

* Update __init__.py

* Update __init__.py

* Update __init__.py

* Update ihc.py

* Update ihc.py

* Update ihc.py

* Update __init__.py

* Update ihc.py

* Update ihc.py

* Update ihc.py

* Update __init__.py

* Update ihc.py

* Update ihc.py

* Update __init__.py

* Update ihc.py

* Update __init__.py

* Update const.py

* Update ihc.py

* Update ihc.py

* Update ihc.py

* Update ihc.py

* Update ihc.py

* Update ihc.py

* Update ihc.py

* Update ihc.py

* Update ihc.py

* Update ihc.py

* Update ihc.py

* Update ihc.py

* Update __init__.py

* Update __init__.py

* Update __init__.py

* Update const.py

* Update __init__.py

* Update ihc.py

* Update ihc.py

* Update ihc.py

* Update ihc.py

* Update __init__.py

* Update __init__.py

* Update ihc.py

* Update ihc.py

* Update ihc.py

* Update __init__.py

* Update ihc.py

* Update ihc.py

* Update __init__.py

* Update ihc.py

* Update ihc.py

* Update __init__.py

* Update ihc.py

* Update ihc.py

* Update ihc.py

* Update ihc.py

* Update ihc.py

* Update ihc.py

* Update ihc.py

* Update ihc.py

* Update __init__.py

* Update __init__.py

* Update ihc.py

* Update __init__.py

* Update __init__.py

* Update __init__.py

* Update __init__.py

* Update __init__.py

* Update __init__.py

indentation was incorrect for "load_platform" in "get_manual_configuration". Load_platform was not called with the correct component name
pull/18640/head
mopolus 2018-11-22 09:45:40 +01:00 committed by Martin Hjelmare
parent 22ab83acae
commit 01ee03a9a1
6 changed files with 214 additions and 180 deletions
homeassistant/components
binary_sensor
light
sensor
switch

View File

@ -3,59 +3,39 @@
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.ihc/
"""
import voluptuous as vol
from homeassistant.components.binary_sensor import (
BinarySensorDevice, PLATFORM_SCHEMA, DEVICE_CLASSES_SCHEMA)
BinarySensorDevice)
from homeassistant.components.ihc import (
validate_name, IHC_DATA, IHC_CONTROLLER, IHC_INFO)
from homeassistant.components.ihc.const import CONF_INVERTING
IHC_DATA, IHC_CONTROLLER, IHC_INFO)
from homeassistant.components.ihc.const import (
CONF_INVERTING)
from homeassistant.components.ihc.ihcdevice import IHCDevice
from homeassistant.const import (
CONF_NAME, CONF_TYPE, CONF_ID, CONF_BINARY_SENSORS)
import homeassistant.helpers.config_validation as cv
CONF_TYPE)
DEPENDENCIES = ['ihc']
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_BINARY_SENSORS, default=[]):
vol.All(cv.ensure_list, [
vol.All({
vol.Required(CONF_ID): cv.positive_int,
vol.Optional(CONF_NAME): cv.string,
vol.Optional(CONF_TYPE): DEVICE_CLASSES_SCHEMA,
vol.Optional(CONF_INVERTING, default=False): cv.boolean,
}, validate_name)
])
})
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the IHC binary sensor platform."""
ihc_controller = hass.data[IHC_DATA][IHC_CONTROLLER]
info = hass.data[IHC_DATA][IHC_INFO]
if discovery_info is None:
return
devices = []
if discovery_info:
for name, device in discovery_info.items():
ihc_id = device['ihc_id']
product_cfg = device['product_cfg']
product = device['product']
sensor = IHCBinarySensor(ihc_controller, name, ihc_id, info,
product_cfg.get(CONF_TYPE),
product_cfg[CONF_INVERTING],
product)
devices.append(sensor)
else:
binary_sensors = config[CONF_BINARY_SENSORS]
for sensor_cfg in binary_sensors:
ihc_id = sensor_cfg[CONF_ID]
name = sensor_cfg[CONF_NAME]
sensor_type = sensor_cfg.get(CONF_TYPE)
inverting = sensor_cfg[CONF_INVERTING]
sensor = IHCBinarySensor(ihc_controller, name, ihc_id, info,
sensor_type, inverting)
devices.append(sensor)
for name, device in discovery_info.items():
ihc_id = device['ihc_id']
product_cfg = device['product_cfg']
product = device['product']
# Find controller that corresponds with device id
ctrl_id = device['ctrl_id']
ihc_key = IHC_DATA.format(ctrl_id)
info = hass.data[ihc_key][IHC_INFO]
ihc_controller = hass.data[ihc_key][IHC_CONTROLLER]
sensor = IHCBinarySensor(ihc_controller, name, ihc_id, info,
product_cfg.get(CONF_TYPE),
product_cfg[CONF_INVERTING],
product)
devices.append(sensor)
add_entities(devices)

View File

@ -9,16 +9,18 @@ import os.path
import xml.etree.ElementTree
import voluptuous as vol
from homeassistant.components.binary_sensor import (
DEVICE_CLASSES_SCHEMA)
from homeassistant.components.ihc.const import (
ATTR_IHC_ID, ATTR_VALUE, CONF_AUTOSETUP, CONF_BINARY_SENSOR, CONF_DIMMABLE,
CONF_INFO, CONF_INVERTING, CONF_LIGHT, CONF_NODE, CONF_SENSOR, CONF_SWITCH,
CONF_XPATH, SERVICE_SET_RUNTIME_VALUE_BOOL,
CONF_INFO, CONF_INVERTING, CONF_LIGHT, CONF_NODE, CONF_NOTE, CONF_POSITION,
CONF_SENSOR, CONF_SWITCH, CONF_XPATH, SERVICE_SET_RUNTIME_VALUE_BOOL,
SERVICE_SET_RUNTIME_VALUE_FLOAT, SERVICE_SET_RUNTIME_VALUE_INT)
from homeassistant.config import load_yaml_config_file
from homeassistant.const import (
CONF_ID, CONF_NAME, CONF_PASSWORD, CONF_TYPE, CONF_UNIT_OF_MEASUREMENT,
CONF_URL, CONF_USERNAME, TEMP_CELSIUS)
CONF_BINARY_SENSORS, CONF_ID, CONF_LIGHTS, CONF_NAME, CONF_PASSWORD,
CONF_SENSORS, CONF_SWITCHES, CONF_TYPE, CONF_UNIT_OF_MEASUREMENT, CONF_URL,
CONF_USERNAME, TEMP_CELSIUS)
from homeassistant.helpers import discovery
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.typing import HomeAssistantType
@ -26,21 +28,87 @@ from homeassistant.helpers.typing import HomeAssistantType
REQUIREMENTS = ['ihcsdk==2.2.0']
DOMAIN = 'ihc'
IHC_DATA = 'ihc'
IHC_DATA = 'ihc{}'
IHC_CONTROLLER = 'controller'
IHC_INFO = 'info'
AUTO_SETUP_YAML = 'ihc_auto_setup.yaml'
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
vol.Required(CONF_URL): cv.string,
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Optional(CONF_AUTOSETUP, default=True): cv.boolean,
vol.Optional(CONF_INFO, default=True): cv.boolean,
}),
def validate_name(config):
"""Validate device name."""
if CONF_NAME in config:
return config
ihcid = config[CONF_ID]
name = 'ihc_{}'.format(ihcid)
config[CONF_NAME] = name
return config
DEVICE_SCHEMA = vol.Schema({
vol.Required(CONF_ID): cv.positive_int,
vol.Optional(CONF_NAME): cv.string,
vol.Optional(CONF_POSITION): cv.string,
vol.Optional(CONF_NOTE): cv.string
}, extra=vol.ALLOW_EXTRA)
SWITCH_SCHEMA = DEVICE_SCHEMA.extend({
})
BINARY_SENSOR_SCHEMA = DEVICE_SCHEMA.extend({
vol.Optional(CONF_TYPE): DEVICE_CLASSES_SCHEMA,
vol.Optional(CONF_INVERTING, default=False): cv.boolean,
})
LIGHT_SCHEMA = DEVICE_SCHEMA.extend({
vol.Optional(CONF_DIMMABLE, default=False): cv.boolean,
})
SENSOR_SCHEMA = DEVICE_SCHEMA.extend({
vol.Optional(CONF_UNIT_OF_MEASUREMENT,
default=TEMP_CELSIUS): cv.string,
})
IHC_SCHEMA = vol.Schema({
vol.Required(CONF_URL): cv.string,
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Optional(CONF_AUTOSETUP, default=True): cv.boolean,
vol.Optional(CONF_INFO, default=True): cv.boolean,
vol.Optional(CONF_BINARY_SENSORS, default=[]):
vol.All(cv.ensure_list, [
vol.All(
BINARY_SENSOR_SCHEMA,
validate_name)
]),
vol.Optional(CONF_LIGHTS, default=[]):
vol.All(cv.ensure_list, [
vol.All(
LIGHT_SCHEMA,
validate_name)
]),
vol.Optional(CONF_SENSORS, default=[]):
vol.All(cv.ensure_list, [
vol.All(
SENSOR_SCHEMA,
validate_name)
]),
vol.Optional(CONF_SWITCHES, default=[]):
vol.All(cv.ensure_list, [
vol.All(
SWITCH_SCHEMA,
validate_name)
]),
}, extra=vol.ALLOW_EXTRA)
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema(vol.All(
cv.ensure_list,
[IHC_SCHEMA]
)),
}, extra=vol.ALLOW_EXTRA)
AUTO_SETUP_SCHEMA = vol.Schema({
vol.Optional(CONF_BINARY_SENSOR, default=[]):
vol.All(cv.ensure_list, [
@ -98,35 +166,79 @@ IHC_PLATFORMS = ('binary_sensor', 'light', 'sensor', 'switch')
def setup(hass, config):
"""Set up the IHC platform."""
conf = config.get(DOMAIN)
for index, controller_conf in enumerate(conf):
if not ihc_setup(hass, config, controller_conf, index):
return False
return True
def ihc_setup(hass, config, conf, controller_id):
"""Set up the IHC component."""
from ihcsdk.ihccontroller import IHCController
conf = config[DOMAIN]
url = conf[CONF_URL]
username = conf[CONF_USERNAME]
password = conf[CONF_PASSWORD]
ihc_controller = IHCController(url, username, password)
ihc_controller = IHCController(url, username, password)
if not ihc_controller.authenticate():
_LOGGER.error("Unable to authenticate on IHC controller")
return False
if (conf[CONF_AUTOSETUP] and
not autosetup_ihc_products(hass, config, ihc_controller)):
not autosetup_ihc_products(hass, config, ihc_controller,
controller_id)):
return False
hass.data[IHC_DATA] = {
# Manual configuration
get_manual_configuration(hass, config, conf, ihc_controller,
controller_id)
# Store controler configuration
ihc_key = IHC_DATA.format(controller_id)
hass.data[ihc_key] = {
IHC_CONTROLLER: ihc_controller,
IHC_INFO: conf[CONF_INFO]}
setup_service_functions(hass, ihc_controller)
return True
def autosetup_ihc_products(hass: HomeAssistantType, config, ihc_controller):
def get_manual_configuration(hass, config, conf, ihc_controller,
controller_id):
"""Get manual configuration for IHC devices."""
for component in IHC_PLATFORMS:
discovery_info = {}
if component in conf:
component_setup = conf.get(component)
for sensor_cfg in component_setup:
name = sensor_cfg[CONF_NAME]
device = {
'ihc_id': sensor_cfg[CONF_ID],
'ctrl_id': controller_id,
'product': {
'name': name,
'note': sensor_cfg.get(CONF_NOTE) or '',
'position': sensor_cfg.get(CONF_POSITION) or ''},
'product_cfg': {
'type': sensor_cfg.get(CONF_TYPE),
'inverting': sensor_cfg.get(CONF_INVERTING),
'dimmable': sensor_cfg.get(CONF_DIMMABLE),
'unit': sensor_cfg.get(CONF_UNIT_OF_MEASUREMENT)
}
}
discovery_info[name] = device
if discovery_info:
discovery.load_platform(hass, component, DOMAIN,
discovery_info, config)
def autosetup_ihc_products(hass: HomeAssistantType, config, ihc_controller,
controller_id):
"""Auto setup of IHC products from the IHC project file."""
project_xml = ihc_controller.get_project()
if not project_xml:
_LOGGER.error("Unable to read project from ICH controller")
_LOGGER.error("Unable to read project from IHC controller")
return False
project = xml.etree.ElementTree.fromstring(project_xml)
@ -143,14 +255,15 @@ def autosetup_ihc_products(hass: HomeAssistantType, config, ihc_controller):
groups = project.findall('.//group')
for component in IHC_PLATFORMS:
component_setup = auto_setup_conf[component]
discovery_info = get_discovery_info(component_setup, groups)
discovery_info = get_discovery_info(component_setup, groups,
controller_id)
if discovery_info:
discovery.load_platform(hass, component, DOMAIN, discovery_info,
config)
return True
def get_discovery_info(component_setup, groups):
def get_discovery_info(component_setup, groups, controller_id):
"""Get discovery info for specified IHC component."""
discovery_data = {}
for group in groups:
@ -167,6 +280,7 @@ def get_discovery_info(component_setup, groups):
name = '{}_{}'.format(groupname, ihc_id)
device = {
'ihc_id': ihc_id,
'ctrl_id': controller_id,
'product': {
'name': product.attrib['name'],
'note': product.attrib['note'],
@ -205,13 +319,3 @@ def setup_service_functions(hass: HomeAssistantType, ihc_controller):
hass.services.register(DOMAIN, SERVICE_SET_RUNTIME_VALUE_FLOAT,
set_runtime_value_float,
schema=SET_RUNTIME_VALUE_FLOAT_SCHEMA)
def validate_name(config):
"""Validate device name."""
if CONF_NAME in config:
return config
ihcid = config[CONF_ID]
name = 'ihc_{}'.format(ihcid)
config[CONF_NAME] = name
return config

View File

@ -10,6 +10,9 @@ CONF_BINARY_SENSOR = 'binary_sensor'
CONF_LIGHT = 'light'
CONF_SENSOR = 'sensor'
CONF_SWITCH = 'switch'
CONF_NAME = 'name'
CONF_POSITION = 'position'
CONF_NOTE = 'note'
ATTR_IHC_ID = 'ihc_id'
ATTR_VALUE = 'value'

View File

@ -3,53 +3,39 @@
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/light.ihc/
"""
import voluptuous as vol
import logging
from homeassistant.components.ihc import (
validate_name, IHC_DATA, IHC_CONTROLLER, IHC_INFO)
from homeassistant.components.ihc.const import CONF_DIMMABLE
IHC_DATA, IHC_CONTROLLER, IHC_INFO)
from homeassistant.components.ihc.const import (
CONF_DIMMABLE)
from homeassistant.components.ihc.ihcdevice import IHCDevice
from homeassistant.components.light import (
ATTR_BRIGHTNESS, SUPPORT_BRIGHTNESS, PLATFORM_SCHEMA, Light)
from homeassistant.const import CONF_ID, CONF_NAME, CONF_LIGHTS
import homeassistant.helpers.config_validation as cv
ATTR_BRIGHTNESS, SUPPORT_BRIGHTNESS, Light)
DEPENDENCIES = ['ihc']
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_LIGHTS, default=[]):
vol.All(cv.ensure_list, [
vol.All({
vol.Required(CONF_ID): cv.positive_int,
vol.Optional(CONF_NAME): cv.string,
vol.Optional(CONF_DIMMABLE, default=False): cv.boolean,
}, validate_name)
])
})
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the IHC lights platform."""
ihc_controller = hass.data[IHC_DATA][IHC_CONTROLLER]
info = hass.data[IHC_DATA][IHC_INFO]
if discovery_info is None:
return
devices = []
if discovery_info:
for name, device in discovery_info.items():
ihc_id = device['ihc_id']
product_cfg = device['product_cfg']
product = device['product']
light = IhcLight(ihc_controller, name, ihc_id, info,
product_cfg[CONF_DIMMABLE], product)
devices.append(light)
else:
lights = config[CONF_LIGHTS]
for light in lights:
ihc_id = light[CONF_ID]
name = light[CONF_NAME]
dimmable = light[CONF_DIMMABLE]
device = IhcLight(ihc_controller, name, ihc_id, info, dimmable)
devices.append(device)
for name, device in discovery_info.items():
ihc_id = device['ihc_id']
product_cfg = device['product_cfg']
product = device['product']
# Find controller that corresponds with device id
ctrl_id = device['ctrl_id']
ihc_key = IHC_DATA.format(ctrl_id)
info = hass.data[ihc_key][IHC_INFO]
ihc_controller = hass.data[ihc_key][IHC_CONTROLLER]
dimmable = product_cfg[CONF_DIMMABLE]
light = IhcLight(ihc_controller, name, ihc_id, info,
dimmable, product)
devices.append(light)
add_entities(devices)

View File

@ -3,56 +3,34 @@
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/sensor.ihc/
"""
import voluptuous as vol
from homeassistant.components.ihc import (
validate_name, IHC_DATA, IHC_CONTROLLER, IHC_INFO)
IHC_DATA, IHC_CONTROLLER, IHC_INFO)
from homeassistant.components.ihc.ihcdevice import IHCDevice
from homeassistant.components.sensor import PLATFORM_SCHEMA
from homeassistant.const import (
CONF_ID, CONF_NAME, CONF_UNIT_OF_MEASUREMENT, CONF_SENSORS,
TEMP_CELSIUS)
import homeassistant.helpers.config_validation as cv
CONF_UNIT_OF_MEASUREMENT)
from homeassistant.helpers.entity import Entity
DEPENDENCIES = ['ihc']
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_SENSORS, default=[]):
vol.All(cv.ensure_list, [
vol.All({
vol.Required(CONF_ID): cv.positive_int,
vol.Optional(CONF_NAME): cv.string,
vol.Optional(CONF_UNIT_OF_MEASUREMENT,
default=TEMP_CELSIUS): cv.string
}, validate_name)
])
})
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the IHC sensor platform."""
ihc_controller = hass.data[IHC_DATA][IHC_CONTROLLER]
info = hass.data[IHC_DATA][IHC_INFO]
if discovery_info is None:
return
devices = []
if discovery_info:
for name, device in discovery_info.items():
ihc_id = device['ihc_id']
product_cfg = device['product_cfg']
product = device['product']
sensor = IHCSensor(ihc_controller, name, ihc_id, info,
product_cfg[CONF_UNIT_OF_MEASUREMENT],
product)
devices.append(sensor)
else:
sensors = config[CONF_SENSORS]
for sensor_cfg in sensors:
ihc_id = sensor_cfg[CONF_ID]
name = sensor_cfg[CONF_NAME]
unit = sensor_cfg[CONF_UNIT_OF_MEASUREMENT]
sensor = IHCSensor(ihc_controller, name, ihc_id, info, unit)
devices.append(sensor)
for name, device in discovery_info.items():
ihc_id = device['ihc_id']
product_cfg = device['product_cfg']
product = device['product']
# Find controller that corresponds with device id
ctrl_id = device['ctrl_id']
ihc_key = IHC_DATA.format(ctrl_id)
info = hass.data[ihc_key][IHC_INFO]
ihc_controller = hass.data[ihc_key][IHC_CONTROLLER]
unit = product_cfg[CONF_UNIT_OF_MEASUREMENT]
sensor = IHCSensor(ihc_controller, name, ihc_id, info,
unit, product)
devices.append(sensor)
add_entities(devices)

View File

@ -3,47 +3,30 @@
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/switch.ihc/
"""
import voluptuous as vol
from homeassistant.components.ihc import (
validate_name, IHC_DATA, IHC_CONTROLLER, IHC_INFO)
IHC_DATA, IHC_CONTROLLER, IHC_INFO)
from homeassistant.components.ihc.ihcdevice import IHCDevice
from homeassistant.components.switch import SwitchDevice, PLATFORM_SCHEMA
from homeassistant.const import CONF_ID, CONF_NAME, CONF_SWITCHES
import homeassistant.helpers.config_validation as cv
from homeassistant.components.switch import SwitchDevice
DEPENDENCIES = ['ihc']
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_SWITCHES, default=[]):
vol.All(cv.ensure_list, [
vol.All({
vol.Required(CONF_ID): cv.positive_int,
vol.Optional(CONF_NAME): cv.string,
}, validate_name)
])
})
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the IHC switch platform."""
ihc_controller = hass.data[IHC_DATA][IHC_CONTROLLER]
info = hass.data[IHC_DATA][IHC_INFO]
if discovery_info is None:
return
devices = []
if discovery_info:
for name, device in discovery_info.items():
ihc_id = device['ihc_id']
product = device['product']
switch = IHCSwitch(ihc_controller, name, ihc_id, info, product)
devices.append(switch)
else:
switches = config[CONF_SWITCHES]
for switch in switches:
ihc_id = switch[CONF_ID]
name = switch[CONF_NAME]
sensor = IHCSwitch(ihc_controller, name, ihc_id, info)
devices.append(sensor)
for name, device in discovery_info.items():
ihc_id = device['ihc_id']
product = device['product']
# Find controller that corresponds with device id
ctrl_id = device['ctrl_id']
ihc_key = IHC_DATA.format(ctrl_id)
info = hass.data[ihc_key][IHC_INFO]
ihc_controller = hass.data[ihc_key][IHC_CONTROLLER]
switch = IHCSwitch(ihc_controller, name, ihc_id, info, product)
devices.append(switch)
add_entities(devices)