Multiple devices support for opentherm_gw (#22932)

* Breaking change: Rewrite opentherm_gw to add support for more than one OpenTherm Gateway.
Breaks config layout and child entity ids and adds a required parameter to all service calls (gateway_id).

* Add schema and parameter description for service opentherm_gw.reset_gateway.

* Add optional name attribute in config to be used for friendly names.
Fix bugs in binary_sensor and climate platforms.

* pylint fixes

* Remove unused variables.

* Update manifest.json, remove REQUIREMENTS from .py file

* Update CODEOWNERS

* Address issues that were brought up (requested changes):
- Move imports to module level
- Change certain functions from async to sync
- Move constants to const.py (new file)
- Call gateway setup from outside of __init__()
- Move validation of monitored_variables to config schema

* Address requested changes:
- Make module imports relative
- Move more functions from async to sync, decorate with @callback where necessary
- Remove monitored_variables option, add all sensors by default
This commit is contained in:
mvn23 2019-06-21 10:52:25 +02:00 committed by Martin Hjelmare
parent d9f2a406f6
commit 43a6be6471
8 changed files with 486 additions and 519 deletions

View file

@ -184,6 +184,7 @@ homeassistant/components/nsw_fuel_station/* @nickw444
homeassistant/components/nuki/* @pschmitt homeassistant/components/nuki/* @pschmitt
homeassistant/components/ohmconnect/* @robbiet480 homeassistant/components/ohmconnect/* @robbiet480
homeassistant/components/onboarding/* @home-assistant/core homeassistant/components/onboarding/* @home-assistant/core
homeassistant/components/opentherm_gw/* @mvn23
homeassistant/components/openuv/* @bachya homeassistant/components/openuv/* @bachya
homeassistant/components/openweathermap/* @fabaff homeassistant/components/openweathermap/* @fabaff
homeassistant/components/orangepi_gpio/* @pascallj homeassistant/components/orangepi_gpio/* @pascallj

View file

@ -2,357 +2,256 @@
import logging import logging
from datetime import datetime, date from datetime import datetime, date
import pyotgw
import pyotgw.vars as gw_vars
import voluptuous as vol import voluptuous as vol
from homeassistant.components.binary_sensor import DOMAIN as COMP_BINARY_SENSOR from homeassistant.components.binary_sensor import DOMAIN as COMP_BINARY_SENSOR
from homeassistant.components.climate import DOMAIN as COMP_CLIMATE
from homeassistant.components.sensor import DOMAIN as COMP_SENSOR from homeassistant.components.sensor import DOMAIN as COMP_SENSOR
from homeassistant.const import ( from homeassistant.const import (
ATTR_DATE, ATTR_ID, ATTR_TEMPERATURE, ATTR_TIME, CONF_DEVICE, ATTR_DATE, ATTR_ID, ATTR_TEMPERATURE, ATTR_TIME, CONF_DEVICE, CONF_NAME,
CONF_MONITORED_VARIABLES, CONF_NAME, EVENT_HOMEASSISTANT_STOP, EVENT_HOMEASSISTANT_STOP, PRECISION_HALVES, PRECISION_TENTHS,
PRECISION_HALVES, PRECISION_TENTHS, PRECISION_WHOLE) PRECISION_WHOLE)
from homeassistant.helpers.discovery import async_load_platform from homeassistant.helpers.discovery import async_load_platform
from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.dispatcher import async_dispatcher_send
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from .const import (
ATTR_GW_ID, ATTR_MODE, ATTR_LEVEL, CONF_CLIMATE, CONF_FLOOR_TEMP,
CONF_PRECISION, DATA_GATEWAYS, DATA_OPENTHERM_GW, SERVICE_RESET_GATEWAY,
SERVICE_SET_CLOCK, SERVICE_SET_CONTROL_SETPOINT, SERVICE_SET_GPIO_MODE,
SERVICE_SET_LED_MODE, SERVICE_SET_MAX_MOD, SERVICE_SET_OAT,
SERVICE_SET_SB_TEMP)
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
DOMAIN = 'opentherm_gw' DOMAIN = 'opentherm_gw'
ATTR_MODE = 'mode'
ATTR_LEVEL = 'level'
CONF_CLIMATE = 'climate'
CONF_FLOOR_TEMP = 'floor_temperature'
CONF_PRECISION = 'precision'
DATA_DEVICE = 'device'
DATA_GW_VARS = 'gw_vars'
DATA_LATEST_STATUS = 'latest_status'
DATA_OPENTHERM_GW = 'opentherm_gw'
SIGNAL_OPENTHERM_GW_UPDATE = 'opentherm_gw_update'
SERVICE_RESET_GATEWAY = 'reset_gateway'
SERVICE_SET_CLOCK = 'set_clock'
SERVICE_SET_CLOCK_SCHEMA = vol.Schema({
vol.Optional(ATTR_DATE, default=date.today()): cv.date,
vol.Optional(ATTR_TIME, default=datetime.now().time()): cv.time,
})
SERVICE_SET_CONTROL_SETPOINT = 'set_control_setpoint'
SERVICE_SET_CONTROL_SETPOINT_SCHEMA = vol.Schema({
vol.Required(ATTR_TEMPERATURE): vol.All(vol.Coerce(float),
vol.Range(min=0, max=90)),
})
SERVICE_SET_GPIO_MODE = 'set_gpio_mode'
SERVICE_SET_GPIO_MODE_SCHEMA = vol.Schema(vol.Any(
vol.Schema({
vol.Required(ATTR_ID): vol.Equal('A'),
vol.Required(ATTR_MODE): vol.All(vol.Coerce(int),
vol.Range(min=0, max=6)),
}),
vol.Schema({
vol.Required(ATTR_ID): vol.Equal('B'),
vol.Required(ATTR_MODE): vol.All(vol.Coerce(int),
vol.Range(min=0, max=7)),
}),
))
SERVICE_SET_LED_MODE = 'set_led_mode'
SERVICE_SET_LED_MODE_SCHEMA = vol.Schema({
vol.Required(ATTR_ID): vol.In('ABCDEF'),
vol.Required(ATTR_MODE): vol.In('RXTBOFHWCEMP'),
})
SERVICE_SET_MAX_MOD = 'set_max_modulation'
SERVICE_SET_MAX_MOD_SCHEMA = vol.Schema({
vol.Required(ATTR_LEVEL): vol.All(vol.Coerce(int),
vol.Range(min=-1, max=100))
})
SERVICE_SET_OAT = 'set_outside_temperature'
SERVICE_SET_OAT_SCHEMA = vol.Schema({
vol.Required(ATTR_TEMPERATURE): vol.All(vol.Coerce(float),
vol.Range(min=-40, max=99)),
})
SERVICE_SET_SB_TEMP = 'set_setback_temperature'
SERVICE_SET_SB_TEMP_SCHEMA = vol.Schema({
vol.Required(ATTR_TEMPERATURE): vol.All(vol.Coerce(float),
vol.Range(min=0, max=30)),
})
CLIMATE_SCHEMA = vol.Schema({ CLIMATE_SCHEMA = vol.Schema({
vol.Optional(CONF_NAME, default="OpenTherm Gateway"): cv.string,
vol.Optional(CONF_PRECISION): vol.In([PRECISION_TENTHS, PRECISION_HALVES, vol.Optional(CONF_PRECISION): vol.In([PRECISION_TENTHS, PRECISION_HALVES,
PRECISION_WHOLE]), PRECISION_WHOLE]),
vol.Optional(CONF_FLOOR_TEMP, default=False): cv.boolean, vol.Optional(CONF_FLOOR_TEMP, default=False): cv.boolean,
}) })
CONFIG_SCHEMA = vol.Schema({ CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({ DOMAIN: cv.schema_with_slug_keys({
vol.Required(CONF_DEVICE): cv.string, vol.Required(CONF_DEVICE): cv.string,
vol.Optional(CONF_CLIMATE, default={}): CLIMATE_SCHEMA, vol.Optional(CONF_CLIMATE, default={}): CLIMATE_SCHEMA,
vol.Optional(CONF_MONITORED_VARIABLES, default=[]): vol.All( vol.Optional(CONF_NAME): cv.string,
cv.ensure_list, [cv.string]),
}), }),
}, extra=vol.ALLOW_EXTRA) }, extra=vol.ALLOW_EXTRA)
async def async_setup(hass, config): async def async_setup(hass, config):
"""Set up the OpenTherm Gateway component.""" """Set up the OpenTherm Gateway component."""
import pyotgw
conf = config[DOMAIN] conf = config[DOMAIN]
gateway = pyotgw.pyotgw() hass.data[DATA_OPENTHERM_GW] = {DATA_GATEWAYS: {}}
monitored_vars = conf.get(CONF_MONITORED_VARIABLES) for gw_id, cfg in conf.items():
hass.data[DATA_OPENTHERM_GW] = { gateway = OpenThermGatewayDevice(hass, gw_id, cfg)
DATA_DEVICE: gateway, hass.data[DATA_OPENTHERM_GW][DATA_GATEWAYS][gw_id] = gateway
DATA_GW_VARS: pyotgw.vars, hass.async_create_task(async_load_platform(hass, COMP_CLIMATE, DOMAIN,
DATA_LATEST_STATUS: {} gw_id, config))
} hass.async_create_task(async_load_platform(hass, COMP_BINARY_SENSOR,
hass.async_create_task(register_services(hass, gateway)) DOMAIN, gw_id, config))
hass.async_create_task(async_load_platform( hass.async_create_task(async_load_platform(hass, COMP_SENSOR, DOMAIN,
hass, 'climate', DOMAIN, conf.get(CONF_CLIMATE), config)) gw_id, config))
if monitored_vars:
hass.async_create_task(setup_monitored_vars(
hass, config, monitored_vars))
# Schedule directly on the loop to avoid blocking HA startup. # Schedule directly on the loop to avoid blocking HA startup.
hass.loop.create_task( hass.loop.create_task(gateway.connect_and_subscribe(cfg[CONF_DEVICE]))
connect_and_subscribe(hass, conf[CONF_DEVICE], gateway)) register_services(hass)
return True return True
async def connect_and_subscribe(hass, device_path, gateway): def register_services(hass):
"""Connect to serial device and subscribe report handler."""
await gateway.connect(hass.loop, device_path)
_LOGGER.debug("Connected to OpenTherm Gateway at %s", device_path)
async def cleanup(event):
"""Reset overrides on the gateway."""
await gateway.set_control_setpoint(0)
await gateway.set_max_relative_mod('-')
hass.bus.async_listen(EVENT_HOMEASSISTANT_STOP, cleanup)
async def handle_report(status):
"""Handle reports from the OpenTherm Gateway."""
_LOGGER.debug("Received report: %s", status)
hass.data[DATA_OPENTHERM_GW][DATA_LATEST_STATUS] = status
async_dispatcher_send(hass, SIGNAL_OPENTHERM_GW_UPDATE, status)
gateway.subscribe(handle_report)
async def register_services(hass, gateway):
"""Register services for the component.""" """Register services for the component."""
gw_vars = hass.data[DATA_OPENTHERM_GW][DATA_GW_VARS] service_reset_schema = vol.Schema({
vol.Required(ATTR_GW_ID): vol.All(
cv.string, vol.In(hass.data[DATA_OPENTHERM_GW][DATA_GATEWAYS])),
})
service_set_clock_schema = vol.Schema({
vol.Required(ATTR_GW_ID): vol.All(
cv.string, vol.In(hass.data[DATA_OPENTHERM_GW][DATA_GATEWAYS])),
vol.Optional(ATTR_DATE, default=date.today()): cv.date,
vol.Optional(ATTR_TIME, default=datetime.now().time()): cv.time,
})
service_set_control_setpoint_schema = vol.Schema({
vol.Required(ATTR_GW_ID): vol.All(
cv.string, vol.In(hass.data[DATA_OPENTHERM_GW][DATA_GATEWAYS])),
vol.Required(ATTR_TEMPERATURE): vol.All(vol.Coerce(float),
vol.Range(min=0, max=90)),
})
service_set_gpio_mode_schema = vol.Schema(vol.Any(
vol.Schema({
vol.Required(ATTR_GW_ID): vol.All(
cv.string, vol.In(
hass.data[DATA_OPENTHERM_GW][DATA_GATEWAYS])),
vol.Required(ATTR_ID): vol.Equal('A'),
vol.Required(ATTR_MODE): vol.All(vol.Coerce(int),
vol.Range(min=0, max=6)),
}),
vol.Schema({
vol.Required(ATTR_GW_ID): vol.All(
cv.string, vol.In(
hass.data[DATA_OPENTHERM_GW][DATA_GATEWAYS])),
vol.Required(ATTR_ID): vol.Equal('B'),
vol.Required(ATTR_MODE): vol.All(vol.Coerce(int),
vol.Range(min=0, max=7)),
}),
))
service_set_led_mode_schema = vol.Schema({
vol.Required(ATTR_GW_ID): vol.All(
cv.string, vol.In(hass.data[DATA_OPENTHERM_GW][DATA_GATEWAYS])),
vol.Required(ATTR_ID): vol.In('ABCDEF'),
vol.Required(ATTR_MODE): vol.In('RXTBOFHWCEMP'),
})
service_set_max_mod_schema = vol.Schema({
vol.Required(ATTR_GW_ID): vol.All(
cv.string, vol.In(hass.data[DATA_OPENTHERM_GW][DATA_GATEWAYS])),
vol.Required(ATTR_LEVEL): vol.All(vol.Coerce(int),
vol.Range(min=-1, max=100))
})
service_set_oat_schema = vol.Schema({
vol.Required(ATTR_GW_ID): vol.All(
cv.string, vol.In(hass.data[DATA_OPENTHERM_GW][DATA_GATEWAYS])),
vol.Required(ATTR_TEMPERATURE): vol.All(vol.Coerce(float),
vol.Range(min=-40, max=99)),
})
service_set_sb_temp_schema = vol.Schema({
vol.Required(ATTR_GW_ID): vol.All(
cv.string, vol.In(hass.data[DATA_OPENTHERM_GW][DATA_GATEWAYS])),
vol.Required(ATTR_TEMPERATURE): vol.All(vol.Coerce(float),
vol.Range(min=0, max=30)),
})
async def reset_gateway(call): async def reset_gateway(call):
"""Reset the OpenTherm Gateway.""" """Reset the OpenTherm Gateway."""
gw_dev = (
hass.data[DATA_OPENTHERM_GW][DATA_GATEWAYS][call.data[ATTR_GW_ID]])
mode_rst = gw_vars.OTGW_MODE_RESET mode_rst = gw_vars.OTGW_MODE_RESET
status = await gateway.set_mode(mode_rst) status = await gw_dev.gateway.set_mode(mode_rst)
hass.data[DATA_OPENTHERM_GW][DATA_LATEST_STATUS] = status gw_dev.status = status
async_dispatcher_send(hass, SIGNAL_OPENTHERM_GW_UPDATE, status) async_dispatcher_send(hass, gw_dev.update_signal, gw_dev.status)
hass.services.async_register(DOMAIN, SERVICE_RESET_GATEWAY, reset_gateway) hass.services.async_register(DOMAIN, SERVICE_RESET_GATEWAY, reset_gateway,
service_reset_schema)
async def set_control_setpoint(call): async def set_control_setpoint(call):
"""Set the control setpoint on the OpenTherm Gateway.""" """Set the control setpoint on the OpenTherm Gateway."""
gw_dev = (
hass.data[DATA_OPENTHERM_GW][DATA_GATEWAYS][call.data[ATTR_GW_ID]])
gw_var = gw_vars.DATA_CONTROL_SETPOINT gw_var = gw_vars.DATA_CONTROL_SETPOINT
value = await gateway.set_control_setpoint(call.data[ATTR_TEMPERATURE]) value = await gw_dev.gateway.set_control_setpoint(
status = hass.data[DATA_OPENTHERM_GW][DATA_LATEST_STATUS] call.data[ATTR_TEMPERATURE])
status.update({gw_var: value}) gw_dev.status.update({gw_var: value})
async_dispatcher_send(hass, SIGNAL_OPENTHERM_GW_UPDATE, status) async_dispatcher_send(hass, gw_dev.update_signal, gw_dev.status)
hass.services.async_register(DOMAIN, SERVICE_SET_CONTROL_SETPOINT, hass.services.async_register(DOMAIN, SERVICE_SET_CONTROL_SETPOINT,
set_control_setpoint, set_control_setpoint,
SERVICE_SET_CONTROL_SETPOINT_SCHEMA) service_set_control_setpoint_schema)
async def set_device_clock(call): async def set_device_clock(call):
"""Set the clock on the OpenTherm Gateway.""" """Set the clock on the OpenTherm Gateway."""
gw_dev = (
hass.data[DATA_OPENTHERM_GW][DATA_GATEWAYS][call.data[ATTR_GW_ID]])
attr_date = call.data[ATTR_DATE] attr_date = call.data[ATTR_DATE]
attr_time = call.data[ATTR_TIME] attr_time = call.data[ATTR_TIME]
await gateway.set_clock(datetime.combine(attr_date, attr_time)) await gw_dev.gateway.set_clock(datetime.combine(attr_date, attr_time))
hass.services.async_register(DOMAIN, SERVICE_SET_CLOCK, set_device_clock, hass.services.async_register(DOMAIN, SERVICE_SET_CLOCK, set_device_clock,
SERVICE_SET_CLOCK_SCHEMA) service_set_clock_schema)
async def set_gpio_mode(call): async def set_gpio_mode(call):
"""Set the OpenTherm Gateway GPIO modes.""" """Set the OpenTherm Gateway GPIO modes."""
gw_dev = (
hass.data[DATA_OPENTHERM_GW][DATA_GATEWAYS][call.data[ATTR_GW_ID]])
gpio_id = call.data[ATTR_ID] gpio_id = call.data[ATTR_ID]
gpio_mode = call.data[ATTR_MODE] gpio_mode = call.data[ATTR_MODE]
mode = await gateway.set_gpio_mode(gpio_id, gpio_mode) mode = await gw_dev.gateway.set_gpio_mode(gpio_id, gpio_mode)
gpio_var = getattr(gw_vars, 'OTGW_GPIO_{}'.format(gpio_id)) gpio_var = getattr(gw_vars, 'OTGW_GPIO_{}'.format(gpio_id))
status = hass.data[DATA_OPENTHERM_GW][DATA_LATEST_STATUS] gw_dev.status.update({gpio_var: mode})
status.update({gpio_var: mode}) async_dispatcher_send(hass, gw_dev.update_signal, gw_dev.status)
async_dispatcher_send(hass, SIGNAL_OPENTHERM_GW_UPDATE, status)
hass.services.async_register(DOMAIN, SERVICE_SET_GPIO_MODE, set_gpio_mode, hass.services.async_register(DOMAIN, SERVICE_SET_GPIO_MODE, set_gpio_mode,
SERVICE_SET_GPIO_MODE_SCHEMA) service_set_gpio_mode_schema)
async def set_led_mode(call): async def set_led_mode(call):
"""Set the OpenTherm Gateway LED modes.""" """Set the OpenTherm Gateway LED modes."""
gw_dev = (
hass.data[DATA_OPENTHERM_GW][DATA_GATEWAYS][call.data[ATTR_GW_ID]])
led_id = call.data[ATTR_ID] led_id = call.data[ATTR_ID]
led_mode = call.data[ATTR_MODE] led_mode = call.data[ATTR_MODE]
mode = await gateway.set_led_mode(led_id, led_mode) mode = await gw_dev.gateway.set_led_mode(led_id, led_mode)
led_var = getattr(gw_vars, 'OTGW_LED_{}'.format(led_id)) led_var = getattr(gw_vars, 'OTGW_LED_{}'.format(led_id))
status = hass.data[DATA_OPENTHERM_GW][DATA_LATEST_STATUS] gw_dev.status.update({led_var: mode})
status.update({led_var: mode}) async_dispatcher_send(hass, gw_dev.update_signal, gw_dev.status)
async_dispatcher_send(hass, SIGNAL_OPENTHERM_GW_UPDATE, status)
hass.services.async_register(DOMAIN, SERVICE_SET_LED_MODE, set_led_mode, hass.services.async_register(DOMAIN, SERVICE_SET_LED_MODE, set_led_mode,
SERVICE_SET_LED_MODE_SCHEMA) service_set_led_mode_schema)
async def set_max_mod(call): async def set_max_mod(call):
"""Set the max modulation level.""" """Set the max modulation level."""
gw_dev = (
hass.data[DATA_OPENTHERM_GW][DATA_GATEWAYS][call.data[ATTR_GW_ID]])
gw_var = gw_vars.DATA_SLAVE_MAX_RELATIVE_MOD gw_var = gw_vars.DATA_SLAVE_MAX_RELATIVE_MOD
level = call.data[ATTR_LEVEL] level = call.data[ATTR_LEVEL]
if level == -1: if level == -1:
# Backend only clears setting on non-numeric values. # Backend only clears setting on non-numeric values.
level = '-' level = '-'
value = await gateway.set_max_relative_mod(level) value = await gw_dev.gateway.set_max_relative_mod(level)
status = hass.data[DATA_OPENTHERM_GW][DATA_LATEST_STATUS] gw_dev.status.update({gw_var: value})
status.update({gw_var: value}) async_dispatcher_send(hass, gw_dev.update_signal, gw_dev.status)
async_dispatcher_send(hass, SIGNAL_OPENTHERM_GW_UPDATE, status)
hass.services.async_register(DOMAIN, SERVICE_SET_MAX_MOD, set_max_mod, hass.services.async_register(DOMAIN, SERVICE_SET_MAX_MOD, set_max_mod,
SERVICE_SET_MAX_MOD_SCHEMA) service_set_max_mod_schema)
async def set_outside_temp(call): async def set_outside_temp(call):
"""Provide the outside temperature to the OpenTherm Gateway.""" """Provide the outside temperature to the OpenTherm Gateway."""
gw_dev = (
hass.data[DATA_OPENTHERM_GW][DATA_GATEWAYS][call.data[ATTR_GW_ID]])
gw_var = gw_vars.DATA_OUTSIDE_TEMP gw_var = gw_vars.DATA_OUTSIDE_TEMP
value = await gateway.set_outside_temp(call.data[ATTR_TEMPERATURE]) value = await gw_dev.gateway.set_outside_temp(
status = hass.data[DATA_OPENTHERM_GW][DATA_LATEST_STATUS] call.data[ATTR_TEMPERATURE])
status.update({gw_var: value}) gw_dev.status.update({gw_var: value})
async_dispatcher_send(hass, SIGNAL_OPENTHERM_GW_UPDATE, status) async_dispatcher_send(hass, gw_dev.update_signal, gw_dev.status)
hass.services.async_register(DOMAIN, SERVICE_SET_OAT, set_outside_temp, hass.services.async_register(DOMAIN, SERVICE_SET_OAT, set_outside_temp,
SERVICE_SET_OAT_SCHEMA) service_set_oat_schema)
async def set_setback_temp(call): async def set_setback_temp(call):
"""Set the OpenTherm Gateway SetBack temperature.""" """Set the OpenTherm Gateway SetBack temperature."""
gw_dev = (
hass.data[DATA_OPENTHERM_GW][DATA_GATEWAYS][call.data[ATTR_GW_ID]])
gw_var = gw_vars.OTGW_SB_TEMP gw_var = gw_vars.OTGW_SB_TEMP
value = await gateway.set_setback_temp(call.data[ATTR_TEMPERATURE]) value = await gw_dev.gateway.set_setback_temp(
status = hass.data[DATA_OPENTHERM_GW][DATA_LATEST_STATUS] call.data[ATTR_TEMPERATURE])
status.update({gw_var: value}) gw_dev.status.update({gw_var: value})
async_dispatcher_send(hass, SIGNAL_OPENTHERM_GW_UPDATE, status) async_dispatcher_send(hass, gw_dev.update_signal, gw_dev.status)
hass.services.async_register(DOMAIN, SERVICE_SET_SB_TEMP, set_setback_temp, hass.services.async_register(DOMAIN, SERVICE_SET_SB_TEMP, set_setback_temp,
SERVICE_SET_SB_TEMP_SCHEMA) service_set_sb_temp_schema)
async def setup_monitored_vars(hass, config, monitored_vars): class OpenThermGatewayDevice():
"""Set up requested sensors.""" """OpenTherm Gateway device class."""
gw_vars = hass.data[DATA_OPENTHERM_GW][DATA_GW_VARS]
sensor_type_map = { def __init__(self, hass, gw_id, config):
COMP_BINARY_SENSOR: [ """Initialize the OpenTherm Gateway."""
gw_vars.DATA_MASTER_CH_ENABLED, self.hass = hass
gw_vars.DATA_MASTER_DHW_ENABLED, self.gw_id = gw_id
gw_vars.DATA_MASTER_COOLING_ENABLED, self.name = config.get(CONF_NAME, gw_id)
gw_vars.DATA_MASTER_OTC_ENABLED, self.climate_config = config[CONF_CLIMATE]
gw_vars.DATA_MASTER_CH2_ENABLED, self.status = {}
gw_vars.DATA_SLAVE_FAULT_IND, self.update_signal = '{}_{}_update'.format(DATA_OPENTHERM_GW, gw_id)
gw_vars.DATA_SLAVE_CH_ACTIVE, self.gateway = pyotgw.pyotgw()
gw_vars.DATA_SLAVE_DHW_ACTIVE,
gw_vars.DATA_SLAVE_FLAME_ON, async def connect_and_subscribe(self, device_path):
gw_vars.DATA_SLAVE_COOLING_ACTIVE, """Connect to serial device and subscribe report handler."""
gw_vars.DATA_SLAVE_CH2_ACTIVE, await self.gateway.connect(self.hass.loop, device_path)
gw_vars.DATA_SLAVE_DIAG_IND, _LOGGER.debug("Connected to OpenTherm Gateway at %s", device_path)
gw_vars.DATA_SLAVE_DHW_PRESENT,
gw_vars.DATA_SLAVE_CONTROL_TYPE, async def cleanup(event):
gw_vars.DATA_SLAVE_COOLING_SUPPORTED, """Reset overrides on the gateway."""
gw_vars.DATA_SLAVE_DHW_CONFIG, await self.gateway.set_control_setpoint(0)
gw_vars.DATA_SLAVE_MASTER_LOW_OFF_PUMP, await self.gateway.set_max_relative_mod('-')
gw_vars.DATA_SLAVE_CH2_PRESENT, self.hass.bus.async_listen(EVENT_HOMEASSISTANT_STOP, cleanup)
gw_vars.DATA_SLAVE_SERVICE_REQ,
gw_vars.DATA_SLAVE_REMOTE_RESET, async def handle_report(status):
gw_vars.DATA_SLAVE_LOW_WATER_PRESS, """Handle reports from the OpenTherm Gateway."""
gw_vars.DATA_SLAVE_GAS_FAULT, _LOGGER.debug("Received report: %s", status)
gw_vars.DATA_SLAVE_AIR_PRESS_FAULT, self.status = status
gw_vars.DATA_SLAVE_WATER_OVERTEMP, async_dispatcher_send(self.hass, self.update_signal, status)
gw_vars.DATA_REMOTE_TRANSFER_DHW, self.gateway.subscribe(handle_report)
gw_vars.DATA_REMOTE_TRANSFER_MAX_CH,
gw_vars.DATA_REMOTE_RW_DHW,
gw_vars.DATA_REMOTE_RW_MAX_CH,
gw_vars.DATA_ROVRD_MAN_PRIO,
gw_vars.DATA_ROVRD_AUTO_PRIO,
gw_vars.OTGW_GPIO_A_STATE,
gw_vars.OTGW_GPIO_B_STATE,
gw_vars.OTGW_IGNORE_TRANSITIONS,
gw_vars.OTGW_OVRD_HB,
],
COMP_SENSOR: [
gw_vars.DATA_CONTROL_SETPOINT,
gw_vars.DATA_MASTER_MEMBERID,
gw_vars.DATA_SLAVE_MEMBERID,
gw_vars.DATA_SLAVE_OEM_FAULT,
gw_vars.DATA_COOLING_CONTROL,
gw_vars.DATA_CONTROL_SETPOINT_2,
gw_vars.DATA_ROOM_SETPOINT_OVRD,
gw_vars.DATA_SLAVE_MAX_RELATIVE_MOD,
gw_vars.DATA_SLAVE_MAX_CAPACITY,
gw_vars.DATA_SLAVE_MIN_MOD_LEVEL,
gw_vars.DATA_ROOM_SETPOINT,
gw_vars.DATA_REL_MOD_LEVEL,
gw_vars.DATA_CH_WATER_PRESS,
gw_vars.DATA_DHW_FLOW_RATE,
gw_vars.DATA_ROOM_SETPOINT_2,
gw_vars.DATA_ROOM_TEMP,
gw_vars.DATA_CH_WATER_TEMP,
gw_vars.DATA_DHW_TEMP,
gw_vars.DATA_OUTSIDE_TEMP,
gw_vars.DATA_RETURN_WATER_TEMP,
gw_vars.DATA_SOLAR_STORAGE_TEMP,
gw_vars.DATA_SOLAR_COLL_TEMP,
gw_vars.DATA_CH_WATER_TEMP_2,
gw_vars.DATA_DHW_TEMP_2,
gw_vars.DATA_EXHAUST_TEMP,
gw_vars.DATA_SLAVE_DHW_MAX_SETP,
gw_vars.DATA_SLAVE_DHW_MIN_SETP,
gw_vars.DATA_SLAVE_CH_MAX_SETP,
gw_vars.DATA_SLAVE_CH_MIN_SETP,
gw_vars.DATA_DHW_SETPOINT,
gw_vars.DATA_MAX_CH_SETPOINT,
gw_vars.DATA_OEM_DIAG,
gw_vars.DATA_TOTAL_BURNER_STARTS,
gw_vars.DATA_CH_PUMP_STARTS,
gw_vars.DATA_DHW_PUMP_STARTS,
gw_vars.DATA_DHW_BURNER_STARTS,
gw_vars.DATA_TOTAL_BURNER_HOURS,
gw_vars.DATA_CH_PUMP_HOURS,
gw_vars.DATA_DHW_PUMP_HOURS,
gw_vars.DATA_DHW_BURNER_HOURS,
gw_vars.DATA_MASTER_OT_VERSION,
gw_vars.DATA_SLAVE_OT_VERSION,
gw_vars.DATA_MASTER_PRODUCT_TYPE,
gw_vars.DATA_MASTER_PRODUCT_VERSION,
gw_vars.DATA_SLAVE_PRODUCT_TYPE,
gw_vars.DATA_SLAVE_PRODUCT_VERSION,
gw_vars.OTGW_MODE,
gw_vars.OTGW_DHW_OVRD,
gw_vars.OTGW_ABOUT,
gw_vars.OTGW_BUILD,
gw_vars.OTGW_CLOCKMHZ,
gw_vars.OTGW_LED_A,
gw_vars.OTGW_LED_B,
gw_vars.OTGW_LED_C,
gw_vars.OTGW_LED_D,
gw_vars.OTGW_LED_E,
gw_vars.OTGW_LED_F,
gw_vars.OTGW_GPIO_A,
gw_vars.OTGW_GPIO_B,
gw_vars.OTGW_SB_TEMP,
gw_vars.OTGW_SETP_OVRD_MODE,
gw_vars.OTGW_SMART_PWR,
gw_vars.OTGW_THRM_DETECT,
gw_vars.OTGW_VREF,
]
}
binary_sensors = []
sensors = []
for var in monitored_vars:
if var in sensor_type_map[COMP_SENSOR]:
sensors.append(var)
elif var in sensor_type_map[COMP_BINARY_SENSOR]:
binary_sensors.append(var)
else:
_LOGGER.error("Monitored variable not supported: %s", var)
if binary_sensors:
hass.async_create_task(async_load_platform(
hass, COMP_BINARY_SENSOR, DOMAIN, binary_sensors, config))
if sensors:
hass.async_create_task(async_load_platform(
hass, COMP_SENSOR, DOMAIN, sensors, config))

View file

@ -3,116 +3,54 @@ import logging
from homeassistant.components.binary_sensor import ( from homeassistant.components.binary_sensor import (
ENTITY_ID_FORMAT, BinarySensorDevice) ENTITY_ID_FORMAT, BinarySensorDevice)
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import async_generate_entity_id from homeassistant.helpers.entity import async_generate_entity_id
from . import DATA_GW_VARS, DATA_OPENTHERM_GW, SIGNAL_OPENTHERM_GW_UPDATE from .const import BINARY_SENSOR_INFO, DATA_GATEWAYS, DATA_OPENTHERM_GW
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
DEVICE_CLASS_COLD = 'cold'
DEVICE_CLASS_HEAT = 'heat'
DEVICE_CLASS_PROBLEM = 'problem'
async def async_setup_platform( async def async_setup_platform(
hass, config, async_add_entities, discovery_info=None): hass, config, async_add_entities, discovery_info=None):
"""Set up the OpenTherm Gateway binary sensors.""" """Set up the OpenTherm Gateway binary sensors."""
if discovery_info is None: if discovery_info is None:
return return
gw_vars = hass.data[DATA_OPENTHERM_GW][DATA_GW_VARS] gw_dev = hass.data[DATA_OPENTHERM_GW][DATA_GATEWAYS][discovery_info]
sensor_info = {
# [device_class, friendly_name]
gw_vars.DATA_MASTER_CH_ENABLED: [
None, "Thermostat Central Heating Enabled"],
gw_vars.DATA_MASTER_DHW_ENABLED: [
None, "Thermostat Hot Water Enabled"],
gw_vars.DATA_MASTER_COOLING_ENABLED: [
None, "Thermostat Cooling Enabled"],
gw_vars.DATA_MASTER_OTC_ENABLED: [
None, "Thermostat Outside Temperature Correction Enabled"],
gw_vars.DATA_MASTER_CH2_ENABLED: [
None, "Thermostat Central Heating 2 Enabled"],
gw_vars.DATA_SLAVE_FAULT_IND: [
DEVICE_CLASS_PROBLEM, "Boiler Fault Indication"],
gw_vars.DATA_SLAVE_CH_ACTIVE: [
DEVICE_CLASS_HEAT, "Boiler Central Heating Status"],
gw_vars.DATA_SLAVE_DHW_ACTIVE: [
DEVICE_CLASS_HEAT, "Boiler Hot Water Status"],
gw_vars.DATA_SLAVE_FLAME_ON: [
DEVICE_CLASS_HEAT, "Boiler Flame Status"],
gw_vars.DATA_SLAVE_COOLING_ACTIVE: [
DEVICE_CLASS_COLD, "Boiler Cooling Status"],
gw_vars.DATA_SLAVE_CH2_ACTIVE: [
DEVICE_CLASS_HEAT, "Boiler Central Heating 2 Status"],
gw_vars.DATA_SLAVE_DIAG_IND: [
DEVICE_CLASS_PROBLEM, "Boiler Diagnostics Indication"],
gw_vars.DATA_SLAVE_DHW_PRESENT: [None, "Boiler Hot Water Present"],
gw_vars.DATA_SLAVE_CONTROL_TYPE: [None, "Boiler Control Type"],
gw_vars.DATA_SLAVE_COOLING_SUPPORTED: [None, "Boiler Cooling Support"],
gw_vars.DATA_SLAVE_DHW_CONFIG: [
None, "Boiler Hot Water Configuration"],
gw_vars.DATA_SLAVE_MASTER_LOW_OFF_PUMP: [
None, "Boiler Pump Commands Support"],
gw_vars.DATA_SLAVE_CH2_PRESENT: [
None, "Boiler Central Heating 2 Present"],
gw_vars.DATA_SLAVE_SERVICE_REQ: [
DEVICE_CLASS_PROBLEM, "Boiler Service Required"],
gw_vars.DATA_SLAVE_REMOTE_RESET: [None, "Boiler Remote Reset Support"],
gw_vars.DATA_SLAVE_LOW_WATER_PRESS: [
DEVICE_CLASS_PROBLEM, "Boiler Low Water Pressure"],
gw_vars.DATA_SLAVE_GAS_FAULT: [
DEVICE_CLASS_PROBLEM, "Boiler Gas Fault"],
gw_vars.DATA_SLAVE_AIR_PRESS_FAULT: [
DEVICE_CLASS_PROBLEM, "Boiler Air Pressure Fault"],
gw_vars.DATA_SLAVE_WATER_OVERTEMP: [
DEVICE_CLASS_PROBLEM, "Boiler Water Overtemperature"],
gw_vars.DATA_REMOTE_TRANSFER_DHW: [
None, "Remote Hot Water Setpoint Transfer Support"],
gw_vars.DATA_REMOTE_TRANSFER_MAX_CH: [
None, "Remote Maximum Central Heating Setpoint Write Support"],
gw_vars.DATA_REMOTE_RW_DHW: [
None, "Remote Hot Water Setpoint Write Support"],
gw_vars.DATA_REMOTE_RW_MAX_CH: [
None, "Remote Central Heating Setpoint Write Support"],
gw_vars.DATA_ROVRD_MAN_PRIO: [
None, "Remote Override Manual Change Priority"],
gw_vars.DATA_ROVRD_AUTO_PRIO: [
None, "Remote Override Program Change Priority"],
gw_vars.OTGW_GPIO_A_STATE: [None, "Gateway GPIO A State"],
gw_vars.OTGW_GPIO_B_STATE: [None, "Gateway GPIO B State"],
gw_vars.OTGW_IGNORE_TRANSITIONS: [None, "Gateway Ignore Transitions"],
gw_vars.OTGW_OVRD_HB: [None, "Gateway Override High Byte"],
}
sensors = [] sensors = []
for var in discovery_info: for var, info in BINARY_SENSOR_INFO.items():
device_class = sensor_info[var][0] device_class = info[0]
friendly_name = sensor_info[var][1] friendly_name_format = info[1]
entity_id = async_generate_entity_id(ENTITY_ID_FORMAT, var, hass=hass) sensors.append(OpenThermBinarySensor(gw_dev, var, device_class,
sensors.append(OpenThermBinarySensor(entity_id, var, device_class, friendly_name_format))
friendly_name))
async_add_entities(sensors) async_add_entities(sensors)
class OpenThermBinarySensor(BinarySensorDevice): class OpenThermBinarySensor(BinarySensorDevice):
"""Represent an OpenTherm Gateway binary sensor.""" """Represent an OpenTherm Gateway binary sensor."""
def __init__(self, entity_id, var, device_class, friendly_name): def __init__(self, gw_dev, var, device_class, friendly_name_format):
"""Initialize the binary sensor.""" """Initialize the binary sensor."""
self.entity_id = entity_id self.entity_id = async_generate_entity_id(
ENTITY_ID_FORMAT, '{}_{}'.format(var, gw_dev.gw_id),
hass=gw_dev.hass)
self._gateway = gw_dev
self._var = var self._var = var
self._state = None self._state = None
self._device_class = device_class self._device_class = device_class
self._friendly_name = friendly_name self._friendly_name = friendly_name_format.format(gw_dev.name)
async def async_added_to_hass(self): async def async_added_to_hass(self):
"""Subscribe to updates from the component.""" """Subscribe to updates from the component."""
_LOGGER.debug( _LOGGER.debug(
"Added OpenTherm Gateway binary sensor %s", self._friendly_name) "Added OpenTherm Gateway binary sensor %s", self._friendly_name)
async_dispatcher_connect(self.hass, SIGNAL_OPENTHERM_GW_UPDATE, async_dispatcher_connect(self.hass, self._gateway.update_signal,
self.receive_report) self.receive_report)
async def receive_report(self, status): @callback
def receive_report(self, status):
"""Handle status updates from the component.""" """Handle status updates from the component."""
self._state = bool(status.get(self._var)) self._state = bool(status.get(self._var))
self.async_schedule_update_ha_state() self.async_schedule_update_ha_state()

View file

@ -1,17 +1,21 @@
"""Support for OpenTherm Gateway climate devices.""" """Support for OpenTherm Gateway climate devices."""
import logging import logging
from homeassistant.components.climate import ClimateDevice from pyotgw import vars as gw_vars
from homeassistant.components.climate import ClimateDevice, ENTITY_ID_FORMAT
from homeassistant.components.climate.const import ( from homeassistant.components.climate.const import (
STATE_COOL, STATE_HEAT, STATE_IDLE, SUPPORT_TARGET_TEMPERATURE) STATE_COOL, STATE_HEAT, STATE_IDLE, SUPPORT_TARGET_TEMPERATURE)
from homeassistant.const import ( from homeassistant.const import (
ATTR_TEMPERATURE, CONF_NAME, PRECISION_HALVES, PRECISION_TENTHS, ATTR_TEMPERATURE, PRECISION_HALVES, PRECISION_TENTHS, PRECISION_WHOLE,
PRECISION_WHOLE, TEMP_CELSIUS) TEMP_CELSIUS)
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import async_generate_entity_id
from .const import (
CONF_FLOOR_TEMP, CONF_PRECISION, DATA_GATEWAYS, DATA_OPENTHERM_GW)
from . import (
CONF_FLOOR_TEMP, CONF_PRECISION, DATA_DEVICE, DATA_GW_VARS,
DATA_OPENTHERM_GW, SIGNAL_OPENTHERM_GW_UPDATE)
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -21,20 +25,22 @@ SUPPORT_FLAGS = SUPPORT_TARGET_TEMPERATURE
async def async_setup_platform( async def async_setup_platform(
hass, config, async_add_entities, discovery_info=None): hass, config, async_add_entities, discovery_info=None):
"""Set up the opentherm_gw device.""" """Set up the opentherm_gw device."""
gateway = OpenThermGateway(hass, discovery_info) gw_dev = hass.data[DATA_OPENTHERM_GW][DATA_GATEWAYS][discovery_info]
gateway = OpenThermClimate(gw_dev)
async_add_entities([gateway]) async_add_entities([gateway])
class OpenThermGateway(ClimateDevice): class OpenThermClimate(ClimateDevice):
"""Representation of a climate device.""" """Representation of a climate device."""
def __init__(self, hass, config): def __init__(self, gw_dev):
"""Initialize the device.""" """Initialize the device."""
self._gateway = hass.data[DATA_OPENTHERM_GW][DATA_DEVICE] self._gateway = gw_dev
self._gw_vars = hass.data[DATA_OPENTHERM_GW][DATA_GW_VARS] self.entity_id = async_generate_entity_id(
self.friendly_name = config.get(CONF_NAME) ENTITY_ID_FORMAT, gw_dev.gw_id, hass=gw_dev.hass)
self.floor_temp = config.get(CONF_FLOOR_TEMP) self.friendly_name = gw_dev.name
self.temp_precision = config.get(CONF_PRECISION) self.floor_temp = gw_dev.climate_config[CONF_FLOOR_TEMP]
self.temp_precision = gw_dev.climate_config.get(CONF_PRECISION)
self._current_operation = STATE_IDLE self._current_operation = STATE_IDLE
self._current_temperature = None self._current_temperature = None
self._new_target_temperature = None self._new_target_temperature = None
@ -47,36 +53,37 @@ class OpenThermGateway(ClimateDevice):
async def async_added_to_hass(self): async def async_added_to_hass(self):
"""Connect to the OpenTherm Gateway device.""" """Connect to the OpenTherm Gateway device."""
_LOGGER.debug("Added device %s", self.friendly_name) _LOGGER.debug("Added device %s", self.friendly_name)
async_dispatcher_connect(self.hass, SIGNAL_OPENTHERM_GW_UPDATE, async_dispatcher_connect(self.hass, self._gateway.update_signal,
self.receive_report) self.receive_report)
async def receive_report(self, status): @callback
def receive_report(self, status):
"""Receive and handle a new report from the Gateway.""" """Receive and handle a new report from the Gateway."""
ch_active = status.get(self._gw_vars.DATA_SLAVE_CH_ACTIVE) ch_active = status.get(gw_vars.DATA_SLAVE_CH_ACTIVE)
flame_on = status.get(self._gw_vars.DATA_SLAVE_FLAME_ON) flame_on = status.get(gw_vars.DATA_SLAVE_FLAME_ON)
cooling_active = status.get(self._gw_vars.DATA_SLAVE_COOLING_ACTIVE) cooling_active = status.get(gw_vars.DATA_SLAVE_COOLING_ACTIVE)
if ch_active and flame_on: if ch_active and flame_on:
self._current_operation = STATE_HEAT self._current_operation = STATE_HEAT
elif cooling_active: elif cooling_active:
self._current_operation = STATE_COOL self._current_operation = STATE_COOL
else: else:
self._current_operation = STATE_IDLE self._current_operation = STATE_IDLE
self._current_temperature = status.get(self._gw_vars.DATA_ROOM_TEMP) self._current_temperature = status.get(gw_vars.DATA_ROOM_TEMP)
temp_upd = status.get(self._gw_vars.DATA_ROOM_SETPOINT) temp_upd = status.get(gw_vars.DATA_ROOM_SETPOINT)
if self._target_temperature != temp_upd: if self._target_temperature != temp_upd:
self._new_target_temperature = None self._new_target_temperature = None
self._target_temperature = temp_upd self._target_temperature = temp_upd
# GPIO mode 5: 0 == Away # GPIO mode 5: 0 == Away
# GPIO mode 6: 1 == Away # GPIO mode 6: 1 == Away
gpio_a_state = status.get(self._gw_vars.OTGW_GPIO_A) gpio_a_state = status.get(gw_vars.OTGW_GPIO_A)
if gpio_a_state == 5: if gpio_a_state == 5:
self._away_mode_a = 0 self._away_mode_a = 0
elif gpio_a_state == 6: elif gpio_a_state == 6:
self._away_mode_a = 1 self._away_mode_a = 1
else: else:
self._away_mode_a = None self._away_mode_a = None
gpio_b_state = status.get(self._gw_vars.OTGW_GPIO_B) gpio_b_state = status.get(gw_vars.OTGW_GPIO_B)
if gpio_b_state == 5: if gpio_b_state == 5:
self._away_mode_b = 0 self._away_mode_b = 0
elif gpio_b_state == 6: elif gpio_b_state == 6:
@ -85,10 +92,10 @@ class OpenThermGateway(ClimateDevice):
self._away_mode_b = None self._away_mode_b = None
if self._away_mode_a is not None: if self._away_mode_a is not None:
self._away_state_a = (status.get( self._away_state_a = (status.get(
self._gw_vars.OTGW_GPIO_A_STATE) == self._away_mode_a) gw_vars.OTGW_GPIO_A_STATE) == self._away_mode_a)
if self._away_mode_b is not None: if self._away_mode_b is not None:
self._away_state_b = (status.get( self._away_state_b = (status.get(
self._gw_vars.OTGW_GPIO_B_STATE) == self._away_mode_b) gw_vars.OTGW_GPIO_B_STATE) == self._away_mode_b)
self.async_schedule_update_ha_state() self.async_schedule_update_ha_state()
@property @property
@ -126,9 +133,9 @@ class OpenThermGateway(ClimateDevice):
if self._current_temperature is None: if self._current_temperature is None:
return return
if self.floor_temp is True: if self.floor_temp is True:
if self.temp_precision == PRECISION_HALVES: if self.precision == PRECISION_HALVES:
return int(2 * self._current_temperature) / 2 return int(2 * self._current_temperature) / 2
if self.temp_precision == PRECISION_TENTHS: if self.precision == PRECISION_TENTHS:
return int(10 * self._current_temperature) / 10 return int(10 * self._current_temperature) / 10
return int(self._current_temperature) return int(self._current_temperature)
return self._current_temperature return self._current_temperature
@ -141,7 +148,7 @@ class OpenThermGateway(ClimateDevice):
@property @property
def target_temperature_step(self): def target_temperature_step(self):
"""Return the supported step of target temperature.""" """Return the supported step of target temperature."""
return self.temp_precision return self.precision
@property @property
def is_away_mode_on(self): def is_away_mode_on(self):
@ -154,8 +161,8 @@ class OpenThermGateway(ClimateDevice):
temp = float(kwargs[ATTR_TEMPERATURE]) temp = float(kwargs[ATTR_TEMPERATURE])
if temp == self.target_temperature: if temp == self.target_temperature:
return return
self._new_target_temperature = await self._gateway.set_target_temp( self._new_target_temperature = (
temp) await self._gateway.gateway.set_target_temp(temp))
self.async_schedule_update_ha_state() self.async_schedule_update_ha_state()
@property @property

View file

@ -0,0 +1,215 @@
"""Constants for the opentherm_gw integration."""
import pyotgw.vars as gw_vars
from homeassistant.const import DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS
ATTR_GW_ID = 'gateway_id'
ATTR_MODE = 'mode'
ATTR_LEVEL = 'level'
CONF_CLIMATE = 'climate'
CONF_FLOOR_TEMP = 'floor_temperature'
CONF_PRECISION = 'precision'
DATA_GATEWAYS = 'gateways'
DATA_OPENTHERM_GW = 'opentherm_gw'
DEVICE_CLASS_COLD = 'cold'
DEVICE_CLASS_HEAT = 'heat'
DEVICE_CLASS_PROBLEM = 'problem'
SERVICE_RESET_GATEWAY = 'reset_gateway'
SERVICE_SET_CLOCK = 'set_clock'
SERVICE_SET_CONTROL_SETPOINT = 'set_control_setpoint'
SERVICE_SET_GPIO_MODE = 'set_gpio_mode'
SERVICE_SET_LED_MODE = 'set_led_mode'
SERVICE_SET_MAX_MOD = 'set_max_modulation'
SERVICE_SET_OAT = 'set_outside_temperature'
SERVICE_SET_SB_TEMP = 'set_setback_temperature'
UNIT_BAR = 'bar'
UNIT_HOUR = 'h'
UNIT_KW = 'kW'
UNIT_L_MIN = 'L/min'
UNIT_PERCENT = '%'
BINARY_SENSOR_INFO = {
# [device_class, friendly_name format]
gw_vars.DATA_MASTER_CH_ENABLED: [
None, "Thermostat Central Heating Enabled {}"],
gw_vars.DATA_MASTER_DHW_ENABLED: [None, "Thermostat Hot Water Enabled {}"],
gw_vars.DATA_MASTER_COOLING_ENABLED: [
None, "Thermostat Cooling Enabled {}"],
gw_vars.DATA_MASTER_OTC_ENABLED: [
None, "Thermostat Outside Temperature Correction Enabled {}"],
gw_vars.DATA_MASTER_CH2_ENABLED: [
None, "Thermostat Central Heating 2 Enabled {}"],
gw_vars.DATA_SLAVE_FAULT_IND: [
DEVICE_CLASS_PROBLEM, "Boiler Fault Indication {}"],
gw_vars.DATA_SLAVE_CH_ACTIVE: [
DEVICE_CLASS_HEAT, "Boiler Central Heating Status {}"],
gw_vars.DATA_SLAVE_DHW_ACTIVE: [
DEVICE_CLASS_HEAT, "Boiler Hot Water Status {}"],
gw_vars.DATA_SLAVE_FLAME_ON: [DEVICE_CLASS_HEAT, "Boiler Flame Status {}"],
gw_vars.DATA_SLAVE_COOLING_ACTIVE: [
DEVICE_CLASS_COLD, "Boiler Cooling Status {}"],
gw_vars.DATA_SLAVE_CH2_ACTIVE: [
DEVICE_CLASS_HEAT, "Boiler Central Heating 2 Status {}"],
gw_vars.DATA_SLAVE_DIAG_IND: [
DEVICE_CLASS_PROBLEM, "Boiler Diagnostics Indication {}"],
gw_vars.DATA_SLAVE_DHW_PRESENT: [None, "Boiler Hot Water Present {}"],
gw_vars.DATA_SLAVE_CONTROL_TYPE: [None, "Boiler Control Type {}"],
gw_vars.DATA_SLAVE_COOLING_SUPPORTED: [None, "Boiler Cooling Support {}"],
gw_vars.DATA_SLAVE_DHW_CONFIG: [None, "Boiler Hot Water Configuration {}"],
gw_vars.DATA_SLAVE_MASTER_LOW_OFF_PUMP: [
None, "Boiler Pump Commands Support {}"],
gw_vars.DATA_SLAVE_CH2_PRESENT: [
None, "Boiler Central Heating 2 Present {}"],
gw_vars.DATA_SLAVE_SERVICE_REQ: [
DEVICE_CLASS_PROBLEM, "Boiler Service Required {}"],
gw_vars.DATA_SLAVE_REMOTE_RESET: [None, "Boiler Remote Reset Support {}"],
gw_vars.DATA_SLAVE_LOW_WATER_PRESS: [
DEVICE_CLASS_PROBLEM, "Boiler Low Water Pressure {}"],
gw_vars.DATA_SLAVE_GAS_FAULT: [
DEVICE_CLASS_PROBLEM, "Boiler Gas Fault {}"],
gw_vars.DATA_SLAVE_AIR_PRESS_FAULT: [
DEVICE_CLASS_PROBLEM, "Boiler Air Pressure Fault {}"],
gw_vars.DATA_SLAVE_WATER_OVERTEMP: [
DEVICE_CLASS_PROBLEM, "Boiler Water Overtemperature {}"],
gw_vars.DATA_REMOTE_TRANSFER_DHW: [
None, "Remote Hot Water Setpoint Transfer Support {}"],
gw_vars.DATA_REMOTE_TRANSFER_MAX_CH: [
None, "Remote Maximum Central Heating Setpoint Write Support {}"],
gw_vars.DATA_REMOTE_RW_DHW: [
None, "Remote Hot Water Setpoint Write Support {}"],
gw_vars.DATA_REMOTE_RW_MAX_CH: [
None, "Remote Central Heating Setpoint Write Support {}"],
gw_vars.DATA_ROVRD_MAN_PRIO: [
None, "Remote Override Manual Change Priority {}"],
gw_vars.DATA_ROVRD_AUTO_PRIO: [
None, "Remote Override Program Change Priority {}"],
gw_vars.OTGW_GPIO_A_STATE: [None, "Gateway GPIO A State {}"],
gw_vars.OTGW_GPIO_B_STATE: [None, "Gateway GPIO B State {}"],
gw_vars.OTGW_IGNORE_TRANSITIONS: [None, "Gateway Ignore Transitions {}"],
gw_vars.OTGW_OVRD_HB: [None, "Gateway Override High Byte {}"],
}
SENSOR_INFO = {
# [device_class, unit, friendly_name]
gw_vars.DATA_CONTROL_SETPOINT: [
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS, "Control Setpoint {}"],
gw_vars.DATA_MASTER_MEMBERID: [None, None, "Thermostat Member ID {}"],
gw_vars.DATA_SLAVE_MEMBERID: [None, None, "Boiler Member ID {}"],
gw_vars.DATA_SLAVE_OEM_FAULT: [None, None, "Boiler OEM Fault Code {}"],
gw_vars.DATA_COOLING_CONTROL: [
None, UNIT_PERCENT, "Cooling Control Signal {}"],
gw_vars.DATA_CONTROL_SETPOINT_2: [
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS, "Control Setpoint 2 {}"],
gw_vars.DATA_ROOM_SETPOINT_OVRD: [
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS,
"Room Setpoint Override {}"],
gw_vars.DATA_SLAVE_MAX_RELATIVE_MOD: [
None, UNIT_PERCENT, "Boiler Maximum Relative Modulation {}"],
gw_vars.DATA_SLAVE_MAX_CAPACITY: [
None, UNIT_KW, "Boiler Maximum Capacity {}"],
gw_vars.DATA_SLAVE_MIN_MOD_LEVEL: [
None, UNIT_PERCENT, "Boiler Minimum Modulation Level {}"],
gw_vars.DATA_ROOM_SETPOINT: [
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS, "Room Setpoint {}"],
gw_vars.DATA_REL_MOD_LEVEL: [
None, UNIT_PERCENT, "Relative Modulation Level {}"],
gw_vars.DATA_CH_WATER_PRESS: [
None, UNIT_BAR, "Central Heating Water Pressure {}"],
gw_vars.DATA_DHW_FLOW_RATE: [
None, UNIT_L_MIN, "Hot Water Flow Rate {}"],
gw_vars.DATA_ROOM_SETPOINT_2: [
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS, "Room Setpoint 2 {}"],
gw_vars.DATA_ROOM_TEMP: [
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS, "Room Temperature {}"],
gw_vars.DATA_CH_WATER_TEMP: [
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS,
"Central Heating Water Temperature {}"],
gw_vars.DATA_DHW_TEMP: [
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS,
"Hot Water Temperature {}"],
gw_vars.DATA_OUTSIDE_TEMP: [
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS, "Outside Temperature {}"],
gw_vars.DATA_RETURN_WATER_TEMP: [
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS,
"Return Water Temperature {}"],
gw_vars.DATA_SOLAR_STORAGE_TEMP: [
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS,
"Solar Storage Temperature {}"],
gw_vars.DATA_SOLAR_COLL_TEMP: [
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS,
"Solar Collector Temperature {}"],
gw_vars.DATA_CH_WATER_TEMP_2: [
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS,
"Central Heating 2 Water Temperature {}"],
gw_vars.DATA_DHW_TEMP_2: [
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS,
"Hot Water 2 Temperature {}"],
gw_vars.DATA_EXHAUST_TEMP: [
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS, "Exhaust Temperature {}"],
gw_vars.DATA_SLAVE_DHW_MAX_SETP: [
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS,
"Hot Water Maximum Setpoint {}"],
gw_vars.DATA_SLAVE_DHW_MIN_SETP: [
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS,
"Hot Water Minimum Setpoint {}"],
gw_vars.DATA_SLAVE_CH_MAX_SETP: [
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS,
"Boiler Maximum Central Heating Setpoint {}"],
gw_vars.DATA_SLAVE_CH_MIN_SETP: [
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS,
"Boiler Minimum Central Heating Setpoint {}"],
gw_vars.DATA_DHW_SETPOINT: [
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS, "Hot Water Setpoint {}"],
gw_vars.DATA_MAX_CH_SETPOINT: [
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS,
"Maximum Central Heating Setpoint {}"],
gw_vars.DATA_OEM_DIAG: [None, None, "OEM Diagnostic Code {}"],
gw_vars.DATA_TOTAL_BURNER_STARTS: [None, None, "Total Burner Starts {}"],
gw_vars.DATA_CH_PUMP_STARTS: [
None, None, "Central Heating Pump Starts {}"],
gw_vars.DATA_DHW_PUMP_STARTS: [None, None, "Hot Water Pump Starts {}"],
gw_vars.DATA_DHW_BURNER_STARTS: [None, None, "Hot Water Burner Starts {}"],
gw_vars.DATA_TOTAL_BURNER_HOURS: [
None, UNIT_HOUR, "Total Burner Hours {}"],
gw_vars.DATA_CH_PUMP_HOURS: [
None, UNIT_HOUR, "Central Heating Pump Hours {}"],
gw_vars.DATA_DHW_PUMP_HOURS: [None, UNIT_HOUR, "Hot Water Pump Hours {}"],
gw_vars.DATA_DHW_BURNER_HOURS: [
None, UNIT_HOUR, "Hot Water Burner Hours {}"],
gw_vars.DATA_MASTER_OT_VERSION: [
None, None, "Thermostat OpenTherm Version {}"],
gw_vars.DATA_SLAVE_OT_VERSION: [None, None, "Boiler OpenTherm Version {}"],
gw_vars.DATA_MASTER_PRODUCT_TYPE: [
None, None, "Thermostat Product Type {}"],
gw_vars.DATA_MASTER_PRODUCT_VERSION: [
None, None, "Thermostat Product Version {}"],
gw_vars.DATA_SLAVE_PRODUCT_TYPE: [None, None, "Boiler Product Type {}"],
gw_vars.DATA_SLAVE_PRODUCT_VERSION: [
None, None, "Boiler Product Version {}"],
gw_vars.OTGW_MODE: [None, None, "Gateway/Monitor Mode {}"],
gw_vars.OTGW_DHW_OVRD: [None, None, "Gateway Hot Water Override Mode {}"],
gw_vars.OTGW_ABOUT: [None, None, "Gateway Firmware Version {}"],
gw_vars.OTGW_BUILD: [None, None, "Gateway Firmware Build {}"],
gw_vars.OTGW_CLOCKMHZ: [None, None, "Gateway Clock Speed {}"],
gw_vars.OTGW_LED_A: [None, None, "Gateway LED A Mode {}"],
gw_vars.OTGW_LED_B: [None, None, "Gateway LED B Mode {}"],
gw_vars.OTGW_LED_C: [None, None, "Gateway LED C Mode {}"],
gw_vars.OTGW_LED_D: [None, None, "Gateway LED D Mode {}"],
gw_vars.OTGW_LED_E: [None, None, "Gateway LED E Mode {}"],
gw_vars.OTGW_LED_F: [None, None, "Gateway LED F Mode {}"],
gw_vars.OTGW_GPIO_A: [None, None, "Gateway GPIO A Mode {}"],
gw_vars.OTGW_GPIO_B: [None, None, "Gateway GPIO B Mode {}"],
gw_vars.OTGW_SB_TEMP: [
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS,
"Gateway Setback Temperature {}"],
gw_vars.OTGW_SETP_OVRD_MODE: [
None, None, "Gateway Room Setpoint Override Mode {}"],
gw_vars.OTGW_SMART_PWR: [None, None, "Gateway Smart Power Mode {}"],
gw_vars.OTGW_THRM_DETECT: [None, None, "Gateway Thermostat Detection {}"],
gw_vars.OTGW_VREF: [None, None, "Gateway Reference Voltage Setting {}"],
}

View file

@ -6,5 +6,7 @@
"pyotgw==0.4b4" "pyotgw==0.4b4"
], ],
"dependencies": [], "dependencies": [],
"codeowners": [] "codeowners": [
"@mvn23"
]
} }

View file

@ -2,175 +2,55 @@
import logging import logging
from homeassistant.components.sensor import ENTITY_ID_FORMAT from homeassistant.components.sensor import ENTITY_ID_FORMAT
from homeassistant.const import DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import Entity, async_generate_entity_id from homeassistant.helpers.entity import Entity, async_generate_entity_id
from . import DATA_GW_VARS, DATA_OPENTHERM_GW, SIGNAL_OPENTHERM_GW_UPDATE from .const import DATA_GATEWAYS, DATA_OPENTHERM_GW, SENSOR_INFO
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
UNIT_BAR = 'bar'
UNIT_HOUR = 'h'
UNIT_KW = 'kW'
UNIT_L_MIN = 'L/min'
UNIT_PERCENT = '%'
async def async_setup_platform( async def async_setup_platform(
hass, config, async_add_entities, discovery_info=None): hass, config, async_add_entities, discovery_info=None):
"""Set up the OpenTherm Gateway sensors.""" """Set up the OpenTherm Gateway sensors."""
if discovery_info is None: if discovery_info is None:
return return
gw_vars = hass.data[DATA_OPENTHERM_GW][DATA_GW_VARS] gw_dev = hass.data[DATA_OPENTHERM_GW][DATA_GATEWAYS][discovery_info]
sensor_info = {
# [device_class, unit, friendly_name]
gw_vars.DATA_CONTROL_SETPOINT: [
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS, "Control Setpoint"],
gw_vars.DATA_MASTER_MEMBERID: [None, None, "Thermostat Member ID"],
gw_vars.DATA_SLAVE_MEMBERID: [None, None, "Boiler Member ID"],
gw_vars.DATA_SLAVE_OEM_FAULT: [None, None, "Boiler OEM Fault Code"],
gw_vars.DATA_COOLING_CONTROL: [
None, UNIT_PERCENT, "Cooling Control Signal"],
gw_vars.DATA_CONTROL_SETPOINT_2: [
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS, "Control Setpoint 2"],
gw_vars.DATA_ROOM_SETPOINT_OVRD: [
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS, "Room Setpoint Override"],
gw_vars.DATA_SLAVE_MAX_RELATIVE_MOD: [
None, UNIT_PERCENT, "Boiler Maximum Relative Modulation"],
gw_vars.DATA_SLAVE_MAX_CAPACITY: [
None, UNIT_KW, "Boiler Maximum Capacity"],
gw_vars.DATA_SLAVE_MIN_MOD_LEVEL: [
None, UNIT_PERCENT, "Boiler Minimum Modulation Level"],
gw_vars.DATA_ROOM_SETPOINT: [
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS, "Room Setpoint"],
gw_vars.DATA_REL_MOD_LEVEL: [
None, UNIT_PERCENT, "Relative Modulation Level"],
gw_vars.DATA_CH_WATER_PRESS: [
None, UNIT_BAR, "Central Heating Water Pressure"],
gw_vars.DATA_DHW_FLOW_RATE: [None, UNIT_L_MIN, "Hot Water Flow Rate"],
gw_vars.DATA_ROOM_SETPOINT_2: [
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS, "Room Setpoint 2"],
gw_vars.DATA_ROOM_TEMP: [
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS, "Room Temperature"],
gw_vars.DATA_CH_WATER_TEMP: [
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS,
"Central Heating Water Temperature"],
gw_vars.DATA_DHW_TEMP: [
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS, "Hot Water Temperature"],
gw_vars.DATA_OUTSIDE_TEMP: [
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS, "Outside Temperature"],
gw_vars.DATA_RETURN_WATER_TEMP: [
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS,
"Return Water Temperature"],
gw_vars.DATA_SOLAR_STORAGE_TEMP: [
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS,
"Solar Storage Temperature"],
gw_vars.DATA_SOLAR_COLL_TEMP: [
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS,
"Solar Collector Temperature"],
gw_vars.DATA_CH_WATER_TEMP_2: [
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS,
"Central Heating 2 Water Temperature"],
gw_vars.DATA_DHW_TEMP_2: [
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS, "Hot Water 2 Temperature"],
gw_vars.DATA_EXHAUST_TEMP: [
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS, "Exhaust Temperature"],
gw_vars.DATA_SLAVE_DHW_MAX_SETP: [
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS,
"Hot Water Maximum Setpoint"],
gw_vars.DATA_SLAVE_DHW_MIN_SETP: [
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS,
"Hot Water Minimum Setpoint"],
gw_vars.DATA_SLAVE_CH_MAX_SETP: [
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS,
"Boiler Maximum Central Heating Setpoint"],
gw_vars.DATA_SLAVE_CH_MIN_SETP: [
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS,
"Boiler Minimum Central Heating Setpoint"],
gw_vars.DATA_DHW_SETPOINT: [
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS, "Hot Water Setpoint"],
gw_vars.DATA_MAX_CH_SETPOINT: [
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS,
"Maximum Central Heating Setpoint"],
gw_vars.DATA_OEM_DIAG: [None, None, "OEM Diagnostic Code"],
gw_vars.DATA_TOTAL_BURNER_STARTS: [
None, None, "Total Burner Starts"],
gw_vars.DATA_CH_PUMP_STARTS: [
None, None, "Central Heating Pump Starts"],
gw_vars.DATA_DHW_PUMP_STARTS: [None, None, "Hot Water Pump Starts"],
gw_vars.DATA_DHW_BURNER_STARTS: [
None, None, "Hot Water Burner Starts"],
gw_vars.DATA_TOTAL_BURNER_HOURS: [
None, UNIT_HOUR, "Total Burner Hours"],
gw_vars.DATA_CH_PUMP_HOURS: [
None, UNIT_HOUR, "Central Heating Pump Hours"],
gw_vars.DATA_DHW_PUMP_HOURS: [None, UNIT_HOUR, "Hot Water Pump Hours"],
gw_vars.DATA_DHW_BURNER_HOURS: [
None, UNIT_HOUR, "Hot Water Burner Hours"],
gw_vars.DATA_MASTER_OT_VERSION: [
None, None, "Thermostat OpenTherm Version"],
gw_vars.DATA_SLAVE_OT_VERSION: [
None, None, "Boiler OpenTherm Version"],
gw_vars.DATA_MASTER_PRODUCT_TYPE: [
None, None, "Thermostat Product Type"],
gw_vars.DATA_MASTER_PRODUCT_VERSION: [
None, None, "Thermostat Product Version"],
gw_vars.DATA_SLAVE_PRODUCT_TYPE: [None, None, "Boiler Product Type"],
gw_vars.DATA_SLAVE_PRODUCT_VERSION: [
None, None, "Boiler Product Version"],
gw_vars.OTGW_MODE: [None, None, "Gateway/Monitor Mode"],
gw_vars.OTGW_DHW_OVRD: [None, None, "Gateway Hot Water Override Mode"],
gw_vars.OTGW_ABOUT: [None, None, "Gateway Firmware Version"],
gw_vars.OTGW_BUILD: [None, None, "Gateway Firmware Build"],
gw_vars.OTGW_CLOCKMHZ: [None, None, "Gateway Clock Speed"],
gw_vars.OTGW_LED_A: [None, None, "Gateway LED A Mode"],
gw_vars.OTGW_LED_B: [None, None, "Gateway LED B Mode"],
gw_vars.OTGW_LED_C: [None, None, "Gateway LED C Mode"],
gw_vars.OTGW_LED_D: [None, None, "Gateway LED D Mode"],
gw_vars.OTGW_LED_E: [None, None, "Gateway LED E Mode"],
gw_vars.OTGW_LED_F: [None, None, "Gateway LED F Mode"],
gw_vars.OTGW_GPIO_A: [None, None, "Gateway GPIO A Mode"],
gw_vars.OTGW_GPIO_B: [None, None, "Gateway GPIO B Mode"],
gw_vars.OTGW_SB_TEMP: [
DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS,
"Gateway Setback Temperature"],
gw_vars.OTGW_SETP_OVRD_MODE: [
None, None, "Gateway Room Setpoint Override Mode"],
gw_vars.OTGW_SMART_PWR: [None, None, "Gateway Smart Power Mode"],
gw_vars.OTGW_THRM_DETECT: [None, None, "Gateway Thermostat Detection"],
gw_vars.OTGW_VREF: [None, None, "Gateway Reference Voltage Setting"],
}
sensors = [] sensors = []
for var in discovery_info: for var, info in SENSOR_INFO.items():
device_class = sensor_info[var][0] device_class = info[0]
unit = sensor_info[var][1] unit = info[1]
friendly_name = sensor_info[var][2] friendly_name_format = info[2]
entity_id = async_generate_entity_id(ENTITY_ID_FORMAT, var, hass=hass) sensors.append(OpenThermSensor(gw_dev, var, device_class, unit,
sensors.append( friendly_name_format))
OpenThermSensor(entity_id, var, device_class, unit, friendly_name))
async_add_entities(sensors) async_add_entities(sensors)
class OpenThermSensor(Entity): class OpenThermSensor(Entity):
"""Representation of an OpenTherm Gateway sensor.""" """Representation of an OpenTherm Gateway sensor."""
def __init__(self, entity_id, var, device_class, unit, friendly_name): def __init__(self, gw_dev, var, device_class, unit, friendly_name_format):
"""Initialize the OpenTherm Gateway sensor.""" """Initialize the OpenTherm Gateway sensor."""
self.entity_id = entity_id self.entity_id = async_generate_entity_id(
ENTITY_ID_FORMAT, '{}_{}'.format(var, gw_dev.gw_id),
hass=gw_dev.hass)
self._gateway = gw_dev
self._var = var self._var = var
self._value = None self._value = None
self._device_class = device_class self._device_class = device_class
self._unit = unit self._unit = unit
self._friendly_name = friendly_name self._friendly_name = friendly_name_format.format(gw_dev.name)
async def async_added_to_hass(self): async def async_added_to_hass(self):
"""Subscribe to updates from the component.""" """Subscribe to updates from the component."""
_LOGGER.debug("Added OpenTherm Gateway sensor %s", self._friendly_name) _LOGGER.debug("Added OpenTherm Gateway sensor %s", self._friendly_name)
async_dispatcher_connect(self.hass, SIGNAL_OPENTHERM_GW_UPDATE, async_dispatcher_connect(self.hass, self._gateway.update_signal,
self.receive_report) self.receive_report)
async def receive_report(self, status): @callback
def receive_report(self, status):
"""Handle status updates from the component.""" """Handle status updates from the component."""
value = status.get(self._var) value = status.get(self._var)
if isinstance(value, float): if isinstance(value, float):

View file

@ -2,10 +2,17 @@
reset_gateway: reset_gateway:
description: Reset the OpenTherm Gateway. description: Reset the OpenTherm Gateway.
fields:
gateway_id:
description: The gateway_id of the OpenTherm Gateway.
example: 'opentherm_gateway'
set_clock: set_clock:
description: Set the clock and day of the week on the connected thermostat. description: Set the clock and day of the week on the connected thermostat.
fields: fields:
gateway_id:
description: The gateway_id of the OpenTherm Gateway.
example: 'opentherm_gateway'
date: date:
description: Optional date from which the day of the week will be extracted. Defaults to today. description: Optional date from which the day of the week will be extracted. Defaults to today.
example: '2018-10-23' example: '2018-10-23'
@ -18,6 +25,9 @@ set_control_setpoint:
Set the central heating control setpoint override on the gateway. Set the central heating control setpoint override on the gateway.
You will only need this if you are writing your own software thermostat. You will only need this if you are writing your own software thermostat.
fields: fields:
gateway_id:
description: The gateway_id of the OpenTherm Gateway.
example: 'opentherm_gateway'
temperature: temperature:
description: > description: >
The central heating setpoint to set on the gateway. The central heating setpoint to set on the gateway.
@ -28,6 +38,9 @@ set_control_setpoint:
set_gpio_mode: set_gpio_mode:
description: Change the function of the GPIO pins of the gateway. description: Change the function of the GPIO pins of the gateway.
fields: fields:
gateway_id:
description: The gateway_id of the OpenTherm Gateway.
example: 'opentherm_gateway'
id: id:
description: The ID of the GPIO pin. Either "A" or "B". description: The ID of the GPIO pin. Either "A" or "B".
example: 'B' example: 'B'
@ -40,6 +53,9 @@ set_gpio_mode:
set_led_mode: set_led_mode:
description: Change the function of the LEDs of the gateway. description: Change the function of the LEDs of the gateway.
fields: fields:
gateway_id:
description: The gateway_id of the OpenTherm Gateway.
example: 'opentherm_gateway'
id: id:
description: The ID of the LED. Possible values are "A" through "F". description: The ID of the LED. Possible values are "A" through "F".
example: 'C' example: 'C'
@ -54,6 +70,9 @@ set_max_modulation:
Override the maximum relative modulation level. Override the maximum relative modulation level.
You will only need this if you are writing your own software thermostat. You will only need this if you are writing your own software thermostat.
fields: fields:
gateway_id:
description: The gateway_id of the OpenTherm Gateway.
example: 'opentherm_gateway'
level: level:
description: > description: >
The modulation level to provide to the gateway. The modulation level to provide to the gateway.
@ -66,6 +85,9 @@ set_outside_temperature:
Provide an outside temperature to the thermostat. Provide an outside temperature to the thermostat.
If your thermostat is unable to display an outside temperature and does not support OTC (Outside Temperature Correction), this has no effect. If your thermostat is unable to display an outside temperature and does not support OTC (Outside Temperature Correction), this has no effect.
fields: fields:
gateway_id:
description: The gateway_id of the OpenTherm Gateway.
example: 'opentherm_gateway'
temperature: temperature:
description: > description: >
The temperature to provide to the thermostat. The temperature to provide to the thermostat.
@ -76,6 +98,9 @@ set_outside_temperature:
set_setback_temperature: set_setback_temperature:
description: Configure the setback temperature to be used with the GPIO away mode function. description: Configure the setback temperature to be used with the GPIO away mode function.
fields: fields:
gateway_id:
description: The gateway_id of the OpenTherm Gateway.
example: 'opentherm_gateway'
temperature: temperature:
description: The setback temperature to configure on the gateway. Values between 0.0 and 30.0 are accepted. description: The setback temperature to configure on the gateway. Values between 0.0 and 30.0 are accepted.
example: '16.0' example: '16.0'