mirror of
https://github.com/home-assistant/core.git
synced 2025-07-21 04:07:08 +00:00
Add number platform for kostal_plenticore (#64927)
This commit is contained in:
parent
9b60b0c23f
commit
305dff0dc1
@ -12,7 +12,7 @@ from .helper import Plenticore
|
|||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
PLATFORMS = [Platform.SELECT, Platform.SENSOR, Platform.SWITCH]
|
PLATFORMS = [Platform.SELECT, Platform.SENSOR, Platform.SWITCH, Platform.NUMBER]
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
"""Constants for the Kostal Plenticore Solar Inverter integration."""
|
"""Constants for the Kostal Plenticore Solar Inverter integration."""
|
||||||
|
from dataclasses import dataclass
|
||||||
from typing import NamedTuple
|
from typing import NamedTuple
|
||||||
|
|
||||||
|
from homeassistant.components.number import NumberEntityDescription
|
||||||
from homeassistant.components.sensor import (
|
from homeassistant.components.sensor import (
|
||||||
ATTR_STATE_CLASS,
|
ATTR_STATE_CLASS,
|
||||||
SensorDeviceClass,
|
SensorDeviceClass,
|
||||||
@ -16,6 +18,7 @@ from homeassistant.const import (
|
|||||||
PERCENTAGE,
|
PERCENTAGE,
|
||||||
POWER_WATT,
|
POWER_WATT,
|
||||||
)
|
)
|
||||||
|
from homeassistant.helpers.entity import EntityCategory
|
||||||
|
|
||||||
DOMAIN = "kostal_plenticore"
|
DOMAIN = "kostal_plenticore"
|
||||||
|
|
||||||
@ -790,31 +793,54 @@ SENSOR_PROCESS_DATA = [
|
|||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
# Defines all entities for settings.
|
|
||||||
#
|
@dataclass
|
||||||
# Each entry is defined with a tuple of these values:
|
class PlenticoreNumberEntityDescriptionMixin:
|
||||||
# - module id (str)
|
"""Define an entity description mixin for number entities."""
|
||||||
# - process data id (str)
|
|
||||||
# - entity name suffix (str)
|
module_id: str
|
||||||
# - sensor properties (dict)
|
data_id: str
|
||||||
# - value formatter (str)
|
fmt_from: str
|
||||||
SENSOR_SETTINGS_DATA = [
|
fmt_to: str
|
||||||
(
|
|
||||||
"devices:local",
|
|
||||||
"Battery:MinHomeComsumption",
|
@dataclass
|
||||||
"Battery min Home Consumption",
|
class PlenticoreNumberEntityDescription(
|
||||||
{
|
NumberEntityDescription, PlenticoreNumberEntityDescriptionMixin
|
||||||
ATTR_UNIT_OF_MEASUREMENT: POWER_WATT,
|
):
|
||||||
ATTR_DEVICE_CLASS: SensorDeviceClass.POWER,
|
"""Describes a Plenticore number entity."""
|
||||||
},
|
|
||||||
"format_round",
|
|
||||||
|
NUMBER_SETTINGS_DATA = [
|
||||||
|
PlenticoreNumberEntityDescription(
|
||||||
|
key="battery_min_soc",
|
||||||
|
entity_category=EntityCategory.CONFIG,
|
||||||
|
entity_registry_enabled_default=False,
|
||||||
|
icon="mdi:battery-negative",
|
||||||
|
name="Battery min SoC",
|
||||||
|
unit_of_measurement=PERCENTAGE,
|
||||||
|
max_value=100,
|
||||||
|
min_value=5,
|
||||||
|
step=5,
|
||||||
|
module_id="devices:local",
|
||||||
|
data_id="Battery:MinSoc",
|
||||||
|
fmt_from="format_round",
|
||||||
|
fmt_to="format_round_back",
|
||||||
),
|
),
|
||||||
(
|
PlenticoreNumberEntityDescription(
|
||||||
"devices:local",
|
key="battery_min_home_consumption",
|
||||||
"Battery:MinSoc",
|
device_class=SensorDeviceClass.POWER,
|
||||||
"Battery min Soc",
|
entity_category=EntityCategory.CONFIG,
|
||||||
{ATTR_UNIT_OF_MEASUREMENT: PERCENTAGE, ATTR_ICON: "mdi:battery-negative"},
|
entity_registry_enabled_default=False,
|
||||||
"format_round",
|
name="Battery min Home Consumption",
|
||||||
|
unit_of_measurement=POWER_WATT,
|
||||||
|
max_value=38000,
|
||||||
|
min_value=50,
|
||||||
|
step=1,
|
||||||
|
module_id="devices:local",
|
||||||
|
data_id="Battery:MinHomeComsumption",
|
||||||
|
fmt_from="format_round",
|
||||||
|
fmt_to="format_round_back",
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -3,9 +3,10 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from collections.abc import Iterable
|
from collections.abc import Callable, Iterable
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
import logging
|
import logging
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from aiohttp.client_exceptions import ClientError
|
from aiohttp.client_exceptions import ClientError
|
||||||
from kostal.plenticore import (
|
from kostal.plenticore import (
|
||||||
@ -122,7 +123,7 @@ class DataUpdateCoordinatorMixin:
|
|||||||
"""Base implementation for read and write data."""
|
"""Base implementation for read and write data."""
|
||||||
|
|
||||||
async def async_read_data(self, module_id: str, data_id: str) -> list[str, bool]:
|
async def async_read_data(self, module_id: str, data_id: str) -> list[str, bool]:
|
||||||
"""Write settings back to Plenticore."""
|
"""Read data from Plenticore."""
|
||||||
if (client := self._plenticore.client) is None:
|
if (client := self._plenticore.client) is None:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -138,6 +139,10 @@ class DataUpdateCoordinatorMixin:
|
|||||||
if (client := self._plenticore.client) is None:
|
if (client := self._plenticore.client) is None:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Setting value for %s in module %s to %s", self.name, module_id, value
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await client.set_setting_values(module_id, value)
|
await client.set_setting_values(module_id, value)
|
||||||
except PlenticoreApiException:
|
except PlenticoreApiException:
|
||||||
@ -328,7 +333,7 @@ class PlenticoreDataFormatter:
|
|||||||
}
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_method(cls, name: str) -> callable:
|
def get_method(cls, name: str) -> Callable[[Any], Any]:
|
||||||
"""Return a callable formatter of the given name."""
|
"""Return a callable formatter of the given name."""
|
||||||
return getattr(cls, name)
|
return getattr(cls, name)
|
||||||
|
|
||||||
@ -340,6 +345,21 @@ class PlenticoreDataFormatter:
|
|||||||
except (TypeError, ValueError):
|
except (TypeError, ValueError):
|
||||||
return state
|
return state
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def format_round_back(value: float) -> str:
|
||||||
|
"""Return a rounded integer value from a float."""
|
||||||
|
try:
|
||||||
|
if isinstance(value, float) and value.is_integer():
|
||||||
|
int_value = int(value)
|
||||||
|
elif isinstance(value, int):
|
||||||
|
int_value = value
|
||||||
|
else:
|
||||||
|
int_value = round(value)
|
||||||
|
|
||||||
|
return str(int_value)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
return ""
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def format_float(state: str) -> int | str:
|
def format_float(state: str) -> int | str:
|
||||||
"""Return the given state value as float rounded to three decimal places."""
|
"""Return the given state value as float rounded to three decimal places."""
|
||||||
|
157
homeassistant/components/kostal_plenticore/number.py
Normal file
157
homeassistant/components/kostal_plenticore/number.py
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
"""Platform for Kostal Plenticore numbers."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from abc import ABC
|
||||||
|
from datetime import timedelta
|
||||||
|
from functools import partial
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from kostal.plenticore import SettingsData
|
||||||
|
|
||||||
|
from homeassistant.components.number import NumberEntity, NumberMode
|
||||||
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers.entity import DeviceInfo
|
||||||
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||||
|
|
||||||
|
from .const import DOMAIN, NUMBER_SETTINGS_DATA, PlenticoreNumberEntityDescription
|
||||||
|
from .helper import PlenticoreDataFormatter, SettingDataUpdateCoordinator
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry(
|
||||||
|
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||||
|
) -> None:
|
||||||
|
"""Add Kostal Plenticore Number entities."""
|
||||||
|
plenticore = hass.data[DOMAIN][entry.entry_id]
|
||||||
|
|
||||||
|
entities = []
|
||||||
|
|
||||||
|
available_settings_data = await plenticore.client.get_settings()
|
||||||
|
settings_data_update_coordinator = SettingDataUpdateCoordinator(
|
||||||
|
hass,
|
||||||
|
_LOGGER,
|
||||||
|
"Settings Data",
|
||||||
|
timedelta(seconds=30),
|
||||||
|
plenticore,
|
||||||
|
)
|
||||||
|
|
||||||
|
for description in NUMBER_SETTINGS_DATA:
|
||||||
|
if (
|
||||||
|
description.module_id not in available_settings_data
|
||||||
|
or description.data_id
|
||||||
|
not in (
|
||||||
|
setting.id for setting in available_settings_data[description.module_id]
|
||||||
|
)
|
||||||
|
):
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Skipping non existing setting data %s/%s",
|
||||||
|
description.module_id,
|
||||||
|
description.data_id,
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
|
setting_data = next(
|
||||||
|
filter(
|
||||||
|
partial(lambda id, sd: id == sd.id, description.data_id),
|
||||||
|
available_settings_data[description.module_id],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
entities.append(
|
||||||
|
PlenticoreDataNumber(
|
||||||
|
settings_data_update_coordinator,
|
||||||
|
entry.entry_id,
|
||||||
|
entry.title,
|
||||||
|
plenticore.device_info,
|
||||||
|
description,
|
||||||
|
setting_data,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
async_add_entities(entities)
|
||||||
|
|
||||||
|
|
||||||
|
class PlenticoreDataNumber(CoordinatorEntity, NumberEntity, ABC):
|
||||||
|
"""Representation of a Kostal Plenticore Number entity."""
|
||||||
|
|
||||||
|
entity_description: PlenticoreNumberEntityDescription
|
||||||
|
coordinator: SettingDataUpdateCoordinator
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
coordinator: SettingDataUpdateCoordinator,
|
||||||
|
entry_id: str,
|
||||||
|
platform_name: str,
|
||||||
|
device_info: DeviceInfo,
|
||||||
|
description: PlenticoreNumberEntityDescription,
|
||||||
|
setting_data: SettingsData,
|
||||||
|
) -> None:
|
||||||
|
"""Initialize the Plenticore Number entity."""
|
||||||
|
super().__init__(coordinator)
|
||||||
|
|
||||||
|
self.entity_description = description
|
||||||
|
self.entry_id = entry_id
|
||||||
|
|
||||||
|
self._attr_device_info = device_info
|
||||||
|
self._attr_unique_id = f"{self.entry_id}_{self.module_id}_{self.data_id}"
|
||||||
|
self._attr_name = f"{platform_name} {description.name}"
|
||||||
|
self._attr_mode = NumberMode.BOX
|
||||||
|
|
||||||
|
self._formatter = PlenticoreDataFormatter.get_method(description.fmt_from)
|
||||||
|
self._formatter_back = PlenticoreDataFormatter.get_method(description.fmt_to)
|
||||||
|
|
||||||
|
# overwrite from retrieved setting data
|
||||||
|
if setting_data.min is not None:
|
||||||
|
self._attr_min_value = self._formatter(setting_data.min)
|
||||||
|
if setting_data.max is not None:
|
||||||
|
self._attr_max_value = self._formatter(setting_data.max)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def module_id(self) -> str:
|
||||||
|
"""Return the plenticore module id of this entity."""
|
||||||
|
return self.entity_description.module_id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def data_id(self) -> str:
|
||||||
|
"""Return the plenticore data id for this entity."""
|
||||||
|
return self.entity_description.data_id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def available(self) -> bool:
|
||||||
|
"""Return if entity is available."""
|
||||||
|
return (
|
||||||
|
super().available
|
||||||
|
and self.coordinator.data is not None
|
||||||
|
and self.module_id in self.coordinator.data
|
||||||
|
and self.data_id in self.coordinator.data[self.module_id]
|
||||||
|
)
|
||||||
|
|
||||||
|
async def async_added_to_hass(self) -> None:
|
||||||
|
"""Register this entity on the Update Coordinator."""
|
||||||
|
await super().async_added_to_hass()
|
||||||
|
self.coordinator.start_fetch_data(self.module_id, self.data_id)
|
||||||
|
|
||||||
|
async def async_will_remove_from_hass(self) -> None:
|
||||||
|
"""Unregister this entity from the Update Coordinator."""
|
||||||
|
self.coordinator.stop_fetch_data(self.module_id, self.data_id)
|
||||||
|
await super().async_will_remove_from_hass()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def value(self) -> float | None:
|
||||||
|
"""Return the current value."""
|
||||||
|
if self.available:
|
||||||
|
raw_value = self.coordinator.data[self.module_id][self.data_id]
|
||||||
|
return self._formatter(raw_value)
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
async def async_set_value(self, value: float) -> None:
|
||||||
|
"""Set a new value."""
|
||||||
|
str_value = self._formatter_back(value)
|
||||||
|
await self.coordinator.async_write_data(
|
||||||
|
self.module_id, {self.data_id: str_value}
|
||||||
|
)
|
||||||
|
await self.coordinator.async_refresh()
|
@ -14,17 +14,8 @@ from homeassistant.helpers.entity import DeviceInfo, EntityCategory
|
|||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||||
|
|
||||||
from .const import (
|
from .const import ATTR_ENABLED_DEFAULT, DOMAIN, SENSOR_PROCESS_DATA
|
||||||
ATTR_ENABLED_DEFAULT,
|
from .helper import PlenticoreDataFormatter, ProcessDataUpdateCoordinator
|
||||||
DOMAIN,
|
|
||||||
SENSOR_PROCESS_DATA,
|
|
||||||
SENSOR_SETTINGS_DATA,
|
|
||||||
)
|
|
||||||
from .helper import (
|
|
||||||
PlenticoreDataFormatter,
|
|
||||||
ProcessDataUpdateCoordinator,
|
|
||||||
SettingDataUpdateCoordinator,
|
|
||||||
)
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -70,38 +61,6 @@ async def async_setup_entry(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
available_settings_data = await plenticore.client.get_settings()
|
|
||||||
settings_data_update_coordinator = SettingDataUpdateCoordinator(
|
|
||||||
hass,
|
|
||||||
_LOGGER,
|
|
||||||
"Settings Data",
|
|
||||||
timedelta(seconds=300),
|
|
||||||
plenticore,
|
|
||||||
)
|
|
||||||
for module_id, data_id, name, sensor_data, fmt in SENSOR_SETTINGS_DATA:
|
|
||||||
if module_id not in available_settings_data or data_id not in (
|
|
||||||
setting.id for setting in available_settings_data[module_id]
|
|
||||||
):
|
|
||||||
_LOGGER.debug(
|
|
||||||
"Skipping non existing setting data %s/%s", module_id, data_id
|
|
||||||
)
|
|
||||||
continue
|
|
||||||
|
|
||||||
entities.append(
|
|
||||||
PlenticoreDataSensor(
|
|
||||||
settings_data_update_coordinator,
|
|
||||||
entry.entry_id,
|
|
||||||
entry.title,
|
|
||||||
module_id,
|
|
||||||
data_id,
|
|
||||||
name,
|
|
||||||
sensor_data,
|
|
||||||
PlenticoreDataFormatter.get_method(fmt),
|
|
||||||
plenticore.device_info,
|
|
||||||
EntityCategory.DIAGNOSTIC,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
async_add_entities(entities)
|
async_add_entities(entities)
|
||||||
|
|
||||||
|
|
||||||
|
197
tests/components/kostal_plenticore/test_number.py
Normal file
197
tests/components/kostal_plenticore/test_number.py
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
"""Test Kostal Plenticore number."""
|
||||||
|
|
||||||
|
from unittest.mock import AsyncMock, MagicMock
|
||||||
|
|
||||||
|
from kostal.plenticore import SettingsData
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from homeassistant.components.kostal_plenticore.const import (
|
||||||
|
PlenticoreNumberEntityDescription,
|
||||||
|
)
|
||||||
|
from homeassistant.components.kostal_plenticore.number import PlenticoreDataNumber
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers.entity_registry import async_get
|
||||||
|
|
||||||
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mock_coordinator() -> MagicMock:
|
||||||
|
"""Return a mocked coordinator for tests."""
|
||||||
|
coordinator = MagicMock()
|
||||||
|
coordinator.async_write_data = AsyncMock()
|
||||||
|
coordinator.async_refresh = AsyncMock()
|
||||||
|
return coordinator
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mock_number_description() -> PlenticoreNumberEntityDescription:
|
||||||
|
"""Return a PlenticoreNumberEntityDescription for tests."""
|
||||||
|
return PlenticoreNumberEntityDescription(
|
||||||
|
key="mock key",
|
||||||
|
module_id="moduleid",
|
||||||
|
data_id="dataid",
|
||||||
|
min_value=0,
|
||||||
|
max_value=1000,
|
||||||
|
fmt_from="format_round",
|
||||||
|
fmt_to="format_round_back",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mock_setting_data() -> SettingsData:
|
||||||
|
"""Return a default SettingsData for tests."""
|
||||||
|
return SettingsData(
|
||||||
|
{
|
||||||
|
"default": None,
|
||||||
|
"min": None,
|
||||||
|
"access": None,
|
||||||
|
"max": None,
|
||||||
|
"unit": None,
|
||||||
|
"type": None,
|
||||||
|
"id": "data_id",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_setup_all_entries(
|
||||||
|
hass: HomeAssistant, mock_config_entry: MockConfigEntry, mock_plenticore: MagicMock
|
||||||
|
):
|
||||||
|
"""Test if all available entries are setup up."""
|
||||||
|
mock_plenticore.client.get_settings.return_value = {
|
||||||
|
"devices:local": [
|
||||||
|
SettingsData({"id": "Battery:MinSoc", "min": None, "max": None}),
|
||||||
|
SettingsData(
|
||||||
|
{"id": "Battery:MinHomeComsumption", "min": None, "max": None}
|
||||||
|
),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
mock_config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
assert await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
ent_reg = async_get(hass)
|
||||||
|
assert ent_reg.async_get("number.scb_battery_min_soc") is not None
|
||||||
|
assert ent_reg.async_get("number.scb_battery_min_home_consumption") is not None
|
||||||
|
|
||||||
|
|
||||||
|
async def test_setup_no_entries(
|
||||||
|
hass: HomeAssistant, mock_config_entry: MockConfigEntry, mock_plenticore: MagicMock
|
||||||
|
):
|
||||||
|
"""Test that no entries are setup up."""
|
||||||
|
mock_plenticore.client.get_settings.return_value = []
|
||||||
|
mock_config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
assert await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
ent_reg = async_get(hass)
|
||||||
|
assert ent_reg.async_get("number.scb_battery_min_soc") is None
|
||||||
|
assert ent_reg.async_get("number.scb_battery_min_home_consumption") is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_number_returns_value_if_available(
|
||||||
|
mock_coordinator: MagicMock,
|
||||||
|
mock_number_description: PlenticoreNumberEntityDescription,
|
||||||
|
mock_setting_data: SettingsData,
|
||||||
|
):
|
||||||
|
"""Test if value property on PlenticoreDataNumber returns an int if available."""
|
||||||
|
|
||||||
|
mock_coordinator.data = {"moduleid": {"dataid": "42"}}
|
||||||
|
|
||||||
|
entity = PlenticoreDataNumber(
|
||||||
|
mock_coordinator, "42", "scb", None, mock_number_description, mock_setting_data
|
||||||
|
)
|
||||||
|
|
||||||
|
assert entity.value == 42
|
||||||
|
assert type(entity.value) == int
|
||||||
|
|
||||||
|
|
||||||
|
def test_number_returns_none_if_unavailable(
|
||||||
|
mock_coordinator: MagicMock,
|
||||||
|
mock_number_description: PlenticoreNumberEntityDescription,
|
||||||
|
mock_setting_data: SettingsData,
|
||||||
|
):
|
||||||
|
"""Test if value property on PlenticoreDataNumber returns none if unavailable."""
|
||||||
|
|
||||||
|
mock_coordinator.data = {} # makes entity not available
|
||||||
|
|
||||||
|
entity = PlenticoreDataNumber(
|
||||||
|
mock_coordinator, "42", "scb", None, mock_number_description, mock_setting_data
|
||||||
|
)
|
||||||
|
|
||||||
|
assert entity.value is None
|
||||||
|
|
||||||
|
|
||||||
|
async def test_set_value(
|
||||||
|
mock_coordinator: MagicMock,
|
||||||
|
mock_number_description: PlenticoreNumberEntityDescription,
|
||||||
|
mock_setting_data: SettingsData,
|
||||||
|
):
|
||||||
|
"""Test if set value calls coordinator with new value."""
|
||||||
|
|
||||||
|
entity = PlenticoreDataNumber(
|
||||||
|
mock_coordinator, "42", "scb", None, mock_number_description, mock_setting_data
|
||||||
|
)
|
||||||
|
|
||||||
|
await entity.async_set_value(42)
|
||||||
|
|
||||||
|
mock_coordinator.async_write_data.assert_called_once_with(
|
||||||
|
"moduleid", {"dataid": "42"}
|
||||||
|
)
|
||||||
|
mock_coordinator.async_refresh.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
|
async def test_minmax_overwrite(
|
||||||
|
mock_coordinator: MagicMock,
|
||||||
|
mock_number_description: PlenticoreNumberEntityDescription,
|
||||||
|
):
|
||||||
|
"""Test if min/max value is overwritten from retrieved settings data."""
|
||||||
|
|
||||||
|
setting_data = SettingsData(
|
||||||
|
{
|
||||||
|
"min": "5",
|
||||||
|
"max": "100",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
entity = PlenticoreDataNumber(
|
||||||
|
mock_coordinator, "42", "scb", None, mock_number_description, setting_data
|
||||||
|
)
|
||||||
|
|
||||||
|
assert entity.min_value == 5
|
||||||
|
assert entity.max_value == 100
|
||||||
|
|
||||||
|
|
||||||
|
async def test_added_to_hass(
|
||||||
|
mock_coordinator: MagicMock,
|
||||||
|
mock_number_description: PlenticoreNumberEntityDescription,
|
||||||
|
mock_setting_data: SettingsData,
|
||||||
|
):
|
||||||
|
"""Test if coordinator starts fetching after added to hass."""
|
||||||
|
|
||||||
|
entity = PlenticoreDataNumber(
|
||||||
|
mock_coordinator, "42", "scb", None, mock_number_description, mock_setting_data
|
||||||
|
)
|
||||||
|
|
||||||
|
await entity.async_added_to_hass()
|
||||||
|
|
||||||
|
mock_coordinator.start_fetch_data.assert_called_once_with("moduleid", "dataid")
|
||||||
|
|
||||||
|
|
||||||
|
async def test_remove_from_hass(
|
||||||
|
mock_coordinator: MagicMock,
|
||||||
|
mock_number_description: PlenticoreNumberEntityDescription,
|
||||||
|
mock_setting_data: SettingsData,
|
||||||
|
):
|
||||||
|
"""Test if coordinator stops fetching after remove from hass."""
|
||||||
|
|
||||||
|
entity = PlenticoreDataNumber(
|
||||||
|
mock_coordinator, "42", "scb", None, mock_number_description, mock_setting_data
|
||||||
|
)
|
||||||
|
|
||||||
|
await entity.async_will_remove_from_hass()
|
||||||
|
|
||||||
|
mock_coordinator.stop_fetch_data.assert_called_once_with("moduleid", "dataid")
|
Loading…
x
Reference in New Issue
Block a user