1010MODEL_FAN_P9 = "dmaker.fan.p9"
1111MODEL_FAN_P10 = "dmaker.fan.p10"
1212MODEL_FAN_P11 = "dmaker.fan.p11"
13+ MODEL_FAN_1C = "dmaker.fan.1c"
1314
1415MIOT_MAPPING = {
16+ MODEL_FAN_1C : {
17+ # https://miot-spec.org/miot-spec-v2/instance?type=urn:miot-spec-v2:device:fan:0000A005:dmaker-1c:1
18+ "power" : {"siid" : 2 , "piid" : 1 },
19+ "fan_level" : {"siid" : 2 , "piid" : 2 },
20+ "child_lock" : {"siid" : 3 , "piid" : 1 },
21+ "swing_mode" : {"siid" : 2 , "piid" : 3 },
22+ "power_off_time" : {"siid" : 2 , "piid" : 10 },
23+ "buzzer" : {"siid" : 2 , "piid" : 11 },
24+ "light" : {"siid" : 2 , "piid" : 12 },
25+ "mode" : {"siid" : 2 , "piid" : 7 },
26+ },
1527 MODEL_FAN_P9 : {
1628 # Source https://miot-spec.org/miot-spec-v2/instance?type=urn:miot-spec-v2:device:fan:0000A005:dmaker-p9:1
1729 "power" : {"siid" : 2 , "piid" : 1 },
@@ -128,7 +140,7 @@ def angle(self) -> int:
128140
129141 @property
130142 def delay_off_countdown (self ) -> int :
131- """Countdown until turning off in seconds ."""
143+ """Countdown until turning off in minutes ."""
132144 return self .data ["power_off_time" ]
133145
134146 @property
@@ -147,6 +159,76 @@ def child_lock(self) -> bool:
147159 return self .data ["child_lock" ]
148160
149161
162+ class FanStatus1C (DeviceStatus ):
163+ """Container for status reports for Xiaomi Mi Smart Pedestal Fan DMaker 1C."""
164+
165+ def __init__ (self , data : Dict [str , Any ]) -> None :
166+ self .data = data
167+ """
168+ Response of a Fan1C (dmaker.fan.1c):
169+
170+ {
171+ 'id': 1,
172+ 'result': [
173+ {'did': 'power', 'siid': 2, 'piid': 1, 'code': 0, 'value': True},
174+ {'did': 'fan_level', 'siid': 2, 'piid': 2, 'code': 0, 'value': 2},
175+ {'did': 'child_lock', 'siid': 3, 'piid': 1, 'code': 0, 'value': False},
176+ {'did': 'swing_mode', 'siid': 2, 'piid': 3, 'code': 0, 'value': False},
177+ {'did': 'power_off_time', 'siid': 2, 'piid': 10, 'code': 0, 'value': 0},
178+ {'did': 'buzzer', 'siid': 2, 'piid': 11, 'code': 0, 'value': False},
179+ {'did': 'light', 'siid': 2, 'piid': 12, 'code': 0, 'value': True},
180+ {'did': 'mode', 'siid': 2, 'piid': 7, 'code': 0, 'value': 0},
181+ ],
182+ 'exe_time': 280
183+ }
184+ """
185+
186+ @property
187+ def power (self ) -> str :
188+ """Power state."""
189+ return "on" if self .data ["power" ] else "off"
190+
191+ @property
192+ def is_on (self ) -> bool :
193+ """True if device is currently on."""
194+ return self .data ["power" ]
195+
196+ @property
197+ def mode (self ) -> OperationMode :
198+ """Operation mode."""
199+ return OperationMode [OperationModeMiot (self .data ["mode" ]).name ]
200+
201+ @property
202+ def speed (self ) -> int :
203+ """Speed of the motor."""
204+ return self .data ["fan_level" ]
205+
206+ @property
207+ def oscillate (self ) -> bool :
208+ """True if oscillation is enabled."""
209+ return self .data ["swing_mode" ]
210+
211+ @property
212+ def delay_off_countdown (self ) -> int :
213+ """Countdown until turning off in minutes."""
214+ return self .data ["power_off_time" ]
215+
216+ @property
217+ def led (self ) -> bool :
218+ """True if LED is turned on."""
219+ return self .data ["light" ]
220+
221+ @property
222+ def buzzer (self ) -> bool :
223+ """True if buzzer is turned on."""
224+ return self .data ["buzzer" ]
225+
226+ @property
227+ def child_lock (self ) -> bool :
228+ """True if child lock is on."""
229+ return self .data ["child_lock" ]
230+
231+
150232class FanMiot (MiotDevice ):
151233 mapping = MIOT_MAPPING [MODEL_FAN_P10 ]
152234
@@ -289,7 +371,7 @@ def set_child_lock(self, lock: bool):
289371 def delay_off (self , minutes : int ):
290372 """Set delay off minutes."""
291373
292- if minutes < 0 :
374+ if minutes < 0 or minutes > 480 :
293375 raise FanException ("Invalid value for a delayed turn off: %s" % minutes )
294376
295377 return self .set_property ("power_off_time" , minutes )
@@ -312,3 +394,124 @@ class FanP10(FanMiot):
312394
313395class FanP11 (FanMiot ):
314396 mapping = MIOT_MAPPING [MODEL_FAN_P11 ]
397+
398+
399+ class Fan1C (MiotDevice ):
400+ mapping = MIOT_MAPPING [MODEL_FAN_1C ]
401+
402+ def __init__ (
403+ self ,
404+ ip : str = None ,
405+ token : str = None ,
406+ start_id : int = 0 ,
407+ debug : int = 0 ,
408+ lazy_discover : bool = True ,
409+ model : str = MODEL_FAN_1C ,
410+ ) -> None :
411+ super ().__init__ (ip , token , start_id , debug , lazy_discover )
412+ self .model = model
413+
414+ @command (
415+ default_output = format_output (
416+ "" ,
417+ "Power: {result.power}\n "
418+ "Operation mode: {result.mode}\n "
419+ "Speed: {result.speed}\n "
420+ "Oscillate: {result.oscillate}\n "
421+ "LED: {result.led}\n "
422+ "Buzzer: {result.buzzer}\n "
423+ "Child lock: {result.child_lock}\n "
424+ "Power-off time: {result.delay_off_countdown}\n " ,
425+ )
426+ )
427+ def status (self ) -> FanStatus1C :
428+ """Retrieve properties."""
429+ return FanStatus1C (
430+ {
431+ prop ["did" ]: prop ["value" ] if prop ["code" ] == 0 else None
432+ for prop in self .get_properties_for_mapping ()
433+ }
434+ )
435+
436+ @command (default_output = format_output ("Powering on" ))
437+ def on (self ):
438+ """Power on."""
439+ return self .set_property ("power" , True )
440+
441+ @command (default_output = format_output ("Powering off" ))
442+ def off (self ):
443+ """Power off."""
444+ return self .set_property ("power" , False )
445+
446+ @command (
447+ click .argument ("mode" , type = EnumType (OperationMode )),
448+ default_output = format_output ("Setting mode to '{mode.value}'" ),
449+ )
450+ def set_mode (self , mode : OperationMode ):
451+ """Set mode."""
452+ return self .set_property ("mode" , OperationModeMiot [mode .name ].value )
453+
454+ @command (
455+ click .argument ("speed" , type = int ),
456+ default_output = format_output ("Setting speed to {speed}" ),
457+ )
458+ def set_speed (self , speed : int ):
459+ """Set speed."""
460+ if speed not in (1 , 2 , 3 ):
461+ raise FanException ("Invalid speed: %s" % speed )
462+
463+ return self .set_property ("fan_level" , speed )
464+
465+ @command (
466+ click .argument ("oscillate" , type = bool ),
467+ default_output = format_output (
468+ lambda oscillate : "Turning on oscillate"
469+ if oscillate
470+ else "Turning off oscillate"
471+ ),
472+ )
473+ def set_oscillate (self , oscillate : bool ):
474+ """Set oscillate on/off."""
475+ return self .set_property ("swing_mode" , oscillate )
476+
477+ @command (
478+ click .argument ("led" , type = bool ),
479+ default_output = format_output (
480+ lambda led : "Turning on LED" if led else "Turning off LED"
481+ ),
482+ )
483+ def set_led (self , led : bool ):
484+ """Turn led on/off."""
485+ return self .set_property ("light" , led )
486+
487+ @command (
488+ click .argument ("buzzer" , type = bool ),
489+ default_output = format_output (
490+ lambda buzzer : "Turning on buzzer" if buzzer else "Turning off buzzer"
491+ ),
492+ )
493+ def set_buzzer (self , buzzer : bool ):
494+ """Set buzzer on/off."""
495+ return self .set_property ("buzzer" , buzzer )
496+
497+ @command (
498+ click .argument ("lock" , type = bool ),
499+ default_output = format_output (
500+ lambda lock : "Turning on child lock" if lock else "Turning off child lock"
501+ ),
502+ )
503+ def set_child_lock (self , lock : bool ):
504+ """Set child lock on/off."""
505+ return self .set_property ("child_lock" , lock )
506+
507+ @command (
508+ click .argument ("minutes" , type = int ),
509+ default_output = format_output ("Setting delayed turn off to {minutes} minutes" ),
510+ )
511+ def delay_off (self , minutes : int ):
512+ """Set delay off minutes."""
513+
514+ if minutes < 0 or minutes > 480 :
515+ raise FanException ("Invalid value for a delayed turn off: %s" % minutes )
516+
517+ return self .set_property ("power_off_time" , minutes )
0 commit comments