mirror of
https://github.com/python-kasa/python-kasa.git
synced 2025-01-24 05:37:59 +00:00
Improve PIR Sensor Readings
- Fix: Read PIR as ADC value centered around ADC midpoint/zeropoint.
This commit is contained in:
parent
84aa74546e
commit
f296d941a1
@ -3,6 +3,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import math
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Literal, overload
|
from typing import Literal, overload
|
||||||
|
|
||||||
@ -84,6 +85,50 @@ class Motion(IotModule):
|
|||||||
attribute_setter="set_threshold",
|
attribute_setter="set_threshold",
|
||||||
type=Feature.Type.Number,
|
type=Feature.Type.Number,
|
||||||
category=Feature.Category.Config,
|
category=Feature.Category.Config,
|
||||||
|
range_getter=lambda: (0, 100),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self._add_feature(
|
||||||
|
Feature(
|
||||||
|
device=self._device,
|
||||||
|
container=self,
|
||||||
|
id="pir_triggered",
|
||||||
|
name="PIR Triggered",
|
||||||
|
icon="mdi:motion-sensor",
|
||||||
|
attribute_getter="pir_triggered",
|
||||||
|
attribute_setter=None,
|
||||||
|
type=Feature.Type.Sensor,
|
||||||
|
category=Feature.Category.Primary,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self._add_feature(
|
||||||
|
Feature(
|
||||||
|
device=self._device,
|
||||||
|
container=self,
|
||||||
|
id="pir_value",
|
||||||
|
name="PIR Reading",
|
||||||
|
icon="mdi:motion-sensor",
|
||||||
|
attribute_getter="pir_value",
|
||||||
|
attribute_setter=None,
|
||||||
|
type=Feature.Type.Sensor,
|
||||||
|
category=Feature.Category.Info,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self._add_feature(
|
||||||
|
Feature(
|
||||||
|
device=self._device,
|
||||||
|
container=self,
|
||||||
|
id="pir_percent",
|
||||||
|
name="PIR Percentage",
|
||||||
|
icon="mdi:motion-sensor",
|
||||||
|
attribute_getter="pir_percent",
|
||||||
|
attribute_setter=None,
|
||||||
|
type=Feature.Type.Sensor,
|
||||||
|
category=Feature.Category.Info,
|
||||||
|
unit_getter=lambda: "%",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -97,7 +142,7 @@ class Motion(IotModule):
|
|||||||
attribute_getter="adc_value",
|
attribute_getter="adc_value",
|
||||||
attribute_setter=None,
|
attribute_setter=None,
|
||||||
type=Feature.Type.Sensor,
|
type=Feature.Type.Sensor,
|
||||||
category=Feature.Category.Primary,
|
category=Feature.Category.Debug,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -105,13 +150,41 @@ class Motion(IotModule):
|
|||||||
Feature(
|
Feature(
|
||||||
device=self._device,
|
device=self._device,
|
||||||
container=self,
|
container=self,
|
||||||
id="pir_triggered",
|
id="pir_adc_min",
|
||||||
name="PIR Triggered",
|
name="PIR ADC Min",
|
||||||
icon="mdi:motion-sensor",
|
icon="mdi:motion-sensor",
|
||||||
attribute_getter="is_triggered",
|
attribute_getter="adc_min",
|
||||||
attribute_setter=None,
|
attribute_setter=None,
|
||||||
type=Feature.Type.Sensor,
|
type=Feature.Type.Sensor,
|
||||||
category=Feature.Category.Primary,
|
category=Feature.Category.Debug,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self._add_feature(
|
||||||
|
Feature(
|
||||||
|
device=self._device,
|
||||||
|
container=self,
|
||||||
|
id="pir_adc_mid",
|
||||||
|
name="PIR ADC Mid",
|
||||||
|
icon="mdi:motion-sensor",
|
||||||
|
attribute_getter="adc_midpoint",
|
||||||
|
attribute_setter=None,
|
||||||
|
type=Feature.Type.Sensor,
|
||||||
|
category=Feature.Category.Debug,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self._add_feature(
|
||||||
|
Feature(
|
||||||
|
device=self._device,
|
||||||
|
container=self,
|
||||||
|
id="pir_adc_max",
|
||||||
|
name="PIR ADC Max",
|
||||||
|
icon="mdi:motion-sensor",
|
||||||
|
attribute_getter="adc_max",
|
||||||
|
attribute_setter=None,
|
||||||
|
type=Feature.Type.Sensor,
|
||||||
|
category=Feature.Category.Debug,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -134,6 +207,28 @@ class Motion(IotModule):
|
|||||||
"""Return True if module is enabled."""
|
"""Return True if module is enabled."""
|
||||||
return bool(self.config["enable"])
|
return bool(self.config["enable"])
|
||||||
|
|
||||||
|
@property
|
||||||
|
def adc_min(self) -> int:
|
||||||
|
"""Return minimum ADC sensor value."""
|
||||||
|
return int(self.config["min_adc"])
|
||||||
|
|
||||||
|
@property
|
||||||
|
def adc_max(self) -> int:
|
||||||
|
"""Return maximum ADC sensor value."""
|
||||||
|
return int(self.config["max_adc"])
|
||||||
|
|
||||||
|
@property
|
||||||
|
def adc_midpoint(self) -> int:
|
||||||
|
"""
|
||||||
|
Return the midpoint for the ADC.
|
||||||
|
|
||||||
|
The midpoint represents the zero point for the PIR sensor waveform.
|
||||||
|
|
||||||
|
Currently this is estimated by:
|
||||||
|
math.floor(abs(adc_max - adc_min) / 2)
|
||||||
|
"""
|
||||||
|
return math.floor(abs(self.adc_max - self.adc_min) / 2)
|
||||||
|
|
||||||
async def set_enabled(self, state: bool) -> dict:
|
async def set_enabled(self, state: bool) -> dict:
|
||||||
"""Enable/disable PIR."""
|
"""Enable/disable PIR."""
|
||||||
return await self.call("set_enable", {"enable": int(state)})
|
return await self.call("set_enable", {"enable": int(state)})
|
||||||
@ -192,7 +287,7 @@ class Motion(IotModule):
|
|||||||
if value is not None:
|
if value is not None:
|
||||||
if range is not None and range is not Range.Custom:
|
if range is not None and range is not Range.Custom:
|
||||||
raise KasaException(
|
raise KasaException(
|
||||||
"Refusing to set non-custom range %s to value %d." % (range, value)
|
f"Refusing to set non-custom range {range} to value {value}."
|
||||||
)
|
)
|
||||||
elif value is None:
|
elif value is None:
|
||||||
raise KasaException("Custom range threshold may not be set to None.")
|
raise KasaException("Custom range threshold may not be set to None.")
|
||||||
@ -210,9 +305,7 @@ class Motion(IotModule):
|
|||||||
elif isinstance(input, int):
|
elif isinstance(input, int):
|
||||||
return await self.set_range(value=input)
|
return await self.set_range(value=input)
|
||||||
else:
|
else:
|
||||||
raise KasaException(
|
raise KasaException(f"Invalid type: {type(input)} given to cli motion set.")
|
||||||
"Invalid type: %s given to cli motion set." % (type(input))
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_range_threshold(self, range_type: Range) -> int:
|
def get_range_threshold(self, range_type: Range) -> int:
|
||||||
"""Get the distance threshold at which the PIR sensor is will trigger."""
|
"""Get the distance threshold at which the PIR sensor is will trigger."""
|
||||||
@ -247,9 +340,25 @@ class Motion(IotModule):
|
|||||||
@property
|
@property
|
||||||
def adc_value(self) -> int:
|
def adc_value(self) -> int:
|
||||||
"""Return motion adc value."""
|
"""Return motion adc value."""
|
||||||
return int(self.data["get_adc_value"]["value"])
|
return self.data["get_adc_value"]["value"]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_triggered(self) -> bool:
|
def pir_value(self) -> int:
|
||||||
|
"""Return the computed PIR sensor value."""
|
||||||
|
return self.adc_midpoint - self.adc_value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def pir_percent(self) -> float:
|
||||||
|
"""Return the computed PIR sensor value, in percentile form."""
|
||||||
|
amp = self.pir_value
|
||||||
|
per: float
|
||||||
|
if amp < 0:
|
||||||
|
per = (float(amp) / (self.adc_midpoint - self.adc_min)) * 100
|
||||||
|
else:
|
||||||
|
per = (float(amp) / (self.adc_max - self.adc_midpoint)) * 100
|
||||||
|
return per
|
||||||
|
|
||||||
|
@property
|
||||||
|
def pir_triggered(self) -> bool:
|
||||||
"""Return if the motion sensor has been triggered."""
|
"""Return if the motion sensor has been triggered."""
|
||||||
return (self.enabled) and (self.adc_value < self.threshold)
|
return (self.enabled) and (abs(self.pir_percent) > (100 - self.threshold))
|
||||||
|
Loading…
Reference in New Issue
Block a user