hass-core/homeassistant/components/moehlenhoff_alpha2/climate.py
j-a-n 243d003acc
Add Moehlenhoff Alpha2 underfloor heating system integration (#42771)
* Add Moehlenhoff Alpha2 underfloor heating system integration

* isort changes

* flake8 changes

* Do not exclude config_flow.py

* pylint changes

* Add config_flow test

* correct requirements_test_all.txt

* more tests

* Update test description

* Test connection and catch TimeoutError in async_setup_entry

* Add version to manifest file

* Remove version from manifest file

* Replace tests.async_mock.patch by unittest.mock.patch

* Update moehlenhoff-alpha2 to version 1.0.1

* Update requirements for moehlenhoff-alpha2 1.0.1

* Update moehlenhoff-alpha2 to 1.0.2

* Use async_setup_platforms

* Use async_unload_platforms

* Separate connection and devices for each entry_id

* Use async_track_time_interval to schedule updates

* Check if input is valid before checking uniqueness

* Move Exception handling to validate_input

* Catch aiohttp.client_exceptions.ClientConnectorError

* Remove translation files

* Mock TimeoutError

* Fix data update

* Replace current callback implementation with ha dispatcher

* Return False in should_poll

* Remove unused argument

* Remove CONNECTION_CLASS

* Use _async_current_entries

* Call async_schedule_update_ha_state after data update

* Remove unneeded async_setup

Co-authored-by: Milan Meulemans <milan.meulemans@live.be>

* Remove unneeded async_setup_platform

Co-authored-by: Milan Meulemans <milan.meulemans@live.be>

* Set Schema attribute host required

Co-authored-by: Milan Meulemans <milan.meulemans@live.be>

* Remove unused Exception class

Co-authored-by: Milan Meulemans <milan.meulemans@live.be>

* Update manifest.json

Co-authored-by: Milan Meulemans <milan.meulemans@live.be>

* pylint constructor return type None

* Replace properties by class variables

* use pass instead of return

* Remove unused sync update method

* remove property hvac_action

* remove pass

* rework exception handling

* Update homeassistant/components/moehlenhoff_alpha2/config_flow.py

Co-authored-by: Milan Meulemans <milan.meulemans@live.be>

* Correct indentation

* catch Exception in validate_input

* Replace HomeAssistantType with HomeAssistant

* Update to moehlenhoff-alpha2 1.0.3

* Allow to switch between heating and cooling mode

* Update moehlenhoff-alpha2 to version 1.0.4

* Update heatarea data after setting target temperature

* Support hvac_action

* Fix heatarea update with multiple bases

* Update data after setting preset mode

* Use custom preset modes like defined by device

* Fix config flow test

* Fix test_duplicate_error

* Rename property to extra_state_attributes

Rename property device_state_attributes to extra_state_attributes and
return lowercase keys in dict.

* Refactor using DataUpdateCoordinator

* Remove _attr_should_poll

* Raise HomeAssistantError on communication error

Catch HTTPError instead of broad except and reraise as HomeAssistantError

* Change DataUpdateCoordinator name to alpha2_base

* Refresh coordinator before setting data

* Raise ValueError on invalid heat area mode

* Rename heatarea to heat_area

* Set type annotation in class attribute

* Move coordinator to top

* Move exception handling to the coordinator

* Use heat_area_id directly

* Sore get_cooling() result into local var

* Add explanation of status attributes

and remove BLOCK_HC

* Fix pylint warnings

* from __future__ import annotations

* Use Platform Enum

* Move data handling to coordinator

* Remove property extra_state_attributes

* Add missing annotations

* Update moehlenhoff-alpha2 to version 1.1.2

* Rework tests based on the scaffold template

* Set also heat/cool/day/night temp with target temp

* Remove unneeded code from tests

Co-authored-by: Milan Meulemans <milan.meulemans@live.be>
2022-02-10 08:28:52 +01:00

131 lines
4.5 KiB
Python

"""Support for Alpha2 room control unit via Alpha2 base."""
import logging
from homeassistant.components.climate import ClimateEntity
from homeassistant.components.climate.const import (
CURRENT_HVAC_COOL,
CURRENT_HVAC_HEAT,
CURRENT_HVAC_IDLE,
HVAC_MODE_COOL,
HVAC_MODE_HEAT,
SUPPORT_PRESET_MODE,
SUPPORT_TARGET_TEMPERATURE,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from . import Alpha2BaseCoordinator
from .const import DOMAIN, PRESET_AUTO, PRESET_DAY, PRESET_NIGHT
_LOGGER = logging.getLogger(__name__)
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Add Alpha2Climate entities from a config_entry."""
coordinator: Alpha2BaseCoordinator = hass.data[DOMAIN][config_entry.entry_id]
async_add_entities(
Alpha2Climate(coordinator, heat_area_id) for heat_area_id in coordinator.data
)
# https://developers.home-assistant.io/docs/core/entity/climate/
class Alpha2Climate(CoordinatorEntity, ClimateEntity):
"""Alpha2 ClimateEntity."""
coordinator: Alpha2BaseCoordinator
target_temperature_step = 0.2
_attr_supported_features = SUPPORT_TARGET_TEMPERATURE | SUPPORT_PRESET_MODE
_attr_hvac_modes = [HVAC_MODE_HEAT, HVAC_MODE_COOL]
_attr_temperature_unit = TEMP_CELSIUS
_attr_preset_modes = [PRESET_AUTO, PRESET_DAY, PRESET_NIGHT]
def __init__(self, coordinator: Alpha2BaseCoordinator, heat_area_id: str) -> None:
"""Initialize Alpha2 ClimateEntity."""
super().__init__(coordinator)
self.heat_area_id = heat_area_id
@property
def name(self) -> str:
"""Return the name of the climate device."""
return self.coordinator.data[self.heat_area_id]["HEATAREA_NAME"]
@property
def min_temp(self) -> float:
"""Return the minimum temperature."""
return float(self.coordinator.data[self.heat_area_id].get("T_TARGET_MIN", 0.0))
@property
def max_temp(self) -> float:
"""Return the maximum temperature."""
return float(self.coordinator.data[self.heat_area_id].get("T_TARGET_MAX", 30.0))
@property
def current_temperature(self) -> float:
"""Return the current temperature."""
return float(self.coordinator.data[self.heat_area_id].get("T_ACTUAL", 0.0))
@property
def hvac_mode(self) -> str:
"""Return current hvac mode."""
if self.coordinator.get_cooling():
return HVAC_MODE_COOL
return HVAC_MODE_HEAT
async def async_set_hvac_mode(self, hvac_mode: str) -> None:
"""Set new target hvac mode."""
await self.coordinator.async_set_cooling(hvac_mode == HVAC_MODE_COOL)
@property
def hvac_action(self) -> str:
"""Return the current running hvac operation."""
if not self.coordinator.data[self.heat_area_id]["_HEATCTRL_STATE"]:
return CURRENT_HVAC_IDLE
if self.coordinator.get_cooling():
return CURRENT_HVAC_COOL
return CURRENT_HVAC_HEAT
@property
def target_temperature(self) -> float:
"""Return the temperature we try to reach."""
return float(self.coordinator.data[self.heat_area_id].get("T_TARGET", 0.0))
async def async_set_temperature(self, **kwargs) -> None:
"""Set new target temperatures."""
target_temperature = kwargs.get(ATTR_TEMPERATURE)
if target_temperature is None:
return
await self.coordinator.async_set_target_temperature(
self.heat_area_id, target_temperature
)
@property
def preset_mode(self) -> str:
"""Return the current preset mode."""
if self.coordinator.data[self.heat_area_id]["HEATAREA_MODE"] == 1:
return PRESET_DAY
if self.coordinator.data[self.heat_area_id]["HEATAREA_MODE"] == 2:
return PRESET_NIGHT
return PRESET_AUTO
async def async_set_preset_mode(self, preset_mode: str) -> None:
"""Set new operation mode."""
heat_area_mode = 0
if preset_mode == PRESET_DAY:
heat_area_mode = 1
elif preset_mode == PRESET_NIGHT:
heat_area_mode = 2
await self.coordinator.async_set_heat_area_mode(
self.heat_area_id, heat_area_mode
)