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": {
|
"memory_stats": {
|
||||||
"name": "System",
|
"name": "System",
|
||||||
"name_plural": "System",
|
"name_plural": "System",
|
||||||
|
@ -79,6 +71,14 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"system_temperature": {
|
||||||
|
"name": "System",
|
||||||
|
"name_plural": "System",
|
||||||
|
"icon": {
|
||||||
|
"class": "fas fa-temperature-half"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
"current_sensor": {
|
"current_sensor": {
|
||||||
"name": "Sensor",
|
"name": "Sensor",
|
||||||
"name_plural": "Sensors",
|
"name_plural": "Sensors",
|
||||||
|
@ -87,6 +87,14 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"cpu": {
|
||||||
|
"name": "System",
|
||||||
|
"name_plural": "System",
|
||||||
|
"icon": {
|
||||||
|
"class": "fas fa-microchip"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
"motion_sensor": {
|
"motion_sensor": {
|
||||||
"name": "Sensor",
|
"name": "Sensor",
|
||||||
"name_plural": "Sensors",
|
"name_plural": "Sensors",
|
||||||
|
|
|
@ -4,6 +4,7 @@ from platypush.common.db import Base
|
||||||
|
|
||||||
from . import Entity
|
from . import Entity
|
||||||
from .devices import Device
|
from .devices import Device
|
||||||
|
from .temperature import TemperatureSensor
|
||||||
|
|
||||||
|
|
||||||
if 'cpu' not in Base.metadata:
|
if 'cpu' not in Base.metadata:
|
||||||
|
@ -208,3 +209,26 @@ if 'network_interface' not in Base.metadata:
|
||||||
__mapper_args__ = {
|
__mapper_args__ = {
|
||||||
'polymorphic_identity': __tablename__,
|
'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
|
pass
|
||||||
|
|
||||||
|
|
||||||
class CpuResponse(SystemResponse):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class MemoryResponse(SystemResponse):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class DiskResponse(SystemResponse):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class NetworkResponse(SystemResponse):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class SensorResponse(SystemResponse):
|
class SensorResponse(SystemResponse):
|
||||||
pass
|
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):
|
class SensorFanResponse(SensorResponse):
|
||||||
def __init__(
|
def __init__(
|
||||||
self, name: str, current: int, label: Optional[str] = None, *args, **kwargs
|
self, name: str, current: int, label: Optional[str] = None, *args, **kwargs
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Tuple, Union, List, Optional, Dict
|
from typing import Tuple, Union, List, Optional
|
||||||
from typing_extensions import override
|
from typing_extensions import override
|
||||||
|
|
||||||
import psutil
|
import psutil
|
||||||
|
@ -19,9 +19,9 @@ from platypush.entities.system import (
|
||||||
MemoryStats as MemoryStatsModel,
|
MemoryStats as MemoryStatsModel,
|
||||||
NetworkInterface as NetworkInterfaceModel,
|
NetworkInterface as NetworkInterfaceModel,
|
||||||
SwapStats as SwapStatsModel,
|
SwapStats as SwapStatsModel,
|
||||||
|
SystemTemperature,
|
||||||
)
|
)
|
||||||
from platypush.message.response.system import (
|
from platypush.message.response.system import (
|
||||||
SensorTemperatureResponse,
|
|
||||||
SensorResponseList,
|
SensorResponseList,
|
||||||
SensorFanResponse,
|
SensorFanResponse,
|
||||||
SensorBatteryResponse,
|
SensorBatteryResponse,
|
||||||
|
@ -51,6 +51,8 @@ from platypush.schemas.system import (
|
||||||
SwapStats,
|
SwapStats,
|
||||||
SwapStatsSchema,
|
SwapStatsSchema,
|
||||||
SystemInfoSchema,
|
SystemInfoSchema,
|
||||||
|
Temperature,
|
||||||
|
TemperatureSchema,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -145,9 +147,9 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
||||||
return list(percent) # type: ignore
|
return list(percent) # type: ignore
|
||||||
return percent
|
return percent
|
||||||
|
|
||||||
def _cpu_stats(self) -> CpuStats:
|
@staticmethod
|
||||||
stats = psutil.cpu_stats()
|
def _cpu_stats() -> CpuStats:
|
||||||
return CpuStatsSchema().load(stats) # type: ignore
|
return CpuStatsSchema().load(psutil.cpu_stats()) # type: ignore
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def cpu_stats(self) -> CpuStats:
|
def cpu_stats(self) -> CpuStats:
|
||||||
|
@ -158,13 +160,13 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
||||||
"""
|
"""
|
||||||
return CpuStatsSchema().dump(self._cpu_stats()) # type: ignore
|
return CpuStatsSchema().dump(self._cpu_stats()) # type: ignore
|
||||||
|
|
||||||
def _cpu_frequency_avg(self) -> CpuFrequency:
|
@staticmethod
|
||||||
freq = psutil.cpu_freq(percpu=False)
|
def _cpu_frequency_avg() -> CpuFrequency:
|
||||||
return CpuFrequencySchema().load(freq) # type: ignore
|
return CpuFrequencySchema().load(psutil.cpu_freq(percpu=False)) # type: ignore
|
||||||
|
|
||||||
def _cpu_frequency_per_cpu(self) -> List[CpuFrequency]:
|
@staticmethod
|
||||||
freq = psutil.cpu_freq(percpu=True)
|
def _cpu_frequency_per_cpu() -> List[CpuFrequency]:
|
||||||
return CpuFrequencySchema().load(freq, many=True) # type: ignore
|
return CpuFrequencySchema().load(psutil.cpu_freq(percpu=True), many=True) # type: ignore
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def cpu_frequency(
|
def cpu_frequency(
|
||||||
|
@ -193,7 +195,8 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
||||||
"""
|
"""
|
||||||
return psutil.getloadavg()
|
return psutil.getloadavg()
|
||||||
|
|
||||||
def _mem_virtual(self) -> MemoryStats:
|
@staticmethod
|
||||||
|
def _mem_virtual() -> MemoryStats:
|
||||||
return MemoryStatsSchema().load(psutil.virtual_memory()) # type: ignore
|
return MemoryStatsSchema().load(psutil.virtual_memory()) # type: ignore
|
||||||
|
|
||||||
@action
|
@action
|
||||||
|
@ -205,7 +208,8 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
||||||
"""
|
"""
|
||||||
return MemoryStatsSchema().dump(self._mem_virtual()) # type: ignore
|
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
|
return SwapStatsSchema().load(psutil.swap_memory()) # type: ignore
|
||||||
|
|
||||||
@action
|
@action
|
||||||
|
@ -217,7 +221,8 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
||||||
"""
|
"""
|
||||||
return SwapStatsSchema().dump(self._mem_swap()) # type: ignore
|
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()}
|
parts = {part.device: part._asdict() for part in psutil.disk_partitions()}
|
||||||
basename_parts = {os.path.basename(part): part for part in parts}
|
basename_parts = {os.path.basename(part): part for part in parts}
|
||||||
io_stats = {
|
io_stats = {
|
||||||
|
@ -252,7 +257,8 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
||||||
"""
|
"""
|
||||||
return DiskSchema().dump(self._disk_info(), many=True)
|
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()
|
addrs = psutil.net_if_addrs()
|
||||||
stats = psutil.net_if_stats()
|
stats = psutil.net_if_stats()
|
||||||
|
|
||||||
|
@ -270,7 +276,8 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
||||||
many=True,
|
many=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _network_info_avg(self) -> NetworkInterface:
|
@staticmethod
|
||||||
|
def _network_info_avg() -> NetworkInterface:
|
||||||
stats = psutil.net_io_counters(pernic=False)
|
stats = psutil.net_io_counters(pernic=False)
|
||||||
return NetworkInterfaceSchema().load( # type: ignore
|
return NetworkInterfaceSchema().load( # type: ignore
|
||||||
{
|
{
|
||||||
|
@ -332,65 +339,29 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
||||||
many=True,
|
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
|
@action
|
||||||
def sensors_temperature(
|
def sensors_temperature(self) -> List[dict]:
|
||||||
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.
|
:return: .. schema:: system.TemperatureSchema(many=True)
|
||||||
:param fahrenheit: Return the temperature in Fahrenheit (default: Celsius).
|
|
||||||
"""
|
"""
|
||||||
stats = psutil.sensors_temperatures(fahrenheit=fahrenheit)
|
return TemperatureSchema().dump(self._sensors_temperature(), many=True)
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def sensors_fan(self, sensor: Optional[str] = None) -> SensorResponseList:
|
def sensors_fan(self, sensor: Optional[str] = None) -> SensorResponseList:
|
||||||
|
@ -588,6 +559,7 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
||||||
'swap': self._mem_swap(),
|
'swap': self._mem_swap(),
|
||||||
'disks': self._disk_info(),
|
'disks': self._disk_info(),
|
||||||
'network': self._network_info(),
|
'network': self._network_info(),
|
||||||
|
'temperature': self._sensors_temperature(),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -679,7 +651,16 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
||||||
reachable=nic.pop('is_up'),
|
reachable=nic.pop('is_up'),
|
||||||
**nic,
|
**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 ._model import SystemInfo
|
||||||
from ._network import NetworkInterface, NetworkInterfaceSchema
|
from ._network import NetworkInterface, NetworkInterfaceSchema
|
||||||
from ._schemas import SystemInfoSchema
|
from ._schemas import SystemInfoSchema
|
||||||
|
from ._temperature import Temperature, TemperatureSchema
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
@ -39,4 +40,6 @@ __all__ = [
|
||||||
"NetworkInterfaceSchema",
|
"NetworkInterfaceSchema",
|
||||||
"SystemInfo",
|
"SystemInfo",
|
||||||
"SystemInfoSchema",
|
"SystemInfoSchema",
|
||||||
|
"Temperature",
|
||||||
|
"TemperatureSchema",
|
||||||
]
|
]
|
||||||
|
|
|
@ -5,6 +5,7 @@ from ._cpu import Cpu
|
||||||
from ._disk import Disk
|
from ._disk import Disk
|
||||||
from ._memory import MemoryStats, SwapStats
|
from ._memory import MemoryStats, SwapStats
|
||||||
from ._network import NetworkInterface
|
from ._network import NetworkInterface
|
||||||
|
from ._temperature import Temperature
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
@ -18,3 +19,4 @@ class SystemInfo:
|
||||||
swap: SwapStats
|
swap: SwapStats
|
||||||
disks: List[Disk]
|
disks: List[Disk]
|
||||||
network: List[NetworkInterface]
|
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