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 <nick@koston.org>

* Patch integration setup functions.

* Exception, ATTR_TEMPERATURE notes

Co-authored-by: J. Nick Koston <nick@koston.org>
pull/48186/head
Kevin Worrel 2021-03-21 03:56:46 -07:00 committed by GitHub
parent 346a724ac3
commit fb48fd7d10
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 354 additions and 194 deletions

View File

@ -840,9 +840,9 @@ omit =
homeassistant/components/scrape/sensor.py homeassistant/components/scrape/sensor.py
homeassistant/components/screenlogic/__init__.py homeassistant/components/screenlogic/__init__.py
homeassistant/components/screenlogic/binary_sensor.py homeassistant/components/screenlogic/binary_sensor.py
homeassistant/components/screenlogic/climate.py
homeassistant/components/screenlogic/sensor.py homeassistant/components/screenlogic/sensor.py
homeassistant/components/screenlogic/switch.py homeassistant/components/screenlogic/switch.py
homeassistant/components/screenlogic/water_heater.py
homeassistant/components/scsgate/* homeassistant/components/scsgate/*
homeassistant/components/scsgate/cover.py homeassistant/components/scsgate/cover.py
homeassistant/components/sendgrid/notify.py homeassistant/components/sendgrid/notify.py

View File

@ -6,7 +6,7 @@ import logging
from screenlogicpy import ScreenLogicError, ScreenLogicGateway from screenlogicpy import ScreenLogicError, ScreenLogicGateway
from screenlogicpy.const import ( from screenlogicpy.const import (
CONTROLLER_HARDWARE, EQUIPMENT,
SL_GATEWAY_IP, SL_GATEWAY_IP,
SL_GATEWAY_NAME, SL_GATEWAY_NAME,
SL_GATEWAY_PORT, SL_GATEWAY_PORT,
@ -28,7 +28,7 @@ from .const import DEFAULT_SCAN_INTERVAL, DISCOVERED_GATEWAYS, DOMAIN
_LOGGER = logging.getLogger(__name__) _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): 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: except ScreenLogicError as ex:
_LOGGER.error("Error while connecting to the gateway %s: %s", connect_info, ex) _LOGGER.error("Error while connecting to the gateway %s: %s", connect_info, ex)
raise ConfigEntryNotReady from 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( coordinator = ScreenlogicDataUpdateCoordinator(
hass, config_entry=entry, gateway=gateway hass, config_entry=entry, gateway=gateway
@ -91,7 +86,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
device_data["pump"].append(pump) device_data["pump"].append(pump)
for body in coordinator.data["bodies"]: for body in coordinator.data["bodies"]:
device_data["water_heater"].append(body) device_data["body"].append(body)
hass.data[DOMAIN][entry.entry_id] = { hass.data[DOMAIN][entry.entry_id] = {
"coordinator": coordinator, "coordinator": coordinator,
@ -151,18 +146,18 @@ class ScreenlogicDataUpdateCoordinator(DataUpdateCoordinator):
"""Fetch data from the Screenlogic gateway.""" """Fetch data from the Screenlogic gateway."""
try: try:
await self.hass.async_add_executor_job(self.gateway.update) await self.hass.async_add_executor_job(self.gateway.update)
return self.gateway.get_data()
except ScreenLogicError as error: except ScreenLogicError as error:
raise UpdateFailed(error) from error raise UpdateFailed(error) from error
return self.gateway.get_data()
class ScreenlogicEntity(CoordinatorEntity): class ScreenlogicEntity(CoordinatorEntity):
"""Base class for all ScreenLogic entities.""" """Base class for all ScreenLogic entities."""
def __init__(self, coordinator, datakey): def __init__(self, coordinator, data_key):
"""Initialize of the entity.""" """Initialize of the entity."""
super().__init__(coordinator) super().__init__(coordinator)
self._data_key = datakey self._data_key = data_key
@property @property
def mac(self): def mac(self):
@ -192,11 +187,11 @@ class ScreenlogicEntity(CoordinatorEntity):
@property @property
def device_info(self): def device_info(self):
"""Return device information for the controller.""" """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"] hardware_type = self.config_data["hardware_type"]
return { return {
"connections": {(dr.CONNECTION_NETWORK_MAC, self.mac)}, "connections": {(dr.CONNECTION_NETWORK_MAC, self.mac)},
"name": self.gateway_name, "name": self.gateway_name,
"manufacturer": "Pentair", "manufacturer": "Pentair",
"model": CONTROLLER_HARDWARE[controller_type][hardware_type], "model": EQUIPMENT.CONTROLLER_HARDWARE[controller_type][hardware_type],
} }

View File

@ -1,15 +1,20 @@
"""Support for a ScreenLogic Binary Sensor.""" """Support for a ScreenLogic Binary Sensor."""
import logging 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 . import ScreenlogicEntity
from .const import DOMAIN from .const import DOMAIN
_LOGGER = logging.getLogger(__name__) _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): async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up entry.""" """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"]: for binary_sensor in data["devices"]["binary_sensor"]:
entities.append(ScreenLogicBinarySensor(coordinator, binary_sensor)) entities.append(ScreenLogicBinarySensor(coordinator, binary_sensor))
async_add_entities(entities, True) async_add_entities(entities)
class ScreenLogicBinarySensor(ScreenlogicEntity, BinarySensorEntity): class ScreenLogicBinarySensor(ScreenlogicEntity, BinarySensorEntity):
@ -33,10 +38,8 @@ class ScreenLogicBinarySensor(ScreenlogicEntity, BinarySensorEntity):
@property @property
def device_class(self): def device_class(self):
"""Return the device class.""" """Return the device class."""
device_class = self.sensor.get("hass_type") device_class = self.sensor.get("device_type")
if device_class in DEVICE_CLASSES: return SL_DEVICE_TYPE_TO_HA_DEVICE_CLASS.get(device_class)
return device_class
return None
@property @property
def is_on(self) -> bool: def is_on(self) -> bool:
@ -46,9 +49,4 @@ class ScreenLogicBinarySensor(ScreenlogicEntity, BinarySensorEntity):
@property @property
def sensor(self): def sensor(self):
"""Shortcut to access the sensor data.""" """Shortcut to access the sensor data."""
return self.sensor_data[self._data_key] return self.coordinator.data["sensors"][self._data_key]
@property
def sensor_data(self):
"""Shortcut to access the sensors data."""
return self.coordinator.data["sensors"]

View File

@ -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]

View File

@ -120,7 +120,7 @@ class ScreenlogicConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
mac = user_input[GATEWAY_SELECT_KEY] mac = user_input[GATEWAY_SELECT_KEY]
selected_gateway = self.discovered_gateways[mac] 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() self._abort_if_unique_id_configured()
return self.async_create_entry( return self.async_create_entry(
title=name_for_mac(mac), title=name_for_mac(mac),
@ -164,7 +164,7 @@ class ScreenlogicConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
errors[CONF_IP_ADDRESS] = "cannot_connect" errors[CONF_IP_ADDRESS] = "cannot_connect"
if not errors: 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() self._abort_if_unique_id_configured()
return self.async_create_entry( return self.async_create_entry(
title=name_for_mac(mac), title=name_for_mac(mac),

View File

@ -3,7 +3,7 @@
"name": "Pentair ScreenLogic", "name": "Pentair ScreenLogic",
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/screenlogic", "documentation": "https://www.home-assistant.io/integrations/screenlogic",
"requirements": ["screenlogicpy==0.1.2"], "requirements": ["screenlogicpy==0.2.1"],
"codeowners": [ "codeowners": [
"@dieselrabbit" "@dieselrabbit"
], ],

View File

@ -1,7 +1,9 @@
"""Support for a ScreenLogic Sensor.""" """Support for a ScreenLogic Sensor."""
import logging 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 . import ScreenlogicEntity
from .const import DOMAIN from .const import DOMAIN
@ -10,6 +12,11 @@ _LOGGER = logging.getLogger(__name__)
PUMP_SENSORS = ("currentWatts", "currentRPM", "currentGPM") 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): async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up entry.""" """Set up entry."""
@ -19,11 +26,12 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
# Generic sensors # Generic sensors
for sensor in data["devices"]["sensor"]: for sensor in data["devices"]["sensor"]:
entities.append(ScreenLogicSensor(coordinator, sensor)) entities.append(ScreenLogicSensor(coordinator, sensor))
# Pump sensors
for pump in data["devices"]["pump"]: for pump in data["devices"]["pump"]:
for pump_key in PUMP_SENSORS: for pump_key in PUMP_SENSORS:
entities.append(ScreenLogicPumpSensor(coordinator, pump, pump_key)) entities.append(ScreenLogicPumpSensor(coordinator, pump, pump_key))
async_add_entities(entities, True) async_add_entities(entities)
class ScreenLogicSensor(ScreenlogicEntity): class ScreenLogicSensor(ScreenlogicEntity):
@ -42,10 +50,8 @@ class ScreenLogicSensor(ScreenlogicEntity):
@property @property
def device_class(self): def device_class(self):
"""Device class of the sensor.""" """Device class of the sensor."""
device_class = self.sensor.get("hass_type") device_class = self.sensor.get("device_type")
if device_class in DEVICE_CLASSES: return SL_DEVICE_TYPE_TO_HA_DEVICE_CLASS.get(device_class)
return device_class
return None
@property @property
def state(self): def state(self):
@ -56,12 +62,7 @@ class ScreenLogicSensor(ScreenlogicEntity):
@property @property
def sensor(self): def sensor(self):
"""Shortcut to access the sensor data.""" """Shortcut to access the sensor data."""
return self.sensor_data[self._data_key] return self.coordinator.data["sensors"][self._data_key]
@property
def sensor_data(self):
"""Shortcut to access the sensors data."""
return self.coordinator.data["sensors"]
class ScreenLogicPumpSensor(ScreenlogicEntity): class ScreenLogicPumpSensor(ScreenlogicEntity):
@ -86,10 +87,8 @@ class ScreenLogicPumpSensor(ScreenlogicEntity):
@property @property
def device_class(self): def device_class(self):
"""Return the device class.""" """Return the device class."""
device_class = self.pump_sensor.get("hass_type") device_class = self.pump_sensor.get("device_type")
if device_class in DEVICE_CLASSES: return SL_DEVICE_TYPE_TO_HA_DEVICE_CLASS.get(device_class)
return device_class
return None
@property @property
def state(self): def state(self):
@ -99,9 +98,4 @@ class ScreenLogicPumpSensor(ScreenlogicEntity):
@property @property
def pump_sensor(self): def pump_sensor(self):
"""Shortcut to access the pump sensor data.""" """Shortcut to access the pump sensor data."""
return self.pumps_data[self._pump_id][self._key] return self.coordinator.data["pumps"][self._pump_id][self._key]
@property
def pumps_data(self):
"""Shortcut to access the pump data."""
return self.coordinator.data["pumps"]

View File

@ -19,7 +19,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
for switch in data["devices"]["switch"]: for switch in data["devices"]["switch"]:
entities.append(ScreenLogicSwitch(coordinator, switch)) entities.append(ScreenLogicSwitch(coordinator, switch))
async_add_entities(entities, True) async_add_entities(entities)
class ScreenLogicSwitch(ScreenlogicEntity, SwitchEntity): class ScreenLogicSwitch(ScreenlogicEntity, SwitchEntity):
@ -47,17 +47,14 @@ class ScreenLogicSwitch(ScreenlogicEntity, SwitchEntity):
if await self.hass.async_add_executor_job( if await self.hass.async_add_executor_job(
self.gateway.set_circuit, self._data_key, circuit_value 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() await self.coordinator.async_request_refresh()
else: 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 @property
def circuit(self): def circuit(self):
"""Shortcut to access the circuit.""" """Shortcut to access the circuit."""
return self.circuits_data[self._data_key] return self.coordinator.data["circuits"][self._data_key]
@property
def circuits_data(self):
"""Shortcut to access the circuits data."""
return self.coordinator.data["circuits"]

View File

@ -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"]

View File

@ -2009,7 +2009,7 @@ scapy==2.4.4
schiene==0.23 schiene==0.23
# homeassistant.components.screenlogic # homeassistant.components.screenlogic
screenlogicpy==0.1.2 screenlogicpy==0.2.1
# homeassistant.components.scsgate # homeassistant.components.scsgate
scsgate==0.1.0 scsgate==0.1.0

View File

@ -1040,7 +1040,7 @@ samsungtvws==1.6.0
scapy==2.4.4 scapy==2.4.4
# homeassistant.components.screenlogic # homeassistant.components.screenlogic
screenlogicpy==0.1.2 screenlogicpy==0.2.1
# homeassistant.components.emulated_kasa # homeassistant.components.emulated_kasa
# homeassistant.components.sense # homeassistant.components.sense

View File

@ -101,9 +101,40 @@ async def test_flow_discover_error(hass):
assert result["errors"] == {} assert result["errors"] == {}
assert result["step_id"] == "gateway_entry" 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): async def test_dhcp(hass):
"""Test DHCP discovery flow.""" """Test DHCP discovery flow."""
await setup.async_setup_component(hass, "persistent_notification", {})
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, DOMAIN,
context={"source": "dhcp"}, context={"source": "dhcp"},
@ -116,6 +147,36 @@ async def test_dhcp(hass):
assert result["type"] == "form" assert result["type"] == "form"
assert result["step_id"] == "gateway_entry" 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): async def test_form_manual_entry(hass):
"""Test we get the form.""" """Test we get the form."""
@ -148,6 +209,11 @@ async def test_form_manual_entry(hass):
assert result2["step_id"] == "gateway_entry" assert result2["step_id"] == "gateway_entry"
with patch( 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", "homeassistant.components.screenlogic.config_flow.login.create_socket",
return_value=True, return_value=True,
), patch( ), patch(
@ -169,6 +235,8 @@ async def test_form_manual_entry(hass):
CONF_IP_ADDRESS: "1.1.1.1", CONF_IP_ADDRESS: "1.1.1.1",
CONF_PORT: 80, 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): async def test_form_cannot_connect(hass):
@ -195,9 +263,18 @@ async def test_form_cannot_connect(hass):
async def test_option_flow(hass): async def test_option_flow(hass):
"""Test config flow options.""" """Test config flow options."""
entry = MockConfigEntry(domain=DOMAIN, data={}, options=None) entry = MockConfigEntry(domain=DOMAIN)
entry.add_to_hass(hass) 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) result = await hass.config_entries.options.async_init(entry.entry_id)
assert result["type"] == "form" assert result["type"] == "form"
@ -213,9 +290,18 @@ async def test_option_flow(hass):
async def test_option_flow_defaults(hass): async def test_option_flow_defaults(hass):
"""Test config flow options.""" """Test config flow options."""
entry = MockConfigEntry(domain=DOMAIN, data={}, options=None) entry = MockConfigEntry(domain=DOMAIN)
entry.add_to_hass(hass) 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) result = await hass.config_entries.options.async_init(entry.entry_id)
assert result["type"] == "form" assert result["type"] == "form"
@ -232,9 +318,18 @@ async def test_option_flow_defaults(hass):
async def test_option_flow_input_floor(hass): async def test_option_flow_input_floor(hass):
"""Test config flow options.""" """Test config flow options."""
entry = MockConfigEntry(domain=DOMAIN, data={}, options=None) entry = MockConfigEntry(domain=DOMAIN)
entry.add_to_hass(hass) 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) result = await hass.config_entries.options.async_init(entry.entry_id)
assert result["type"] == "form" assert result["type"] == "form"