222
222
# Screen
223
223
"led_brightness" : {"siid" : 13 , "piid" : 2 },
224
224
# Device Display Unit
225
- "device-display-unit " : {"siid" : 14 , "piid" : 1 },
225
+ "device_display_unit " : {"siid" : 14 , "piid" : 1 },
226
226
}
227
227
228
228
# https://miot-spec.org/miot-spec-v2/instance?type=urn:miot-spec-v2:device:air-purifier:0000A007:zhimi-za1:2
258
258
"filter_rfid_tag" : {"siid" : 14 , "piid" : 1 },
259
259
"filter_rfid_product_id" : {"siid" : 14 , "piid" : 3 },
260
260
# Device Display Unit
261
- "device-display-unit " : {"siid" : 16 , "piid" : 1 },
261
+ "device_display_unit " : {"siid" : 16 , "piid" : 1 },
262
262
# Other
263
263
"gestures" : {"siid" : 15 , "piid" : 13 },
264
264
}
265
265
266
+ # https://home.miot-spec.com/spec/zhimi.airp.meb1
267
+ _MAPPING_MEB1 = {
268
+ # Air Purifier (siid=2)
269
+ "power" : {"siid" : 2 , "piid" : 1 },
270
+ "fault" : {"siid" : 2 , "piid" : 2 },
271
+ "mode" : {"siid" : 2 , "piid" : 4 },
272
+ "fan_level" : {"siid" : 2 , "piid" : 5 },
273
+ "plasma" : {"siid" : 2 , "piid" : 6 },
274
+ "uv" : {"siid" : 2 , "piid" : 7 },
275
+ # Environment (siid=3)
276
+ "pm2_5_density" : {"siid" : 3 , "piid" : 4 },
277
+ "pm10_density" : {"siid" : 3 , "piid" : 8 },
278
+ "aqi" : {"siid" : 3 , "piid" : 9 },
279
+ "humidity" : {"siid" : 3 , "piid" : 1 },
280
+ "temperature" : {"siid" : 3 , "piid" : 7 },
281
+ # Filter (siid=4)
282
+ "filter_life_remaining" : {"siid" : 4 , "piid" : 1 },
283
+ "filter_hours_used" : {"siid" : 4 , "piid" : 3 },
284
+ # Alarm (siid=6)
285
+ "buzzer" : {"siid" : 6 , "piid" : 1 },
286
+ "buzzer_volume" : {"siid" : 6 , "piid" : 2 },
287
+ # Physical Control Locked (siid=8)
288
+ "child_lock" : {"siid" : 8 , "piid" : 1 },
289
+ # Custom Service (siid=9)
290
+ "motor_speed" : {"siid" : 9 , "piid" : 1 },
291
+ "reboot_cause" : {"siid" : 9 , "piid" : 8 },
292
+ "country_code" : {"siid" : 9 , "piid" : 11 },
293
+ # AQI (siid=11)
294
+ "aqi_realtime_update_duration" : {"siid" : 11 , "piid" : 4 },
295
+ # RFID (siid=12)
296
+ "filter_rfid_tag" : {"siid" : 12 , "piid" : 1 },
297
+ "filter_rfid_product_id" : {"siid" : 12 , "piid" : 3 },
298
+ # Screen (siid=13)
299
+ "led_brightness" : {"siid" : 13 , "piid" : 2 },
300
+ # Device Display Unit (siid=15)
301
+ "temperature_display_unit" : {"siid" : 15 , "piid" : 1 },
302
+ }
303
+
266
304
267
305
_MAPPINGS = {
268
306
"zhimi.airpurifier.ma4" : _MAPPING , # airpurifier 3
281
319
"zhimi.airpurifier.rma2" : _MAPPING_RMA2 , # airpurifier 4 lite
282
320
"zhimi.airp.rmb1" : _MAPPING_RMB1 , # airpurifier 4 lite
283
321
"zhimi.airpurifier.za1" : _MAPPING_ZA1 , # smartmi air purifier
322
+ "zhimi.airp.meb1" : _MAPPING_MEB1 , # air purifier elite
284
323
}
285
324
286
325
# Models requiring reversed led brightness value
290
329
"zhimi.airp.mb5a" ,
291
330
"zhimi.airp.vb4" ,
292
331
"zhimi.airp.rmb1" ,
332
+ "zhimi.airp.meb1" ,
293
333
]
294
334
295
335
@@ -307,6 +347,14 @@ class LedBrightness(enum.Enum):
307
347
Off = 2
308
348
309
349
350
+ class FaultCode (enum .Enum ):
351
+ NO_FAULT = 0
352
+ SENSOR_PM_ERROR = 1
353
+ SENSOR_HUM_ERROR = 2
354
+ NO_FILTER = 4
355
+ UNKNOWN_ERROR = - 1
356
+
357
+
310
358
class AirPurifierMiotStatus (DeviceStatus ):
311
359
"""Container for status reports from the air purifier.
312
360
@@ -429,11 +477,66 @@ def temperature(self) -> Optional[float]:
429
477
return round (temperate , 1 ) if temperate is not None else None
430
478
431
479
@property
480
+ @sensor ("PM10 Density" , unit = "μg/m³" )
432
481
def pm10_density (self ) -> Optional [float ]:
433
- """Current temperature , if available."""
482
+ """Current PM10 density , if available."""
434
483
pm10_density = self .data .get ("pm10_density" )
435
484
return round (pm10_density , 1 ) if pm10_density is not None else None
436
485
486
+ @property
487
+ @sensor ("PM2.5 Density" , unit = "μg/m³" )
488
+ def pm25_density (self ) -> Optional [float ]:
489
+ """Return the PM2.5 density."""
490
+ return self .data .get ("pm2_5_density" )
491
+
492
+ @property
493
+ def is_plasma_on (self ) -> bool :
494
+ """Return True if plasma is on."""
495
+ return bool (self .data .get ("plasma" ))
496
+
497
+ @property
498
+ @setting ("Plasma" , setter_name = "set_plasma" )
499
+ def plasma (self ) -> str :
500
+ """Plasma state."""
501
+ return "on" if self .is_plasma_on else "off"
502
+
503
+ @property
504
+ def is_uv_on (self ) -> bool :
505
+ """Return True if UV is on."""
506
+ return bool (self .data .get ("uv" ))
507
+
508
+ @property
509
+ @setting ("UV" , setter_name = "set_uv" )
510
+ def uv (self ) -> str :
511
+ """UV state."""
512
+ return "on" if self .is_uv_on else "off"
513
+
514
+ @property
515
+ @sensor ("Fault Code" , unit = "" )
516
+ def fault (self ) -> Optional [FaultCode ]:
517
+ """Return fault code if any."""
518
+ fault_code = self .data .get ("fault" )
519
+ try :
520
+ return FaultCode (fault_code ) if fault_code is not None else None
521
+ except ValueError :
522
+ _LOGGER .warning ("Unknown fault code: %s" , fault_code )
523
+ return None
524
+
525
+ @property
526
+ def temperature_display_unit (self ) -> Optional [int ]:
527
+ """Return temperature display unit."""
528
+ return self .data .get ("temperature_display_unit" )
529
+
530
+ @property
531
+ def country_code (self ) -> Optional [int ]:
532
+ """Return country code."""
533
+ return self .data .get ("country_code" )
534
+
535
+ @property
536
+ def reboot_cause (self ) -> Optional [int ]:
537
+ """Return reboot cause."""
538
+ return self .data .get ("reboot_cause" )
539
+
437
540
@property
438
541
def fan_level (self ) -> Optional [int ]:
439
542
"""Current fan level."""
@@ -530,13 +633,19 @@ class AirPurifierMiot(MiotDevice):
530
633
default_output = format_output (
531
634
"" ,
532
635
"Power: {result.power}\n "
636
+ "Fault Code: {result.fault}\n "
637
+ "Fault Description: {result.fault_description}\n "
638
+ "Plasma: {result.plasma}\n "
639
+ "UV: {result.uv}\n "
533
640
"Anion: {result.anion}\n "
534
641
"AQI: {result.aqi} μg/m³\n "
535
642
"TVOC: {result.tvoc}\n "
536
643
"Average AQI: {result.average_aqi} μg/m³\n "
537
644
"Humidity: {result.humidity} %\n "
538
645
"Temperature: {result.temperature} °C\n "
646
+ "Temperature Unit: {result.temperature_display_unit}\n "
539
647
"PM10 Density: {result.pm10_density} μg/m³\n "
648
+ "PM2.5 Density: {result.pm25_density} μg/m³\n "
540
649
"Fan Level: {result.fan_level}\n "
541
650
"Mode: {result.mode}\n "
542
651
"LED: {result.led}\n "
@@ -555,7 +664,9 @@ class AirPurifierMiot(MiotDevice):
555
664
"Motor speed: {result.motor_speed} rpm\n "
556
665
"Filter RFID product id: {result.filter_rfid_product_id}\n "
557
666
"Filter RFID tag: {result.filter_rfid_tag}\n "
558
- "Filter type: {result.filter_type}\n " ,
667
+ "Filter type: {result.filter_type}\n "
668
+ "Country Code: {result.country_code}\n "
669
+ "Reboot Cause: {result.reboot_cause}\n " ,
559
670
)
560
671
)
561
672
def status (self ) -> AirPurifierMiotStatus :
@@ -764,3 +875,33 @@ def set_led_brightness_level(self, level: int):
764
875
raise ValueError ("Invalid brightness level: %s" % level )
765
876
766
877
return self .set_property ("led_brightness_level" , level )
878
+
879
+ @command (
880
+ click .argument ("plasma" , type = bool ),
881
+ default_output = format_output (
882
+ lambda plasma : "Turning on plasma" if plasma else "Turning off plasma"
883
+ ),
884
+ )
885
+ def set_plasma (self , plasma : bool ):
886
+ """Set plasma on/off."""
887
+ if "plasma" not in self ._get_mapping ():
888
+ raise UnsupportedFeatureException (
889
+ "Unsupported plasma for model '%s'" % self .model
890
+ )
891
+
892
+ return self .set_property ("plasma" , plasma )
893
+
894
+ @command (
895
+ click .argument ("uv" , type = bool ),
896
+ default_output = format_output (
897
+ lambda uv : "Turning on UV" if uv else "Turning off UV"
898
+ ),
899
+ )
900
+ def set_uv (self , uv : bool ):
901
+ """Set UV on/off."""
902
+ if "uv" not in self ._get_mapping ():
903
+ raise UnsupportedFeatureException (
904
+ "Unsupported UV for model '%s'" % self .model
905
+ )
906
+
907
+ return self .set_property ("uv" , uv )
0 commit comments