Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions custom_components/plugwise/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@
Platform.SELECT,
Platform.SENSOR,
Platform.SWITCH,
Platform.WATER_HEATER,
]
SERVICE_DELETE: Final = "delete_notification"
SEVERITIES: Final[list[str]] = ["other", "info", "message", "warning", "error"]
Expand Down
8 changes: 0 additions & 8 deletions custom_components/plugwise/number.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
LOGGER,
LOWER_BOUND,
MAX_BOILER_TEMP,
MAX_DHW_TEMP,
RESOLUTION,
TEMPERATURE_OFFSET,
UPPER_BOUND,
Expand Down Expand Up @@ -47,13 +46,6 @@ class PlugwiseNumberEntityDescription(NumberEntityDescription):
entity_category=EntityCategory.CONFIG,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
),
PlugwiseNumberEntityDescription(
key=MAX_DHW_TEMP,
translation_key=MAX_DHW_TEMP,
device_class=NumberDeviceClass.TEMPERATURE,
entity_category=EntityCategory.CONFIG,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
),
PlugwiseNumberEntityDescription(
key=TEMPERATURE_OFFSET,
translation_key=TEMPERATURE_OFFSET,
Expand Down
134 changes: 134 additions & 0 deletions custom_components/plugwise/water_heater.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
"""Plugwise water heater component for HomeAssistant."""

from typing import Any

from homeassistant.components.water_heater import (
WaterHeaterEntity,
WaterHeaterEntityFeature,
)
from homeassistant.const import (
ATTR_NAME,
ATTR_TEMPERATURE,
STATE_OFF,
STATE_ON,
UnitOfTemperature,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

from .const import (
BINARY_SENSORS,
DEV_CLASS,
DHW_CM_SWITCH,
DHW_SETPOINT,
LOGGER,
LOWER_BOUND,
MAX_DHW_TEMP,
SENSORS,
TARGET_TEMP,
UPPER_BOUND,
)
from .coordinator import PlugwiseConfigEntry, PlugwiseDataUpdateCoordinator
from .entity import PlugwiseEntity
from .util import plugwise_command

MODE_DHW_COMFORT = "Dhw comfort"
MODE_DHW_NORMAL = "Dhw normal"
OPERATION_MODES = [MODE_DHW_COMFORT, MODE_DHW_NORMAL]


async def async_setup_entry(

Check warning on line 40 in custom_components/plugwise/water_heater.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use asynchronous features in this function or remove the `async` keyword.

See more on https://sonarcloud.io/project/issues?id=plugwise_plugwise-beta&issues=AZ6iBNOYDIaptS4_727P&open=AZ6iBNOYDIaptS4_727P&pullRequest=1085
_hass: HomeAssistant,
entry: PlugwiseConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up Plugwise water_heater from a config entry."""
coordinator = entry.runtime_data

@callback
def _add_entities() -> None:
"""Add Entities."""
if not coordinator.new_devices:
return

entities: list[PlugwiseWaterHeaterEntity] = []
for device_id in coordinator.new_devices:
device = coordinator.data[device_id]
if device[DEV_CLASS] == "heater_central" and device.get(BINARY_SENSORS, {}).get("dhw_state") is not None:
Comment thread
coderabbitai[bot] marked this conversation as resolved.
entities.append(PlugwiseWaterHeaterEntity(coordinator, device_id))
LOGGER.debug("Add %s water_heater", device[ATTR_NAME])
async_add_entities(entities)

_add_entities()
entry.async_on_unload(coordinator.async_add_listener(_add_entities))


class PlugwiseWaterHeaterEntity(PlugwiseEntity, WaterHeaterEntity):
"""Representation of a Plugwise water heater."""

_attr_name = None
_attr_operation_list = OPERATION_MODES
_attr_temperature_unit = UnitOfTemperature.CELSIUS

def __init__(
self,
coordinator: PlugwiseDataUpdateCoordinator,
device_id: str,
) -> None:
"""Initialise the water_heater."""
super().__init__(coordinator, device_id)
self._attr_unique_id = f"{device_id}-water_heater"

self._attr_max_temp = self.device.get("max_dhw_temperature", {}).get(UPPER_BOUND, 75.0)
self._attr_min_temp = self.device.get("max_dhw_temperature", {}).get(LOWER_BOUND, 40.0)
self._attr_supported_features = WaterHeaterEntityFeature.OPERATION_MODE
self._supports_temperature_control = False
if self.device.get("max_dhw_temperature"):
self._attr_supported_features |= WaterHeaterEntityFeature.TARGET_TEMPERATURE
self._supports_temperature_control = True


@property
def current_operation(self) -> str | None:
"""Return current readable operation mode."""
if (state := self.device.get(BINARY_SENSORS, {}).get("dhw_state")) is not None:
if state:
return MODE_DHW_COMFORT
return MODE_DHW_NORMAL
return None

Comment thread
coderabbitai[bot] marked this conversation as resolved.
@property
def current_temperature(self) -> float | None:
"""Return the current water temperature."""
return self.device.get(SENSORS, {}).get("water_temperature")

@property
def target_temperature(self) -> float | None:
"""Return the water temperature we try to reach."""
return (
self.device.get("max_dhw_temperature", {}).get(TARGET_TEMP)
or self.device.get(SENSORS, {}).get(DHW_SETPOINT)
)

@plugwise_command
async def async_set_operation_mode(self, operation_mode: str) -> None:
"""Set the operation mode."""
state = STATE_ON if operation_mode == MODE_DHW_COMFORT else STATE_OFF
await self.coordinator.api.set_switch_state(
self._dev_id,
None,
DHW_CM_SWITCH,
state,
)

@plugwise_command
async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set new target temperature."""
temperature = kwargs.get(ATTR_TEMPERATURE)
if not self._supports_temperature_control or temperature is None:
return

await self.coordinator.api.set_number(self._dev_id, MAX_DHW_TEMP, temperature)
LOGGER.debug(
"Setting %s to %s was successful", MAX_DHW_TEMP, temperature
)
98 changes: 98 additions & 0 deletions tests/components/plugwise/fixtures/anna_v4_dhw/data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
{
"01b85360fdd243d0aaad4d6ac2a5ba7e": {
"active_preset": "home",
"available_schedules": [
"Standaard",
"Thuiswerken",
"off"
],
"climate_mode": "heat",
"control_state": "idle",
"dev_class": "thermostat",
"firmware": "2018-02-08T11:15:53+01:00",
"hardware": "6539-1301-5002",
"location": "eb5309212bf5407bb143e5bfa3b18aee",
"model": "ThermoTouch",
"name": "Anna",
"preset_modes": [
"vacation",
"no_frost",
"away",
"asleep",
"home"
],
"select_schedule": "off",
"sensors": {
"illuminance": 60.0,
"setpoint": 20.5,
"temperature": 20.6
},
"temperature_offset": {
"lower_bound": -2.0,
"resolution": 0.1,
"setpoint": 0.0,
"upper_bound": 2.0
},
"thermostat": {
"lower_bound": 4.0,
"resolution": 0.1,
"setpoint": 20.5,
"upper_bound": 30.0
},
"vendor": "Plugwise"
},
"0466eae8520144c78afb29628384edeb": {
"binary_sensors": {
"plugwise_notification": false
},
"dev_class": "gateway",
"firmware": "4.0.15",
"hardware": "AME Smile 2.0 board",
"location": "94c107dc6ac84ed98e9f68c0dd06bf71",
"mac_address": "012345670001",
"model": "Gateway",
"model_id": "smile_thermo",
"name": "Smile Anna",
"notifications": {},
"sensors": {
"outdoor_temperature": 7.44
},
"vendor": "Plugwise"
},
"cd0e6156b1f04d5f952349ffbe397481": {
"available": true,
"binary_sensors": {
"dhw_state": true,
"flame_state": true,
"heating_state": false
},
"dev_class": "heater_central",
"location": "94c107dc6ac84ed98e9f68c0dd06bf71",
"max_dhw_temperature": {
"lower_bound": 30.0,
"resolution": 0.01,
"setpoint": 60.0,
"upper_bound": 60.0
},
"maximum_boiler_temperature": {
"lower_bound": 0.0,
"resolution": 1.0,
"setpoint": 70.0,
"upper_bound": 100.0
},
"model": "Generic heater",
"model_id": "2.32",
"name": "OpenTherm",
"sensors": {
"intended_boiler_temperature": 39.9,
"modulation_level": 0.0,
"return_temperature": 32.0,
"water_pressure": 2.2,
"water_temperature": 45.0
},
"switches": {
"dhw_cm_switch": false
},
"vendor": "Bosch Thermotechniek B.V."
}
}
61 changes: 0 additions & 61 deletions tests/components/plugwise/snapshots/test_number.ambr
Original file line number Diff line number Diff line change
Expand Up @@ -609,67 +609,6 @@
'state': '-0.5',
})
# ---
# name: test_anna_number_entities[platforms0-True-anna_heatpump_heating][number.opentherm_domestic_hot_water_setpoint-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
None,
]),
'area_id': None,
'capabilities': dict({
'max': 60.0,
'min': 35.0,
'mode': <NumberMode.BOX: 'box'>,
'step': 0.5,
}),
'config_entry_id': <ANY>,
'config_subentry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'number',
'entity_category': <EntityCategory.CONFIG: 'config'>,
'entity_id': 'number.opentherm_domestic_hot_water_setpoint',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'object_id_base': 'Domestic hot water setpoint',
'options': dict({
}),
'original_device_class': <NumberDeviceClass.TEMPERATURE: 'temperature'>,
'original_icon': None,
'original_name': 'Domestic hot water setpoint',
'platform': 'plugwise',
'previous_unique_id': None,
'suggested_object_id': None,
'supported_features': 0,
'translation_key': 'max_dhw_temperature',
'unique_id': '1cbf783bb11e4a7c8a6843dee3a86927-max_dhw_temperature',
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
})
# ---
# name: test_anna_number_entities[platforms0-True-anna_heatpump_heating][number.opentherm_domestic_hot_water_setpoint-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'temperature',
'friendly_name': 'OpenTherm Domestic hot water setpoint',
'max': 60.0,
'min': 35.0,
'mode': <NumberMode.BOX: 'box'>,
'step': 0.5,
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
}),
'context': <ANY>,
'entity_id': 'number.opentherm_domestic_hot_water_setpoint',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': '53.0',
})
# ---
# name: test_anna_number_entities[platforms0-True-anna_heatpump_heating][number.opentherm_maximum_boiler_temperature_setpoint-entry]
EntityRegistryEntrySnapshot({
'aliases': list([
Expand Down
Loading
Loading