Add min/max values as extra attributes for measurements for fyta (#150562)

This commit is contained in:
dontinelli
2025-08-16 20:49:55 +02:00
committed by GitHub
parent 0d5ebdb692
commit a71ae4db37
4 changed files with 189 additions and 32 deletions

View File

@@ -2,3 +2,8 @@
DOMAIN = "fyta"
CONF_EXPIRATION = "expiration"
CONF_MAX_ACCEPTABLE = "max_acceptable"
CONF_MAX_GOOD = "max_good"
CONF_MIN_ACCEPTABLE = "min_acceptable"
CONF_MIN_GOOD = "min_good"

View File

@@ -25,6 +25,12 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.typing import StateType
from .const import (
CONF_MAX_ACCEPTABLE,
CONF_MAX_GOOD,
CONF_MIN_ACCEPTABLE,
CONF_MIN_GOOD,
)
from .coordinator import FytaConfigEntry, FytaCoordinator
from .entity import FytaPlantEntity
@@ -36,6 +42,13 @@ class FytaSensorEntityDescription(SensorEntityDescription):
value_fn: Callable[[Plant], StateType | datetime]
@dataclass(frozen=True, kw_only=True)
class FytaMeasurementSensorEntityDescription(FytaSensorEntityDescription):
"""Describes Fyta sensor entity."""
attribute_fn: Callable[[Plant], dict[str, float | None]]
PLANT_STATUS_LIST: list[str] = ["deleted", "doing_great", "need_attention", "no_sensor"]
PLANT_MEASUREMENT_STATUS_LIST: list[str] = [
"no_data",
@@ -95,35 +108,6 @@ SENSORS: Final[list[FytaSensorEntityDescription]] = [
options=PLANT_MEASUREMENT_STATUS_LIST,
value_fn=lambda plant: plant.salinity_status.name.lower(),
),
FytaSensorEntityDescription(
key="temperature",
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda plant: plant.temperature,
),
FytaSensorEntityDescription(
key="light",
translation_key="light",
native_unit_of_measurement="μmol/s⋅m²",
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda plant: plant.light,
),
FytaSensorEntityDescription(
key="moisture",
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.MOISTURE,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda plant: plant.moisture,
),
FytaSensorEntityDescription(
key="salinity",
translation_key="salinity",
native_unit_of_measurement=UnitOfConductivity.MILLISIEMENS_PER_CM,
device_class=SensorDeviceClass.CONDUCTIVITY,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda plant: plant.salinity,
),
FytaSensorEntityDescription(
key="ph",
device_class=SensorDeviceClass.PH,
@@ -152,6 +136,62 @@ SENSORS: Final[list[FytaSensorEntityDescription]] = [
),
]
MEASUREMENT_SENSORS: Final[list[FytaMeasurementSensorEntityDescription]] = [
FytaMeasurementSensorEntityDescription(
key="temperature",
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
attribute_fn=lambda plant: {
CONF_MAX_ACCEPTABLE: plant.temperature_max_acceptable,
CONF_MAX_GOOD: plant.temperature_max_good,
CONF_MIN_ACCEPTABLE: plant.temperature_min_acceptable,
CONF_MIN_GOOD: plant.temperature_min_good,
},
value_fn=lambda plant: plant.temperature,
),
FytaMeasurementSensorEntityDescription(
key="light",
translation_key="light",
native_unit_of_measurement="μmol/s⋅m²",
state_class=SensorStateClass.MEASUREMENT,
attribute_fn=lambda plant: {
CONF_MAX_ACCEPTABLE: plant.light_max_acceptable,
CONF_MAX_GOOD: plant.light_max_good,
CONF_MIN_ACCEPTABLE: plant.light_min_acceptable,
CONF_MIN_GOOD: plant.light_min_good,
},
value_fn=lambda plant: plant.light,
),
FytaMeasurementSensorEntityDescription(
key="moisture",
native_unit_of_measurement=PERCENTAGE,
device_class=SensorDeviceClass.MOISTURE,
state_class=SensorStateClass.MEASUREMENT,
attribute_fn=lambda plant: {
CONF_MAX_ACCEPTABLE: plant.moisture_max_acceptable,
CONF_MAX_GOOD: plant.moisture_max_good,
CONF_MIN_ACCEPTABLE: plant.moisture_min_acceptable,
CONF_MIN_GOOD: plant.moisture_min_good,
},
value_fn=lambda plant: plant.moisture,
),
FytaMeasurementSensorEntityDescription(
key="salinity",
translation_key="salinity",
native_unit_of_measurement=UnitOfConductivity.MILLISIEMENS_PER_CM,
device_class=SensorDeviceClass.CONDUCTIVITY,
state_class=SensorStateClass.MEASUREMENT,
attribute_fn=lambda plant: {
CONF_MAX_ACCEPTABLE: plant.salinity_max_acceptable,
CONF_MAX_GOOD: plant.salinity_max_good,
CONF_MIN_ACCEPTABLE: plant.salinity_min_acceptable,
CONF_MIN_GOOD: plant.salinity_min_good,
},
value_fn=lambda plant: plant.salinity,
),
]
async def async_setup_entry(
hass: HomeAssistant,
@@ -168,14 +208,28 @@ async def async_setup_entry(
if sensor.key in dir(coordinator.data.get(plant_id))
]
plant_entities.extend(
FytaPlantMeasurementSensor(coordinator, entry, sensor, plant_id)
for plant_id in coordinator.fyta.plant_list
for sensor in MEASUREMENT_SENSORS
if sensor.key in dir(coordinator.data.get(plant_id))
)
async_add_entities(plant_entities)
def _async_add_new_device(plant_id: int) -> None:
async_add_entities(
plant_entities = [
FytaPlantSensor(coordinator, entry, sensor, plant_id)
for sensor in SENSORS
if sensor.key in dir(coordinator.data.get(plant_id))
]
plant_entities.extend(
FytaPlantMeasurementSensor(coordinator, entry, sensor, plant_id)
for sensor in MEASUREMENT_SENSORS
if sensor.key in dir(coordinator.data.get(plant_id))
)
async_add_entities(plant_entities)
coordinator.new_device_callbacks.append(_async_add_new_device)
@@ -190,3 +244,15 @@ class FytaPlantSensor(FytaPlantEntity, SensorEntity):
"""Return the state for this sensor."""
return self.entity_description.value_fn(self.plant)
class FytaPlantMeasurementSensor(FytaPlantSensor):
"""Represents a Fyta measurement sensor."""
entity_description: FytaMeasurementSensorEntityDescription
@property
def extra_state_attributes(self) -> dict[str, float | None]:
"""Return the device state attributes."""
return self.entity_description.attribute_fn(self.plant)

View File

@@ -138,10 +138,64 @@
}
},
"light": {
"name": "Light"
"name": "Light",
"state_attributes": {
"max_acceptable": { "name": "Maximum acceptable" },
"max_good": { "name": "Maximum good" },
"min_acceptable": { "name": "Minimum acceptable" },
"min_good": { "name": "Minimum good" }
}
},
"moisture": {
"name": "[%key:component::sensor::entity_component::moisture::name%]",
"state_attributes": {
"max_acceptable": {
"name": "[%key:component::fyta::entity::sensor::light::state_attributes::max_acceptable::name%]"
},
"max_good": {
"name": "[%key:component::fyta::entity::sensor::light::state_attributes::max_good::name%]"
},
"min_acceptable": {
"name": "[%key:component::fyta::entity::sensor::light::state_attributes::min_acceptable::name%]"
},
"min_good": {
"name": "[%key:component::fyta::entity::sensor::light::state_attributes::min_good::name%]"
}
}
},
"salinity": {
"name": "Salinity"
"name": "Salinity",
"state_attributes": {
"max_acceptable": {
"name": "[%key:component::fyta::entity::sensor::light::state_attributes::max_acceptable::name%]"
},
"max_good": {
"name": "[%key:component::fyta::entity::sensor::light::state_attributes::max_good::name%]"
},
"min_acceptable": {
"name": "[%key:component::fyta::entity::sensor::light::state_attributes::min_acceptable::name%]"
},
"min_good": {
"name": "[%key:component::fyta::entity::sensor::light::state_attributes::min_good::name%]"
}
}
},
"temperature": {
"name": "[%key:component::sensor::entity_component::temperature::name%]",
"state_attributes": {
"max_acceptable": {
"name": "[%key:component::fyta::entity::sensor::light::state_attributes::max_acceptable::name%]"
},
"max_good": {
"name": "[%key:component::fyta::entity::sensor::light::state_attributes::max_good::name%]"
},
"min_acceptable": {
"name": "[%key:component::fyta::entity::sensor::light::state_attributes::min_acceptable::name%]"
},
"min_good": {
"name": "[%key:component::fyta::entity::sensor::light::state_attributes::min_good::name%]"
}
}
},
"last_fertilised": {
"name": "Last fertilized"

View File

@@ -142,6 +142,10 @@
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Gummibaum Light',
'max_acceptable': 675.0,
'max_good': 450.0,
'min_acceptable': 18.0,
'min_good': 20.0,
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'μmol/s⋅m²',
}),
@@ -261,6 +265,10 @@
'attributes': ReadOnlyDict({
'device_class': 'moisture',
'friendly_name': 'Gummibaum Moisture',
'max_acceptable': 80.0,
'max_good': 70.0,
'min_acceptable': 25.0,
'min_good': 35.0,
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
}),
@@ -612,6 +620,10 @@
'attributes': ReadOnlyDict({
'device_class': 'conductivity',
'friendly_name': 'Gummibaum Salinity',
'max_acceptable': 1.2,
'max_good': 1.0,
'min_acceptable': 0.4,
'min_good': 0.6,
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfConductivity.MILLISIEMENS_PER_CM: 'mS/cm'>,
}),
@@ -782,6 +794,10 @@
'attributes': ReadOnlyDict({
'device_class': 'temperature',
'friendly_name': 'Gummibaum Temperature',
'max_acceptable': 42.0,
'max_good': 36.0,
'min_acceptable': 10.0,
'min_good': 17.0,
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
}),
@@ -1002,6 +1018,10 @@
StateSnapshot({
'attributes': ReadOnlyDict({
'friendly_name': 'Kakaobaum Light',
'max_acceptable': 675.0,
'max_good': 450.0,
'min_acceptable': 18.0,
'min_good': 20.0,
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'μmol/s⋅m²',
}),
@@ -1121,6 +1141,10 @@
'attributes': ReadOnlyDict({
'device_class': 'moisture',
'friendly_name': 'Kakaobaum Moisture',
'max_acceptable': 80.0,
'max_good': 70.0,
'min_acceptable': 25.0,
'min_good': 35.0,
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
}),
@@ -1472,6 +1496,10 @@
'attributes': ReadOnlyDict({
'device_class': 'conductivity',
'friendly_name': 'Kakaobaum Salinity',
'max_acceptable': 1.2,
'max_good': 1.0,
'min_acceptable': 0.4,
'min_good': 0.6,
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfConductivity.MILLISIEMENS_PER_CM: 'mS/cm'>,
}),
@@ -1642,6 +1670,10 @@
'attributes': ReadOnlyDict({
'device_class': 'temperature',
'friendly_name': 'Kakaobaum Temperature',
'max_acceptable': 42.0,
'max_good': 36.0,
'min_acceptable': 10.0,
'min_good': 17.0,
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
}),