Palazzetti power control (#131833)
* Add number entity * Catch exceptions * Add test coverage * Add translation * Fix exception message * Simplify number.py * Remove dead codepull/132715/head
parent
4e2e6619d0
commit
bd0da03eb9
|
@ -7,7 +7,7 @@ from homeassistant.core import HomeAssistant
|
|||
|
||||
from .coordinator import PalazzettiConfigEntry, PalazzettiDataUpdateCoordinator
|
||||
|
||||
PLATFORMS: list[Platform] = [Platform.CLIMATE, Platform.SENSOR]
|
||||
PLATFORMS: list[Platform] = [Platform.CLIMATE, Platform.NUMBER, Platform.SENSOR]
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: PalazzettiConfigEntry) -> bool:
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
"""Number platform for Palazzetti settings."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pypalazzetti.exceptions import CommunicationError, ValidationError
|
||||
|
||||
from homeassistant.components.number import NumberDeviceClass, NumberEntity
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from . import PalazzettiConfigEntry
|
||||
from .const import DOMAIN
|
||||
from .coordinator import PalazzettiDataUpdateCoordinator
|
||||
from .entity import PalazzettiEntity
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: PalazzettiConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Palazzetti number platform."""
|
||||
async_add_entities([PalazzettiCombustionPowerEntity(config_entry.runtime_data)])
|
||||
|
||||
|
||||
class PalazzettiCombustionPowerEntity(PalazzettiEntity, NumberEntity):
|
||||
"""Representation of Palazzetti number entity for Combustion power."""
|
||||
|
||||
_attr_translation_key = "combustion_power"
|
||||
_attr_device_class = NumberDeviceClass.POWER_FACTOR
|
||||
_attr_native_min_value = 1
|
||||
_attr_native_max_value = 5
|
||||
_attr_native_step = 1
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: PalazzettiDataUpdateCoordinator,
|
||||
) -> None:
|
||||
"""Initialize the Palazzetti number entity."""
|
||||
super().__init__(coordinator)
|
||||
self._attr_unique_id = f"{coordinator.config_entry.unique_id}-combustion_power"
|
||||
|
||||
@property
|
||||
def native_value(self) -> float:
|
||||
"""Return the state of the setting entity."""
|
||||
return self.coordinator.client.power_mode
|
||||
|
||||
async def async_set_native_value(self, value: float) -> None:
|
||||
"""Update the setting."""
|
||||
try:
|
||||
await self.coordinator.client.set_power_mode(int(value))
|
||||
except CommunicationError as err:
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN, translation_key="cannot_connect"
|
||||
) from err
|
||||
except ValidationError as err:
|
||||
raise ServiceValidationError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="invalid_combustion_power",
|
||||
translation_placeholders={
|
||||
"value": str(value),
|
||||
},
|
||||
) from err
|
||||
|
||||
await self.coordinator.async_request_refresh()
|
|
@ -30,6 +30,9 @@
|
|||
"invalid_target_temperature": {
|
||||
"message": "Target temperature {value} is invalid."
|
||||
},
|
||||
"invalid_combustion_power": {
|
||||
"message": "Combustion power {value} is invalid."
|
||||
},
|
||||
"cannot_connect": {
|
||||
"message": "Could not connect to the device."
|
||||
}
|
||||
|
@ -48,6 +51,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"number": {
|
||||
"combustion_power": {
|
||||
"name": "Combustion power"
|
||||
}
|
||||
},
|
||||
"sensor": {
|
||||
"pellet_quantity": {
|
||||
"name": "Pellet quantity"
|
||||
|
|
|
@ -87,6 +87,8 @@ def mock_palazzetti_client() -> Generator[AsyncMock]:
|
|||
mock_client.set_fan_silent.return_value = True
|
||||
mock_client.set_fan_high.return_value = True
|
||||
mock_client.set_fan_auto.return_value = True
|
||||
mock_client.set_power_mode.return_value = True
|
||||
mock_client.power_mode = 3
|
||||
mock_client.list_temperatures.return_value = [
|
||||
TemperatureDefinition(
|
||||
description_key=TemperatureDescriptionKey.ROOM_TEMP,
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
# serializer version: 1
|
||||
# name: test_all_entities[number.stove_combustion_power-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'max': 5,
|
||||
'min': 1,
|
||||
'mode': <NumberMode.AUTO: 'auto'>,
|
||||
'step': 1,
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'number',
|
||||
'entity_category': None,
|
||||
'entity_id': 'number.stove_combustion_power',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <NumberDeviceClass.POWER_FACTOR: 'power_factor'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Combustion power',
|
||||
'platform': 'palazzetti',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'combustion_power',
|
||||
'unique_id': '11:22:33:44:55:66-combustion_power',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[number.stove_combustion_power-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'power_factor',
|
||||
'friendly_name': 'Stove Combustion power',
|
||||
'max': 5,
|
||||
'min': 1,
|
||||
'mode': <NumberMode.AUTO: 'auto'>,
|
||||
'step': 1,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'number.stove_combustion_power',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '3',
|
||||
})
|
||||
# ---
|
|
@ -0,0 +1,72 @@
|
|||
"""Tests for the Palazzetti sensor platform."""
|
||||
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
from pypalazzetti.exceptions import CommunicationError, ValidationError
|
||||
import pytest
|
||||
from syrupy import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.number import DOMAIN as NUMBER_DOMAIN, SERVICE_SET_VALUE
|
||||
from homeassistant.const import ATTR_ENTITY_ID, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from . import setup_integration
|
||||
|
||||
from tests.common import MockConfigEntry, snapshot_platform
|
||||
|
||||
ENTITY_ID = "number.stove_combustion_power"
|
||||
|
||||
|
||||
async def test_all_entities(
|
||||
hass: HomeAssistant,
|
||||
snapshot: SnapshotAssertion,
|
||||
mock_palazzetti_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
entity_registry: er.EntityRegistry,
|
||||
) -> None:
|
||||
"""Test all entities."""
|
||||
with patch("homeassistant.components.palazzetti.PLATFORMS", [Platform.NUMBER]):
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
|
||||
|
||||
|
||||
async def test_async_set_data(
|
||||
hass: HomeAssistant,
|
||||
mock_palazzetti_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test setting number data via service call."""
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
# Set value: Success
|
||||
await hass.services.async_call(
|
||||
NUMBER_DOMAIN,
|
||||
SERVICE_SET_VALUE,
|
||||
{ATTR_ENTITY_ID: ENTITY_ID, "value": 1},
|
||||
blocking=True,
|
||||
)
|
||||
mock_palazzetti_client.set_power_mode.assert_called_once_with(1)
|
||||
mock_palazzetti_client.set_on.reset_mock()
|
||||
|
||||
# Set value: Error
|
||||
mock_palazzetti_client.set_power_mode.side_effect = CommunicationError()
|
||||
with pytest.raises(HomeAssistantError):
|
||||
await hass.services.async_call(
|
||||
NUMBER_DOMAIN,
|
||||
SERVICE_SET_VALUE,
|
||||
{ATTR_ENTITY_ID: ENTITY_ID, "value": 1},
|
||||
blocking=True,
|
||||
)
|
||||
mock_palazzetti_client.set_on.reset_mock()
|
||||
|
||||
mock_palazzetti_client.set_power_mode.side_effect = ValidationError()
|
||||
with pytest.raises(ServiceValidationError):
|
||||
await hass.services.async_call(
|
||||
NUMBER_DOMAIN,
|
||||
SERVICE_SET_VALUE,
|
||||
{ATTR_ENTITY_ID: ENTITY_ID, "value": 1},
|
||||
blocking=True,
|
||||
)
|
Loading…
Reference in New Issue