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#[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}