3
3
from __future__ import annotations
4
4
5
5
import logging
6
+ import math
6
7
from enum import Enum
7
8
from typing import Literal , overload
8
9
@@ -84,6 +85,50 @@ def _initialize_features(self) -> None:
84
85
attribute_setter = "set_threshold" ,
85
86
type = Feature .Type .Number ,
86
87
category = Feature .Category .Config ,
88
+ range_getter = lambda : (0 , 100 ),
89
+ )
90
+ )
91
+
92
+ self ._add_feature (
93
+ Feature (
94
+ device = self ._device ,
95
+ container = self ,
96
+ id = "pir_triggered" ,
97
+ name = "PIR Triggered" ,
98
+ icon = "mdi:motion-sensor" ,
99
+ attribute_getter = "pir_triggered" ,
100
+ attribute_setter = None ,
101
+ type = Feature .Type .Sensor ,
102
+ category = Feature .Category .Primary ,
103
+ )
104
+ )
105
+
106
+ self ._add_feature (
107
+ Feature (
108
+ device = self ._device ,
109
+ container = self ,
110
+ id = "pir_value" ,
111
+ name = "PIR Reading" ,
112
+ icon = "mdi:motion-sensor" ,
113
+ attribute_getter = "pir_value" ,
114
+ attribute_setter = None ,
115
+ type = Feature .Type .Sensor ,
116
+ category = Feature .Category .Info ,
117
+ )
118
+ )
119
+
120
+ self ._add_feature (
121
+ Feature (
122
+ device = self ._device ,
123
+ container = self ,
124
+ id = "pir_percent" ,
125
+ name = "PIR Percentage" ,
126
+ icon = "mdi:motion-sensor" ,
127
+ attribute_getter = "pir_percent" ,
128
+ attribute_setter = None ,
129
+ type = Feature .Type .Sensor ,
130
+ category = Feature .Category .Info ,
131
+ unit_getter = lambda : "%" ,
87
132
)
88
133
)
89
134
@@ -97,21 +142,49 @@ def _initialize_features(self) -> None:
97
142
attribute_getter = "adc_value" ,
98
143
attribute_setter = None ,
99
144
type = Feature .Type .Sensor ,
100
- category = Feature .Category .Primary ,
145
+ category = Feature .Category .Debug ,
101
146
)
102
147
)
103
148
104
149
self ._add_feature (
105
150
Feature (
106
151
device = self ._device ,
107
152
container = self ,
108
- id = "pir_triggered " ,
109
- name = "PIR Triggered " ,
153
+ id = "pir_adc_min " ,
154
+ name = "PIR ADC Min " ,
110
155
icon = "mdi:motion-sensor" ,
111
- attribute_getter = "is_triggered " ,
156
+ attribute_getter = "adc_min " ,
112
157
attribute_setter = None ,
113
158
type = Feature .Type .Sensor ,
114
- category = Feature .Category .Primary ,
159
+ category = Feature .Category .Debug ,
160
+ )
161
+ )
162
+
163
+ self ._add_feature (
164
+ Feature (
165
+ device = self ._device ,
166
+ container = self ,
167
+ id = "pir_adc_mid" ,
168
+ name = "PIR ADC Mid" ,
169
+ icon = "mdi:motion-sensor" ,
170
+ attribute_getter = "adc_midpoint" ,
171
+ attribute_setter = None ,
172
+ type = Feature .Type .Sensor ,
173
+ category = Feature .Category .Debug ,
174
+ )
175
+ )
176
+
177
+ self ._add_feature (
178
+ Feature (
179
+ device = self ._device ,
180
+ container = self ,
181
+ id = "pir_adc_max" ,
182
+ name = "PIR ADC Max" ,
183
+ icon = "mdi:motion-sensor" ,
184
+ attribute_getter = "adc_max" ,
185
+ attribute_setter = None ,
186
+ type = Feature .Type .Sensor ,
187
+ category = Feature .Category .Debug ,
115
188
)
116
189
)
117
190
@@ -134,6 +207,28 @@ def enabled(self) -> bool:
134
207
"""Return True if module is enabled."""
135
208
return bool (self .config ["enable" ])
136
209
210
+ @property
211
+ def adc_min (self ) -> int :
212
+ """Return minimum ADC sensor value."""
213
+ return int (self .config ["min_adc" ])
214
+
215
+ @property
216
+ def adc_max (self ) -> int :
217
+ """Return maximum ADC sensor value."""
218
+ return int (self .config ["max_adc" ])
219
+
220
+ @property
221
+ def adc_midpoint (self ) -> int :
222
+ """
223
+ Return the midpoint for the ADC.
224
+
225
+ The midpoint represents the zero point for the PIR sensor waveform.
226
+
227
+ Currently this is estimated by:
228
+ math.floor(abs(adc_max - adc_min) / 2)
229
+ """
230
+ return math .floor (abs (self .adc_max - self .adc_min ) / 2 )
231
+
137
232
async def set_enabled (self , state : bool ) -> dict :
138
233
"""Enable/disable PIR."""
139
234
return await self .call ("set_enable" , {"enable" : int (state )})
@@ -192,7 +287,7 @@ async def set_range(
192
287
if value is not None :
193
288
if range is not None and range is not Range .Custom :
194
289
raise KasaException (
195
- "Refusing to set non-custom range %s to value %d." % ( range , value )
290
+ f "Refusing to set non-custom range { range } to value { value } ."
196
291
)
197
292
elif value is None :
198
293
raise KasaException ("Custom range threshold may not be set to None." )
@@ -210,9 +305,7 @@ async def _set_range_cli(self, input: Range | int) -> dict:
210
305
elif isinstance (input , int ):
211
306
return await self .set_range (value = input )
212
307
else :
213
- raise KasaException (
214
- "Invalid type: %s given to cli motion set." % (type (input ))
215
- )
308
+ raise KasaException (f"Invalid type: { type (input )} given to cli motion set." )
216
309
217
310
def get_range_threshold (self , range_type : Range ) -> int :
218
311
"""Get the distance threshold at which the PIR sensor is will trigger."""
@@ -247,9 +340,25 @@ async def set_inactivity_timeout(self, timeout: int) -> dict:
247
340
@property
248
341
def adc_value (self ) -> int :
249
342
"""Return motion adc value."""
250
- return int (self .data ["get_adc_value" ]["value" ])
343
+ return self .data ["get_adc_value" ]["value" ]
344
+
345
+ @property
346
+ def pir_value (self ) -> int :
347
+ """Return the computed PIR sensor value."""
348
+ return self .adc_midpoint - self .adc_value
349
+
350
+ @property
351
+ def pir_percent (self ) -> float :
352
+ """Return the computed PIR sensor value, in percentile form."""
353
+ amp = self .pir_value
354
+ per : float
355
+ if amp < 0 :
356
+ per = (float (amp ) / (self .adc_midpoint - self .adc_min )) * 100
357
+ else :
358
+ per = (float (amp ) / (self .adc_max - self .adc_midpoint )) * 100
359
+ return per
251
360
252
361
@property
253
- def is_triggered (self ) -> bool :
362
+ def pir_triggered (self ) -> bool :
254
363
"""Return if the motion sensor has been triggered."""
255
- return (self .enabled ) and (self .adc_value < self .threshold )
364
+ return (self .enabled ) and (abs ( self .pir_percent ) > ( 100 - self .threshold ) )
0 commit comments