From fb48fd7d10a18c322634aa9f0821f2619f7b4ca6 Mon Sep 17 00:00:00 2001 From: Kevin Worrel <37058192+dieselrabbit@users.noreply.github.com> Date: Sun, 21 Mar 2021 03:56:46 -0700 Subject: [PATCH] ScreenLogic cleanups (#48136) * ScreenLogic cleanup. Bump screenlogicpy to 0.2.0. Move heating functions from water_heater to climate platform. Address notes from original PR. * Fix temperature attribute * Addressing notes. Bump screenlogicpy to 0.2.1. Made device_types constants. Made (known) equipment flags constants. Used dict.get() in places where None is the default. Return fast with good _last_preset. * Update homeassistant/components/screenlogic/climate.py Let base entity handle state property. Co-authored-by: J. Nick Koston * Patch integration setup functions. * Exception, ATTR_TEMPERATURE notes Co-authored-by: J. Nick Koston --- .coveragerc | 2 +- .../components/screenlogic/__init__.py | 21 +- .../components/screenlogic/binary_sensor.py | 24 +- .../components/screenlogic/climate.py | 209 ++++++++++++++++++ .../components/screenlogic/config_flow.py | 4 +- .../components/screenlogic/manifest.json | 2 +- .../components/screenlogic/sensor.py | 38 ++-- .../components/screenlogic/switch.py | 15 +- .../components/screenlogic/water_heater.py | 128 ----------- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- .../screenlogic/test_config_flow.py | 101 ++++++++- 12 files changed, 354 insertions(+), 194 deletions(-) create mode 100644 homeassistant/components/screenlogic/climate.py delete mode 100644 homeassistant/components/screenlogic/water_heater.py diff --git a/.coveragerc b/.coveragerc index 33f4071c8f4..48f71f5a998 100644 --- a/.coveragerc +++ b/.coveragerc @@ -840,9 +840,9 @@ omit = homeassistant/components/scrape/sensor.py homeassistant/components/screenlogic/__init__.py homeassistant/components/screenlogic/binary_sensor.py + homeassistant/components/screenlogic/climate.py homeassistant/components/screenlogic/sensor.py homeassistant/components/screenlogic/switch.py - homeassistant/components/screenlogic/water_heater.py homeassistant/components/scsgate/* homeassistant/components/scsgate/cover.py homeassistant/components/sendgrid/notify.py diff --git a/homeassistant/components/screenlogic/__init__.py b/homeassistant/components/screenlogic/__init__.py index a5e0f248bc6..cca3e7e8785 100644 --- a/homeassistant/components/screenlogic/__init__.py +++ b/homeassistant/components/screenlogic/__init__.py @@ -6,7 +6,7 @@ import logging from screenlogicpy import ScreenLogicError, ScreenLogicGateway from screenlogicpy.const import ( - CONTROLLER_HARDWARE, + EQUIPMENT, SL_GATEWAY_IP, SL_GATEWAY_NAME, SL_GATEWAY_PORT, @@ -28,7 +28,7 @@ from .const import DEFAULT_SCAN_INTERVAL, DISCOVERED_GATEWAYS, DOMAIN _LOGGER = logging.getLogger(__name__) -PLATFORMS = ["switch", "sensor", "binary_sensor", "water_heater"] +PLATFORMS = ["switch", "sensor", "binary_sensor", "climate"] async def async_setup(hass: HomeAssistant, config: dict): @@ -59,11 +59,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): except ScreenLogicError as ex: _LOGGER.error("Error while connecting to the gateway %s: %s", connect_info, ex) raise ConfigEntryNotReady from ex - except AttributeError as ex: - _LOGGER.exception( - "Unexpected error while connecting to the gateway %s", connect_info - ) - raise ConfigEntryNotReady from ex coordinator = ScreenlogicDataUpdateCoordinator( hass, config_entry=entry, gateway=gateway @@ -91,7 +86,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): device_data["pump"].append(pump) for body in coordinator.data["bodies"]: - device_data["water_heater"].append(body) + device_data["body"].append(body) hass.data[DOMAIN][entry.entry_id] = { "coordinator": coordinator, @@ -151,18 +146,18 @@ class ScreenlogicDataUpdateCoordinator(DataUpdateCoordinator): """Fetch data from the Screenlogic gateway.""" try: await self.hass.async_add_executor_job(self.gateway.update) - return self.gateway.get_data() except ScreenLogicError as error: raise UpdateFailed(error) from error + return self.gateway.get_data() class ScreenlogicEntity(CoordinatorEntity): """Base class for all ScreenLogic entities.""" - def __init__(self, coordinator, datakey): + def __init__(self, coordinator, data_key): """Initialize of the entity.""" super().__init__(coordinator) - self._data_key = datakey + self._data_key = data_key @property def mac(self): @@ -192,11 +187,11 @@ class ScreenlogicEntity(CoordinatorEntity): @property def device_info(self): """Return device information for the controller.""" - controller_type = self.config_data["controler_type"] + controller_type = self.config_data["controller_type"] hardware_type = self.config_data["hardware_type"] return { "connections": {(dr.CONNECTION_NETWORK_MAC, self.mac)}, "name": self.gateway_name, "manufacturer": "Pentair", - "model": CONTROLLER_HARDWARE[controller_type][hardware_type], + "model": EQUIPMENT.CONTROLLER_HARDWARE[controller_type][hardware_type], } diff --git a/homeassistant/components/screenlogic/binary_sensor.py b/homeassistant/components/screenlogic/binary_sensor.py index fa8d63ee5e6..0001223030a 100644 --- a/homeassistant/components/screenlogic/binary_sensor.py +++ b/homeassistant/components/screenlogic/binary_sensor.py @@ -1,15 +1,20 @@ """Support for a ScreenLogic Binary Sensor.""" import logging -from screenlogicpy.const import ON_OFF +from screenlogicpy.const import DEVICE_TYPE, ON_OFF -from homeassistant.components.binary_sensor import DEVICE_CLASSES, BinarySensorEntity +from homeassistant.components.binary_sensor import ( + DEVICE_CLASS_PROBLEM, + BinarySensorEntity, +) from . import ScreenlogicEntity from .const import DOMAIN _LOGGER = logging.getLogger(__name__) +SL_DEVICE_TYPE_TO_HA_DEVICE_CLASS = {DEVICE_TYPE.ALARM: DEVICE_CLASS_PROBLEM} + async def async_setup_entry(hass, config_entry, async_add_entities): """Set up entry.""" @@ -19,7 +24,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): for binary_sensor in data["devices"]["binary_sensor"]: entities.append(ScreenLogicBinarySensor(coordinator, binary_sensor)) - async_add_entities(entities, True) + async_add_entities(entities) class ScreenLogicBinarySensor(ScreenlogicEntity, BinarySensorEntity): @@ -33,10 +38,8 @@ class ScreenLogicBinarySensor(ScreenlogicEntity, BinarySensorEntity): @property def device_class(self): """Return the device class.""" - device_class = self.sensor.get("hass_type") - if device_class in DEVICE_CLASSES: - return device_class - return None + device_class = self.sensor.get("device_type") + return SL_DEVICE_TYPE_TO_HA_DEVICE_CLASS.get(device_class) @property def is_on(self) -> bool: @@ -46,9 +49,4 @@ class ScreenLogicBinarySensor(ScreenlogicEntity, BinarySensorEntity): @property def sensor(self): """Shortcut to access the sensor data.""" - return self.sensor_data[self._data_key] - - @property - def sensor_data(self): - """Shortcut to access the sensors data.""" - return self.coordinator.data["sensors"] + return self.coordinator.data["sensors"][self._data_key] diff --git a/homeassistant/components/screenlogic/climate.py b/homeassistant/components/screenlogic/climate.py new file mode 100644 index 00000000000..d289c00228c --- /dev/null +++ b/homeassistant/components/screenlogic/climate.py @@ -0,0 +1,209 @@ +"""Support for a ScreenLogic heating device.""" +import logging + +from screenlogicpy.const import EQUIPMENT, HEAT_MODE + +from homeassistant.components.climate import ClimateEntity +from homeassistant.components.climate.const import ( + ATTR_PRESET_MODE, + CURRENT_HVAC_HEAT, + CURRENT_HVAC_IDLE, + CURRENT_HVAC_OFF, + HVAC_MODE_HEAT, + HVAC_MODE_OFF, + SUPPORT_PRESET_MODE, + SUPPORT_TARGET_TEMPERATURE, +) +from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS, TEMP_FAHRENHEIT +from homeassistant.exceptions import HomeAssistantError +from homeassistant.helpers.restore_state import RestoreEntity + +from . import ScreenlogicEntity +from .const import DOMAIN + +_LOGGER = logging.getLogger(__name__) + +SUPPORTED_FEATURES = SUPPORT_TARGET_TEMPERATURE | SUPPORT_PRESET_MODE + +SUPPORTED_MODES = [HVAC_MODE_OFF, HVAC_MODE_HEAT] + +SUPPORTED_PRESETS = [ + HEAT_MODE.SOLAR, + HEAT_MODE.SOLAR_PREFERRED, + HEAT_MODE.HEATER, +] + + +async def async_setup_entry(hass, config_entry, async_add_entities): + """Set up entry.""" + entities = [] + data = hass.data[DOMAIN][config_entry.entry_id] + coordinator = data["coordinator"] + + for body in data["devices"]["body"]: + entities.append(ScreenLogicClimate(coordinator, body)) + async_add_entities(entities) + + +class ScreenLogicClimate(ScreenlogicEntity, ClimateEntity, RestoreEntity): + """Represents a ScreenLogic climate entity.""" + + def __init__(self, coordinator, body): + """Initialize a ScreenLogic climate entity.""" + super().__init__(coordinator, body) + self._configured_heat_modes = [] + # Is solar listed as available equipment? + if self.coordinator.data["config"]["equipment_flags"] & EQUIPMENT.FLAG_SOLAR: + self._configured_heat_modes.extend( + [HEAT_MODE.SOLAR, HEAT_MODE.SOLAR_PREFERRED] + ) + self._configured_heat_modes.append(HEAT_MODE.HEATER) + self._last_preset = None + + @property + def name(self) -> str: + """Name of the heater.""" + ent_name = self.body["heat_status"]["name"] + return f"{self.gateway_name} {ent_name}" + + @property + def min_temp(self) -> float: + """Minimum allowed temperature.""" + return self.body["min_set_point"]["value"] + + @property + def max_temp(self) -> float: + """Maximum allowed temperature.""" + return self.body["max_set_point"]["value"] + + @property + def current_temperature(self) -> float: + """Return water temperature.""" + return self.body["last_temperature"]["value"] + + @property + def target_temperature(self) -> float: + """Target temperature.""" + return self.body["heat_set_point"]["value"] + + @property + def temperature_unit(self) -> str: + """Return the unit of measurement.""" + if self.config_data["is_celcius"]["value"] == 1: + return TEMP_CELSIUS + return TEMP_FAHRENHEIT + + @property + def hvac_mode(self) -> str: + """Return the current hvac mode.""" + if self.body["heat_mode"]["value"] > 0: + return HVAC_MODE_HEAT + return HVAC_MODE_OFF + + @property + def hvac_modes(self): + """Return th supported hvac modes.""" + return SUPPORTED_MODES + + @property + def hvac_action(self) -> str: + """Return the current action of the heater.""" + if self.body["heat_status"]["value"] > 0: + return CURRENT_HVAC_HEAT + if self.hvac_mode == HVAC_MODE_HEAT: + return CURRENT_HVAC_IDLE + return CURRENT_HVAC_OFF + + @property + def preset_mode(self) -> str: + """Return current/last preset mode.""" + if self.hvac_mode == HVAC_MODE_OFF: + return HEAT_MODE.NAME_FOR_NUM[self._last_preset] + return HEAT_MODE.NAME_FOR_NUM[self.body["heat_mode"]["value"]] + + @property + def preset_modes(self): + """All available presets.""" + return [ + HEAT_MODE.NAME_FOR_NUM[mode_num] for mode_num in self._configured_heat_modes + ] + + @property + def supported_features(self): + """Supported features of the heater.""" + return SUPPORTED_FEATURES + + async def async_set_temperature(self, **kwargs) -> None: + """Change the setpoint of the heater.""" + if (temperature := kwargs.get(ATTR_TEMPERATURE)) is None: + raise ValueError(f"Expected attribute {ATTR_TEMPERATURE}") + + if await self.hass.async_add_executor_job( + self.gateway.set_heat_temp, int(self._data_key), int(temperature) + ): + await self.coordinator.async_request_refresh() + else: + raise HomeAssistantError( + f"Failed to set_temperature {temperature} on body {self.body['body_type']['value']}" + ) + + async def async_set_hvac_mode(self, hvac_mode) -> None: + """Set the operation mode.""" + if hvac_mode == HVAC_MODE_OFF: + mode = HEAT_MODE.OFF + else: + mode = HEAT_MODE.NUM_FOR_NAME[self.preset_mode] + if await self.hass.async_add_executor_job( + self.gateway.set_heat_mode, int(self._data_key), int(mode) + ): + await self.coordinator.async_request_refresh() + else: + raise HomeAssistantError( + f"Failed to set_hvac_mode {mode} on body {self.body['body_type']['value']}" + ) + + async def async_set_preset_mode(self, preset_mode: str) -> None: + """Set the preset mode.""" + _LOGGER.debug("Setting last_preset to %s", HEAT_MODE.NUM_FOR_NAME[preset_mode]) + self._last_preset = mode = HEAT_MODE.NUM_FOR_NAME[preset_mode] + if self.hvac_mode == HVAC_MODE_OFF: + return + if await self.hass.async_add_executor_job( + self.gateway.set_heat_mode, int(self._data_key), int(mode) + ): + await self.coordinator.async_request_refresh() + else: + raise HomeAssistantError( + f"Failed to set_preset_mode {mode} on body {self.body['body_type']['value']}" + ) + + async def async_added_to_hass(self): + """Run when entity is about to be added.""" + await super().async_added_to_hass() + + _LOGGER.debug("Startup last preset is %s", self._last_preset) + if self._last_preset is not None: + return + prev_state = await self.async_get_last_state() + if ( + prev_state is not None + and prev_state.attributes.get(ATTR_PRESET_MODE) is not None + ): + _LOGGER.debug( + "Startup setting last_preset to %s from prev_state", + HEAT_MODE.NUM_FOR_NAME[prev_state.attributes.get(ATTR_PRESET_MODE)], + ) + self._last_preset = HEAT_MODE.NUM_FOR_NAME[ + prev_state.attributes.get(ATTR_PRESET_MODE) + ] + else: + _LOGGER.debug( + "Startup setting last_preset to default (%s)", + self._configured_heat_modes[0], + ) + self._last_preset = self._configured_heat_modes[0] + + @property + def body(self): + """Shortcut to access body data.""" + return self.coordinator.data["bodies"][self._data_key] diff --git a/homeassistant/components/screenlogic/config_flow.py b/homeassistant/components/screenlogic/config_flow.py index 865c0fdbbf4..32a29564844 100644 --- a/homeassistant/components/screenlogic/config_flow.py +++ b/homeassistant/components/screenlogic/config_flow.py @@ -120,7 +120,7 @@ class ScreenlogicConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): mac = user_input[GATEWAY_SELECT_KEY] selected_gateway = self.discovered_gateways[mac] - await self.async_set_unique_id(mac) + await self.async_set_unique_id(mac, raise_on_progress=False) self._abort_if_unique_id_configured() return self.async_create_entry( title=name_for_mac(mac), @@ -164,7 +164,7 @@ class ScreenlogicConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): errors[CONF_IP_ADDRESS] = "cannot_connect" if not errors: - await self.async_set_unique_id(mac) + await self.async_set_unique_id(mac, raise_on_progress=False) self._abort_if_unique_id_configured() return self.async_create_entry( title=name_for_mac(mac), diff --git a/homeassistant/components/screenlogic/manifest.json b/homeassistant/components/screenlogic/manifest.json index 2ff3f2c683d..ab3d08a0702 100644 --- a/homeassistant/components/screenlogic/manifest.json +++ b/homeassistant/components/screenlogic/manifest.json @@ -3,7 +3,7 @@ "name": "Pentair ScreenLogic", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/screenlogic", - "requirements": ["screenlogicpy==0.1.2"], + "requirements": ["screenlogicpy==0.2.1"], "codeowners": [ "@dieselrabbit" ], diff --git a/homeassistant/components/screenlogic/sensor.py b/homeassistant/components/screenlogic/sensor.py index f4f86cdd2aa..c9c66a75681 100644 --- a/homeassistant/components/screenlogic/sensor.py +++ b/homeassistant/components/screenlogic/sensor.py @@ -1,7 +1,9 @@ """Support for a ScreenLogic Sensor.""" import logging -from homeassistant.components.sensor import DEVICE_CLASSES +from screenlogicpy.const import DEVICE_TYPE + +from homeassistant.components.sensor import DEVICE_CLASS_POWER, DEVICE_CLASS_TEMPERATURE from . import ScreenlogicEntity from .const import DOMAIN @@ -10,6 +12,11 @@ _LOGGER = logging.getLogger(__name__) PUMP_SENSORS = ("currentWatts", "currentRPM", "currentGPM") +SL_DEVICE_TYPE_TO_HA_DEVICE_CLASS = { + DEVICE_TYPE.TEMPERATURE: DEVICE_CLASS_TEMPERATURE, + DEVICE_TYPE.ENERGY: DEVICE_CLASS_POWER, +} + async def async_setup_entry(hass, config_entry, async_add_entities): """Set up entry.""" @@ -19,11 +26,12 @@ async def async_setup_entry(hass, config_entry, async_add_entities): # Generic sensors for sensor in data["devices"]["sensor"]: entities.append(ScreenLogicSensor(coordinator, sensor)) + # Pump sensors for pump in data["devices"]["pump"]: for pump_key in PUMP_SENSORS: entities.append(ScreenLogicPumpSensor(coordinator, pump, pump_key)) - async_add_entities(entities, True) + async_add_entities(entities) class ScreenLogicSensor(ScreenlogicEntity): @@ -42,10 +50,8 @@ class ScreenLogicSensor(ScreenlogicEntity): @property def device_class(self): """Device class of the sensor.""" - device_class = self.sensor.get("hass_type") - if device_class in DEVICE_CLASSES: - return device_class - return None + device_class = self.sensor.get("device_type") + return SL_DEVICE_TYPE_TO_HA_DEVICE_CLASS.get(device_class) @property def state(self): @@ -56,12 +62,7 @@ class ScreenLogicSensor(ScreenlogicEntity): @property def sensor(self): """Shortcut to access the sensor data.""" - return self.sensor_data[self._data_key] - - @property - def sensor_data(self): - """Shortcut to access the sensors data.""" - return self.coordinator.data["sensors"] + return self.coordinator.data["sensors"][self._data_key] class ScreenLogicPumpSensor(ScreenlogicEntity): @@ -86,10 +87,8 @@ class ScreenLogicPumpSensor(ScreenlogicEntity): @property def device_class(self): """Return the device class.""" - device_class = self.pump_sensor.get("hass_type") - if device_class in DEVICE_CLASSES: - return device_class - return None + device_class = self.pump_sensor.get("device_type") + return SL_DEVICE_TYPE_TO_HA_DEVICE_CLASS.get(device_class) @property def state(self): @@ -99,9 +98,4 @@ class ScreenLogicPumpSensor(ScreenlogicEntity): @property def pump_sensor(self): """Shortcut to access the pump sensor data.""" - return self.pumps_data[self._pump_id][self._key] - - @property - def pumps_data(self): - """Shortcut to access the pump data.""" - return self.coordinator.data["pumps"] + return self.coordinator.data["pumps"][self._pump_id][self._key] diff --git a/homeassistant/components/screenlogic/switch.py b/homeassistant/components/screenlogic/switch.py index 7c1a468df7d..d1ef9397bfc 100644 --- a/homeassistant/components/screenlogic/switch.py +++ b/homeassistant/components/screenlogic/switch.py @@ -19,7 +19,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): for switch in data["devices"]["switch"]: entities.append(ScreenLogicSwitch(coordinator, switch)) - async_add_entities(entities, True) + async_add_entities(entities) class ScreenLogicSwitch(ScreenlogicEntity, SwitchEntity): @@ -47,17 +47,14 @@ class ScreenLogicSwitch(ScreenlogicEntity, SwitchEntity): if await self.hass.async_add_executor_job( self.gateway.set_circuit, self._data_key, circuit_value ): - _LOGGER.debug("Screenlogic turn %s %s", circuit_value, self._data_key) + _LOGGER.debug("Turn %s %s", self._data_key, circuit_value) await self.coordinator.async_request_refresh() else: - _LOGGER.info("Screenlogic turn %s %s error", circuit_value, self._data_key) + _LOGGER.warning( + "Failed to set_circuit %s %s", self._data_key, circuit_value + ) @property def circuit(self): """Shortcut to access the circuit.""" - return self.circuits_data[self._data_key] - - @property - def circuits_data(self): - """Shortcut to access the circuits data.""" - return self.coordinator.data["circuits"] + return self.coordinator.data["circuits"][self._data_key] diff --git a/homeassistant/components/screenlogic/water_heater.py b/homeassistant/components/screenlogic/water_heater.py deleted file mode 100644 index 6b16f68e141..00000000000 --- a/homeassistant/components/screenlogic/water_heater.py +++ /dev/null @@ -1,128 +0,0 @@ -"""Support for a ScreenLogic Water Heater.""" -import logging - -from screenlogicpy.const import HEAT_MODE - -from homeassistant.components.water_heater import ( - SUPPORT_OPERATION_MODE, - SUPPORT_TARGET_TEMPERATURE, - WaterHeaterEntity, -) -from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS, TEMP_FAHRENHEIT - -from . import ScreenlogicEntity -from .const import DOMAIN - -_LOGGER = logging.getLogger(__name__) - -SUPPORTED_FEATURES = SUPPORT_TARGET_TEMPERATURE | SUPPORT_OPERATION_MODE - -HEAT_MODE_NAMES = HEAT_MODE.Names - -MODE_NAME_TO_MODE_NUM = { - HEAT_MODE_NAMES[num]: num for num in range(len(HEAT_MODE_NAMES)) -} - - -async def async_setup_entry(hass, config_entry, async_add_entities): - """Set up entry.""" - entities = [] - data = hass.data[DOMAIN][config_entry.entry_id] - coordinator = data["coordinator"] - - for body in data["devices"]["water_heater"]: - entities.append(ScreenLogicWaterHeater(coordinator, body)) - async_add_entities(entities, True) - - -class ScreenLogicWaterHeater(ScreenlogicEntity, WaterHeaterEntity): - """Represents the heating functions for a body of water.""" - - @property - def name(self) -> str: - """Name of the water heater.""" - ent_name = self.body["heat_status"]["name"] - return f"{self.gateway_name} {ent_name}" - - @property - def state(self) -> str: - """State of the water heater.""" - return HEAT_MODE.GetFriendlyName(self.body["heat_status"]["value"]) - - @property - def min_temp(self) -> float: - """Minimum allowed temperature.""" - return self.body["min_set_point"]["value"] - - @property - def max_temp(self) -> float: - """Maximum allowed temperature.""" - return self.body["max_set_point"]["value"] - - @property - def current_temperature(self) -> float: - """Return water temperature.""" - return self.body["last_temperature"]["value"] - - @property - def target_temperature(self) -> float: - """Target temperature.""" - return self.body["heat_set_point"]["value"] - - @property - def temperature_unit(self) -> str: - """Return the unit of measurement.""" - if self.config_data["is_celcius"]["value"] == 1: - return TEMP_CELSIUS - return TEMP_FAHRENHEIT - - @property - def current_operation(self) -> str: - """Return operation.""" - return HEAT_MODE_NAMES[self.body["heat_mode"]["value"]] - - @property - def operation_list(self): - """All available operations.""" - supported_heat_modes = [HEAT_MODE.OFF] - # Is solar listed as available equipment? - if self.coordinator.data["config"]["equipment_flags"] & 0x1: - supported_heat_modes.extend([HEAT_MODE.SOLAR, HEAT_MODE.SOLAR_PREFERED]) - supported_heat_modes.append(HEAT_MODE.HEATER) - - return [HEAT_MODE_NAMES[mode_num] for mode_num in supported_heat_modes] - - @property - def supported_features(self): - """Supported features of the water heater.""" - return SUPPORTED_FEATURES - - async def async_set_temperature(self, **kwargs) -> None: - """Change the setpoint of the heater.""" - temperature = kwargs.get(ATTR_TEMPERATURE) - if await self.hass.async_add_executor_job( - self.gateway.set_heat_temp, int(self._data_key), int(temperature) - ): - await self.coordinator.async_request_refresh() - else: - _LOGGER.error("Screenlogic set_temperature error") - - async def async_set_operation_mode(self, operation_mode) -> None: - """Set the operation mode.""" - mode = MODE_NAME_TO_MODE_NUM[operation_mode] - if await self.hass.async_add_executor_job( - self.gateway.set_heat_mode, int(self._data_key), int(mode) - ): - await self.coordinator.async_request_refresh() - else: - _LOGGER.error("Screenlogic set_operation_mode error") - - @property - def body(self): - """Shortcut to access body data.""" - return self.bodies_data[self._data_key] - - @property - def bodies_data(self): - """Shortcut to access bodies data.""" - return self.coordinator.data["bodies"] diff --git a/requirements_all.txt b/requirements_all.txt index 6c3793e6c1f..0ee7a4edb72 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2009,7 +2009,7 @@ scapy==2.4.4 schiene==0.23 # homeassistant.components.screenlogic -screenlogicpy==0.1.2 +screenlogicpy==0.2.1 # homeassistant.components.scsgate scsgate==0.1.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 04a5c645087..6ceeccec6b5 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1040,7 +1040,7 @@ samsungtvws==1.6.0 scapy==2.4.4 # homeassistant.components.screenlogic -screenlogicpy==0.1.2 +screenlogicpy==0.2.1 # homeassistant.components.emulated_kasa # homeassistant.components.sense diff --git a/tests/components/screenlogic/test_config_flow.py b/tests/components/screenlogic/test_config_flow.py index 6d2c1ee2595..71dc4935001 100644 --- a/tests/components/screenlogic/test_config_flow.py +++ b/tests/components/screenlogic/test_config_flow.py @@ -101,9 +101,40 @@ async def test_flow_discover_error(hass): assert result["errors"] == {} assert result["step_id"] == "gateway_entry" + with patch( + "homeassistant.components.screenlogic.async_setup", return_value=True + ) as mock_setup, patch( + "homeassistant.components.screenlogic.async_setup_entry", + return_value=True, + ) as mock_setup_entry, patch( + "homeassistant.components.screenlogic.config_flow.login.create_socket", + return_value=True, + ), patch( + "homeassistant.components.screenlogic.config_flow.login.gateway_connect", + return_value="00-C0-33-01-01-01", + ): + result3 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_IP_ADDRESS: "1.1.1.1", + CONF_PORT: 80, + }, + ) + await hass.async_block_till_done() + + assert result3["type"] == "create_entry" + assert result3["title"] == "Pentair: 01-01-01" + assert result3["data"] == { + CONF_IP_ADDRESS: "1.1.1.1", + CONF_PORT: 80, + } + assert len(mock_setup.mock_calls) == 1 + assert len(mock_setup_entry.mock_calls) == 1 + async def test_dhcp(hass): """Test DHCP discovery flow.""" + await setup.async_setup_component(hass, "persistent_notification", {}) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": "dhcp"}, @@ -116,6 +147,36 @@ async def test_dhcp(hass): assert result["type"] == "form" assert result["step_id"] == "gateway_entry" + with patch( + "homeassistant.components.screenlogic.async_setup", return_value=True + ) as mock_setup, patch( + "homeassistant.components.screenlogic.async_setup_entry", + return_value=True, + ) as mock_setup_entry, patch( + "homeassistant.components.screenlogic.config_flow.login.create_socket", + return_value=True, + ), patch( + "homeassistant.components.screenlogic.config_flow.login.gateway_connect", + return_value="00-C0-33-01-01-01", + ): + result3 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + CONF_IP_ADDRESS: "1.1.1.1", + CONF_PORT: 80, + }, + ) + await hass.async_block_till_done() + + assert result3["type"] == "create_entry" + assert result3["title"] == "Pentair: 01-01-01" + assert result3["data"] == { + CONF_IP_ADDRESS: "1.1.1.1", + CONF_PORT: 80, + } + assert len(mock_setup.mock_calls) == 1 + assert len(mock_setup_entry.mock_calls) == 1 + async def test_form_manual_entry(hass): """Test we get the form.""" @@ -148,6 +209,11 @@ async def test_form_manual_entry(hass): assert result2["step_id"] == "gateway_entry" with patch( + "homeassistant.components.screenlogic.async_setup", return_value=True + ) as mock_setup, patch( + "homeassistant.components.screenlogic.async_setup_entry", + return_value=True, + ) as mock_setup_entry, patch( "homeassistant.components.screenlogic.config_flow.login.create_socket", return_value=True, ), patch( @@ -169,6 +235,8 @@ async def test_form_manual_entry(hass): CONF_IP_ADDRESS: "1.1.1.1", CONF_PORT: 80, } + assert len(mock_setup.mock_calls) == 1 + assert len(mock_setup_entry.mock_calls) == 1 async def test_form_cannot_connect(hass): @@ -195,9 +263,18 @@ async def test_form_cannot_connect(hass): async def test_option_flow(hass): """Test config flow options.""" - entry = MockConfigEntry(domain=DOMAIN, data={}, options=None) + entry = MockConfigEntry(domain=DOMAIN) entry.add_to_hass(hass) + with patch( + "homeassistant.components.screenlogic.async_setup", return_value=True + ), patch( + "homeassistant.components.screenlogic.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) assert result["type"] == "form" @@ -213,9 +290,18 @@ async def test_option_flow(hass): async def test_option_flow_defaults(hass): """Test config flow options.""" - entry = MockConfigEntry(domain=DOMAIN, data={}, options=None) + entry = MockConfigEntry(domain=DOMAIN) entry.add_to_hass(hass) + with patch( + "homeassistant.components.screenlogic.async_setup", return_value=True + ), patch( + "homeassistant.components.screenlogic.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) assert result["type"] == "form" @@ -232,9 +318,18 @@ async def test_option_flow_defaults(hass): async def test_option_flow_input_floor(hass): """Test config flow options.""" - entry = MockConfigEntry(domain=DOMAIN, data={}, options=None) + entry = MockConfigEntry(domain=DOMAIN) entry.add_to_hass(hass) + with patch( + "homeassistant.components.screenlogic.async_setup", return_value=True + ), patch( + "homeassistant.components.screenlogic.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) assert result["type"] == "form"