forked from platypush/platypush
Added support for system temperature sensor entities.
This commit is contained in:
parent
1b048e1952
commit
45d5f439be
11 changed files with 178 additions and 119 deletions
|
@ -0,0 +1 @@
|
|||
TemperatureSensor.vue
|
|
@ -39,14 +39,6 @@
|
|||
}
|
||||
},
|
||||
|
||||
"cpu": {
|
||||
"name": "System",
|
||||
"name_plural": "System",
|
||||
"icon": {
|
||||
"class": "fas fa-microchip"
|
||||
}
|
||||
},
|
||||
|
||||
"memory_stats": {
|
||||
"name": "System",
|
||||
"name_plural": "System",
|
||||
|
@ -79,6 +71,14 @@
|
|||
}
|
||||
},
|
||||
|
||||
"system_temperature": {
|
||||
"name": "System",
|
||||
"name_plural": "System",
|
||||
"icon": {
|
||||
"class": "fas fa-temperature-half"
|
||||
}
|
||||
},
|
||||
|
||||
"current_sensor": {
|
||||
"name": "Sensor",
|
||||
"name_plural": "Sensors",
|
||||
|
@ -87,6 +87,14 @@
|
|||
}
|
||||
},
|
||||
|
||||
"cpu": {
|
||||
"name": "System",
|
||||
"name_plural": "System",
|
||||
"icon": {
|
||||
"class": "fas fa-microchip"
|
||||
}
|
||||
},
|
||||
|
||||
"motion_sensor": {
|
||||
"name": "Sensor",
|
||||
"name_plural": "Sensors",
|
||||
|
|
|
@ -4,6 +4,7 @@ from platypush.common.db import Base
|
|||
|
||||
from . import Entity
|
||||
from .devices import Device
|
||||
from .temperature import TemperatureSensor
|
||||
|
||||
|
||||
if 'cpu' not in Base.metadata:
|
||||
|
@ -208,3 +209,26 @@ if 'network_interface' not in Base.metadata:
|
|||
__mapper_args__ = {
|
||||
'polymorphic_identity': __tablename__,
|
||||
}
|
||||
|
||||
|
||||
if 'system_temperature' not in Base.metadata:
|
||||
|
||||
class SystemTemperature(TemperatureSensor):
|
||||
"""
|
||||
Extends the ``TemperatureSensor``.
|
||||
"""
|
||||
|
||||
__tablename__ = 'system_temperature'
|
||||
|
||||
id = Column(
|
||||
Integer,
|
||||
ForeignKey(TemperatureSensor.id, ondelete='CASCADE'),
|
||||
primary_key=True,
|
||||
)
|
||||
|
||||
high = Column(Float)
|
||||
critical = Column(Float)
|
||||
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity': __tablename__,
|
||||
}
|
||||
|
|
|
@ -8,50 +8,10 @@ class SystemResponse(Response):
|
|||
pass
|
||||
|
||||
|
||||
class CpuResponse(SystemResponse):
|
||||
pass
|
||||
|
||||
|
||||
class MemoryResponse(SystemResponse):
|
||||
pass
|
||||
|
||||
|
||||
class DiskResponse(SystemResponse):
|
||||
pass
|
||||
|
||||
|
||||
class NetworkResponse(SystemResponse):
|
||||
pass
|
||||
|
||||
|
||||
class SensorResponse(SystemResponse):
|
||||
pass
|
||||
|
||||
|
||||
class SensorTemperatureResponse(SensorResponse):
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
current: float,
|
||||
high: Optional[float] = None,
|
||||
critical: Optional[float] = None,
|
||||
label: Optional[str] = None,
|
||||
*args,
|
||||
**kwargs
|
||||
):
|
||||
super().__init__(
|
||||
*args,
|
||||
output={
|
||||
'name': name,
|
||||
'current': current,
|
||||
'high': high,
|
||||
'critical': critical,
|
||||
'label': label,
|
||||
},
|
||||
**kwargs
|
||||
)
|
||||
|
||||
|
||||
class SensorFanResponse(SensorResponse):
|
||||
def __init__(
|
||||
self, name: str, current: int, label: Optional[str] = None, *args, **kwargs
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import os
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Tuple, Union, List, Optional, Dict
|
||||
from typing import Tuple, Union, List, Optional
|
||||
from typing_extensions import override
|
||||
|
||||
import psutil
|
||||
|
@ -19,9 +19,9 @@ from platypush.entities.system import (
|
|||
MemoryStats as MemoryStatsModel,
|
||||
NetworkInterface as NetworkInterfaceModel,
|
||||
SwapStats as SwapStatsModel,
|
||||
SystemTemperature,
|
||||
)
|
||||
from platypush.message.response.system import (
|
||||
SensorTemperatureResponse,
|
||||
SensorResponseList,
|
||||
SensorFanResponse,
|
||||
SensorBatteryResponse,
|
||||
|
@ -51,6 +51,8 @@ from platypush.schemas.system import (
|
|||
SwapStats,
|
||||
SwapStatsSchema,
|
||||
SystemInfoSchema,
|
||||
Temperature,
|
||||
TemperatureSchema,
|
||||
)
|
||||
|
||||
|
||||
|
@ -145,9 +147,9 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
|||
return list(percent) # type: ignore
|
||||
return percent
|
||||
|
||||
def _cpu_stats(self) -> CpuStats:
|
||||
stats = psutil.cpu_stats()
|
||||
return CpuStatsSchema().load(stats) # type: ignore
|
||||
@staticmethod
|
||||
def _cpu_stats() -> CpuStats:
|
||||
return CpuStatsSchema().load(psutil.cpu_stats()) # type: ignore
|
||||
|
||||
@action
|
||||
def cpu_stats(self) -> CpuStats:
|
||||
|
@ -158,13 +160,13 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
|||
"""
|
||||
return CpuStatsSchema().dump(self._cpu_stats()) # type: ignore
|
||||
|
||||
def _cpu_frequency_avg(self) -> CpuFrequency:
|
||||
freq = psutil.cpu_freq(percpu=False)
|
||||
return CpuFrequencySchema().load(freq) # type: ignore
|
||||
@staticmethod
|
||||
def _cpu_frequency_avg() -> CpuFrequency:
|
||||
return CpuFrequencySchema().load(psutil.cpu_freq(percpu=False)) # type: ignore
|
||||
|
||||
def _cpu_frequency_per_cpu(self) -> List[CpuFrequency]:
|
||||
freq = psutil.cpu_freq(percpu=True)
|
||||
return CpuFrequencySchema().load(freq, many=True) # type: ignore
|
||||
@staticmethod
|
||||
def _cpu_frequency_per_cpu() -> List[CpuFrequency]:
|
||||
return CpuFrequencySchema().load(psutil.cpu_freq(percpu=True), many=True) # type: ignore
|
||||
|
||||
@action
|
||||
def cpu_frequency(
|
||||
|
@ -193,7 +195,8 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
|||
"""
|
||||
return psutil.getloadavg()
|
||||
|
||||
def _mem_virtual(self) -> MemoryStats:
|
||||
@staticmethod
|
||||
def _mem_virtual() -> MemoryStats:
|
||||
return MemoryStatsSchema().load(psutil.virtual_memory()) # type: ignore
|
||||
|
||||
@action
|
||||
|
@ -205,7 +208,8 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
|||
"""
|
||||
return MemoryStatsSchema().dump(self._mem_virtual()) # type: ignore
|
||||
|
||||
def _mem_swap(self) -> SwapStats:
|
||||
@staticmethod
|
||||
def _mem_swap() -> SwapStats:
|
||||
return SwapStatsSchema().load(psutil.swap_memory()) # type: ignore
|
||||
|
||||
@action
|
||||
|
@ -217,7 +221,8 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
|||
"""
|
||||
return SwapStatsSchema().dump(self._mem_swap()) # type: ignore
|
||||
|
||||
def _disk_info(self) -> List[Disk]:
|
||||
@staticmethod
|
||||
def _disk_info() -> List[Disk]:
|
||||
parts = {part.device: part._asdict() for part in psutil.disk_partitions()}
|
||||
basename_parts = {os.path.basename(part): part for part in parts}
|
||||
io_stats = {
|
||||
|
@ -252,7 +257,8 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
|||
"""
|
||||
return DiskSchema().dump(self._disk_info(), many=True)
|
||||
|
||||
def _network_info(self) -> List[NetworkInterface]:
|
||||
@staticmethod
|
||||
def _network_info() -> List[NetworkInterface]:
|
||||
addrs = psutil.net_if_addrs()
|
||||
stats = psutil.net_if_stats()
|
||||
|
||||
|
@ -270,7 +276,8 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
|||
many=True,
|
||||
)
|
||||
|
||||
def _network_info_avg(self) -> NetworkInterface:
|
||||
@staticmethod
|
||||
def _network_info_avg() -> NetworkInterface:
|
||||
stats = psutil.net_io_counters(pernic=False)
|
||||
return NetworkInterfaceSchema().load( # type: ignore
|
||||
{
|
||||
|
@ -332,65 +339,29 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
|||
many=True,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _sensors_temperature() -> List[Temperature]:
|
||||
return TemperatureSchema().load( # type: ignore
|
||||
[
|
||||
{
|
||||
**sensor._asdict(),
|
||||
'id': f'{kind}_{i + 1}',
|
||||
'label': (f'{kind} #{i + 1}' if not sensor.label else sensor.label),
|
||||
}
|
||||
for kind, sensors in psutil.sensors_temperatures().items()
|
||||
for i, sensor in enumerate(sensors)
|
||||
],
|
||||
many=True,
|
||||
)
|
||||
|
||||
@action
|
||||
def sensors_temperature(
|
||||
self, sensor: Optional[str] = None, fahrenheit: bool = False
|
||||
) -> Union[
|
||||
SensorTemperatureResponse,
|
||||
List[SensorTemperatureResponse],
|
||||
Dict[str, Union[SensorTemperatureResponse, List[SensorTemperatureResponse]]],
|
||||
]:
|
||||
def sensors_temperature(self) -> List[dict]:
|
||||
"""
|
||||
Get stats from the temperature sensors.
|
||||
|
||||
:param sensor: Select the sensor name.
|
||||
:param fahrenheit: Return the temperature in Fahrenheit (default: Celsius).
|
||||
:return: .. schema:: system.TemperatureSchema(many=True)
|
||||
"""
|
||||
stats = psutil.sensors_temperatures(fahrenheit=fahrenheit)
|
||||
|
||||
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=sensor,
|
||||
current=s.current,
|
||||
high=s.high,
|
||||
critical=s.critical,
|
||||
label=s.label,
|
||||
)
|
||||
for s in stats
|
||||
]
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
return TemperatureSchema().dump(self._sensors_temperature(), many=True)
|
||||
|
||||
@action
|
||||
def sensors_fan(self, sensor: Optional[str] = None) -> SensorResponseList:
|
||||
|
@ -588,6 +559,7 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
|||
'swap': self._mem_swap(),
|
||||
'disks': self._disk_info(),
|
||||
'network': self._network_info(),
|
||||
'temperature': self._sensors_temperature(),
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -679,7 +651,16 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
|||
reachable=nic.pop('is_up'),
|
||||
**nic,
|
||||
)
|
||||
for nic in entities['network']
|
||||
for nic in entities.get('network', [])
|
||||
],
|
||||
*[
|
||||
SystemTemperature(
|
||||
id=f'system:temperature:{temp.pop("id")}',
|
||||
name=temp.pop('label'),
|
||||
unit='°C',
|
||||
**temp,
|
||||
)
|
||||
for temp in entities.get('temperature', [])
|
||||
],
|
||||
]
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ from ._memory import MemoryStats, MemoryStatsSchema, SwapStats, SwapStatsSchema
|
|||
from ._model import SystemInfo
|
||||
from ._network import NetworkInterface, NetworkInterfaceSchema
|
||||
from ._schemas import SystemInfoSchema
|
||||
from ._temperature import Temperature, TemperatureSchema
|
||||
|
||||
|
||||
__all__ = [
|
||||
|
@ -39,4 +40,6 @@ __all__ = [
|
|||
"NetworkInterfaceSchema",
|
||||
"SystemInfo",
|
||||
"SystemInfoSchema",
|
||||
"Temperature",
|
||||
"TemperatureSchema",
|
||||
]
|
||||
|
|
|
@ -5,6 +5,7 @@ from ._cpu import Cpu
|
|||
from ._disk import Disk
|
||||
from ._memory import MemoryStats, SwapStats
|
||||
from ._network import NetworkInterface
|
||||
from ._temperature import Temperature
|
||||
|
||||
|
||||
@dataclass
|
||||
|
@ -18,3 +19,4 @@ class SystemInfo:
|
|||
swap: SwapStats
|
||||
disks: List[Disk]
|
||||
network: List[NetworkInterface]
|
||||
temperature: List[Temperature]
|
||||
|
|
4
platypush/schemas/system/_temperature/__init__.py
Normal file
4
platypush/schemas/system/_temperature/__init__.py
Normal file
|
@ -0,0 +1,4 @@
|
|||
from ._model import Temperature
|
||||
from ._schemas import TemperatureSchema
|
||||
|
||||
__all__ = ["Temperature", "TemperatureSchema"]
|
15
platypush/schemas/system/_temperature/_base.py
Normal file
15
platypush/schemas/system/_temperature/_base.py
Normal file
|
@ -0,0 +1,15 @@
|
|||
from marshmallow import pre_load
|
||||
|
||||
from .._base import SystemBaseSchema
|
||||
|
||||
|
||||
class TemperatureBaseSchema(SystemBaseSchema):
|
||||
"""
|
||||
Base schema for system temperature sensors.
|
||||
"""
|
||||
|
||||
@pre_load
|
||||
def pre_load(self, data: dict, **_) -> dict:
|
||||
data = super().pre_load(data)
|
||||
data['value'] = data.pop('current', data.pop('value', None))
|
||||
return data
|
54
platypush/schemas/system/_temperature/_model.py
Normal file
54
platypush/schemas/system/_temperature/_model.py
Normal file
|
@ -0,0 +1,54 @@
|
|||
from dataclasses import dataclass, field
|
||||
from typing import Optional
|
||||
|
||||
|
||||
@dataclass
|
||||
class Temperature:
|
||||
"""
|
||||
System temperature sensor wrapper.
|
||||
"""
|
||||
|
||||
id: str = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Unique ID for the sensor',
|
||||
'example': 'acpi_1',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
label: str = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Name of the sensor',
|
||||
'example': 'CPU',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
value: float = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Current temperature value, in Celsius',
|
||||
'example': 55,
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
high: Optional[float] = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'High threshold for the temperature sensor, in Celsius',
|
||||
'example': 75,
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
critical: Optional[float] = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Critical threshold for the temperature sensor, in Celsius',
|
||||
'example': 95,
|
||||
}
|
||||
}
|
||||
)
|
7
platypush/schemas/system/_temperature/_schemas.py
Normal file
7
platypush/schemas/system/_temperature/_schemas.py
Normal file
|
@ -0,0 +1,7 @@
|
|||
from marshmallow_dataclass import class_schema
|
||||
|
||||
from ._base import TemperatureBaseSchema
|
||||
from ._model import Temperature
|
||||
|
||||
|
||||
TemperatureSchema = class_schema(Temperature, base_schema=TemperatureBaseSchema)
|
Loading…
Reference in a new issue