embassy_ha/
entity_number.rs

1use crate::{
2    CommandPolicy, Entity, EntityCommonConfig, EntityConfig, NumberCommand, NumberState,
3    NumberUnit, constants,
4};
5
6#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
7pub enum NumberMode {
8    #[default]
9    Auto,
10    Box,
11    Slider,
12}
13
14#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
15pub enum NumberClass {
16    #[default]
17    Generic,
18    ApparentPower,
19    Aqi,
20    AtmosphericPressure,
21    Battery,
22    CarbonDioxide,
23    CarbonMonoxide,
24    Current,
25    DataRate,
26    DataSize,
27    Distance,
28    Duration,
29    Energy,
30    Frequency,
31    Gas,
32    Humidity,
33    Illuminance,
34    Irradiance,
35    Moisture,
36    Monetary,
37    NitrogenDioxide,
38    NitrogenMonoxide,
39    NitrousOxide,
40    Ozone,
41    Ph,
42    Pm1,
43    Pm25,
44    Pm10,
45    PowerFactor,
46    Power,
47    Precipitation,
48    PrecipitationIntensity,
49    Pressure,
50    ReactivePower,
51    SignalStrength,
52    SoundPressure,
53    Speed,
54    SulphurDioxide,
55    Temperature,
56    VolatileOrganicCompounds,
57    VolatileOrganicCompoundsParts,
58    Voltage,
59    Volume,
60    Water,
61    Weight,
62    WindSpeed,
63}
64
65/// Configuration for a number entity.
66///
67/// See [`CommandPolicy`] for details on how commands are handled.
68#[derive(Debug)]
69pub struct NumberConfig {
70    pub common: EntityCommonConfig,
71    pub unit: Option<NumberUnit>,
72    pub min: Option<f32>,
73    pub max: Option<f32>,
74    pub step: Option<f32>,
75    pub mode: NumberMode,
76    pub class: NumberClass,
77    pub command_policy: CommandPolicy,
78}
79
80impl Default for NumberConfig {
81    fn default() -> Self {
82        Self {
83            common: EntityCommonConfig::default(),
84            unit: None,
85            min: None,
86            max: None,
87            step: None,
88            mode: NumberMode::Auto,
89            class: NumberClass::Generic,
90            command_policy: CommandPolicy::default(),
91        }
92    }
93}
94
95impl NumberConfig {
96    pub(crate) fn populate(&self, config: &mut EntityConfig) {
97        self.common.populate(config);
98        config.domain = constants::HA_DOMAIN_NUMBER;
99        config.mode = Some(match self.mode {
100            NumberMode::Auto => constants::HA_NUMBER_MODE_AUTO,
101            NumberMode::Box => constants::HA_NUMBER_MODE_BOX,
102            NumberMode::Slider => constants::HA_NUMBER_MODE_SLIDER,
103        });
104        config.device_class = match self.class {
105            NumberClass::Generic => None,
106            NumberClass::ApparentPower => Some(constants::HA_DEVICE_CLASS_NUMBER_APPARENT_POWER),
107            NumberClass::Aqi => Some(constants::HA_DEVICE_CLASS_NUMBER_AQI),
108            NumberClass::AtmosphericPressure => {
109                Some(constants::HA_DEVICE_CLASS_NUMBER_ATMOSPHERIC_PRESSURE)
110            }
111            NumberClass::Battery => Some(constants::HA_DEVICE_CLASS_NUMBER_BATTERY),
112            NumberClass::CarbonDioxide => Some(constants::HA_DEVICE_CLASS_NUMBER_CARBON_DIOXIDE),
113            NumberClass::CarbonMonoxide => Some(constants::HA_DEVICE_CLASS_NUMBER_CARBON_MONOXIDE),
114            NumberClass::Current => Some(constants::HA_DEVICE_CLASS_NUMBER_CURRENT),
115            NumberClass::DataRate => Some(constants::HA_DEVICE_CLASS_NUMBER_DATA_RATE),
116            NumberClass::DataSize => Some(constants::HA_DEVICE_CLASS_NUMBER_DATA_SIZE),
117            NumberClass::Distance => Some(constants::HA_DEVICE_CLASS_NUMBER_DISTANCE),
118            NumberClass::Duration => Some(constants::HA_DEVICE_CLASS_NUMBER_DURATION),
119            NumberClass::Energy => Some(constants::HA_DEVICE_CLASS_NUMBER_ENERGY),
120            NumberClass::Frequency => Some(constants::HA_DEVICE_CLASS_NUMBER_FREQUENCY),
121            NumberClass::Gas => Some(constants::HA_DEVICE_CLASS_NUMBER_GAS),
122            NumberClass::Humidity => Some(constants::HA_DEVICE_CLASS_NUMBER_HUMIDITY),
123            NumberClass::Illuminance => Some(constants::HA_DEVICE_CLASS_NUMBER_ILLUMINANCE),
124            NumberClass::Irradiance => Some(constants::HA_DEVICE_CLASS_NUMBER_IRRADIANCE),
125            NumberClass::Moisture => Some(constants::HA_DEVICE_CLASS_NUMBER_MOISTURE),
126            NumberClass::Monetary => Some(constants::HA_DEVICE_CLASS_NUMBER_MONETARY),
127            NumberClass::NitrogenDioxide => {
128                Some(constants::HA_DEVICE_CLASS_NUMBER_NITROGEN_DIOXIDE)
129            }
130            NumberClass::NitrogenMonoxide => {
131                Some(constants::HA_DEVICE_CLASS_NUMBER_NITROGEN_MONOXIDE)
132            }
133            NumberClass::NitrousOxide => Some(constants::HA_DEVICE_CLASS_NUMBER_NITROUS_OXIDE),
134            NumberClass::Ozone => Some(constants::HA_DEVICE_CLASS_NUMBER_OZONE),
135            NumberClass::Ph => Some(constants::HA_DEVICE_CLASS_NUMBER_PH),
136            NumberClass::Pm1 => Some(constants::HA_DEVICE_CLASS_NUMBER_PM1),
137            NumberClass::Pm25 => Some(constants::HA_DEVICE_CLASS_NUMBER_PM25),
138            NumberClass::Pm10 => Some(constants::HA_DEVICE_CLASS_NUMBER_PM10),
139            NumberClass::PowerFactor => Some(constants::HA_DEVICE_CLASS_NUMBER_POWER_FACTOR),
140            NumberClass::Power => Some(constants::HA_DEVICE_CLASS_NUMBER_POWER),
141            NumberClass::Precipitation => Some(constants::HA_DEVICE_CLASS_NUMBER_PRECIPITATION),
142            NumberClass::PrecipitationIntensity => {
143                Some(constants::HA_DEVICE_CLASS_NUMBER_PRECIPITATION_INTENSITY)
144            }
145            NumberClass::Pressure => Some(constants::HA_DEVICE_CLASS_NUMBER_PRESSURE),
146            NumberClass::ReactivePower => Some(constants::HA_DEVICE_CLASS_NUMBER_REACTIVE_POWER),
147            NumberClass::SignalStrength => Some(constants::HA_DEVICE_CLASS_NUMBER_SIGNAL_STRENGTH),
148            NumberClass::SoundPressure => Some(constants::HA_DEVICE_CLASS_NUMBER_SOUND_PRESSURE),
149            NumberClass::Speed => Some(constants::HA_DEVICE_CLASS_NUMBER_SPEED),
150            NumberClass::SulphurDioxide => Some(constants::HA_DEVICE_CLASS_NUMBER_SULPHUR_DIOXIDE),
151            NumberClass::Temperature => Some(constants::HA_DEVICE_CLASS_NUMBER_TEMPERATURE),
152            NumberClass::VolatileOrganicCompounds => {
153                Some(constants::HA_DEVICE_CLASS_NUMBER_VOLATILE_ORGANIC_COMPOUNDS)
154            }
155            NumberClass::VolatileOrganicCompoundsParts => {
156                Some(constants::HA_DEVICE_CLASS_NUMBER_VOLATILE_ORGANIC_COMPOUNDS_PARTS)
157            }
158            NumberClass::Voltage => Some(constants::HA_DEVICE_CLASS_NUMBER_VOLTAGE),
159            NumberClass::Volume => Some(constants::HA_DEVICE_CLASS_NUMBER_VOLUME),
160            NumberClass::Water => Some(constants::HA_DEVICE_CLASS_NUMBER_WATER),
161            NumberClass::Weight => Some(constants::HA_DEVICE_CLASS_NUMBER_WEIGHT),
162            NumberClass::WindSpeed => Some(constants::HA_DEVICE_CLASS_NUMBER_WIND_SPEED),
163        };
164        config.measurement_unit = self.unit.as_ref().map(|u| u.as_str());
165        config.min = self.min;
166        config.max = self.max;
167        config.step = self.step;
168    }
169}
170
171pub struct Number<'a>(Entity<'a>);
172
173impl<'a> Number<'a> {
174    pub(crate) fn new(entity: Entity<'a>) -> Self {
175        Self(entity)
176    }
177
178    pub fn state(&mut self) -> Option<f32> {
179        self.0.with_data(|data| {
180            let storage = data.storage.as_number_mut();
181            storage.state.as_ref().map(|s| s.value)
182        })
183    }
184
185    pub fn command(&self) -> Option<f32> {
186        self.0.with_data(|data| {
187            let storage = data.storage.as_number_mut();
188            storage.command.as_ref().map(|s| s.value)
189        })
190    }
191
192    pub async fn wait(&mut self) -> f32 {
193        loop {
194            self.0.wait_command().await;
195            match self.command() {
196                Some(value) => return value,
197                None => continue,
198            }
199        }
200    }
201
202    pub fn publish(&mut self, value: f32) {
203        let publish = self.0.with_data(|data| {
204            let storage = data.storage.as_number_mut();
205            let timestamp = embassy_time::Instant::now();
206            let publish = match &storage.command {
207                Some(command) => command.value != value,
208                None => true,
209            };
210            storage.state = Some(NumberState { value, timestamp });
211            storage.command = Some(NumberCommand { value, timestamp });
212            publish
213        });
214        if publish {
215            self.0.queue_publish();
216        }
217    }
218}