forked from platypush/platypush
Improved consistency and robustness of sensors backend
This commit is contained in:
parent
9d592fe370
commit
35cefcc9f5
3 changed files with 87 additions and 54 deletions
|
@ -94,6 +94,12 @@ class SensorBackend(Backend):
|
|||
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
def _get_value(value):
|
||||
if isinstance(value, float) or isinstance(value, int) or isinstance(value, bool):
|
||||
return value
|
||||
return float(value)
|
||||
|
||||
def get_new_data(self, new_data):
|
||||
if self.data is None or new_data is None:
|
||||
return new_data
|
||||
|
@ -101,11 +107,11 @@ class SensorBackend(Backend):
|
|||
# noinspection PyBroadException
|
||||
try:
|
||||
# Scalar data case
|
||||
new_data = float(new_data)
|
||||
new_data = self._get_value(new_data)
|
||||
return new_data if abs(new_data - self.data) >= self.tolerance else None
|
||||
except:
|
||||
# If it's not a scalar then it should be a dict
|
||||
assert isinstance(new_data, dict)
|
||||
assert isinstance(new_data, dict), 'Invalid type {} received for sensor data'.format(type(new_data))
|
||||
|
||||
ret = {}
|
||||
for k, v in new_data.items():
|
||||
|
@ -123,8 +129,8 @@ class SensorBackend(Backend):
|
|||
old_v = None
|
||||
|
||||
try:
|
||||
v = float(v)
|
||||
old_v = float(self.data.get(k))
|
||||
v = self._get_value(v)
|
||||
old_v = self._get_value(self.data.get(k))
|
||||
except (TypeError, ValueError):
|
||||
is_nan = True
|
||||
|
||||
|
@ -155,46 +161,49 @@ class SensorBackend(Backend):
|
|||
self.logger.info('Initialized {} sensor backend'.format(self.__class__.__name__))
|
||||
|
||||
while not self.should_stop():
|
||||
data = self.get_measurement()
|
||||
new_data = self.get_new_data(data)
|
||||
try:
|
||||
data = self.get_measurement()
|
||||
new_data = self.get_new_data(data)
|
||||
|
||||
if new_data:
|
||||
self.bus.post(SensorDataChangeEvent(data=new_data))
|
||||
if new_data:
|
||||
self.bus.post(SensorDataChangeEvent(data=new_data))
|
||||
|
||||
data_below_threshold = {}
|
||||
data_above_threshold = {}
|
||||
data_below_threshold = {}
|
||||
data_above_threshold = {}
|
||||
|
||||
if self.thresholds:
|
||||
if isinstance(self.thresholds, dict) and isinstance(data, dict):
|
||||
for (measure, thresholds) in self.thresholds.items():
|
||||
if measure not in data:
|
||||
continue
|
||||
if self.thresholds:
|
||||
if isinstance(self.thresholds, dict) and isinstance(data, dict):
|
||||
for (measure, thresholds) in self.thresholds.items():
|
||||
if measure not in data:
|
||||
continue
|
||||
|
||||
if not isinstance(thresholds, list):
|
||||
thresholds = [thresholds]
|
||||
if not isinstance(thresholds, list):
|
||||
thresholds = [thresholds]
|
||||
|
||||
for threshold in thresholds:
|
||||
if data[measure] > threshold and (self.data is None or (
|
||||
measure in self.data and self.data[measure] <= threshold)):
|
||||
data_above_threshold[measure] = data[measure]
|
||||
elif data[measure] < threshold and (self.data is None or (
|
||||
measure in self.data and self.data[measure] >= threshold)):
|
||||
data_below_threshold[measure] = data[measure]
|
||||
for threshold in thresholds:
|
||||
if data[measure] > threshold and (self.data is None or (
|
||||
measure in self.data and self.data[measure] <= threshold)):
|
||||
data_above_threshold[measure] = data[measure]
|
||||
elif data[measure] < threshold and (self.data is None or (
|
||||
measure in self.data and self.data[measure] >= threshold)):
|
||||
data_below_threshold[measure] = data[measure]
|
||||
|
||||
if data_below_threshold:
|
||||
self.bus.post(SensorDataBelowThresholdEvent(data=data_below_threshold))
|
||||
if data_below_threshold:
|
||||
self.bus.post(SensorDataBelowThresholdEvent(data=data_below_threshold))
|
||||
|
||||
if data_above_threshold:
|
||||
self.bus.post(SensorDataAboveThresholdEvent(data=data_above_threshold))
|
||||
if data_above_threshold:
|
||||
self.bus.post(SensorDataAboveThresholdEvent(data=data_above_threshold))
|
||||
|
||||
self.data = data
|
||||
self.data = data
|
||||
|
||||
if new_data:
|
||||
if isinstance(new_data, dict):
|
||||
for k, v in new_data.items():
|
||||
self.data[k] = v
|
||||
else:
|
||||
self.data = new_data
|
||||
if new_data:
|
||||
if isinstance(new_data, dict):
|
||||
for k, v in new_data.items():
|
||||
self.data[k] = v
|
||||
else:
|
||||
self.data = new_data
|
||||
except Exception as e:
|
||||
self.logger.exception(e)
|
||||
|
||||
if self.poll_seconds:
|
||||
time.sleep(self.poll_seconds)
|
||||
|
|
|
@ -8,7 +8,6 @@ class SensorBatteryBackend(SensorBackend):
|
|||
The sensor events triggered by this backend will include any of the following fields:
|
||||
|
||||
- ``battery_percent``
|
||||
- ``battery_secs_left``
|
||||
- ``battery_power_plugged``
|
||||
|
||||
Requires:
|
||||
|
@ -21,9 +20,11 @@ class SensorBatteryBackend(SensorBackend):
|
|||
|
||||
def get_measurement(self):
|
||||
plugin = get_plugin('system')
|
||||
battery = plugin.sensors_battery().output
|
||||
|
||||
return {
|
||||
'battery_' + name: value
|
||||
for name, value in plugin.sensors_battery().output.items()
|
||||
'battery_percent': battery.get('percent'),
|
||||
'battery_power_plugged': bool(battery.get('power_plugged')),
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import socket
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Union, List, Optional
|
||||
from typing import Union, List, Optional, Dict
|
||||
|
||||
from platypush.message.response.system import CpuInfoResponse, CpuTimesResponse, CpuResponseList, CpuStatsResponse, \
|
||||
CpuFrequencyResponse, VirtualMemoryUsageResponse, SwapMemoryUsageResponse, DiskResponseList, \
|
||||
|
@ -468,38 +468,61 @@ class SystemPlugin(Plugin):
|
|||
|
||||
# noinspection DuplicatedCode
|
||||
@action
|
||||
def sensors_temperature(self, sensor: Optional[str] = None, fahrenheit: bool = False) -> SensorResponseList:
|
||||
def sensors_temperature(self, sensor: Optional[str] = None, fahrenheit: bool = False) \
|
||||
-> Union[SensorTemperatureResponse, List[SensorTemperatureResponse],
|
||||
Dict[str, Union[SensorTemperatureResponse, List[SensorTemperatureResponse]]]]:
|
||||
"""
|
||||
Get stats from the temperature sensors.
|
||||
|
||||
:param sensor: Select the sensor name.
|
||||
:param fahrenheit: Return the temperature in Fahrenheit (default: Celsius).
|
||||
:return: List of :class:`platypush.message.response.system.SensorTemperatureResponse`.
|
||||
"""
|
||||
import psutil
|
||||
stats = psutil.sensors_temperatures(fahrenheit=fahrenheit)
|
||||
|
||||
def _expand_stats(name, _stats):
|
||||
return SensorResponseList([
|
||||
if sensor:
|
||||
stats = [addr for name, addr in stats.items() if name == sensor]
|
||||
assert stats, 'No such sensor name: {}'.format(sensor)
|
||||
if len(stats) == 1:
|
||||
return SensorTemperatureResponse(
|
||||
name=sensor,
|
||||
current=stats[0].current,
|
||||
high=stats[0].high,
|
||||
critical=stats[0].critical,
|
||||
label=stats[0].label,
|
||||
)
|
||||
|
||||
return [
|
||||
SensorTemperatureResponse(
|
||||
name=name,
|
||||
name=sensor,
|
||||
current=s.current,
|
||||
high=s.high,
|
||||
critical=s.critical,
|
||||
label=s.label,
|
||||
)
|
||||
for s in _stats
|
||||
])
|
||||
for s in stats
|
||||
]
|
||||
|
||||
if sensor:
|
||||
stats = [addr for name, addr in stats.items() if name == sensor]
|
||||
assert stats, 'No such sensor name: {}'.format(sensor)
|
||||
return _expand_stats(sensor, stats[0])
|
||||
ret = {}
|
||||
for name, data in stats.items():
|
||||
for stat in data:
|
||||
resp = SensorTemperatureResponse(
|
||||
name=sensor,
|
||||
current=stat.current,
|
||||
high=stat.high,
|
||||
critical=stat.critical,
|
||||
label=stat.label,
|
||||
).output
|
||||
|
||||
return SensorResponseList([
|
||||
_expand_stats(name, stat)
|
||||
for name, stat in stats.items()
|
||||
])
|
||||
if name not in ret:
|
||||
ret[name] = resp
|
||||
else:
|
||||
if isinstance(ret[name], list):
|
||||
ret[name].append(resp)
|
||||
else:
|
||||
ret[name] = [ret[name], resp]
|
||||
|
||||
return ret
|
||||
|
||||
# noinspection DuplicatedCode
|
||||
@action
|
||||
|
|
Loading…
Reference in a new issue