Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions homeassistant/components/opentherm_gw/.translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,16 @@
}
},
"title": "OpenTherm Gateway"
},
"options": {
"step": {
"init": {
"description": "Options for the OpenTherm Gateway",
"data": {
"floor_temperature": "Floor Temperature",
"precision": "Precision"
}
}
}
}
}
9 changes: 9 additions & 0 deletions homeassistant/components/opentherm_gw/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,12 @@
)


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:
Expand All @@ -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())

Expand Down Expand Up @@ -348,6 +356,7 @@ def __init__(self, hass, config_entry):
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):
Expand Down
19 changes: 15 additions & 4 deletions homeassistant/components/opentherm_gw/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)
)

Expand All @@ -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
Expand All @@ -65,12 +66,22 @@ def __init__(self, gw_dev):
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):
Expand Down
53 changes: 52 additions & 1 deletion homeassistant/components/opentherm_gw/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,20 @@
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):
Expand All @@ -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:
Expand Down Expand Up @@ -89,3 +104,39 @@ def _create_entry(self, gw_id, name, device):
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,
}
),
)
15 changes: 12 additions & 3 deletions homeassistant/components/opentherm_gw/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@
"data": {
"name": "Name",
"device": "Path or URL",
"id": "ID",
"precision": "Climate temperature precision",
"floor_temperature": "Floor climate temperature"
"id": "ID"
}
}
},
Expand All @@ -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"
}
}
}
}
}
54 changes: 50 additions & 4 deletions tests/components/opentherm_gw/test_config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@
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):
Expand Down Expand Up @@ -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