4
4
5
5
import logging
6
6
import math
7
+ from dataclasses import dataclass
7
8
from enum import Enum
8
9
9
10
from ...exceptions import KasaException
@@ -25,6 +26,64 @@ def __str__(self) -> str:
25
26
return self .name
26
27
27
28
29
+ @dataclass
30
+ class PIRConfig :
31
+ """Dataclass representing a PIR sensor configuration."""
32
+
33
+ enabled : bool
34
+ adc_min : int
35
+ adc_max : int
36
+ range : Range
37
+ threshold : int
38
+
39
+ @property
40
+ def adc_mid (self ) -> int :
41
+ """Compute the ADC midpoint from the configured ADC Max and Min values."""
42
+ return math .floor (abs (self .adc_max - self .adc_min ) / 2 )
43
+
44
+
45
+ @dataclass
46
+ class PIRStatus :
47
+ """Dataclass representing the current trigger state of an ADC PIR sensor."""
48
+
49
+ adc_value : int
50
+
51
+ def get_pir_value (self , config : PIRConfig ) -> int :
52
+ """
53
+ Get the PIR status value in integer form.
54
+
55
+ Computes the PIR status value that this object represents,
56
+ using the given PIR configuration.
57
+ """
58
+ return config .adc_mid - self .adc_value
59
+
60
+ def get_pir_percent (self , config : PIRConfig ) -> float :
61
+ """
62
+ Get the PIR status value in percentile form.
63
+
64
+ Computes the PIR status percentage that this object represents,
65
+ using the given PIR configuration.
66
+ """
67
+ value = self .get_pir_value (config )
68
+ divisor = (
69
+ (config .adc_mid - config .adc_min )
70
+ if (value < 0 )
71
+ else (config .adc_max - config .adc_mid )
72
+ )
73
+ return (float (value ) / divisor ) * 100
74
+
75
+ def get_pir_triggered (self , config : PIRConfig ) -> bool :
76
+ """
77
+ Get the PIR status trigger state.
78
+
79
+ Compute the PIR trigger state this object represents,
80
+ using the given PIR configuration.
81
+ """
82
+ return (config .enabled ) and (
83
+ abs (self .get_pir_percent (config )) > (100 - config .threshold )
84
+ )
85
+
57AE
86
+
28
87
class Motion (IotModule ):
29
88
"""Implements the motion detection (PIR) module."""
30
89
@@ -150,7 +209,7 @@ def _initialize_features(self) -> None:
150
209
id = "pir_adc_mid" ,
151
210
name = "PIR ADC Mid" ,
152
211
icon = "mdi:motion-sensor" ,
153
- attribute_getter = "adc_midpoint " ,
212
+ attribute_getter = "adc_mid " ,
154
213
attribute_setter = None ,
155
214
type = Feature .Type .Sensor ,
156
215
category = Feature .Category .Debug ,
@@ -200,23 +259,35 @@ def config(self) -> dict:
200
259
"""Return current configuration."""
201
260
return self .data ["get_config" ]
202
261
262
+ @property
263
+ def pir_config (self ) -> PIRConfig :
264
+ """Return PIR sensor configuration."""
265
+ pir_range = Range (self .config ["trigger_index" ])
266
+ return PIRConfig (
267
+ enabled = bool (self .config ["enable" ]),
268
+ adc_min = int (self .config ["min_adc" ]),
269
+ adc_max = int (self .config ["max_adc" ]),
270
+ range = pir_range ,
271
+ threshold = self .get_range_threshold (pir_range ),
272
+ )
273
+
203
274
@property
204
275
def enabled (self ) -> bool :
205
276
"""Return True if module is enabled."""
206
- return bool ( self .config [ "enable" ])
277
+ return self .pir_config . enabled
207
278
208
279
@property
209
280
def adc_min (self ) -> int :
210
281
"""Return minimum ADC sensor value."""
211
- return int ( self .config [ "min_adc" ])
282
+ return self .pir_config . adc_min
212
283
213
284
@property
214
285
def adc_max (self ) -> int :
215
286
"""Return maximum ADC sensor value."""
216
- return int ( self .config [ "max_adc" ])
287
+ return self .pir_config . adc_max
217
288
218
289
@property
219
- def adc_midpoint (self ) -> int :
290
+ def adc_mid (self ) -> int :
220
291
"""
221
292
Return the midpoint for the ADC.
222
293
@@ -225,7 +296,7 @@ def adc_midpoint(self) -> int:
225
296
Currently this is estimated by:
226
297
math.floor(abs(adc_max - adc_min) / 2)
227
298
"""
228
- return math . floor ( abs ( self .adc_max - self . adc_min ) / 2 )
299
+ return self .pir_config . adc_mid
229
300
230
301
async def set_enabled (self , state : bool ) -> dict :
231
302
"""Enable/disable PIR."""
@@ -245,7 +316,7 @@ def ranges(self) -> list[str]:
245
316
@property
246
317
def range (self ) -> Range :
247
318
"""Return motion detection Range."""
248
- return Range ( self .config [ "trigger_index" ])
319
+ return self .pir_config . range
249
320
250
321
async def set_range (self , range : Range ) -> dict :
251
322
"""Set the Range for the sensor.
@@ -280,7 +351,7 @@ def get_range_threshold(self, range_type: Range) -> int:
280
351
@property
281
352
def threshold (self ) -> int :
282
353
"""Return motion detection Range."""
283
- return self .get_range_threshold ( self . range )
354
+ return self .pir_config . threshold
284
355
285
356
async def set_threshold (self , value : int ) -> dict :
286
357
"""Set the distance threshold at which the PIR sensor is will trigger."""
@@ -300,28 +371,32 @@ async def set_inactivity_timeout(self, timeout: int) -> dict:
300
371
"""
301
372
return await self .call ("set_cold_time" , {"cold_time" : timeout })
302
373
374
+ @property
375
+ def pir_state (self ) -> PIRStatus :
376
+ """Return cached PIR status."""
377
+ return PIRStatus (self .data ["get_adc_value" ]["value" ])
378
+
379
+ async def get_pir_state (self ) -> PIRStatus :
380
+ """Return real-time PIR status."""
381
+ current = await self .call ("get_adc_value" )
382
+ return PIRStatus (current ["value" ])
383
+
303
384
@property
304
385
def adc_value (self ) -> int :
305
386
"""Return motion adc value."""
306
- return self .data [ "get_adc_value" ][ "value" ]
387
+ return self .pir_state . adc_value
307
388
308
389
@property
309
390
def pir_value (self ) -> int :
310
391
"""Return the computed PIR sensor value."""
311
- return self .adc_midpoint - self .adc_value
392
+ return self .pir_state . get_pir_value ( self .pir_config )
312
393
313
394
@property
314
395
def pir_percent (self ) -> float :
315
396
"""Return the computed PIR sensor value, in percentile form."""
316
- amp = self .pir_value
317
- per : float
318
- if amp < 0 :
319
- per = (float (amp ) / (self .adc_midpoint - self .adc_min )) * 100
320
- else :
321
- per = (float (amp ) / (self .adc_max - self .adc_midpoint )) * 100
322
- return per
397
+ return self .pir_state .get_pir_percent (self .pir_config )
323
398
324
399
@property
325
400
def pir_triggered (self ) -> bool :
326
401
"""Return if the motion sensor has been triggered."""
327
- return ( self .enabled ) and ( abs ( self . pir_percent ) > ( 100 - self .threshold ) )
402
+ return self .pir_state . get_pir_triggered ( self .pir_config )
0 commit comments