Add opentherm_gw options flow. (#27316)

This commit is contained in:
mvn23 2019-10-18 02:36:34 +02:00 committed by Paulus Schoutsen
parent 0888098718
commit 489340160b
6 changed files with 149 additions and 12 deletions

View file

@ -19,5 +19,16 @@
}
},
"title": "OpenTherm Gateway"
},
"options": {
"step": {
"init": {
"description": "Options for the OpenTherm Gateway",
"data": {
"floor_temperature": "Floor Temperature",
"precision": "Precision"
}
}
}
}
}

View file

@ -75,6 +75,12 @@ CONFIG_SCHEMA = vol.Schema(
)
async def options_updated(hass, entry):
"""Handle options update."""
gateway = hass.data[DATA_OPENTHERM_GW][DATA_GATEWAYS][entry.data[CONF_ID]]
async_dispatcher_send(hass, gateway.options_update_signal, entry)
async def async_setup_entry(hass, config_entry):
"""Set up the OpenTherm Gateway component."""
if DATA_OPENTHERM_GW not in hass.data:
@ -83,6 +89,8 @@ async def async_setup_entry(hass, config_entry):
gateway = OpenThermGatewayDevice(hass, config_entry)
hass.data[DATA_OPENTHERM_GW][DATA_GATEWAYS][config_entry.data[CONF_ID]] = gateway
config_entry.add_update_listener(options_updated)
# Schedule directly on the loop to avoid blocking HA startup.
hass.loop.create_task(gateway.connect_and_subscribe())
@ -348,6 +356,7 @@ class OpenThermGatewayDevice:
self.climate_config = config_entry.options
self.status = {}
self.update_signal = f"{DATA_OPENTHERM_GW}_{self.gw_id}_update"
self.options_update_signal = f"{DATA_OPENTHERM_GW}_{self.gw_id}_options_update"
self.gateway = pyotgw.pyotgw()
async def connect_and_subscribe(self):

View file

@ -39,7 +39,8 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
ents = []
ents.append(
OpenThermClimate(
hass.data[DATA_OPENTHERM_GW][DATA_GATEWAYS][config_entry.data[CONF_ID]]
hass.data[DATA_OPENTHERM_GW][DATA_GATEWAYS][config_entry.data[CONF_ID]],
config_entry.options,
)
)
@ -49,12 +50,12 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
class OpenThermClimate(ClimateDevice):
"""Representation of a climate device."""
def __init__(self, gw_dev):
def __init__(self, gw_dev, options):
"""Initialize the device."""
self._gateway = gw_dev
self.friendly_name = gw_dev.name
self.floor_temp = gw_dev.climate_config.get(CONF_FLOOR_TEMP)
self.temp_precision = gw_dev.climate_config.get(CONF_PRECISION)
self.floor_temp = options[CONF_FLOOR_TEMP]
self.temp_precision = options.get(CONF_PRECISION)
self._current_operation = None
self._current_temperature = None
self._hvac_mode = HVAC_MODE_HEAT
@ -65,12 +66,22 @@ class OpenThermClimate(ClimateDevice):
self._away_state_a = False
self._away_state_b = False
@callback
def update_options(self, entry):
"""Update climate entity options."""
self.floor_temp = entry.options[CONF_FLOOR_TEMP]
self.temp_precision = entry.options.get(CONF_PRECISION)
self.async_schedule_update_ha_state()
async def async_added_to_hass(self):
"""Connect to the OpenTherm Gateway device."""
_LOGGER.debug("Added OpenTherm Gateway climate device %s", self.friendly_name)
async_dispatcher_connect(
self.hass, self._gateway.update_signal, self.receive_report
)
async_dispatcher_connect(
self.hass, self._gateway.options_update_signal, self.update_options
)
@callback
def receive_report(self, status):

View file

@ -6,11 +6,20 @@ import pyotgw
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.const import CONF_DEVICE, CONF_ID, CONF_NAME
from homeassistant.const import (
CONF_DEVICE,
CONF_ID,
CONF_NAME,
PRECISION_HALVES,
PRECISION_TENTHS,
PRECISION_WHOLE,
)
from homeassistant.core import callback
import homeassistant.helpers.config_validation as cv
from . import DOMAIN
from .const import CONF_FLOOR_TEMP, CONF_PRECISION
class OpenThermGwConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
@ -19,6 +28,12 @@ class OpenThermGwConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
VERSION = 1
CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_PUSH
@staticmethod
@callback
def async_get_options_flow(config_entry):
"""Get the options flow for this handler."""
return OpenThermGwOptionsFlow(config_entry)
async def async_step_init(self, info=None):
"""Handle config flow initiation."""
if info:
@ -89,3 +104,39 @@ class OpenThermGwConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
return self.async_create_entry(
title=name, data={CONF_ID: gw_id, CONF_DEVICE: device, CONF_NAME: name}
)
class OpenThermGwOptionsFlow(config_entries.OptionsFlow):
"""Handle opentherm_gw options."""
def __init__(self, config_entry):
"""Initialize the options flow."""
self.config_entry = config_entry
async def async_step_init(self, user_input=None):
"""Manage the opentherm_gw options."""
if user_input is not None:
if user_input.get(CONF_PRECISION) == 0:
user_input[CONF_PRECISION] = None
return self.async_create_entry(title="", data=user_input)
return self.async_show_form(
step_id="init",
data_schema=vol.Schema(
{
vol.Optional(
CONF_PRECISION,
default=self.config_entry.options.get(CONF_PRECISION, 0),
): vol.All(
vol.Coerce(float),
vol.In(
[0, PRECISION_TENTHS, PRECISION_HALVES, PRECISION_WHOLE]
),
),
vol.Optional(
CONF_FLOOR_TEMP,
default=self.config_entry.options.get(CONF_FLOOR_TEMP, False),
): bool,
}
),
)

View file

@ -7,9 +7,7 @@
"data": {
"name": "Name",
"device": "Path or URL",
"id": "ID",
"precision": "Climate temperature precision",
"floor_temperature": "Floor climate temperature"
"id": "ID"
}
}
},
@ -19,5 +17,16 @@
"serial_error": "Error connecting to device",
"timeout": "Connection attempt timed out"
}
},
"options": {
"step": {
"init": {
"description": "Options for the OpenTherm Gateway",
"data": {
"floor_temperature": "Floor Temperature",
"precision": "Precision"
}
}
}
}
}

View file

@ -3,12 +3,16 @@ import asyncio
from serial import SerialException
from unittest.mock import patch
from homeassistant import config_entries, setup
from homeassistant.const import CONF_DEVICE, CONF_ID, CONF_NAME
from homeassistant.components.opentherm_gw.const import DOMAIN
from homeassistant import config_entries, data_entry_flow, setup
from homeassistant.const import CONF_DEVICE, CONF_ID, CONF_NAME, PRECISION_HALVES
from homeassistant.components.opentherm_gw.const import (
DOMAIN,
CONF_FLOOR_TEMP,
CONF_PRECISION,
)
from pyotgw import OTGW_ABOUT
from tests.common import mock_coro
from tests.common import mock_coro, MockConfigEntry
async def test_form_user(hass):
@ -161,3 +165,45 @@ async def test_form_connection_error(hass):
assert result2["type"] == "form"
assert result2["errors"] == {"base": "serial_error"}
assert len(mock_connect.mock_calls) == 1
async def test_options_form(hass):
"""Test the options form."""
entry = MockConfigEntry(
domain=DOMAIN,
title="Mock Gateway",
data={
CONF_NAME: "Mock Gateway",
CONF_DEVICE: "/dev/null",
CONF_ID: "mock_gateway",
},
options={},
)
entry.add_to_hass(hass)
result = await hass.config_entries.options.flow.async_init(
entry.entry_id, context={"source": "test"}, data=None
)
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result["step_id"] == "init"
result = await hass.config_entries.options.flow.async_configure(
result["flow_id"],
user_input={CONF_FLOOR_TEMP: True, CONF_PRECISION: PRECISION_HALVES},
)
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
assert result["data"][CONF_PRECISION] == PRECISION_HALVES
assert result["data"][CONF_FLOOR_TEMP] is True
result = await hass.config_entries.options.flow.async_init(
entry.entry_id, context={"source": "test"}, data=None
)
result = await hass.config_entries.options.flow.async_configure(
result["flow_id"], user_input={CONF_PRECISION: 0}
)
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
assert result["data"][CONF_PRECISION] is None
assert result["data"][CONF_FLOOR_TEMP] is True