Add support for MELCloud Air-to-Water devices (#32078)

* Add support for melcloud Air-to-Water devices

* Add water_heater entity for the water heater component.

* Add individual climate entities for 0-2 supported radiator zones.

* Add sensors for zone room temperatures, outdoor temperature and tank
temperature.

* Update .coveragerc

* Use device_state_attributes

Co-Authored-By: Martin Hjelmare <marhje52@gmail.com>

* Apply suggestions from code review

Co-Authored-By: Martin Hjelmare <marhje52@gmail.com>

* Complete state_attributes -> device_state_attributes migration

* Move constants to top of file

* Remove async_turn_on/off

* Drop mac from ATW unique_ids

* Add MAC to device_info connections

* Remove redundant ABC inheritance

* Update homeassistant/components/melcloud/water_heater.py

Co-Authored-By: Martin Hjelmare <marhje52@gmail.com>

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
Vilppu Vuorinen 2020-03-10 20:00:07 +02:00 committed by GitHub
parent 16d7f84be7
commit b2bb9cf134
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 392 additions and 61 deletions

View File

@ -414,7 +414,9 @@ omit =
homeassistant/components/mediaroom/media_player.py homeassistant/components/mediaroom/media_player.py
homeassistant/components/melcloud/__init__.py homeassistant/components/melcloud/__init__.py
homeassistant/components/melcloud/climate.py homeassistant/components/melcloud/climate.py
homeassistant/components/melcloud/const.py
homeassistant/components/melcloud/sensor.py homeassistant/components/melcloud/sensor.py
homeassistant/components/melcloud/water_heater.py
homeassistant/components/message_bird/notify.py homeassistant/components/message_bird/notify.py
homeassistant/components/met/weather.py homeassistant/components/met/weather.py
homeassistant/components/meteo_france/__init__.py homeassistant/components/meteo_france/__init__.py

View File

@ -13,6 +13,7 @@ from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
from homeassistant.const import CONF_TOKEN, CONF_USERNAME from homeassistant.const import CONF_TOKEN, CONF_USERNAME
from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.exceptions import ConfigEntryNotReady
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
from homeassistant.helpers.typing import HomeAssistantType from homeassistant.helpers.typing import HomeAssistantType
from homeassistant.util import Throttle from homeassistant.util import Throttle
@ -22,7 +23,7 @@ _LOGGER = logging.getLogger(__name__)
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60) MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60)
PLATFORMS = ["climate", "sensor"] PLATFORMS = ["climate", "sensor", "water_heater"]
CONF_LANGUAGE = "language" CONF_LANGUAGE = "language"
CONFIG_SCHEMA = vol.Schema( CONFIG_SCHEMA = vol.Schema(
@ -128,6 +129,7 @@ class MelCloudDevice:
def device_info(self): def device_info(self):
"""Return a device description for device registry.""" """Return a device description for device registry."""
_device_info = { _device_info = {
"connections": {(CONNECTION_NETWORK_MAC, self.device.mac)},
"identifiers": {(DOMAIN, f"{self.device.mac}-{self.device.serial}")}, "identifiers": {(DOMAIN, f"{self.device.mac}-{self.device.serial}")},
"manufacturer": "Mitsubishi Electric", "manufacturer": "Mitsubishi Electric",
"name": self.name, "name": self.name,

View File

@ -1,14 +1,26 @@
"""Platform for climate integration.""" """Platform for climate integration."""
from datetime import timedelta from datetime import timedelta
import logging import logging
from typing import List, Optional from typing import Any, Dict, List, Optional
from pymelcloud import DEVICE_TYPE_ATA from pymelcloud import DEVICE_TYPE_ATA, DEVICE_TYPE_ATW, AtaDevice, AtwDevice
import pymelcloud.ata_device as ata
import pymelcloud.atw_device as atw
from pymelcloud.atw_device import (
PROPERTY_ZONE_1_OPERATION_MODE,
PROPERTY_ZONE_2_OPERATION_MODE,
Zone,
)
from homeassistant.components.climate import ClimateDevice from homeassistant.components.climate import ClimateDevice
from homeassistant.components.climate.const import ( from homeassistant.components.climate.const import (
DEFAULT_MAX_TEMP, DEFAULT_MAX_TEMP,
DEFAULT_MIN_TEMP, DEFAULT_MIN_TEMP,
HVAC_MODE_COOL,
HVAC_MODE_DRY,
HVAC_MODE_FAN_ONLY,
HVAC_MODE_HEAT,
HVAC_MODE_HEAT_COOL,
HVAC_MODE_OFF, HVAC_MODE_OFF,
SUPPORT_FAN_MODE, SUPPORT_FAN_MODE,
SUPPORT_TARGET_TEMPERATURE, SUPPORT_TARGET_TEMPERATURE,
@ -19,51 +31,90 @@ from homeassistant.helpers.typing import HomeAssistantType
from homeassistant.util.temperature import convert as convert_temperature from homeassistant.util.temperature import convert as convert_temperature
from . import MelCloudDevice from . import MelCloudDevice
from .const import DOMAIN, HVAC_MODE_LOOKUP, HVAC_MODE_REVERSE_LOOKUP, TEMP_UNIT_LOOKUP from .const import ATTR_STATUS, DOMAIN, TEMP_UNIT_LOOKUP
SCAN_INTERVAL = timedelta(seconds=60) SCAN_INTERVAL = timedelta(seconds=60)
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
ATA_HVAC_MODE_LOOKUP = {
ata.OPERATION_MODE_HEAT: HVAC_MODE_HEAT,
ata.OPERATION_MODE_DRY: HVAC_MODE_DRY,
ata.OPERATION_MODE_COOL: HVAC_MODE_COOL,
ata.OPERATION_MODE_FAN_ONLY: HVAC_MODE_FAN_ONLY,
ata.OPERATION_MODE_HEAT_COOL: HVAC_MODE_HEAT_COOL,
}
ATA_HVAC_MODE_REVERSE_LOOKUP = {v: k for k, v in ATA_HVAC_MODE_LOOKUP.items()}
ATW_ZONE_HVAC_MODE_LOOKUP = {
atw.ZONE_OPERATION_MODE_HEAT: HVAC_MODE_HEAT,
atw.ZONE_OPERATION_MODE_COOL: HVAC_MODE_COOL,
}
ATW_ZONE_HVAC_MODE_REVERSE_LOOKUP = {v: k for k, v in ATW_ZONE_HVAC_MODE_LOOKUP.items()}
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistantType, entry: ConfigEntry, async_add_entities hass: HomeAssistantType, entry: ConfigEntry, async_add_entities
): ):
"""Set up MelCloud device climate based on config_entry.""" """Set up MelCloud device climate based on config_entry."""
mel_devices = hass.data[DOMAIN][entry.entry_id] mel_devices = hass.data[DOMAIN][entry.entry_id]
async_add_entities( async_add_entities(
[AtaDeviceClimate(mel_device) for mel_device in mel_devices[DEVICE_TYPE_ATA]], [
AtaDeviceClimate(mel_device, mel_device.device)
for mel_device in mel_devices[DEVICE_TYPE_ATA]
]
+ [
AtwDeviceZoneClimate(mel_device, mel_device.device, zone)
for mel_device in mel_devices[DEVICE_TYPE_ATW]
for zone in mel_device.device.zones
],
True, True,
) )
class AtaDeviceClimate(ClimateDevice): class MelCloudClimate(ClimateDevice):
"""Air-to-Air climate device.""" """Base climate device."""
def __init__(self, device: MelCloudDevice): def __init__(self, device: MelCloudDevice):
"""Initialize the climate.""" """Initialize the climate."""
self._api = device self.api = device
self._device = self._api.device self._base_device = self.api.device
self._name = device.name self._name = device.name
@property
def unique_id(self) -> Optional[str]:
"""Return a unique ID."""
return f"{self._device.serial}-{self._device.mac}"
@property
def name(self):
"""Return the display name of this light."""
return self._name
async def async_update(self): async def async_update(self):
"""Update state from MELCloud.""" """Update state from MELCloud."""
await self._api.async_update() await self.api.async_update()
@property @property
def device_info(self): def device_info(self):
"""Return a device description for device registry.""" """Return a device description for device registry."""
return self._api.device_info return self.api.device_info
@property
def target_temperature_step(self) -> Optional[float]:
"""Return the supported step of target temperature."""
return self._base_device.temperature_increment
class AtaDeviceClimate(MelCloudClimate):
"""Air-to-Air climate device."""
def __init__(self, device: MelCloudDevice, ata_device: AtaDevice) -> None:
"""Initialize the climate."""
super().__init__(device)
self._device = ata_device
@property
def unique_id(self) -> Optional[str]:
"""Return a unique ID."""
return f"{self.api.device.serial}-{self.api.device.mac}"
@property
def name(self):
"""Return the display name of this entity."""
return self._name
@property @property
def temperature_unit(self) -> str: def temperature_unit(self) -> str:
@ -76,7 +127,7 @@ class AtaDeviceClimate(ClimateDevice):
mode = self._device.operation_mode mode = self._device.operation_mode
if not self._device.power or mode is None: if not self._device.power or mode is None:
return HVAC_MODE_OFF return HVAC_MODE_OFF
return HVAC_MODE_LOOKUP.get(mode) return ATA_HVAC_MODE_LOOKUP.get(mode)
async def async_set_hvac_mode(self, hvac_mode: str) -> None: async def async_set_hvac_mode(self, hvac_mode: str) -> None:
"""Set new target hvac mode.""" """Set new target hvac mode."""
@ -84,7 +135,7 @@ class AtaDeviceClimate(ClimateDevice):
await self._device.set({"power": False}) await self._device.set({"power": False})
return return
operation_mode = HVAC_MODE_REVERSE_LOOKUP.get(hvac_mode) operation_mode = ATA_HVAC_MODE_REVERSE_LOOKUP.get(hvac_mode)
if operation_mode is None: if operation_mode is None:
raise ValueError(f"Invalid hvac_mode [{hvac_mode}]") raise ValueError(f"Invalid hvac_mode [{hvac_mode}]")
@ -97,7 +148,7 @@ class AtaDeviceClimate(ClimateDevice):
def hvac_modes(self) -> List[str]: def hvac_modes(self) -> List[str]:
"""Return the list of available hvac operation modes.""" """Return the list of available hvac operation modes."""
return [HVAC_MODE_OFF] + [ return [HVAC_MODE_OFF] + [
HVAC_MODE_LOOKUP.get(mode) for mode in self._device.operation_modes ATA_HVAC_MODE_LOOKUP.get(mode) for mode in self._device.operation_modes
] ]
@property @property
@ -116,11 +167,6 @@ class AtaDeviceClimate(ClimateDevice):
{"target_temperature": kwargs.get("temperature", self.target_temperature)} {"target_temperature": kwargs.get("temperature", self.target_temperature)}
) )
@property
def target_temperature_step(self) -> Optional[float]:
"""Return the supported step of target temperature."""
return self._device.target_temperature_step
@property @property
def fan_mode(self) -> Optional[str]: def fan_mode(self) -> Optional[str]:
"""Return the fan setting.""" """Return the fan setting."""
@ -135,6 +181,11 @@ class AtaDeviceClimate(ClimateDevice):
"""Return the list of available fan modes.""" """Return the list of available fan modes."""
return self._device.fan_speeds return self._device.fan_speeds
@property
def supported_features(self) -> int:
"""Return the list of supported features."""
return SUPPORT_FAN_MODE | SUPPORT_TARGET_TEMPERATURE
async def async_turn_on(self) -> None: async def async_turn_on(self) -> None:
"""Turn the entity on.""" """Turn the entity on."""
await self._device.set({"power": True}) await self._device.set({"power": True})
@ -143,11 +194,6 @@ class AtaDeviceClimate(ClimateDevice):
"""Turn the entity off.""" """Turn the entity off."""
await self._device.set({"power": False}) await self._device.set({"power": False})
@property
def supported_features(self) -> int:
"""Return the list of supported features."""
return SUPPORT_FAN_MODE | SUPPORT_TARGET_TEMPERATURE
@property @property
def min_temp(self) -> float: def min_temp(self) -> float:
"""Return the minimum temperature.""" """Return the minimum temperature."""
@ -169,3 +215,108 @@ class AtaDeviceClimate(ClimateDevice):
return convert_temperature( return convert_temperature(
DEFAULT_MAX_TEMP, TEMP_CELSIUS, self.temperature_unit DEFAULT_MAX_TEMP, TEMP_CELSIUS, self.temperature_unit
) )
class AtwDeviceZoneClimate(MelCloudClimate):
"""Air-to-Water zone climate device."""
def __init__(
self, device: MelCloudDevice, atw_device: AtwDevice, atw_zone: Zone
) -> None:
"""Initialize the climate."""
super().__init__(device)
self._device = atw_device
self._zone = atw_zone
@property
def unique_id(self) -> Optional[str]:
"""Return a unique ID."""
return f"{self.api.device.serial}-{self._zone.zone_index}"
@property
def name(self) -> str:
"""Return the display name of this entity."""
return f"{self._name} {self._zone.name}"
@property
def device_state_attributes(self) -> Dict[str, Any]:
"""Return the optional state attributes with device specific additions."""
data = {
ATTR_STATUS: ATW_ZONE_HVAC_MODE_LOOKUP.get(
self._zone.status, self._zone.status
)
}
return data
@property
def temperature_unit(self) -> str:
"""Return the unit of measurement used by the platform."""
return TEMP_UNIT_LOOKUP.get(self._device.temp_unit, TEMP_CELSIUS)
@property
def hvac_mode(self) -> str:
"""Return hvac operation ie. heat, cool mode."""
mode = self._zone.operation_mode
if not self._device.power or mode is None:
return HVAC_MODE_OFF
return ATW_ZONE_HVAC_MODE_LOOKUP.get(mode, HVAC_MODE_OFF)
async def async_set_hvac_mode(self, hvac_mode: str) -> None:
"""Set new target hvac mode."""
if hvac_mode == HVAC_MODE_OFF:
await self._device.set({"power": False})
return
operation_mode = ATW_ZONE_HVAC_MODE_REVERSE_LOOKUP.get(hvac_mode)
if operation_mode is None:
raise ValueError(f"Invalid hvac_mode [{hvac_mode}]")
if self._zone.zone_index == 1:
props = {PROPERTY_ZONE_1_OPERATION_MODE: operation_mode}
else:
props = {PROPERTY_ZONE_2_OPERATION_MODE: operation_mode}
if self.hvac_mode == HVAC_MODE_OFF:
props["power"] = True
await self._device.set(props)
@property
def hvac_modes(self) -> List[str]:
"""Return the list of available hvac operation modes."""
return [self.hvac_mode]
@property
def current_temperature(self) -> Optional[float]:
"""Return the current temperature."""
return self._zone.room_temperature
@property
def target_temperature(self) -> Optional[float]:
"""Return the temperature we try to reach."""
return self._zone.target_temperature
async def async_set_temperature(self, **kwargs) -> None:
"""Set new target temperature."""
await self._zone.set_target_temperature(
kwargs.get("temperature", self.target_temperature)
)
@property
def supported_features(self) -> int:
"""Return the list of supported features."""
return SUPPORT_TARGET_TEMPERATURE
@property
def min_temp(self) -> float:
"""Return the minimum temperature.
MELCloud API does not expose radiator zone temperature limits.
"""
return convert_temperature(10, TEMP_CELSIUS, self.temperature_unit)
@property
def max_temp(self) -> float:
"""Return the maximum temperature.
MELCloud API does not expose radiator zone temperature limits.
"""
return convert_temperature(30, TEMP_CELSIUS, self.temperature_unit)

View File

@ -1,26 +1,11 @@
"""Constants for the MELCloud Climate integration.""" """Constants for the MELCloud Climate integration."""
import pymelcloud.ata_device as ata_device
from pymelcloud.const import UNIT_TEMP_CELSIUS, UNIT_TEMP_FAHRENHEIT from pymelcloud.const import UNIT_TEMP_CELSIUS, UNIT_TEMP_FAHRENHEIT
from homeassistant.components.climate.const import (
HVAC_MODE_COOL,
HVAC_MODE_DRY,
HVAC_MODE_FAN_ONLY,
HVAC_MODE_HEAT,
HVAC_MODE_HEAT_COOL,
)
from homeassistant.const import TEMP_CELSIUS, TEMP_FAHRENHEIT from homeassistant.const import TEMP_CELSIUS, TEMP_FAHRENHEIT
DOMAIN = "melcloud" DOMAIN = "melcloud"
HVAC_MODE_LOOKUP = { ATTR_STATUS = "status"
ata_device.OPERATION_MODE_HEAT: HVAC_MODE_HEAT,
ata_device.OPERATION_MODE_DRY: HVAC_MODE_DRY,
ata_device.OPERATION_MODE_COOL: HVAC_MODE_COOL,
ata_device.OPERATION_MODE_FAN_ONLY: HVAC_MODE_FAN_ONLY,
ata_device.OPERATION_MODE_HEAT_COOL: HVAC_MODE_HEAT_COOL,
}
HVAC_MODE_REVERSE_LOOKUP = {v: k for k, v in HVAC_MODE_LOOKUP.items()}
TEMP_UNIT_LOOKUP = { TEMP_UNIT_LOOKUP = {
UNIT_TEMP_CELSIUS: TEMP_CELSIUS, UNIT_TEMP_CELSIUS: TEMP_CELSIUS,

View File

@ -3,7 +3,7 @@
"name": "MELCloud", "name": "MELCloud",
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/melcloud", "documentation": "https://www.home-assistant.io/integrations/melcloud",
"requirements": ["pymelcloud==2.1.0"], "requirements": ["pymelcloud==2.4.0"],
"dependencies": [], "dependencies": [],
"codeowners": ["@vilppuvuorinen"] "codeowners": ["@vilppuvuorinen"]
} }

View File

@ -1,7 +1,8 @@
"""Support for MelCloud device sensors.""" """Support for MelCloud device sensors."""
import logging import logging
from pymelcloud import DEVICE_TYPE_ATA from pymelcloud import DEVICE_TYPE_ATA, DEVICE_TYPE_ATW
from pymelcloud.atw_device import Zone
from homeassistant.const import DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS from homeassistant.const import DEVICE_CLASS_TEMPERATURE, TEMP_CELSIUS
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
@ -16,7 +17,7 @@ ATTR_DEVICE_CLASS = "device_class"
ATTR_VALUE_FN = "value_fn" ATTR_VALUE_FN = "value_fn"
ATTR_ENABLED_FN = "enabled" ATTR_ENABLED_FN = "enabled"
SENSORS = { ATA_SENSORS = {
"room_temperature": { "room_temperature": {
ATTR_MEASUREMENT_NAME: "Room Temperature", ATTR_MEASUREMENT_NAME: "Room Temperature",
ATTR_ICON: "mdi:thermometer", ATTR_ICON: "mdi:thermometer",
@ -34,6 +35,34 @@ SENSORS = {
ATTR_ENABLED_FN: lambda x: x.device.has_energy_consumed_meter, ATTR_ENABLED_FN: lambda x: x.device.has_energy_consumed_meter,
}, },
} }
ATW_SENSORS = {
"outside_temperature": {
ATTR_MEASUREMENT_NAME: "Outside Temperature",
ATTR_ICON: "mdi:thermometer",
ATTR_UNIT_FN: lambda x: TEMP_UNIT_LOOKUP.get(x.device.temp_unit, TEMP_CELSIUS),
ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
ATTR_VALUE_FN: lambda x: x.device.outside_temperature,
ATTR_ENABLED_FN: lambda x: True,
},
"tank_temperature": {
ATTR_MEASUREMENT_NAME: "Tank Temperature",
ATTR_ICON: "mdi:thermometer",
ATTR_UNIT_FN: lambda x: TEMP_UNIT_LOOKUP.get(x.device.temp_unit, TEMP_CELSIUS),
ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
ATTR_VALUE_FN: lambda x: x.device.tank_temperature,
ATTR_ENABLED_FN: lambda x: True,
},
}
ATW_ZONE_SENSORS = {
"room_temperature": {
ATTR_MEASUREMENT_NAME: "Room Temperature",
ATTR_ICON: "mdi:thermometer",
ATTR_UNIT_FN: lambda x: TEMP_UNIT_LOOKUP.get(x.device.temp_unit, TEMP_CELSIUS),
ATTR_DEVICE_CLASS: DEVICE_CLASS_TEMPERATURE,
ATTR_VALUE_FN: lambda zone: zone.room_temperature,
ATTR_ENABLED_FN: lambda x: True,
}
}
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -43,22 +72,35 @@ async def async_setup_entry(hass, entry, async_add_entities):
mel_devices = hass.data[DOMAIN].get(entry.entry_id) mel_devices = hass.data[DOMAIN].get(entry.entry_id)
async_add_entities( async_add_entities(
[ [
MelCloudSensor(mel_device, measurement, definition) MelDeviceSensor(mel_device, measurement, definition)
for measurement, definition in SENSORS.items() for measurement, definition in ATA_SENSORS.items()
for mel_device in mel_devices[DEVICE_TYPE_ATA] for mel_device in mel_devices[DEVICE_TYPE_ATA]
if definition[ATTR_ENABLED_FN](mel_device) if definition[ATTR_ENABLED_FN](mel_device)
]
+ [
MelDeviceSensor(mel_device, measurement, definition)
for measurement, definition in ATW_SENSORS.items()
for mel_device in mel_devices[DEVICE_TYPE_ATW]
if definition[ATTR_ENABLED_FN](mel_device)
]
+ [
AtwZoneSensor(mel_device, zone, measurement, definition)
for mel_device in mel_devices[DEVICE_TYPE_ATW]
for zone in mel_device.device.zones
for measurement, definition, in ATW_ZONE_SENSORS.items()
if definition[ATTR_ENABLED_FN](zone)
], ],
True, True,
) )
class MelCloudSensor(Entity): class MelDeviceSensor(Entity):
"""Representation of a Sensor.""" """Representation of a Sensor."""
def __init__(self, device: MelCloudDevice, measurement, definition): def __init__(self, api: MelCloudDevice, measurement, definition):
"""Initialize the sensor.""" """Initialize the sensor."""
self._api = device self._api = api
self._name_slug = device.name self._name_slug = api.name
self._measurement = measurement self._measurement = measurement
self._def = definition self._def = definition
@ -100,3 +142,20 @@ class MelCloudSensor(Entity):
def device_info(self): def device_info(self):
"""Return a device description for device registry.""" """Return a device description for device registry."""
return self._api.device_info return self._api.device_info
class AtwZoneSensor(MelDeviceSensor):
"""Air-to-Air device sensor."""
def __init__(
self, api: MelCloudDevice, zone: Zone, measurement, definition,
):
"""Initialize the sensor."""
super().__init__(api, measurement, definition)
self._zone = zone
self._name_slug = f"{api.name} {zone.name}"
@property
def state(self):
"""Return zone based state."""
return self._def[ATTR_VALUE_FN](self._zone)

View File

@ -0,0 +1,132 @@
"""Platform for water_heater integration."""
from typing import List, Optional
from pymelcloud import DEVICE_TYPE_ATW, AtwDevice
from pymelcloud.atw_device import (
PROPERTY_OPERATION_MODE,
PROPERTY_TARGET_TANK_TEMPERATURE,
)
from pymelcloud.device import PROPERTY_POWER
from homeassistant.components.water_heater import (
SUPPORT_OPERATION_MODE,
SUPPORT_TARGET_TEMPERATURE,
WaterHeaterDevice,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import TEMP_CELSIUS
from homeassistant.helpers.typing import HomeAssistantType
from . import DOMAIN, MelCloudDevice
from .const import ATTR_STATUS, TEMP_UNIT_LOOKUP
async def async_setup_entry(
hass: HomeAssistantType, entry: ConfigEntry, async_add_entities
):
"""Set up MelCloud device climate based on config_entry."""
mel_devices = hass.data[DOMAIN][entry.entry_id]
async_add_entities(
[
AtwWaterHeater(mel_device, mel_device.device)
for mel_device in mel_devices[DEVICE_TYPE_ATW]
],
True,
)
class AtwWaterHeater(WaterHeaterDevice):
"""Air-to-Water water heater."""
def __init__(self, api: MelCloudDevice, device: AtwDevice) -> None:
"""Initialize water heater device."""
self._api = api
self._device = device
self._name = device.name
async def async_update(self):
"""Update state from MELCloud."""
await self._api.async_update()
@property
def unique_id(self) -> Optional[str]:
"""Return a unique ID."""
return f"{self._api.device.serial}"
@property
def name(self):
"""Return the display name of this entity."""
return self._name
@property
def device_info(self):
"""Return a device description for device registry."""
return self._api.device_info
async def async_turn_on(self) -> None:
"""Turn the entity on."""
await self._device.set({PROPERTY_POWER: True})
async def async_turn_off(self) -> None:
"""Turn the entity off."""
await self._device.set({PROPERTY_POWER: False})
@property
def device_state_attributes(self):
"""Return the optional state attributes with device specific additions."""
data = {ATTR_STATUS: self._device.status}
return data
@property
def temperature_unit(self) -> str:
"""Return the unit of measurement used by the platform."""
return TEMP_UNIT_LOOKUP.get(self._device.temp_unit, TEMP_CELSIUS)
@property
def current_operation(self) -> Optional[str]:
"""Return current operation as reported by pymelcloud."""
return self._device.operation_mode
@property
def operation_list(self) -> List[str]:
"""Return the list of available operation modes as reported by pymelcloud."""
return self._device.operation_modes
@property
def current_temperature(self) -> Optional[float]:
"""Return the current temperature."""
return self._device.tank_temperature
@property
def target_temperature(self):
"""Return the temperature we try to reach."""
return self._device.target_tank_temperature
async def async_set_temperature(self, **kwargs):
"""Set new target temperature."""
await self._device.set(
{
PROPERTY_TARGET_TANK_TEMPERATURE: kwargs.get(
"temperature", self.target_temperature
)
}
)
async def async_set_operation_mode(self, operation_mode):
"""Set new target operation mode."""
await self._device.set({PROPERTY_OPERATION_MODE: operation_mode})
@property
def supported_features(self):
"""Return the list of supported features."""
return SUPPORT_TARGET_TEMPERATURE | SUPPORT_OPERATION_MODE
@property
def min_temp(self) -> Optional[float]:
"""Return the minimum temperature."""
return self._device.target_tank_temperature_min
@property
def max_temp(self) -> Optional[float]:
"""Return the maximum temperature."""
return self._device.target_tank_temperature_max

View File

@ -1372,7 +1372,7 @@ pymailgunner==1.4
pymediaroom==0.6.4 pymediaroom==0.6.4
# homeassistant.components.melcloud # homeassistant.components.melcloud
pymelcloud==2.1.0 pymelcloud==2.4.0
# homeassistant.components.somfy # homeassistant.components.somfy
pymfy==0.7.1 pymfy==0.7.1

View File

@ -501,7 +501,7 @@ pylitejet==0.1
pymailgunner==1.4 pymailgunner==1.4
# homeassistant.components.melcloud # homeassistant.components.melcloud
pymelcloud==2.1.0 pymelcloud==2.4.0
# homeassistant.components.somfy # homeassistant.components.somfy
pymfy==0.7.1 pymfy==0.7.1