Skip to content
Merged
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
12 changes: 12 additions & 0 deletions homeassistant/components/pooldose/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,17 @@

from __future__ import annotations

from homeassistant.const import UnitOfTemperature, UnitOfVolumeFlowRate

DOMAIN = "pooldose"
MANUFACTURER = "SEKO"

# Mapping of device units to Home Assistant units
UNIT_MAPPING: dict[str, str] = {
# Temperature units
"°C": UnitOfTemperature.CELSIUS,
"°F": UnitOfTemperature.FAHRENHEIT,
# Volume flow rate units
"m3/h": UnitOfVolumeFlowRate.CUBIC_METERS_PER_HOUR,
"L/s": UnitOfVolumeFlowRate.LITERS_PER_SECOND,
}
12 changes: 12 additions & 0 deletions homeassistant/components/pooldose/icons.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
{
"entity": {
"sensor": {
"cl": {
"default": "mdi:pool"
},
"cl_type_dosing": {
"default": "mdi:flask"
},
"flow_rate": {
"default": "mdi:pipe-valve"
},
"ofa_orp_time": {
"default": "mdi:clock"
},
Expand All @@ -22,6 +31,9 @@
"orp_type_dosing": {
"default": "mdi:flask"
},
"peristaltic_cl_dosing": {
"default": "mdi:pump"
},
"peristaltic_orp_dosing": {
"default": "mdi:pump"
},
Expand Down
87 changes: 67 additions & 20 deletions homeassistant/components/pooldose/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from __future__ import annotations

from dataclasses import dataclass
import logging
from typing import TYPE_CHECKING

Expand All @@ -10,84 +11,125 @@
SensorEntity,
SensorEntityDescription,
)
from homeassistant.const import EntityCategory, UnitOfElectricPotential, UnitOfTime
from homeassistant.const import (
CONCENTRATION_PARTS_PER_MILLION,
EntityCategory,
UnitOfElectricPotential,
UnitOfTime,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

from . import PooldoseConfigEntry
from .const import UNIT_MAPPING
from .entity import PooldoseEntity

_LOGGER = logging.getLogger(__name__)

SENSOR_DESCRIPTIONS: tuple[SensorEntityDescription, ...] = (
SensorEntityDescription(

@dataclass(frozen=True, kw_only=True)
class PooldoseSensorEntityDescription(SensorEntityDescription):
"""Describes PoolDose sensor entity."""

use_dynamic_unit: bool = False


SENSOR_DESCRIPTIONS: tuple[PooldoseSensorEntityDescription, ...] = (
PooldoseSensorEntityDescription(
key="temperature",
device_class=SensorDeviceClass.TEMPERATURE,
# Unit dynamically determined via API
use_dynamic_unit=True,
),
SensorEntityDescription(key="ph", device_class=SensorDeviceClass.PH),
SensorEntityDescription(
PooldoseSensorEntityDescription(key="ph", device_class=SensorDeviceClass.PH),
PooldoseSensorEntityDescription(
key="orp",
translation_key="orp",
device_class=SensorDeviceClass.VOLTAGE,
native_unit_of_measurement=UnitOfElectricPotential.MILLIVOLT,
),
SensorEntityDescription(
PooldoseSensorEntityDescription(
key="cl",
translation_key="cl",
native_unit_of_measurement=CONCENTRATION_PARTS_PER_MILLION,
),
PooldoseSensorEntityDescription(
key="flow_rate",
translation_key="flow_rate",
device_class=SensorDeviceClass.VOLUME_FLOW_RATE,
use_dynamic_unit=True,
),
PooldoseSensorEntityDescription(
key="ph_type_dosing",
translation_key="ph_type_dosing",
entity_category=EntityCategory.DIAGNOSTIC,
device_class=SensorDeviceClass.ENUM,
options=["alcalyne", "acid"],
),
SensorEntityDescription(
PooldoseSensorEntityDescription(
key="peristaltic_ph_dosing",
translation_key="peristaltic_ph_dosing",
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
device_class=SensorDeviceClass.ENUM,
options=["proportional", "on_off", "timed"],
),
SensorEntityDescription(
PooldoseSensorEntityDescription(
key="ofa_ph_time",
translation_key="ofa_ph_time",
entity_category=EntityCategory.DIAGNOSTIC,
device_class=SensorDeviceClass.DURATION,
entity_registry_enabled_default=False,
native_unit_of_measurement=UnitOfTime.MINUTES,
),
SensorEntityDescription(
PooldoseSensorEntityDescription(
key="orp_type_dosing",
translation_key="orp_type_dosing",
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
device_class=SensorDeviceClass.ENUM,
options=["low", "high"],
),
SensorEntityDescription(
PooldoseSensorEntityDescription(
key="peristaltic_orp_dosing",
translation_key="peristaltic_orp_dosing",
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
device_class=SensorDeviceClass.ENUM,
options=["off", "proportional", "on_off", "timed"],
),
SensorEntityDescription(
PooldoseSensorEntityDescription(
key="cl_type_dosing",
translation_key="cl_type_dosing",
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
device_class=SensorDeviceClass.ENUM,
options=["low", "high"],
),
PooldoseSensorEntityDescription(
key="peristaltic_cl_dosing",
translation_key="peristaltic_cl_dosing",
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
device_class=SensorDeviceClass.ENUM,
options=["off", "proportional", "on_off", "timed"],
),
PooldoseSensorEntityDescription(
key="ofa_orp_time",
translation_key="ofa_orp_time",
device_class=SensorDeviceClass.DURATION,
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
native_unit_of_measurement=UnitOfTime.MINUTES,
),
SensorEntityDescription(
PooldoseSensorEntityDescription(
key="ph_calibration_type",
translation_key="ph_calibration_type",
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
device_class=SensorDeviceClass.ENUM,
options=["off", "reference", "1_point", "2_points"],
),
SensorEntityDescription(
PooldoseSensorEntityDescription(
key="ph_calibration_offset",
translation_key="ph_calibration_offset",
entity_category=EntityCategory.DIAGNOSTIC,
Expand All @@ -96,7 +138,7 @@
entity_registry_enabled_default=False,
native_unit_of_measurement=UnitOfElectricPotential.MILLIVOLT,
),
SensorEntityDescription(
PooldoseSensorEntityDescription(
key="ph_calibration_slope",
translation_key="ph_calibration_slope",
entity_category=EntityCategory.DIAGNOSTIC,
Expand All @@ -105,15 +147,15 @@
entity_registry_enabled_default=False,
native_unit_of_measurement=UnitOfElectricPotential.MILLIVOLT,
),
SensorEntityDescription(
PooldoseSensorEntityDescription(
key="orp_calibration_type",
translation_key="orp_calibration_type",
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
device_class=SensorDeviceClass.ENUM,
options=["off", "reference", "1_point"],
),
SensorEntityDescription(
PooldoseSensorEntityDescription(
key="orp_calibration_offset",
translation_key="orp_calibration_offset",
entity_category=EntityCategory.DIAGNOSTIC,
Expand All @@ -122,7 +164,7 @@
entity_registry_enabled_default=False,
native_unit_of_measurement=UnitOfElectricPotential.MILLIVOLT,
),
SensorEntityDescription(
PooldoseSensorEntityDescription(
key="orp_calibration_slope",
translation_key="orp_calibration_slope",
entity_category=EntityCategory.DIAGNOSTIC,
Expand Down Expand Up @@ -163,6 +205,8 @@ async def async_setup_entry(
class PooldoseSensor(PooldoseEntity, SensorEntity):
"""Sensor entity for the Seko PoolDose Python API."""

entity_description: PooldoseSensorEntityDescription

@property
def native_value(self) -> float | int | str | None:
"""Return the current value of the sensor."""
Expand All @@ -175,9 +219,12 @@ def native_value(self) -> float | int | str | None:
def native_unit_of_measurement(self) -> str | None:
"""Return the unit of measurement."""
if (
self.entity_description.key == "temperature"
self.entity_description.use_dynamic_unit
and (data := self.get_data()) is not None
and (device_unit := data.get("unit"))
):
return data["unit"] # °C or °F
# Map device unit to Home Assistant unit, return None if unknown
return UNIT_MAPPING.get(device_unit)

# Fall back to static unit from entity description
return super().native_unit_of_measurement
22 changes: 22 additions & 0 deletions homeassistant/components/pooldose/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,19 @@
},
"entity": {
"sensor": {
"cl": {
"name": "Chlorine"
},
"cl_type_dosing": {
"name": "Chlorine dosing type",
"state": {
"high": "[%key:common::state::high%]",
"low": "[%key:common::state::low%]"
}
},
"flow_rate": {
"name": "Flow rate"
},
"ofa_orp_time": {
"name": "ORP overfeed alert time"
},
Expand Down Expand Up @@ -64,6 +77,15 @@
"low": "[%key:common::state::low%]"
}
},
"peristaltic_cl_dosing": {
"name": "Chlorine peristaltic dosing",
"state": {
"off": "[%key:common::state::off%]",
"on_off": "[%key:component::pooldose::entity::sensor::peristaltic_ph_dosing::state::on_off%]",
"proportional": "[%key:component::pooldose::entity::sensor::peristaltic_ph_dosing::state::proportional%]",
"timed": "[%key:component::pooldose::entity::sensor::peristaltic_ph_dosing::state::timed%]"
}
},
"peristaltic_orp_dosing": {
"name": "ORP peristaltic dosing",
"state": {
Expand Down
8 changes: 8 additions & 0 deletions tests/components/pooldose/fixtures/instantvalues.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@
"value": "proportional",
"unit": null
},
"cl_type_dosing": {
"value": "low",
"unit": null
},
"peristaltic_cl_dosing": {
"value": "off",
"unit": null
},
"ofa_orp_time": {
"value": 0,
"unit": "min"
Expand Down
Loading
Loading