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