diff --git a/homeassistant/components/opentherm_gw/__init__.py b/homeassistant/components/opentherm_gw/__init__.py index cc08ec3da69..8686997e748 100644 --- a/homeassistant/components/opentherm_gw/__init__.py +++ b/homeassistant/components/opentherm_gw/__init__.py @@ -39,6 +39,8 @@ from .const import ( CONF_CLIMATE, CONF_FLOOR_TEMP, CONF_PRECISION, + CONF_READ_PRECISION, + CONF_SET_PRECISION, DATA_GATEWAYS, DATA_OPENTHERM_GW, DOMAIN, @@ -94,6 +96,17 @@ 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 + if config_entry.options.get(CONF_PRECISION): + migrate_options = dict(config_entry.options) + migrate_options.update( + { + CONF_READ_PRECISION: config_entry.options[CONF_PRECISION], + CONF_SET_PRECISION: config_entry.options[CONF_PRECISION], + } + ) + del migrate_options[CONF_PRECISION] + hass.config_entries.async_update_entry(config_entry, options=migrate_options) + config_entry.add_update_listener(options_updated) # Schedule directly on the loop to avoid blocking HA startup. diff --git a/homeassistant/components/opentherm_gw/climate.py b/homeassistant/components/opentherm_gw/climate.py index 8ec536e7331..1253fe4c6d2 100644 --- a/homeassistant/components/opentherm_gw/climate.py +++ b/homeassistant/components/opentherm_gw/climate.py @@ -28,7 +28,13 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import async_generate_entity_id from . import DOMAIN -from .const import CONF_FLOOR_TEMP, CONF_PRECISION, DATA_GATEWAYS, DATA_OPENTHERM_GW +from .const import ( + CONF_FLOOR_TEMP, + CONF_READ_PRECISION, + CONF_SET_PRECISION, + DATA_GATEWAYS, + DATA_OPENTHERM_GW, +) _LOGGER = logging.getLogger(__name__) @@ -61,7 +67,8 @@ class OpenThermClimate(ClimateEntity): ) self.friendly_name = gw_dev.name self.floor_temp = options.get(CONF_FLOOR_TEMP, DEFAULT_FLOOR_TEMP) - self.temp_precision = options.get(CONF_PRECISION) + self.temp_read_precision = options.get(CONF_READ_PRECISION) + self.temp_set_precision = options.get(CONF_SET_PRECISION) self._available = False self._current_operation = None self._current_temperature = None @@ -79,7 +86,8 @@ class OpenThermClimate(ClimateEntity): def update_options(self, entry): """Update climate entity options.""" self.floor_temp = entry.options[CONF_FLOOR_TEMP] - self.temp_precision = entry.options[CONF_PRECISION] + self.temp_read_precision = entry.options[CONF_READ_PRECISION] + self.temp_set_precision = entry.options[CONF_SET_PRECISION] self.async_write_ha_state() async def async_added_to_hass(self): @@ -178,8 +186,8 @@ class OpenThermClimate(ClimateEntity): @property def precision(self): """Return the precision of the system.""" - if self.temp_precision is not None and self.temp_precision != 0: - return self.temp_precision + if self.temp_read_precision: + return self.temp_read_precision if self.hass.config.units.temperature_unit == TEMP_CELSIUS: return PRECISION_HALVES return PRECISION_WHOLE @@ -234,7 +242,11 @@ class OpenThermClimate(ClimateEntity): @property def target_temperature_step(self): """Return the supported step of target temperature.""" - return self.precision + if self.temp_set_precision: + return self.temp_set_precision + if self.hass.config.units.temperature_unit == TEMP_CELSIUS: + return PRECISION_HALVES + return PRECISION_WHOLE @property def preset_mode(self): diff --git a/homeassistant/components/opentherm_gw/config_flow.py b/homeassistant/components/opentherm_gw/config_flow.py index 8da530bebda..8081d3bf96c 100644 --- a/homeassistant/components/opentherm_gw/config_flow.py +++ b/homeassistant/components/opentherm_gw/config_flow.py @@ -19,7 +19,7 @@ from homeassistant.core import callback import homeassistant.helpers.config_validation as cv from . import DOMAIN -from .const import CONF_FLOOR_TEMP, CONF_PRECISION +from .const import CONF_FLOOR_TEMP, CONF_READ_PRECISION, CONF_SET_PRECISION class OpenThermGwConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): @@ -121,8 +121,17 @@ class OpenThermGwOptionsFlow(config_entries.OptionsFlow): data_schema=vol.Schema( { vol.Optional( - CONF_PRECISION, - default=self.config_entry.options.get(CONF_PRECISION, 0), + CONF_READ_PRECISION, + default=self.config_entry.options.get(CONF_READ_PRECISION, 0), + ): vol.All( + vol.Coerce(float), + vol.In( + [0, PRECISION_TENTHS, PRECISION_HALVES, PRECISION_WHOLE] + ), + ), + vol.Optional( + CONF_SET_PRECISION, + default=self.config_entry.options.get(CONF_SET_PRECISION, 0), ): vol.All( vol.Coerce(float), vol.In( diff --git a/homeassistant/components/opentherm_gw/const.py b/homeassistant/components/opentherm_gw/const.py index 2c3e2f7071d..1a129d1dfbd 100644 --- a/homeassistant/components/opentherm_gw/const.py +++ b/homeassistant/components/opentherm_gw/const.py @@ -19,6 +19,8 @@ ATTR_CH_OVRD = "ch_override" CONF_CLIMATE = "climate" CONF_FLOOR_TEMP = "floor_temperature" CONF_PRECISION = "precision" +CONF_READ_PRECISION = "read_precision" +CONF_SET_PRECISION = "set_precision" DATA_GATEWAYS = "gateways" DATA_OPENTHERM_GW = "opentherm_gw" diff --git a/homeassistant/components/opentherm_gw/strings.json b/homeassistant/components/opentherm_gw/strings.json index 306529e7be1..937917608fa 100644 --- a/homeassistant/components/opentherm_gw/strings.json +++ b/homeassistant/components/opentherm_gw/strings.json @@ -22,7 +22,8 @@ "description": "Options for the OpenTherm Gateway", "data": { "floor_temperature": "Floor Temperature", - "precision": "Precision" + "read_precision": "Read Precision", + "set_precision": "Set Precision" } } } diff --git a/tests/components/opentherm_gw/test_config_flow.py b/tests/components/opentherm_gw/test_config_flow.py index 4d811b9f985..43da10b19cf 100644 --- a/tests/components/opentherm_gw/test_config_flow.py +++ b/tests/components/opentherm_gw/test_config_flow.py @@ -9,9 +9,17 @@ from homeassistant import config_entries, data_entry_flow, setup from homeassistant.components.opentherm_gw.const import ( CONF_FLOOR_TEMP, CONF_PRECISION, + CONF_READ_PRECISION, + CONF_SET_PRECISION, DOMAIN, ) -from homeassistant.const import CONF_DEVICE, CONF_ID, CONF_NAME, PRECISION_HALVES +from homeassistant.const import ( + CONF_DEVICE, + CONF_ID, + CONF_NAME, + PRECISION_HALVES, + PRECISION_TENTHS, +) from tests.common import MockConfigEntry @@ -167,6 +175,48 @@ async def test_form_connection_error(hass): assert len(mock_connect.mock_calls) == 1 +async def test_options_migration(hass): + """Test migration of precision option after update.""" + entry = MockConfigEntry( + domain=DOMAIN, + title="Mock Gateway", + data={ + CONF_NAME: "Test Entry 1", + CONF_DEVICE: "/dev/ttyUSB0", + CONF_ID: "test_entry_1", + }, + options={ + CONF_FLOOR_TEMP: True, + CONF_PRECISION: PRECISION_TENTHS, + }, + ) + entry.add_to_hass(hass) + + with patch( + "homeassistant.components.opentherm_gw.OpenThermGatewayDevice.connect_and_subscribe", + return_value=True, + ), patch("homeassistant.components.opentherm_gw.async_setup", return_value=True): + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + result = await hass.config_entries.options.async_init( + entry.entry_id, context={"source": config_entries.SOURCE_USER}, data=None + ) + + assert result["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result["step_id"] == "init" + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={}, + ) + + assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert result["data"][CONF_READ_PRECISION] == PRECISION_TENTHS + assert result["data"][CONF_SET_PRECISION] == PRECISION_TENTHS + assert result["data"][CONF_FLOOR_TEMP] is True + + async def test_options_form(hass): """Test the options form.""" entry = MockConfigEntry( @@ -181,6 +231,14 @@ async def test_options_form(hass): ) entry.add_to_hass(hass) + with patch( + "homeassistant.components.opentherm_gw.async_setup", return_value=True + ), patch( + "homeassistant.components.opentherm_gw.async_setup_entry", return_value=True + ): + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + result = await hass.config_entries.options.async_init( entry.entry_id, context={"source": "test"}, data=None ) @@ -189,11 +247,16 @@ async def test_options_form(hass): result = await hass.config_entries.options.async_configure( result["flow_id"], - user_input={CONF_FLOOR_TEMP: True, CONF_PRECISION: PRECISION_HALVES}, + user_input={ + CONF_FLOOR_TEMP: True, + CONF_READ_PRECISION: PRECISION_HALVES, + CONF_SET_PRECISION: PRECISION_HALVES, + }, ) assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY - assert result["data"][CONF_PRECISION] == PRECISION_HALVES + assert result["data"][CONF_READ_PRECISION] == PRECISION_HALVES + assert result["data"][CONF_SET_PRECISION] == PRECISION_HALVES assert result["data"][CONF_FLOOR_TEMP] is True result = await hass.config_entries.options.async_init( @@ -201,9 +264,28 @@ async def test_options_form(hass): ) result = await hass.config_entries.options.async_configure( - result["flow_id"], user_input={CONF_PRECISION: 0} + result["flow_id"], user_input={CONF_READ_PRECISION: 0} ) assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY - assert result["data"][CONF_PRECISION] == 0.0 + assert result["data"][CONF_READ_PRECISION] == 0.0 + assert result["data"][CONF_SET_PRECISION] == PRECISION_HALVES assert result["data"][CONF_FLOOR_TEMP] is True + + result = await hass.config_entries.options.async_init( + entry.entry_id, context={"source": "test"}, data=None + ) + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + CONF_FLOOR_TEMP: False, + CONF_READ_PRECISION: PRECISION_TENTHS, + CONF_SET_PRECISION: PRECISION_HALVES, + }, + ) + + assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert result["data"][CONF_READ_PRECISION] == PRECISION_TENTHS + assert result["data"][CONF_SET_PRECISION] == PRECISION_HALVES + assert result["data"][CONF_FLOOR_TEMP] is False