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
parent
346a724ac3
commit
fb48fd7d10
|
@ -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
|
||||
|
|
|
@ -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],
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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]
|
|
@ -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),
|
||||
|
|
|
@ -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"
|
||||
],
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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"]
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue