diff options
| author | diogo464 <[email protected]> | 2025-12-06 14:31:54 +0000 |
|---|---|---|
| committer | diogo464 <[email protected]> | 2025-12-06 14:31:54 +0000 |
| commit | e5416e848c8caeed59e8aca6fb3e191fbc621b1b (patch) | |
| tree | bd086e6d96f7acf2e051d29c5ce2fee3be4ea5f5 | |
| parent | 0fbce715a32f5e05c2a66e5cc401105f72083ff5 (diff) | |
Refactor sensor API to support generic sensor types with configurable display precision
Replaced TemperatureSensor with a generic Sensor type that accepts sensor class, state class, unit, and display precision to support various sensor types. Added suggested_display_precision field to control decimal places in Home Assistant UI.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <[email protected]>
| -rw-r--r-- | examples/sensor.rs (renamed from examples/temperature.rs) | 38 | ||||
| -rw-r--r-- | src/entity.rs | 1 | ||||
| -rw-r--r-- | src/entity_sensor.rs | 27 | ||||
| -rw-r--r-- | src/lib.rs | 12 |
4 files changed, 47 insertions, 31 deletions
diff --git a/examples/temperature.rs b/examples/sensor.rs index d449cd4..69bd87a 100644 --- a/examples/temperature.rs +++ b/examples/sensor.rs | |||
| @@ -21,46 +21,52 @@ async fn main_task(spawner: Spawner) { | |||
| 21 | }, | 21 | }, |
| 22 | ); | 22 | ); |
| 23 | 23 | ||
| 24 | let constant_temperature_sensor = device.create_temperature_sensor( | 24 | let temperature_sensor = device.create_sensor( |
| 25 | "constant-temperature-sensor-id", | 25 | "random-temperature-sensor-id", |
| 26 | embassy_ha::TemperatureSensorConfig { | 26 | embassy_ha::SensorConfig { |
| 27 | common: embassy_ha::EntityCommonConfig { | 27 | common: embassy_ha::EntityCommonConfig { |
| 28 | name: Some("Constant Temperature Sensor"), | 28 | name: Some("Random Temperature Sensor"), |
| 29 | ..Default::default() | 29 | ..Default::default() |
| 30 | }, | 30 | }, |
| 31 | unit: embassy_ha::TemperatureUnit::Celcius, | 31 | class: embassy_ha::SensorClass::Temperature, |
| 32 | state_class: embassy_ha::StateClass::Measurement, | ||
| 33 | unit: Some(embassy_ha::constants::HA_UNIT_TEMPERATURE_CELSIUS), | ||
| 34 | suggested_display_precision: Some(1), | ||
| 32 | }, | 35 | }, |
| 33 | ); | 36 | ); |
| 34 | 37 | ||
| 35 | let random_temperature_sensor = device.create_temperature_sensor( | 38 | let humidity_sensor = device.create_sensor( |
| 36 | "random-temperature-sensor-id", | 39 | "random-humidity-sensor-id", |
| 37 | embassy_ha::TemperatureSensorConfig { | 40 | embassy_ha::SensorConfig { |
| 38 | common: embassy_ha::EntityCommonConfig { | 41 | common: embassy_ha::EntityCommonConfig { |
| 39 | name: Some("Random Temperature Sensor"), | 42 | name: Some("Random Humidity Sensor"), |
| 40 | ..Default::default() | 43 | ..Default::default() |
| 41 | }, | 44 | }, |
| 42 | unit: embassy_ha::TemperatureUnit::Celcius, | 45 | class: embassy_ha::SensorClass::Humidity, |
| 46 | state_class: embassy_ha::StateClass::Measurement, | ||
| 47 | unit: Some(embassy_ha::constants::HA_UNIT_PERCENTAGE), | ||
| 48 | suggested_display_precision: Some(0), | ||
| 43 | }, | 49 | }, |
| 44 | ); | 50 | ); |
| 45 | 51 | ||
| 46 | spawner.must_spawn(constant_temperature_task(constant_temperature_sensor)); | 52 | spawner.must_spawn(random_temperature_task(temperature_sensor)); |
| 47 | spawner.must_spawn(random_temperature_task(random_temperature_sensor)); | 53 | spawner.must_spawn(random_humidity_task(humidity_sensor)); |
| 48 | 54 | ||
| 49 | device.run(&mut stream).await.unwrap(); | 55 | device.run(&mut stream).await.unwrap(); |
| 50 | } | 56 | } |
| 51 | 57 | ||
| 52 | #[embassy_executor::task] | 58 | #[embassy_executor::task] |
| 53 | async fn constant_temperature_task(mut sensor: embassy_ha::TemperatureSensor<'static>) { | 59 | async fn random_temperature_task(mut sensor: embassy_ha::Sensor<'static>) { |
| 54 | loop { | 60 | loop { |
| 55 | sensor.publish(42.0); | 61 | sensor.publish(rand::random_range(0.0..50.0)); |
| 56 | Timer::after_secs(1).await; | 62 | Timer::after_secs(1).await; |
| 57 | } | 63 | } |
| 58 | } | 64 | } |
| 59 | 65 | ||
| 60 | #[embassy_executor::task] | 66 | #[embassy_executor::task] |
| 61 | async fn random_temperature_task(mut sensor: embassy_ha::TemperatureSensor<'static>) { | 67 | async fn random_humidity_task(mut sensor: embassy_ha::Sensor<'static>) { |
| 62 | loop { | 68 | loop { |
| 63 | sensor.publish(rand::random_range(0.0..50.0)); | 69 | sensor.publish(rand::random_range(0.0..100.0)); |
| 64 | Timer::after_secs(1).await; | 70 | Timer::after_secs(1).await; |
| 65 | } | 71 | } |
| 66 | } | 72 | } |
diff --git a/src/entity.rs b/src/entity.rs index ac15921..ef2b9ad 100644 --- a/src/entity.rs +++ b/src/entity.rs | |||
| @@ -33,4 +33,5 @@ pub struct EntityConfig { | |||
| 33 | pub max: Option<f32>, | 33 | pub max: Option<f32>, |
| 34 | pub step: Option<f32>, | 34 | pub step: Option<f32>, |
| 35 | pub mode: Option<&'static str>, | 35 | pub mode: Option<&'static str>, |
| 36 | pub suggested_display_precision: Option<u8>, | ||
| 36 | } | 37 | } |
diff --git a/src/entity_sensor.rs b/src/entity_sensor.rs index 702c9de..1a99754 100644 --- a/src/entity_sensor.rs +++ b/src/entity_sensor.rs | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | use crate::{ | 1 | use crate::{ |
| 2 | Entity, EntityCommonConfig, EntityConfig, NumericSensorState, TemperatureUnit, constants, | 2 | Entity, EntityCommonConfig, EntityConfig, NumericSensorState, constants, |
| 3 | }; | 3 | }; |
| 4 | 4 | ||
| 5 | #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] | 5 | #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] |
| @@ -144,36 +144,41 @@ impl SensorClass { | |||
| 144 | } | 144 | } |
| 145 | 145 | ||
| 146 | #[derive(Debug, Default)] | 146 | #[derive(Debug, Default)] |
| 147 | pub struct TemperatureSensorConfig { | 147 | pub struct SensorConfig { |
| 148 | pub common: EntityCommonConfig, | 148 | pub common: EntityCommonConfig, |
| 149 | pub unit: TemperatureUnit, | 149 | pub class: SensorClass, |
| 150 | pub state_class: StateClass, | ||
| 151 | pub unit: Option<&'static str>, | ||
| 152 | pub suggested_display_precision: Option<u8>, | ||
| 150 | } | 153 | } |
| 151 | 154 | ||
| 152 | impl TemperatureSensorConfig { | 155 | impl SensorConfig { |
| 153 | pub(crate) fn populate(&self, config: &mut EntityConfig) { | 156 | pub(crate) fn populate(&self, config: &mut EntityConfig) { |
| 154 | self.common.populate(config); | 157 | self.common.populate(config); |
| 155 | config.domain = constants::HA_DOMAIN_SENSOR; | 158 | config.domain = constants::HA_DOMAIN_SENSOR; |
| 156 | config.device_class = Some(constants::HA_DEVICE_CLASS_SENSOR_TEMPERATURE); | 159 | config.device_class = self.class.as_str(); |
| 157 | config.measurement_unit = Some(self.unit.as_str()); | 160 | config.state_class = Some(self.state_class.as_str()); |
| 161 | config.measurement_unit = self.unit; | ||
| 162 | config.suggested_display_precision = self.suggested_display_precision; | ||
| 158 | } | 163 | } |
| 159 | } | 164 | } |
| 160 | 165 | ||
| 161 | pub struct TemperatureSensor<'a>(Entity<'a>); | 166 | pub struct Sensor<'a>(Entity<'a>); |
| 162 | 167 | ||
| 163 | impl<'a> TemperatureSensor<'a> { | 168 | impl<'a> Sensor<'a> { |
| 164 | pub(crate) fn new(entity: Entity<'a>) -> Self { | 169 | pub(crate) fn new(entity: Entity<'a>) -> Self { |
| 165 | Self(entity) | 170 | Self(entity) |
| 166 | } | 171 | } |
| 167 | 172 | ||
| 168 | pub fn publish(&mut self, temperature: f32) { | 173 | pub fn publish(&mut self, value: f32) { |
| 169 | let publish = self.0.with_data(|data| { | 174 | let publish = self.0.with_data(|data| { |
| 170 | let storage = data.storage.as_numeric_sensor_mut(); | 175 | let storage = data.storage.as_numeric_sensor_mut(); |
| 171 | let prev_state = storage.state.replace(NumericSensorState { | 176 | let prev_state = storage.state.replace(NumericSensorState { |
| 172 | value: temperature, | 177 | value, |
| 173 | timestamp: embassy_time::Instant::now(), | 178 | timestamp: embassy_time::Instant::now(), |
| 174 | }); | 179 | }); |
| 175 | match prev_state { | 180 | match prev_state { |
| 176 | Some(state) => state.value != temperature, | 181 | Some(state) => state.value != value, |
| 177 | None => true, | 182 | None => true, |
| 178 | } | 183 | } |
| 179 | }); | 184 | }); |
| @@ -115,6 +115,9 @@ struct EntityDiscovery<'a> { | |||
| 115 | #[serde(skip_serializing_if = "Option::is_none")] | 115 | #[serde(skip_serializing_if = "Option::is_none")] |
| 116 | mode: Option<&'a str>, | 116 | mode: Option<&'a str>, |
| 117 | 117 | ||
| 118 | #[serde(skip_serializing_if = "Option::is_none")] | ||
| 119 | suggested_display_precision: Option<u8>, | ||
| 120 | |||
| 118 | device: &'a DeviceDiscovery<'a>, | 121 | device: &'a DeviceDiscovery<'a>, |
| 119 | } | 122 | } |
| 120 | 123 | ||
| @@ -427,11 +430,11 @@ impl<'a> Device<'a> { | |||
| 427 | } | 430 | } |
| 428 | } | 431 | } |
| 429 | 432 | ||
| 430 | pub fn create_temperature_sensor( | 433 | pub fn create_sensor( |
| 431 | &self, | 434 | &self, |
| 432 | id: &'static str, | 435 | id: &'static str, |
| 433 | config: TemperatureSensorConfig, | 436 | config: SensorConfig, |
| 434 | ) -> TemperatureSensor<'a> { | 437 | ) -> Sensor<'a> { |
| 435 | let mut entity_config = EntityConfig::default(); | 438 | let mut entity_config = EntityConfig::default(); |
| 436 | entity_config.id = id; | 439 | entity_config.id = id; |
| 437 | config.populate(&mut entity_config); | 440 | config.populate(&mut entity_config); |
| @@ -440,7 +443,7 @@ impl<'a> Device<'a> { | |||
| 440 | entity_config, | 443 | entity_config, |
| 441 | EntityStorage::NumericSensor(Default::default()), | 444 | EntityStorage::NumericSensor(Default::default()), |
| 442 | ); | 445 | ); |
| 443 | TemperatureSensor::new(entity) | 446 | Sensor::new(entity) |
| 444 | } | 447 | } |
| 445 | 448 | ||
| 446 | pub fn create_button(&self, id: &'static str, config: ButtonConfig) -> Button<'a> { | 449 | pub fn create_button(&self, id: &'static str, config: ButtonConfig) -> Button<'a> { |
| @@ -573,6 +576,7 @@ impl<'a> Device<'a> { | |||
| 573 | max: entity_config.max, | 576 | max: entity_config.max, |
| 574 | step: entity_config.step, | 577 | step: entity_config.step, |
| 575 | mode: entity_config.mode, | 578 | mode: entity_config.mode, |
| 579 | suggested_display_precision: entity_config.suggested_display_precision, | ||
| 576 | device: &device_discovery, | 580 | device: &device_discovery, |
| 577 | }; | 581 | }; |
| 578 | crate::log::debug!( | 582 | crate::log::debug!( |
