Add opentherm_gw services (#17762)

* Move components/opentherm_gw.py to components/opentherm_gw/__init__.py

* Update requirements-all.txt

* Await set_clock coroutine rather than scheduling it.

* Create task for async_load_platform
This commit is contained in:
mvn23 2018-10-31 12:33:43 +01:00 committed by Martin Hjelmare
parent 239e314dc1
commit b12e79e5cf
4 changed files with 246 additions and 7 deletions

View File

@ -247,7 +247,7 @@ omit =
homeassistant/components/opencv.py
homeassistant/components/*/opencv.py
homeassistant/components/opentherm_gw.py
homeassistant/components/opentherm_gw/*
homeassistant/components/*/opentherm_gw.py
homeassistant/components/openuv/__init__.py

View File

@ -5,14 +5,16 @@ For more details about this component, please refer to the documentation at
http://home-assistant.io/components/opentherm_gw/
"""
import logging
from datetime import datetime, date
import voluptuous as vol
from homeassistant.components.binary_sensor import DOMAIN as COMP_BINARY_SENSOR
from homeassistant.components.sensor import DOMAIN as COMP_SENSOR
from homeassistant.const import (CONF_DEVICE, CONF_MONITORED_VARIABLES,
CONF_NAME, PRECISION_HALVES, PRECISION_TENTHS,
PRECISION_WHOLE)
from homeassistant.const import (
ATTR_DATE, ATTR_ID, ATTR_TEMPERATURE, ATTR_TIME, CONF_DEVICE,
CONF_MONITORED_VARIABLES, CONF_NAME, EVENT_HOMEASSISTANT_STOP,
PRECISION_HALVES, PRECISION_TENTHS, PRECISION_WHOLE)
from homeassistant.helpers.discovery import async_load_platform
from homeassistant.helpers.dispatcher import async_dispatcher_send
@ -20,16 +22,72 @@ import homeassistant.helpers.config_validation as cv
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({
vol.Optional(CONF_NAME, default="OpenTherm Gateway"): cv.string,
vol.Optional(CONF_PRECISION): vol.In([PRECISION_TENTHS, PRECISION_HALVES,
@ -46,7 +104,7 @@ CONFIG_SCHEMA = vol.Schema({
}),
}, extra=vol.ALLOW_EXTRA)
REQUIREMENTS = ['pyotgw==0.2b1']
REQUIREMENTS = ['pyotgw==0.3b0']
_LOGGER = logging.getLogger(__name__)
@ -60,9 +118,11 @@ async def async_setup(hass, config):
hass.data[DATA_OPENTHERM_GW] = {
DATA_DEVICE: gateway,
DATA_GW_VARS: pyotgw.vars,
DATA_LATEST_STATUS: {}
}
hass.async_create_task(connect_and_subscribe(
hass, conf[CONF_DEVICE], gateway))
hass.async_create_task(register_services(hass, gateway))
hass.async_create_task(async_load_platform(
hass, 'climate', DOMAIN, conf.get(CONF_CLIMATE), config))
if monitored_vars:
@ -76,13 +136,110 @@ async def connect_and_subscribe(hass, device_path, gateway):
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."""
gw_vars = hass.data[DATA_OPENTHERM_GW][DATA_GW_VARS]
async def reset_gateway(call):
"""Reset the OpenTherm Gateway."""
mode_rst = gw_vars.OTGW_MODE_RESET
status = await gateway.set_mode(mode_rst)
hass.data[DATA_OPENTHERM_GW][DATA_LATEST_STATUS] = status
async_dispatcher_send(hass, SIGNAL_OPENTHERM_GW_UPDATE, status)
hass.services.async_register(DOMAIN, SERVICE_RESET_GATEWAY, reset_gateway)
async def set_control_setpoint(call):
"""Set the control setpoint on the OpenTherm Gateway."""
gw_var = gw_vars.DATA_CONTROL_SETPOINT
value = await gateway.set_control_setpoint(call.data[ATTR_TEMPERATURE])
status = hass.data[DATA_OPENTHERM_GW][DATA_LATEST_STATUS]
status.update({gw_var: value})
async_dispatcher_send(hass, SIGNAL_OPENTHERM_GW_UPDATE, status)
hass.services.async_register(DOMAIN, SERVICE_SET_CONTROL_SETPOINT,
set_control_setpoint,
SERVICE_SET_CONTROL_SETPOINT_SCHEMA)
async def set_device_clock(call):
"""Set the clock on the OpenTherm Gateway."""
attr_date = call.data[ATTR_DATE]
attr_time = call.data[ATTR_TIME]
await gateway.set_clock(datetime.combine(attr_date, attr_time))
hass.services.async_register(DOMAIN, SERVICE_SET_CLOCK, set_device_clock,
SERVICE_SET_CLOCK_SCHEMA)
async def set_gpio_mode(call):
"""Set the OpenTherm Gateway GPIO modes."""
gpio_id = call.data[ATTR_ID]
gpio_mode = call.data[ATTR_MODE]
mode = await gateway.set_gpio_mode(gpio_id, gpio_mode)
gpio_var = getattr(gw_vars, 'OTGW_GPIO_{}'.format(gpio_id))
status = hass.data[DATA_OPENTHERM_GW][DATA_LATEST_STATUS]
status.update({gpio_var: mode})
async_dispatcher_send(hass, SIGNAL_OPENTHERM_GW_UPDATE, status)
hass.services.async_register(DOMAIN, SERVICE_SET_GPIO_MODE, set_gpio_mode,
SERVICE_SET_GPIO_MODE_SCHEMA)
async def set_led_mode(call):
"""Set the OpenTherm Gateway LED modes."""
led_id = call.data[ATTR_ID]
led_mode = call.data[ATTR_MODE]
mode = await gateway.set_led_mode(led_id, led_mode)
led_var = getattr(gw_vars, 'OTGW_LED_{}'.format(led_id))
status = hass.data[DATA_OPENTHERM_GW][DATA_LATEST_STATUS]
status.update({led_var: mode})
async_dispatcher_send(hass, SIGNAL_OPENTHERM_GW_UPDATE, status)
hass.services.async_register(DOMAIN, SERVICE_SET_LED_MODE, set_led_mode,
SERVICE_SET_LED_MODE_SCHEMA)
async def set_max_mod(call):
"""Set the max modulation level."""
gw_var = gw_vars.DATA_SLAVE_MAX_RELATIVE_MOD
level = call.data[ATTR_LEVEL]
if level == -1:
# Backend only clears setting on non-numeric values.
level = '-'
value = await gateway.set_max_relative_mod(level)
status = hass.data[DATA_OPENTHERM_GW][DATA_LATEST_STATUS]
status.update({gw_var: value})
async_dispatcher_send(hass, SIGNAL_OPENTHERM_GW_UPDATE, status)
hass.services.async_register(DOMAIN, SERVICE_SET_MAX_MOD, set_max_mod,
SERVICE_SET_MAX_MOD_SCHEMA)
async def set_outside_temp(call):
"""Provide the outside temperature to the OpenTherm Gateway."""
gw_var = gw_vars.DATA_OUTSIDE_TEMP
value = await gateway.set_outside_temp(call.data[ATTR_TEMPERATURE])
status = hass.data[DATA_OPENTHERM_GW][DATA_LATEST_STATUS]
status.update({gw_var: value})
async_dispatcher_send(hass, SIGNAL_OPENTHERM_GW_UPDATE, status)
hass.services.async_register(DOMAIN, SERVICE_SET_OAT, set_outside_temp,
SERVICE_SET_OAT_SCHEMA)
async def set_setback_temp(call):
"""Set the OpenTherm Gateway SetBack temperature."""
gw_var = gw_vars.OTGW_SB_TEMP
value = await gateway.set_setback_temp(call.data[ATTR_TEMPERATURE])
status = hass.data[DATA_OPENTHERM_GW][DATA_LATEST_STATUS]
status.update({gw_var: value})
async_dispatcher_send(hass, SIGNAL_OPENTHERM_GW_UPDATE, status)
hass.services.async_register(DOMAIN, SERVICE_SET_SB_TEMP, set_setback_temp,
SERVICE_SET_SB_TEMP_SCHEMA)
async def setup_monitored_vars(hass, config, monitored_vars):
"""Set up requested sensors."""
gw_vars = hass.data[DATA_OPENTHERM_GW][DATA_GW_VARS]
@ -203,4 +360,5 @@ async def setup_monitored_vars(hass, config, monitored_vars):
hass.async_create_task(async_load_platform(
hass, COMP_BINARY_SENSOR, DOMAIN, binary_sensors, config))
if sensors:
await async_load_platform(hass, COMP_SENSOR, DOMAIN, sensors, config)
hass.async_create_task(async_load_platform(
hass, COMP_SENSOR, DOMAIN, sensors, config))

View File

@ -0,0 +1,81 @@
# Describes the format for available opentherm_gw services
reset_gateway:
description: Reset the OpenTherm Gateway.
set_clock:
description: Set the clock and day of the week on the connected thermostat.
fields:
date:
description: Optional date from which the day of the week will be extracted. Defaults to today.
example: '2018-10-23'
time:
description: Optional time in 24h format which will be provided to the thermostat. Defaults to the current time.
example: '19:34'
set_control_setpoint:
description: >
Set the central heating control setpoint override on the gateway.
You will only need this if you are writing your own software thermostat.
fields:
temperature:
description: >
The central heating setpoint to set on the gateway.
Values between 0 and 90 are accepted, but not all boilers support this range.
A value of 0 disables the central heating setpoint override.
example: '37.5'
set_gpio_mode:
description: Change the function of the GPIO pins of the gateway.
fields:
id:
description: The ID of the GPIO pin. Either "A" or "B".
example: 'B'
mode:
description: >
Mode to set on the GPIO pin. Values 0 through 6 are accepted for both GPIOs, 7 is only accepted for GPIO "B".
See https://www.home-assistant.io/components/opentherm_gw/#gpio-modes for an explanation of the values.
example: '5'
set_led_mode:
description: Change the function of the LEDs of the gateway.
fields:
id:
description: The ID of the LED. Possible values are "A" through "F".
example: 'C'
mode:
description: >
The function to assign to the LED. One of "R", "X", "T", "B", "O", "F", "H", "W", "C", "E", "M" or "P".
See https://www.home-assistant.io/components/opentherm_gw/#led-modes for an explanation of the values.
example: 'F'
set_max_modulation:
description: >
Override the maximum relative modulation level.
You will only need this if you are writing your own software thermostat.
fields:
level:
description: >
The modulation level to provide to the gateway.
Values between 0 and 100 will set the modulation level.
Provide a value of -1 to clear the override and forward the value from the thermostat again.
example: '42'
set_outside_temperature:
description: >
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.
fields:
temperature:
description: >
The temperature to provide to the thermostat.
Values between -40.0 and 64.0 will be accepted, but not all thermostats can display the full range.
Any value above 64.0 will clear a previously configured value (suggestion: 99)
example: '-2.3'
set_setback_temperature:
description: Configure the setback temperature to be used with the GPIO away mode function.
fields:
temperature:
description: The setback temperature to configure on the gateway. Values between 0.0 and 30.0 are accepted.
example: '16.0'

View File

@ -1038,7 +1038,7 @@ pyoppleio==1.0.5
pyota==2.0.5
# homeassistant.components.opentherm_gw
pyotgw==0.2b1
pyotgw==0.3b0
# homeassistant.auth.mfa_modules.notify
# homeassistant.auth.mfa_modules.totp