mirror of
https://github.com/home-assistant/core.git
synced 2025-06-25 01:21:51 +02:00
Ihc component and platforms (#10916)
* Added IHC platform * Updated requirements for IHC platform * Exclude IHC from test * Correcting flake8 issues * Fixing more flake8 issues * Fixed flake8 issues * Fixing pylint issues * Fixed flake8 issues * Changes from PR review. * STATE_UNKNOWN changed to None * Spelling mistake in comment * Added IHC platform * Updated requirements for IHC platform * Exclude IHC from test * Correcting flake8 issues * Fixing more flake8 issues * Fixed flake8 issues * Fixing pylint issues * Fixed flake8 issues * Changes from PR review. * STATE_UNKNOWN changed to None * Spelling mistake in comment * Updated requirements_all.txt with gen_requirements_app.py * Pylint fix: No space allowed around keyword argument assignment * PR review changes * Moved auto setup from platforms to ihc component * Do no auto setup if there are no IHC products found * Changes from PR review
This commit is contained in:
committed by
Martin Hjelmare
parent
323992e224
commit
e02d5e7ff1
@ -97,6 +97,9 @@ omit =
|
||||
homeassistant/components/homematic/__init__.py
|
||||
homeassistant/components/*/homematic.py
|
||||
|
||||
homeassistant/components/ihc/*
|
||||
homeassistant/components/*/ihc.py
|
||||
|
||||
homeassistant/components/insteon_local.py
|
||||
homeassistant/components/*/insteon_local.py
|
||||
|
||||
|
95
homeassistant/components/binary_sensor/ihc.py
Normal file
95
homeassistant/components/binary_sensor/ihc.py
Normal file
@ -0,0 +1,95 @@
|
||||
"""IHC binary sensor platform.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/binary_sensor.ihc/
|
||||
"""
|
||||
from xml.etree.ElementTree import Element
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.binary_sensor import (
|
||||
BinarySensorDevice, PLATFORM_SCHEMA, DEVICE_CLASSES_SCHEMA)
|
||||
from homeassistant.components.ihc import (
|
||||
validate_name, 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
|
||||
|
||||
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, default=None): DEVICE_CLASSES_SCHEMA,
|
||||
vol.Optional(CONF_INVERTING, default=False): cv.boolean,
|
||||
}, validate_name)
|
||||
])
|
||||
})
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, 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]
|
||||
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[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[CONF_TYPE]
|
||||
inverting = sensor_cfg[CONF_INVERTING]
|
||||
sensor = IHCBinarySensor(ihc_controller, name, ihc_id, info,
|
||||
sensor_type, inverting)
|
||||
devices.append(sensor)
|
||||
|
||||
add_devices(devices)
|
||||
|
||||
|
||||
class IHCBinarySensor(IHCDevice, BinarySensorDevice):
|
||||
"""IHC Binary Sensor.
|
||||
|
||||
The associated IHC resource can be any in or output from a IHC product
|
||||
or function block, but it must be a boolean ON/OFF resources.
|
||||
"""
|
||||
|
||||
def __init__(self, ihc_controller, name, ihc_id: int, info: bool,
|
||||
sensor_type: str, inverting: bool, product: Element=None):
|
||||
"""Initialize the IHC binary sensor."""
|
||||
super().__init__(ihc_controller, name, ihc_id, info, product)
|
||||
self._state = None
|
||||
self._sensor_type = sensor_type
|
||||
self.inverting = inverting
|
||||
|
||||
@property
|
||||
def device_class(self):
|
||||
"""Return the class of this sensor."""
|
||||
return self._sensor_type
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
"""Return true if the binary sensor is on/open."""
|
||||
return self._state
|
||||
|
||||
def on_ihc_change(self, ihc_id, value):
|
||||
"""IHC resource has changed."""
|
||||
if self.inverting:
|
||||
self._state = not value
|
||||
else:
|
||||
self._state = value
|
||||
self.schedule_update_ha_state()
|
213
homeassistant/components/ihc/__init__.py
Normal file
213
homeassistant/components/ihc/__init__.py
Normal file
@ -0,0 +1,213 @@
|
||||
"""IHC component.
|
||||
|
||||
For more details about this component, please refer to the documentation at
|
||||
https://home-assistant.io/components/ihc/
|
||||
"""
|
||||
import logging
|
||||
import os.path
|
||||
import xml.etree.ElementTree
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.ihc.const import (
|
||||
ATTR_IHC_ID, ATTR_VALUE, CONF_INFO, CONF_AUTOSETUP,
|
||||
CONF_BINARY_SENSOR, CONF_LIGHT, CONF_SENSOR, CONF_SWITCH,
|
||||
CONF_XPATH, CONF_NODE, CONF_DIMMABLE, CONF_INVERTING,
|
||||
SERVICE_SET_RUNTIME_VALUE_BOOL, SERVICE_SET_RUNTIME_VALUE_INT,
|
||||
SERVICE_SET_RUNTIME_VALUE_FLOAT)
|
||||
from homeassistant.config import load_yaml_config_file
|
||||
from homeassistant.const import (
|
||||
CONF_URL, CONF_USERNAME, CONF_PASSWORD, CONF_ID, CONF_NAME,
|
||||
CONF_UNIT_OF_MEASUREMENT, CONF_TYPE, TEMP_CELSIUS)
|
||||
from homeassistant.helpers import discovery
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.typing import HomeAssistantType
|
||||
|
||||
REQUIREMENTS = ['ihcsdk==2.1.1']
|
||||
|
||||
DOMAIN = '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
|
||||
}),
|
||||
}, extra=vol.ALLOW_EXTRA)
|
||||
|
||||
AUTO_SETUP_SCHEMA = vol.Schema({
|
||||
vol.Optional(CONF_BINARY_SENSOR, default=[]):
|
||||
vol.All(cv.ensure_list, [
|
||||
vol.All({
|
||||
vol.Required(CONF_XPATH): cv.string,
|
||||
vol.Required(CONF_NODE): cv.string,
|
||||
vol.Optional(CONF_TYPE, default=None): cv.string,
|
||||
vol.Optional(CONF_INVERTING, default=False): cv.boolean,
|
||||
})
|
||||
]),
|
||||
vol.Optional(CONF_LIGHT, default=[]):
|
||||
vol.All(cv.ensure_list, [
|
||||
vol.All({
|
||||
vol.Required(CONF_XPATH): cv.string,
|
||||
vol.Required(CONF_NODE): cv.string,
|
||||
vol.Optional(CONF_DIMMABLE, default=False): cv.boolean,
|
||||
})
|
||||
]),
|
||||
vol.Optional(CONF_SENSOR, default=[]):
|
||||
vol.All(cv.ensure_list, [
|
||||
vol.All({
|
||||
vol.Required(CONF_XPATH): cv.string,
|
||||
vol.Required(CONF_NODE): cv.string,
|
||||
vol.Optional(CONF_UNIT_OF_MEASUREMENT,
|
||||
default=TEMP_CELSIUS): cv.string,
|
||||
})
|
||||
]),
|
||||
vol.Optional(CONF_SWITCH, default=[]):
|
||||
vol.All(cv.ensure_list, [
|
||||
vol.All({
|
||||
vol.Required(CONF_XPATH): cv.string,
|
||||
vol.Required(CONF_NODE): cv.string,
|
||||
})
|
||||
]),
|
||||
})
|
||||
|
||||
SET_RUNTIME_VALUE_BOOL_SCHEMA = vol.Schema({
|
||||
vol.Required(ATTR_IHC_ID): cv.positive_int,
|
||||
vol.Required(ATTR_VALUE): cv.boolean
|
||||
})
|
||||
|
||||
SET_RUNTIME_VALUE_INT_SCHEMA = vol.Schema({
|
||||
vol.Required(ATTR_IHC_ID): cv.positive_int,
|
||||
vol.Required(ATTR_VALUE): int
|
||||
})
|
||||
|
||||
SET_RUNTIME_VALUE_FLOAT_SCHEMA = vol.Schema({
|
||||
vol.Required(ATTR_IHC_ID): cv.positive_int,
|
||||
vol.Required(ATTR_VALUE): vol.Coerce(float)
|
||||
})
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
IHC_PLATFORMS = ('binary_sensor', 'light', 'sensor', 'switch')
|
||||
|
||||
|
||||
def setup(hass, config):
|
||||
"""Setup 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)
|
||||
|
||||
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)):
|
||||
return False
|
||||
|
||||
hass.data[IHC_DATA] = {
|
||||
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):
|
||||
"""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 ihc controller.")
|
||||
return False
|
||||
project = xml.etree.ElementTree.fromstring(project_xml)
|
||||
|
||||
# if an auto setup file exist in the configuration it will override
|
||||
yaml_path = hass.config.path(AUTO_SETUP_YAML)
|
||||
if not os.path.isfile(yaml_path):
|
||||
yaml_path = os.path.join(os.path.dirname(__file__), AUTO_SETUP_YAML)
|
||||
yaml = load_yaml_config_file(yaml_path)
|
||||
try:
|
||||
auto_setup_conf = AUTO_SETUP_SCHEMA(yaml)
|
||||
except vol.Invalid as exception:
|
||||
_LOGGER.error("Invalid IHC auto setup data: %s", exception)
|
||||
return False
|
||||
groups = project.findall('.//group')
|
||||
for component in IHC_PLATFORMS:
|
||||
component_setup = auto_setup_conf[component]
|
||||
discovery_info = get_discovery_info(component_setup, groups)
|
||||
if discovery_info:
|
||||
discovery.load_platform(hass, component, DOMAIN, discovery_info,
|
||||
config)
|
||||
return True
|
||||
|
||||
|
||||
def get_discovery_info(component_setup, groups):
|
||||
"""Get discovery info for specified component."""
|
||||
discovery_data = {}
|
||||
for group in groups:
|
||||
groupname = group.attrib['name']
|
||||
for product_cfg in component_setup:
|
||||
products = group.findall(product_cfg[CONF_XPATH])
|
||||
for product in products:
|
||||
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 = '{}_{}'.format(groupname, ihc_id)
|
||||
device = {
|
||||
'ihc_id': ihc_id,
|
||||
'product': product,
|
||||
'product_cfg': product_cfg}
|
||||
discovery_data[name] = device
|
||||
return discovery_data
|
||||
|
||||
|
||||
def setup_service_functions(hass: HomeAssistantType, ihc_controller):
|
||||
"""Setup the ihc service functions."""
|
||||
def set_runtime_value_bool(call):
|
||||
"""Set a IHC runtime bool value service function."""
|
||||
ihc_id = call.data[ATTR_IHC_ID]
|
||||
value = call.data[ATTR_VALUE]
|
||||
ihc_controller.set_runtime_value_bool(ihc_id, value)
|
||||
|
||||
def set_runtime_value_int(call):
|
||||
"""Set a IHC runtime integer value service function."""
|
||||
ihc_id = call.data[ATTR_IHC_ID]
|
||||
value = call.data[ATTR_VALUE]
|
||||
ihc_controller.set_runtime_value_int(ihc_id, value)
|
||||
|
||||
def set_runtime_value_float(call):
|
||||
"""Set a IHC runtime float value service function."""
|
||||
ihc_id = call.data[ATTR_IHC_ID]
|
||||
value = call.data[ATTR_VALUE]
|
||||
ihc_controller.set_runtime_value_float(ihc_id, value)
|
||||
|
||||
hass.services.register(DOMAIN, SERVICE_SET_RUNTIME_VALUE_BOOL,
|
||||
set_runtime_value_bool,
|
||||
schema=SET_RUNTIME_VALUE_BOOL_SCHEMA)
|
||||
hass.services.register(DOMAIN, SERVICE_SET_RUNTIME_VALUE_INT,
|
||||
set_runtime_value_int,
|
||||
schema=SET_RUNTIME_VALUE_INT_SCHEMA)
|
||||
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
|
19
homeassistant/components/ihc/const.py
Normal file
19
homeassistant/components/ihc/const.py
Normal file
@ -0,0 +1,19 @@
|
||||
"""IHC component constants."""
|
||||
|
||||
CONF_AUTOSETUP = 'auto_setup'
|
||||
CONF_INFO = 'info'
|
||||
CONF_XPATH = 'xpath'
|
||||
CONF_NODE = 'node'
|
||||
CONF_INVERTING = 'inverting'
|
||||
CONF_DIMMABLE = 'dimmable'
|
||||
CONF_BINARY_SENSOR = 'binary_sensor'
|
||||
CONF_LIGHT = 'light'
|
||||
CONF_SENSOR = 'sensor'
|
||||
CONF_SWITCH = 'switch'
|
||||
|
||||
ATTR_IHC_ID = 'ihc_id'
|
||||
ATTR_VALUE = 'value'
|
||||
|
||||
SERVICE_SET_RUNTIME_VALUE_BOOL = "set_runtime_value_bool"
|
||||
SERVICE_SET_RUNTIME_VALUE_INT = "set_runtime_value_int"
|
||||
SERVICE_SET_RUNTIME_VALUE_FLOAT = "set_runtime_value_float"
|
98
homeassistant/components/ihc/ihc_auto_setup.yaml
Normal file
98
homeassistant/components/ihc/ihc_auto_setup.yaml
Normal file
@ -0,0 +1,98 @@
|
||||
# IHC auto setup configuration.
|
||||
# To customize this, copy this file to the home assistant configuration
|
||||
# folder and make your changes.
|
||||
|
||||
binary_sensor:
|
||||
# Magnet contact
|
||||
- xpath: './/product_dataline[@product_identifier="_0x2109"]'
|
||||
node: 'dataline_input'
|
||||
type: 'opening'
|
||||
inverting: True
|
||||
# Pir sensors
|
||||
- xpath: './/product_dataline[@product_identifier="_0x210e"]'
|
||||
node: 'dataline_input[1]'
|
||||
type: 'motion'
|
||||
# Pir sensors twilight sensor
|
||||
- xpath: './/product_dataline[@product_identifier="_0x0"]'
|
||||
node: 'dataline_input[1]'
|
||||
type: 'motion'
|
||||
# Pir sensors alarm
|
||||
- xpath: './/product_dataline[@product_identifier="_0x210f"]'
|
||||
node: 'dataline_input'
|
||||
type: 'motion'
|
||||
# Smoke detector
|
||||
- xpath: './/product_dataline[@product_identifier="_0x210a"]'
|
||||
node: 'dataline_input'
|
||||
type: 'smoke'
|
||||
# leak detector
|
||||
- xpath: './/product_dataline[@product_identifier="_0x210c"]'
|
||||
node: 'dataline_input'
|
||||
type: 'moisture'
|
||||
# light detector
|
||||
- xpath: './/product_dataline[@product_identifier="_0x2110"]'
|
||||
node: 'dataline_input'
|
||||
type: 'light'
|
||||
|
||||
light:
|
||||
# Wireless Combi dimmer 4 buttons
|
||||
- xpath: './/product_airlink[@product_identifier="_0x4406"]'
|
||||
node: 'airlink_dimming'
|
||||
dimmable: True
|
||||
# Wireless Lamp outlet dimmer
|
||||
- xpath: './/product_airlink[@product_identifier="_0x4304"]'
|
||||
node: 'airlink_dimming'
|
||||
dimmable: True
|
||||
# Wireless universal dimmer
|
||||
- xpath: './/product_airlink[@product_identifier="_0x4306"]'
|
||||
node: 'airlink_dimming'
|
||||
dimmable: True
|
||||
# Wireless Lamp outlet relay
|
||||
- xpath: './/product_airlink[@product_identifier="_0x4202"]'
|
||||
node: 'airlink_relay'
|
||||
# Wireless Combi relay 4 buttons
|
||||
- xpath: './/product_airlink[@product_identifier="_0x4404"]'
|
||||
node: 'airlink_relay'
|
||||
# Dataline Lamp outlet
|
||||
- xpath: './/product_dataline[@product_identifier="_0x2202"]'
|
||||
node: 'dataline_output'
|
||||
# Mobile Wireless dimmer
|
||||
- xpath: './/product_airlink[@product_identifier="_0x4303"]'
|
||||
node: 'airlink_dimming'
|
||||
dimmable: True
|
||||
|
||||
sensor:
|
||||
# Temperature sensor
|
||||
- xpath: './/product_dataline[@product_identifier="_0x2124"]'
|
||||
node: 'resource_temperature'
|
||||
unit_of_measurement: '°C'
|
||||
# Humidity/temperature
|
||||
- xpath: './/product_dataline[@product_identifier="_0x2135"]'
|
||||
node: 'resource_humidity_level'
|
||||
unit_of_measurement: '%'
|
||||
# Humidity/temperature
|
||||
- xpath: './/product_dataline[@product_identifier="_0x2135"]'
|
||||
node: 'resource_temperature'
|
||||
unit_of_measurement: '°C'
|
||||
# Lux/temperature
|
||||
- xpath: './/product_dataline[@product_identifier="_0x2136"]'
|
||||
node: 'resource_light'
|
||||
unit_of_measurement: 'Lux'
|
||||
# Lux/temperature
|
||||
- xpath: './/product_dataline[@product_identifier="_0x2136"]'
|
||||
node: 'resource_temperature'
|
||||
unit_of_measurement: '°C'
|
||||
|
||||
switch:
|
||||
# Wireless Plug outlet
|
||||
- xpath: './/product_airlink[@product_identifier="_0x4201"]'
|
||||
node: 'airlink_relay'
|
||||
# Dataline universal relay
|
||||
- xpath: './/product_airlink[@product_identifier="_0x4203"]'
|
||||
node: 'airlink_relay'
|
||||
# Dataline plug outlet
|
||||
- xpath: './/product_dataline[@product_identifier="_0x2201"]'
|
||||
node: 'dataline_output'
|
||||
# Wireless mobile relay
|
||||
- xpath: './/product_airlink[@product_identifier="_0x4204"]'
|
||||
node: 'airlink_relay'
|
||||
|
65
homeassistant/components/ihc/ihcdevice.py
Normal file
65
homeassistant/components/ihc/ihcdevice.py
Normal file
@ -0,0 +1,65 @@
|
||||
"""Implements a base class for all IHC devices."""
|
||||
import asyncio
|
||||
from xml.etree.ElementTree import Element
|
||||
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
|
||||
class IHCDevice(Entity):
|
||||
"""Base class for all ihc devices.
|
||||
|
||||
All IHC devices have an associated IHC resource. IHCDevice handled the
|
||||
registration of the IHC controller callback when the IHC resource changes.
|
||||
Derived classes must implement the on_ihc_change method
|
||||
"""
|
||||
|
||||
def __init__(self, ihc_controller, name, ihc_id: int, info: bool,
|
||||
product: Element=None):
|
||||
"""Initialize IHC attributes."""
|
||||
self.ihc_controller = ihc_controller
|
||||
self._name = name
|
||||
self.ihc_id = ihc_id
|
||||
self.info = info
|
||||
if product:
|
||||
self.ihc_name = product.attrib['name']
|
||||
self.ihc_note = product.attrib['note']
|
||||
self.ihc_position = product.attrib['position']
|
||||
else:
|
||||
self.ihc_name = ''
|
||||
self.ihc_note = ''
|
||||
self.ihc_position = ''
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_added_to_hass(self):
|
||||
"""Add callback for ihc changes."""
|
||||
self.ihc_controller.add_notify_event(
|
||||
self.ihc_id, self.on_ihc_change, True)
|
||||
|
||||
@property
|
||||
def should_poll(self) -> bool:
|
||||
"""No polling needed for ihc devices."""
|
||||
return False
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the device name."""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
"""Return the state attributes."""
|
||||
if not self.info:
|
||||
return {}
|
||||
return {
|
||||
'ihc_id': self.ihc_id,
|
||||
'ihc_name': self.ihc_name,
|
||||
'ihc_note': self.ihc_note,
|
||||
'ihc_position': self.ihc_position
|
||||
}
|
||||
|
||||
def on_ihc_change(self, ihc_id, value):
|
||||
"""Callback when ihc resource changes.
|
||||
|
||||
Derived classes must overwrite this to do device specific stuff.
|
||||
"""
|
||||
raise NotImplementedError
|
26
homeassistant/components/ihc/services.yaml
Normal file
26
homeassistant/components/ihc/services.yaml
Normal file
@ -0,0 +1,26 @@
|
||||
# Describes the format for available ihc services
|
||||
|
||||
set_runtime_value_bool:
|
||||
description: Set a boolean runtime value on the ihc controller
|
||||
fields:
|
||||
ihc_id:
|
||||
description: The integer ihc resource id
|
||||
value:
|
||||
description: The boolean value to set
|
||||
|
||||
set_runtime_value_int:
|
||||
description: Set an integer runtime value on the ihc controller
|
||||
fields:
|
||||
ihc_id:
|
||||
description: The integer ihc resource id
|
||||
value:
|
||||
description: The integer value to set
|
||||
|
||||
set_runtime_value_float:
|
||||
description: Set a float runtime value on the ihc controller
|
||||
fields:
|
||||
ihc_id:
|
||||
description: The integer ihc resource id
|
||||
value:
|
||||
description: The float value to set
|
||||
|
123
homeassistant/components/light/ihc.py
Normal file
123
homeassistant/components/light/ihc.py
Normal file
@ -0,0 +1,123 @@
|
||||
"""IHC light platform.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/light.ihc/
|
||||
"""
|
||||
from xml.etree.ElementTree import Element
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.ihc import (
|
||||
validate_name, 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
|
||||
|
||||
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)
|
||||
])
|
||||
})
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
"""Setup the ihc lights platform."""
|
||||
ihc_controller = hass.data[IHC_DATA][IHC_CONTROLLER]
|
||||
info = hass.data[IHC_DATA][IHC_INFO]
|
||||
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)
|
||||
|
||||
add_devices(devices)
|
||||
|
||||
|
||||
class IhcLight(IHCDevice, Light):
|
||||
"""Representation of a IHC light.
|
||||
|
||||
For dimmable lights, the associated IHC resource should be a light
|
||||
level (integer). For non dimmable light the IHC resource should be
|
||||
an on/off (boolean) resource
|
||||
"""
|
||||
|
||||
def __init__(self, ihc_controller, name, ihc_id: int, info: bool,
|
||||
dimmable=False, product: Element=None):
|
||||
"""Initialize the light."""
|
||||
super().__init__(ihc_controller, name, ihc_id, info, product)
|
||||
self._brightness = 0
|
||||
self._dimmable = dimmable
|
||||
self._state = None
|
||||
|
||||
@property
|
||||
def brightness(self) -> int:
|
||||
"""Return the brightness of this light between 0..255."""
|
||||
return self._brightness
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
"""Return true if light is on."""
|
||||
return self._state
|
||||
|
||||
@property
|
||||
def supported_features(self):
|
||||
"""Flag supported features."""
|
||||
if self._dimmable:
|
||||
return SUPPORT_BRIGHTNESS
|
||||
return 0
|
||||
|
||||
def turn_on(self, **kwargs) -> None:
|
||||
"""Turn the light on."""
|
||||
if ATTR_BRIGHTNESS in kwargs:
|
||||
brightness = kwargs[ATTR_BRIGHTNESS]
|
||||
else:
|
||||
brightness = self._brightness
|
||||
if brightness == 0:
|
||||
brightness = 255
|
||||
|
||||
if self._dimmable:
|
||||
self.ihc_controller.set_runtime_value_int(
|
||||
self.ihc_id, int(brightness * 100 / 255))
|
||||
else:
|
||||
self.ihc_controller.set_runtime_value_bool(self.ihc_id, True)
|
||||
|
||||
def turn_off(self, **kwargs) -> None:
|
||||
"""Turn the light off."""
|
||||
if self._dimmable:
|
||||
self.ihc_controller.set_runtime_value_int(self.ihc_id, 0)
|
||||
else:
|
||||
self.ihc_controller.set_runtime_value_bool(self.ihc_id, False)
|
||||
|
||||
def on_ihc_change(self, ihc_id, value):
|
||||
"""Callback from Ihc notifications."""
|
||||
if isinstance(value, bool):
|
||||
self._dimmable = False
|
||||
self._state = value != 0
|
||||
else:
|
||||
self._dimmable = True
|
||||
self._state = value > 0
|
||||
if self._state:
|
||||
self._brightness = int(value * 255 / 100)
|
||||
self.schedule_update_ha_state()
|
84
homeassistant/components/sensor/ihc.py
Normal file
84
homeassistant/components/sensor/ihc.py
Normal file
@ -0,0 +1,84 @@
|
||||
"""IHC sensor platform.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/sensor.ihc/
|
||||
"""
|
||||
from xml.etree.ElementTree import Element
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.ihc import (
|
||||
validate_name, 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
|
||||
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_devices, discovery_info=None):
|
||||
"""Setup the ihc sensor platform."""
|
||||
ihc_controller = hass.data[IHC_DATA][IHC_CONTROLLER]
|
||||
info = hass.data[IHC_DATA][IHC_INFO]
|
||||
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)
|
||||
|
||||
add_devices(devices)
|
||||
|
||||
|
||||
class IHCSensor(IHCDevice, Entity):
|
||||
"""Implementation of the IHC sensor."""
|
||||
|
||||
def __init__(self, ihc_controller, name, ihc_id: int, info: bool,
|
||||
unit, product: Element=None):
|
||||
"""Initialize the IHC sensor."""
|
||||
super().__init__(ihc_controller, name, ihc_id, info, product)
|
||||
self._state = None
|
||||
self._unit_of_measurement = unit
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
"""Return the state of the sensor."""
|
||||
return self._state
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
"""Return the unit of measurement of this entity, if any."""
|
||||
return self._unit_of_measurement
|
||||
|
||||
def on_ihc_change(self, ihc_id, value):
|
||||
"""Callback when ihc resource changes."""
|
||||
self._state = value
|
||||
self.schedule_update_ha_state()
|
77
homeassistant/components/switch/ihc.py
Normal file
77
homeassistant/components/switch/ihc.py
Normal file
@ -0,0 +1,77 @@
|
||||
"""IHC switch platform.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/switch.ihc/
|
||||
"""
|
||||
from xml.etree.ElementTree import Element
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.ihc import (
|
||||
validate_name, 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
|
||||
|
||||
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_devices, discovery_info=None):
|
||||
"""Setup the ihc switch platform."""
|
||||
ihc_controller = hass.data[IHC_DATA][IHC_CONTROLLER]
|
||||
info = hass.data[IHC_DATA][IHC_INFO]
|
||||
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)
|
||||
|
||||
add_devices(devices)
|
||||
|
||||
|
||||
class IHCSwitch(IHCDevice, SwitchDevice):
|
||||
"""IHC Switch."""
|
||||
|
||||
def __init__(self, ihc_controller, name: str, ihc_id: int,
|
||||
info: bool, product: Element=None):
|
||||
"""Initialize the IHC switch."""
|
||||
super().__init__(ihc_controller, name, ihc_id, product)
|
||||
self._state = False
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
"""Return true if switch is on."""
|
||||
return self._state
|
||||
|
||||
def turn_on(self, **kwargs):
|
||||
"""Turn the switch on."""
|
||||
self.ihc_controller.set_runtime_value_bool(self.ihc_id, True)
|
||||
|
||||
def turn_off(self, **kwargs):
|
||||
"""Turn the device off."""
|
||||
self.ihc_controller.set_runtime_value_bool(self.ihc_id, False)
|
||||
|
||||
def on_ihc_change(self, ihc_id, value):
|
||||
"""Callback when the ihc resource changes."""
|
||||
self._state = value
|
||||
self.schedule_update_ha_state()
|
@ -398,6 +398,9 @@ https://github.com/wokar/pylgnetcast/archive/v0.2.0.zip#pylgnetcast==0.2.0
|
||||
# homeassistant.components.light.iglo
|
||||
iglo==1.1.3
|
||||
|
||||
# homeassistant.components.ihc
|
||||
ihcsdk==2.1.1
|
||||
|
||||
# homeassistant.components.influxdb
|
||||
# homeassistant.components.sensor.influxdb
|
||||
influxdb==4.1.1
|
||||
|
Reference in New Issue
Block a user