1use crate::{deserialize, ElPacket, Properties};
2use core::fmt::{self, Formatter};
3pub use property_maps::*;
4use serde::{de::Visitor, ser::SerializeTuple, Deserialize, Serialize};
5
6mod property_maps;
7
8pub enum ClassPacket {
10 Unimplemented(UnimplementedPacket),
12 SolarPower(SolarPowerPacket),
14 StorageBattery(StorageBatteryPacket),
16 Evps(EvpsPacket),
18 Hp(HpPacket),
20 SmartMeter(SmartMeterPacket),
22 AirConditioner(AirConditionerPacket),
24 Metering(MeteringPacket),
26 FuelCell(FuelCellPacket),
28 InstantaneousWaterHeater(InstantaneousWaterHeaterPacket),
30 GeneralLighting(GeneralLightingPacket),
32 MonoFunctionLighting(MonoFunctionLightingPacket),
34 LightingSystem(LightingSystemPacket),
36 Profile(ProfilePacket),
38 Controller(ControllerPacket),
40}
41
42impl ClassPacket {
43 pub fn new(eoj: EchonetObject, props: Properties) -> ClassPacket {
44 match eoj.class {
45 ClassCode(code::HOUSEHOLD_SOLAR_POWER) => {
46 ClassPacket::SolarPower(SolarPowerPacket(props))
47 }
48 ClassCode(code::STORAGE_BATTERY) => {
49 ClassPacket::StorageBattery(StorageBatteryPacket(props))
50 }
51 ClassCode(code::EVPS) => ClassPacket::Evps(EvpsPacket(props)),
52 ClassCode(code::HP) => ClassPacket::Hp(HpPacket(props)),
53 ClassCode(code::SMART_METER) => ClassPacket::SmartMeter(SmartMeterPacket(props)),
54 ClassCode(code::HOME_AIR_CONDITIONER) => {
55 ClassPacket::AirConditioner(AirConditionerPacket(props))
56 }
57 ClassCode(code::POWER_DISTRIBUTION_BOARD_METERING) => {
58 ClassPacket::Metering(MeteringPacket(props))
59 }
60 ClassCode(code::FUEL_CELL) => ClassPacket::FuelCell(FuelCellPacket(props)),
61 ClassCode(code::INSTANTANEOUS_WATER_HEATER) => {
62 ClassPacket::InstantaneousWaterHeater(InstantaneousWaterHeaterPacket(props))
63 }
64 ClassCode(code::GENERAL_LIGHTING) => {
65 ClassPacket::GeneralLighting(GeneralLightingPacket(props))
66 }
67 ClassCode(code::MONO_FUNCTION_LIGHTING) => {
68 ClassPacket::MonoFunctionLighting(MonoFunctionLightingPacket(props))
69 }
70 ClassCode(code::LIGHTING_SYSTEM) => {
71 ClassPacket::LightingSystem(LightingSystemPacket(props))
72 }
73 ClassCode(code::PROFILE) => ClassPacket::Profile(ProfilePacket(props)),
74 ClassCode(code::CONTROLLER) => ClassPacket::Controller(ControllerPacket(props)),
75 _ => ClassPacket::Unimplemented(UnimplementedPacket(eoj.class, props)),
76 }
77 }
78
79 pub fn properties(&self) -> &Properties {
81 match self {
82 Self::Unimplemented(p) => p.properties(),
83 Self::SolarPower(p) => p.properties(),
84 Self::StorageBattery(p) => p.properties(),
85 Self::Evps(p) => p.properties(),
86 Self::Hp(p) => p.properties(),
87 Self::SmartMeter(p) => p.properties(),
88 Self::AirConditioner(p) => p.properties(),
89 Self::Metering(p) => p.properties(),
90 Self::FuelCell(p) => p.properties(),
91 Self::InstantaneousWaterHeater(p) => p.properties(),
92 Self::GeneralLighting(p) => p.properties(),
93 Self::MonoFunctionLighting(p) => p.properties(),
94 Self::LightingSystem(p) => p.properties(),
95 Self::Profile(p) => p.properties(),
96 Self::Controller(p) => p.properties(),
97 }
98 }
99}
100
101impl From<ElPacket> for ClassPacket {
102 fn from(value: ElPacket) -> Self {
103 match value.seoj.class {
104 ClassCode(code::HOUSEHOLD_SOLAR_POWER) => ClassPacket::SolarPower(value.into()),
105 ClassCode(code::STORAGE_BATTERY) => ClassPacket::StorageBattery(value.into()),
106 ClassCode(code::EVPS) => ClassPacket::Evps(value.into()),
107 ClassCode(code::HP) => ClassPacket::Hp(value.into()),
108 ClassCode(code::SMART_METER) => ClassPacket::SmartMeter(value.into()),
109 ClassCode(code::HOME_AIR_CONDITIONER) => ClassPacket::AirConditioner(value.into()),
110 ClassCode(code::POWER_DISTRIBUTION_BOARD_METERING) => {
111 ClassPacket::Metering(value.into())
112 }
113 ClassCode(code::FUEL_CELL) => ClassPacket::FuelCell(value.into()),
114 ClassCode(code::INSTANTANEOUS_WATER_HEATER) => {
115 ClassPacket::InstantaneousWaterHeater(value.into())
116 }
117 ClassCode(code::GENERAL_LIGHTING) => ClassPacket::GeneralLighting(value.into()),
118 ClassCode(code::MONO_FUNCTION_LIGHTING) => {
119 ClassPacket::MonoFunctionLighting(value.into())
120 }
121 ClassCode(code::LIGHTING_SYSTEM) => ClassPacket::LightingSystem(value.into()),
122 ClassCode(code::PROFILE) => ClassPacket::Profile(value.into()),
123 ClassCode(code::CONTROLLER) => ClassPacket::Controller(value.into()),
124 _ => ClassPacket::Unimplemented(value.into()),
125 }
126 }
127}
128
129impl fmt::Display for ClassPacket {
130 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131 match self {
132 ClassPacket::SolarPower(v) => write!(f, "{v}")?,
133 ClassPacket::StorageBattery(v) => write!(f, "{v}")?,
134 ClassPacket::Evps(v) => write!(f, "{v}")?,
135 ClassPacket::Hp(v) => write!(f, "{v}")?,
136 ClassPacket::SmartMeter(v) => write!(f, "{v}")?,
137 ClassPacket::AirConditioner(v) => write!(f, "{v}")?,
138 ClassPacket::Metering(v) => write!(f, "{v}")?,
139 ClassPacket::FuelCell(v) => write!(f, "{v}")?,
140 ClassPacket::InstantaneousWaterHeater(v) => write!(f, "{v}")?,
141 ClassPacket::GeneralLighting(v) => write!(f, "{v}")?,
142 ClassPacket::MonoFunctionLighting(v) => write!(f, "{v}")?,
143 ClassPacket::LightingSystem(v) => write!(f, "{v}")?,
144 ClassPacket::Profile(v) => write!(f, "{v}")?,
145 ClassPacket::Controller(v) => write!(f, "{v}")?,
146 ClassPacket::Unimplemented(v) => write!(f, "{v}")?,
147 }
148 Ok(())
149 }
150}
151
152pub mod code {
153 pub const HOME_AIR_CONDITIONER: [u8; 2] = [0x01, 0x30];
154 pub const INSTANTANEOUS_WATER_HEATER: [u8; 2] = [0x02, 0x72];
155 pub const HOUSEHOLD_SOLAR_POWER: [u8; 2] = [0x02, 0x79];
156 pub const FUEL_CELL: [u8; 2] = [0x02, 0x7C];
157 pub const STORAGE_BATTERY: [u8; 2] = [0x02, 0x7D];
158 pub const EVPS: [u8; 2] = [0x02, 0x7E];
159 pub const HP: [u8; 2] = [0x02, 0x6B];
160 pub const POWER_DISTRIBUTION_BOARD_METERING: [u8; 2] = [0x02, 0x87];
161 pub const SMART_METER: [u8; 2] = [0x02, 0x88];
162 pub const GENERAL_LIGHTING: [u8; 2] = [0x02, 0x90];
163 pub const MONO_FUNCTION_LIGHTING: [u8; 2] = [0x02, 0x91];
164 pub const LIGHTING_SYSTEM: [u8; 2] = [0x02, 0xA3];
165 pub const CONTROLLER: [u8; 2] = [0x05, 0xFF];
166 pub const PROFILE: [u8; 2] = [0x0E, 0xF0];
167}
168
169#[derive(Debug, Clone, Copy, PartialEq, Default, Serialize, Deserialize)]
170struct ClassCode([u8; 2]);
171
172impl From<[u8; 2]> for ClassCode {
173 fn from(value: [u8; 2]) -> Self {
174 Self(value)
175 }
176}
177
178impl fmt::Display for ClassCode {
179 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
180 write!(f, "{:02X} {:02X}", self.0[0], self.0[1])
181 }
182}
183
184pub struct UnimplementedPacket(ClassCode, Properties);
185
186impl UnimplementedPacket {
187 pub fn properties(&self) -> &Properties {
188 &self.1
189 }
190}
191
192impl fmt::Display for UnimplementedPacket {
193 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
194 writeln!(f, "Unimplemented Class: {}", self.0)?;
195 for prop in self.1.iter() {
196 if let Some(name) = SUPER_CLASS.get(&prop.epc) {
197 writeln!(f, "{prop}\t\t[{name}]")?;
198 continue;
199 }
200 writeln!(f, "{prop}\t\t[unknown]")?;
201 }
202 Ok(())
203 }
204}
205
206impl From<ElPacket> for UnimplementedPacket {
207 fn from(value: ElPacket) -> Self {
208 UnimplementedPacket(value.seoj.class, value.props)
209 }
210}
211
212macro_rules! convert_packet {
213 ( $code:expr, $ty:ty, $class:expr, $class_desc:expr) => {
214 impl $ty {
215 #[allow(dead_code)]
216 const CODE: [u8; 2] = $code;
217
218 pub fn properties(&self) -> &Properties {
220 &self.0
221 }
222 }
223
224 impl fmt::Display for $ty {
225 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
226 writeln!(
227 f,
228 "{}: 0x{:02X}{:02X}",
229 $class_desc,
230 Self::CODE[0],
231 Self::CODE[1]
232 )?;
233 for prop in self.0.iter() {
234 if let Some(name) = SUPER_CLASS.get(&prop.epc) {
235 writeln!(f, "{prop}\t\t[{name}]")?;
236 continue;
237 }
238 if let Some(name) = $class.get(&prop.epc) {
239 writeln!(f, "{prop}\t\t[{name}]")?;
240 continue;
241 }
242 writeln!(f, "{prop}\t\t[unknown]")?;
243 }
244 Ok(())
245 }
246 }
247
248 impl From<ElPacket> for $ty {
249 fn from(value: ElPacket) -> Self {
250 if value.seoj.class != ClassCode(Self::CODE) {
251 panic!("invalid source object class.")
252 }
253 Self(value.props)
254 }
255 }
256 };
257}
258
259pub struct SmartMeterPacket(Properties);
260convert_packet!(
261 code::SMART_METER,
262 SmartMeterPacket,
263 SMART_METER_CLASS,
264 "Smart Meter"
265);
266
267pub struct SolarPowerPacket(Properties);
268convert_packet!(
269 code::HOUSEHOLD_SOLAR_POWER,
270 SolarPowerPacket,
271 HOUSEHOLD_SOLAR_POWER_CLASS,
272 "House Hold Solar Power"
273);
274pub struct StorageBatteryPacket(Properties);
275convert_packet!(
276 code::STORAGE_BATTERY,
277 StorageBatteryPacket,
278 STORAGE_BATTERY_CLASS,
279 "Storage Battery"
280);
281
282pub struct EvpsPacket(Properties);
283convert_packet!(code::EVPS, EvpsPacket, EVPS_CLASS, "EVPS");
284
285pub struct HpPacket(Properties);
286convert_packet!(code::HP, HpPacket, HP_CLASS, "HP");
287
288pub struct AirConditionerPacket(Properties);
289convert_packet!(
290 code::HOME_AIR_CONDITIONER,
291 AirConditionerPacket,
292 HOME_AIR_CONDITIONER_CLASS,
293 "Home Air Conditioner"
294);
295
296pub struct MeteringPacket(Properties);
297convert_packet!(
298 code::POWER_DISTRIBUTION_BOARD_METERING,
299 MeteringPacket,
300 POWER_DISTRIBUTION_BOARD_METERING_CLASS,
301 "Power Distribution Board Metering"
302);
303
304pub struct FuelCellPacket(Properties);
305convert_packet!(
306 code::FUEL_CELL,
307 FuelCellPacket,
308 FUEL_CELL_CLASS,
309 "Fuel Cell"
310);
311
312pub struct InstantaneousWaterHeaterPacket(Properties);
313convert_packet!(
314 code::INSTANTANEOUS_WATER_HEATER,
315 InstantaneousWaterHeaterPacket,
316 INSTANTANEOUS_WATER_HEATER_CLASS,
317 "Instantaneous Water Heater"
318);
319
320pub struct GeneralLightingPacket(Properties);
321convert_packet!(
322 code::GENERAL_LIGHTING,
323 GeneralLightingPacket,
324 GENERAL_LIGHTING_CLASS,
325 "General Lighting"
326);
327
328pub struct MonoFunctionLightingPacket(Properties);
329convert_packet!(
330 code::MONO_FUNCTION_LIGHTING,
331 MonoFunctionLightingPacket,
332 MONO_FUNCTION_LIGHTING_CLASS,
333 "Mono Function Lighting"
334);
335
336pub struct LightingSystemPacket(Properties);
337convert_packet!(
338 code::LIGHTING_SYSTEM,
339 LightingSystemPacket,
340 LIGHTING_SYSTEM_CLASS,
341 "Lighting System"
342);
343
344pub struct ProfilePacket(Properties);
345convert_packet!(code::PROFILE, ProfilePacket, PROFILE_CLASS, "Node Profile");
346
347pub struct ControllerPacket(Properties);
348convert_packet!(
349 code::CONTROLLER,
350 ControllerPacket,
351 CONTROLLER_CLASS,
352 "Controller"
353);
354
355pub struct Controller;
356impl Controller {
357 #[allow(dead_code)]
358 const CODE: [u8; 2] = code::CONTROLLER;
359}
360
361impl fmt::Display for Controller {
362 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
363 write!(
364 f,
365 "Controller: 0x{:02X}{:02X}",
366 Self::CODE[0],
367 Self::CODE[1]
368 )
369 }
370}
371
372#[derive(Debug, Clone, Copy, PartialEq, Default, Serialize, Deserialize)]
379pub struct EchonetObject {
380 class: ClassCode,
382 instance: u8,
383}
384
385impl From<[u8; 3]> for EchonetObject {
386 fn from(eobj: [u8; 3]) -> Self {
387 Self {
388 class: ClassCode([eobj[0], eobj[1]]),
389 instance: eobj[2],
390 }
391 }
392}
393
394impl From<(ElClass, u8)> for EchonetObject {
395 fn from(value: (ElClass, u8)) -> Self {
396 let (class, instance) = value;
397 use code::*;
398 match class {
399 ElClass::HomeAC => Self {
400 class: HOME_AIR_CONDITIONER.into(),
401 instance,
402 },
403 ElClass::Hp => Self {
404 class: HP.into(),
405 instance,
406 },
407 ElClass::InstantaneousWaterHeater => Self {
408 class: INSTANTANEOUS_WATER_HEATER.into(),
409 instance,
410 },
411 ElClass::Pv => Self {
412 class: HOUSEHOLD_SOLAR_POWER.into(),
413 instance,
414 },
415 ElClass::FuelCell => Self {
416 class: FUEL_CELL.into(),
417 instance,
418 },
419 ElClass::Battery => Self {
420 class: STORAGE_BATTERY.into(),
421 instance,
422 },
423 ElClass::Evps => Self {
424 class: EVPS.into(),
425 instance,
426 },
427 ElClass::Metering => Self {
428 class: POWER_DISTRIBUTION_BOARD_METERING.into(),
429 instance,
430 },
431 ElClass::SmartMeter => Self {
432 class: SMART_METER.into(),
433 instance,
434 },
435 ElClass::MultiInputPCS => Self {
436 class: [0x02, 0xA5].into(),
437 instance,
438 },
439 ElClass::GeneralLighting => Self {
440 class: GENERAL_LIGHTING.into(),
441 instance,
442 },
443 ElClass::MonoFunctionLighting => Self {
444 class: MONO_FUNCTION_LIGHTING.into(),
445 instance,
446 },
447 ElClass::LightingSystem => Self {
448 class: LIGHTING_SYSTEM.into(),
449 instance,
450 },
451 ElClass::Controller => Self {
452 class: CONTROLLER.into(),
453 instance,
454 },
455 ElClass::Profile => Self {
456 class: PROFILE.into(),
457 instance,
458 },
459 ElClass::Unknown(code) => Self {
460 class: code.into(),
461 instance,
462 },
463 }
464 }
465}
466
467impl TryFrom<&[u8]> for EchonetObject {
468 type Error = crate::error::Error;
469
470 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
471 let (_, eobj) = deserialize(value)?;
472 Ok(eobj)
473 }
474}
475
476impl fmt::Display for EchonetObject {
477 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
478 use code::*;
479 let class = match self.class.0 {
480 HOME_AIR_CONDITIONER => "Home AC",
481 HP => "Heat pump",
482 INSTANTANEOUS_WATER_HEATER => "Instantaneous water heater",
483 HOUSEHOLD_SOLAR_POWER => "Household solar power",
484 FUEL_CELL => "Fuel cell",
485 STORAGE_BATTERY => "Storage battery",
486 EVPS => "V2H",
487 POWER_DISTRIBUTION_BOARD_METERING => "Power distribution Metering",
488 SMART_METER => "Smart meter",
489 GENERAL_LIGHTING => "General lighting",
490 MONO_FUNCTION_LIGHTING => "Mono function lighting",
491 LIGHTING_SYSTEM => "Lighting system",
492 CONTROLLER => "Controller",
493 PROFILE => "Profile",
494 _ => "Unknown",
495 };
496 write!(f, "{} [{} {:02X}]", class, self.class, self.instance)
497 }
498}
499
500#[derive(Debug, PartialEq, Eq, Copy, Clone)]
502pub enum ElClass {
503 HomeAC,
504 Hp,
505 InstantaneousWaterHeater,
506 Pv,
507 FuelCell,
508 Battery,
509 Evps,
510 Metering,
511 SmartMeter,
512 MultiInputPCS,
513 GeneralLighting,
514 MonoFunctionLighting,
515 LightingSystem,
516 Controller,
517 Profile,
518 Unknown([u8; 2]),
519}
520
521impl Serialize for ElClass {
522 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
523 where
524 S: serde::Serializer,
525 {
526 let raw = Into::<[u8; 2]>::into(*self);
527 let mut seq = serializer.serialize_tuple(2)?;
528 seq.serialize_element(&raw[0])?;
529 seq.serialize_element(&raw[1])?;
530 seq.end()
531 }
532}
533
534struct ElClassVisitor;
535impl<'de> Visitor<'de> for ElClassVisitor {
536 type Value = (u8, u8);
537
538 fn expecting(&self, formatter: &mut Formatter) -> Result<(), fmt::Error> {
539 formatter.write_str("never failed")
540 }
541
542 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
543 where
544 A: serde::de::SeqAccess<'de>,
545 {
546 let group: u8 = seq.next_element()?.unwrap();
547 let class: u8 = seq.next_element()?.unwrap();
548 Ok((group, class))
549 }
550}
551
552impl<'de> Deserialize<'de> for ElClass {
553 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
554 where
555 D: serde::Deserializer<'de>,
556 {
557 let (group, class) = deserializer.deserialize_tuple(2, ElClassVisitor)?;
558 Ok(ElClass::from(&[group, class]))
559 }
560}
561
562impl From<&[u8; 2]> for ElClass {
563 fn from(value: &[u8; 2]) -> Self {
564 use code::*;
565 use ElClass::*;
566 match *value {
567 HOME_AIR_CONDITIONER => HomeAC,
568 HP => Hp,
569 INSTANTANEOUS_WATER_HEATER => InstantaneousWaterHeater,
570 HOUSEHOLD_SOLAR_POWER => Pv,
571 FUEL_CELL => FuelCell,
572 STORAGE_BATTERY => Battery,
573 EVPS => Evps,
574 POWER_DISTRIBUTION_BOARD_METERING => Metering,
575 SMART_METER => SmartMeter,
576 [0x02, 0xA5] => MultiInputPCS,
577 GENERAL_LIGHTING => GeneralLighting,
578 MONO_FUNCTION_LIGHTING => MonoFunctionLighting,
579 LIGHTING_SYSTEM => LightingSystem,
580 CONTROLLER => Controller,
581 PROFILE => Profile,
582 _ => Unknown(*value),
583 }
584 }
585}
586
587#[allow(clippy::from_over_into)]
588impl Into<[u8; 2]> for ElClass {
589 fn into(self) -> [u8; 2] {
590 use code::*;
591 use ElClass::*;
592 match self {
593 HomeAC => HOME_AIR_CONDITIONER,
594 Hp => HP,
595 InstantaneousWaterHeater => INSTANTANEOUS_WATER_HEATER,
596 Pv => HOUSEHOLD_SOLAR_POWER,
597 FuelCell => FUEL_CELL,
598 Battery => STORAGE_BATTERY,
599 Evps => EVPS,
600 Metering => POWER_DISTRIBUTION_BOARD_METERING,
601 SmartMeter => SMART_METER,
602 MultiInputPCS => [0x02, 0xA5],
603 GeneralLighting => GENERAL_LIGHTING,
604 MonoFunctionLighting => MONO_FUNCTION_LIGHTING,
605 LightingSystem => LIGHTING_SYSTEM,
606 Controller => CONTROLLER,
607 Profile => PROFILE,
608 Unknown(raw) => raw,
609 }
610 }
611}
612
613impl From<EchonetObject> for ElClass {
614 fn from(value: EchonetObject) -> Self {
615 use code::*;
616 use ElClass::*;
617 match value.class.0 {
618 HOME_AIR_CONDITIONER => HomeAC,
619 HP => Hp,
620 INSTANTANEOUS_WATER_HEATER => InstantaneousWaterHeater,
621 HOUSEHOLD_SOLAR_POWER => Pv,
622 FUEL_CELL => FuelCell,
623 STORAGE_BATTERY => Battery,
624 EVPS => Evps,
625 POWER_DISTRIBUTION_BOARD_METERING => Metering,
626 SMART_METER => SmartMeter,
627 [0x02, 0xA5] => MultiInputPCS,
628 GENERAL_LIGHTING => GeneralLighting,
629 MONO_FUNCTION_LIGHTING => MonoFunctionLighting,
630 LIGHTING_SYSTEM => LightingSystem,
631 CONTROLLER => Controller,
632 PROFILE => Profile,
633 _ => Unknown(value.class.0),
634 }
635 }
636}
637
638#[cfg(test)]
639mod test {
640 use super::*;
641
642 #[test]
643 fn to_elclass() {
644 let eobj = EchonetObject::from([0x01, 0x30, 0x01]);
645 let class = ElClass::from(eobj);
646 assert_eq!(class, ElClass::HomeAC);
647
648 let raw = [0x01u8, 0x30u8];
649 let class = ElClass::from(&raw);
650 assert_eq!(class, ElClass::HomeAC);
651 }
652
653 #[test]
654 fn to_echonet_object() {
655 let class = ElClass::HomeAC;
656 let eobj: EchonetObject = (class, 1u8).into();
657 assert_eq!(
658 eobj,
659 EchonetObject {
660 class: ClassCode([0x01, 0x30]),
661 instance: 1
662 }
663 );
664 }
665
666 #[test]
667 fn serialize_el_class() {
668 let class = ElClass::HomeAC;
669 let bytes = crate::ser::serialize(&class).unwrap();
670 assert_eq!(bytes, vec![0x01, 0x30]);
671 }
672
673 #[test]
674 fn deserialize_el_class() {
675 let input = [0x01, 0x30];
676 let (bytes_read, class): (usize, ElClass) = deserialize(&input).unwrap();
677 assert_eq!(bytes_read, 2);
678 assert_eq!(class, ElClass::HomeAC);
679 }
680}