Improved consistency and robustness of sensors backend

This commit is contained in:
Fabio Manganiello 2020-01-08 22:58:24 +01:00
parent 9d592fe370
commit 35cefcc9f5
3 changed files with 87 additions and 54 deletions

View File

@ -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)

View File

@ -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')),
}

View File

@ -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