Added battery entity support to system plugin.

This commit is contained in:
Fabio Manganiello 2023-04-23 00:41:21 +02:00
parent b3440ab96b
commit a72c32cb00
Signed by: blacklight
GPG key ID: D90FBA7F76362774
11 changed files with 141 additions and 40 deletions

View file

@ -0,0 +1 @@
Battery.vue

View file

@ -79,14 +79,6 @@
} }
}, },
"current_sensor": {
"name": "Sensor",
"name_plural": "Sensors",
"icon": {
"class": "fas fa-bolt"
}
},
"system_fan": { "system_fan": {
"name": "System", "name": "System",
"name_plural": "System", "name_plural": "System",
@ -95,6 +87,22 @@
} }
}, },
"system_battery": {
"name": "System",
"name_plural": "System",
"icon": {
"class": "fas fa-battery-full"
}
},
"current_sensor": {
"name": "Sensor",
"name_plural": "Sensors",
"icon": {
"class": "fas fa-bolt"
}
},
"cpu": { "cpu": {
"name": "System", "name": "System",
"name_plural": "System", "name_plural": "System",

View file

@ -1,10 +1,10 @@
from sqlalchemy import Column, Float, ForeignKey, Integer, JSON, String from sqlalchemy import Boolean, Column, Float, ForeignKey, Integer, JSON, String
from platypush.common.db import Base from platypush.common.db import Base
from . import Entity from . import Entity
from .devices import Device from .devices import Device
from .sensors import NumericSensor from .sensors import NumericSensor, PercentSensor
from .temperature import TemperatureSensor from .temperature import TemperatureSensor
@ -253,3 +253,26 @@ if 'system_fan' not in Base.metadata:
__mapper_args__ = { __mapper_args__ = {
'polymorphic_identity': __tablename__, 'polymorphic_identity': __tablename__,
} }
if 'system_battery' not in Base.metadata:
class SystemBattery(PercentSensor):
"""
``SystemBattery`` ORM model.
"""
__tablename__ = 'system_battery'
id = Column(
Integer,
ForeignKey(PercentSensor.id, ondelete='CASCADE'),
primary_key=True,
)
seconds_left = Column(Float)
power_plugged = Column(Boolean)
__mapper_args__ = {
'polymorphic_identity': __tablename__,
}

View file

@ -8,25 +8,6 @@ class SystemResponse(Response):
pass pass
class SensorResponse(SystemResponse):
pass
class SensorBatteryResponse(SensorResponse):
def __init__(
self, percent: float, secs_left: int, power_plugged: bool, *args, **kwargs
):
super().__init__(
*args,
output={
'percent': percent,
'secs_left': secs_left,
'power_plugged': power_plugged,
},
**kwargs
)
class ConnectUserResponse(SystemResponse): class ConnectUserResponse(SystemResponse):
def __init__( def __init__(
self, self,

View file

@ -19,11 +19,11 @@ from platypush.entities.system import (
MemoryStats as MemoryStatsModel, MemoryStats as MemoryStatsModel,
NetworkInterface as NetworkInterfaceModel, NetworkInterface as NetworkInterfaceModel,
SwapStats as SwapStatsModel, SwapStats as SwapStatsModel,
SystemBattery,
SystemFan, SystemFan,
SystemTemperature, SystemTemperature,
) )
from platypush.message.response.system import ( from platypush.message.response.system import (
SensorBatteryResponse,
ConnectedUserResponseList, ConnectedUserResponseList,
ConnectUserResponse, ConnectUserResponse,
ProcessResponseList, ProcessResponseList,
@ -32,6 +32,8 @@ from platypush.message.response.system import (
from platypush.plugins import action from platypush.plugins import action
from platypush.plugins.sensor import SensorPlugin from platypush.plugins.sensor import SensorPlugin
from platypush.schemas.system import ( from platypush.schemas.system import (
Battery,
BatterySchema,
ConnectionSchema, ConnectionSchema,
CpuFrequency, CpuFrequency,
CpuFrequencySchema, CpuFrequencySchema,
@ -387,19 +389,19 @@ class SystemPlugin(SensorPlugin, EntityManager):
""" """
return FanSchema().dump(self._sensors_fan(), many=True) return FanSchema().dump(self._sensors_fan(), many=True)
def _sensors_battery(self) -> Optional[Battery]:
battery = psutil.sensors_battery()
return BatterySchema().load(battery) if battery else None # type: ignore
@action @action
def sensors_battery(self) -> SensorBatteryResponse: def sensors_battery(self) -> Optional[dict]:
""" """
Get stats from the battery sensor. Get stats from the battery sensor.
:return: List of :class:`platypush.message.response.system.SensorFanResponse`.
"""
stats = psutil.sensors_battery()
return SensorBatteryResponse( :return: .. schema:: system.BatterySchema
percent=stats.percent, """
secs_left=stats.secsleft, battery = self._sensors_battery()
power_plugged=stats.power_plugged, return BatterySchema().dump(battery) if battery else None # type: ignore
)
@action @action
def connected_users(self) -> ConnectedUserResponseList: def connected_users(self) -> ConnectedUserResponseList:
@ -554,12 +556,14 @@ class SystemPlugin(SensorPlugin, EntityManager):
'network': self._network_info(), 'network': self._network_info(),
'temperature': self._sensors_temperature(), 'temperature': self._sensors_temperature(),
'fans': self._sensors_fan(), 'fans': self._sensors_fan(),
'battery': self._sensors_battery(),
} }
) )
@override @override
def transform_entities(self, entities: dict) -> List[Entity]: def transform_entities(self, entities: dict) -> List[Entity]:
cpu = entities['cpu'].copy() cpu = entities['cpu'].copy()
battery = entities['battery']
return [ return [
Cpu( Cpu(
@ -663,6 +667,15 @@ class SystemPlugin(SensorPlugin, EntityManager):
) )
for fan in entities.get('fans', []) for fan in entities.get('fans', [])
], ],
*[
SystemBattery(
id='system:battery',
name='Battery',
**battery,
)
if battery
else ()
],
] ]

View file

@ -1,3 +1,4 @@
from ._battery import Battery, BatterySchema
from ._connection import Connection, ConnectionSchema from ._connection import Connection, ConnectionSchema
from ._cpu import ( from ._cpu import (
Cpu, Cpu,
@ -20,6 +21,8 @@ from ._temperature import Temperature, TemperatureSchema
__all__ = [ __all__ = [
"Battery",
"BatterySchema",
"Connection", "Connection",
"ConnectionSchema", "ConnectionSchema",
"Cpu", "Cpu",

View file

@ -0,0 +1,4 @@
from ._model import Battery
from ._schemas import BatterySchema
__all__ = ['Battery', 'BatterySchema']

View file

@ -0,0 +1,20 @@
from enum import Enum
from marshmallow import pre_load
from .._base import SystemBaseSchema
class BatteryBaseSchema(SystemBaseSchema):
"""
Base schema for system battery sensors.
"""
@pre_load
def pre_load(self, data: dict, **_) -> dict:
data = super().pre_load(data)
percent = data.pop('percent', data.pop('value', None))
seconds_left = data.pop('secsleft', data.pop('seconds_left', None))
data['value'] = percent / 100 if percent is not None else None
data['seconds_left'] = None if isinstance(seconds_left, Enum) else seconds_left
return data

View file

@ -0,0 +1,39 @@
from dataclasses import dataclass, field
from typing import Optional
from platypush.schemas.dataclasses import percent_field
@dataclass
class Battery:
"""
System battery sensor wrapper.
"""
seconds_left: Optional[float] = field(
metadata={
'metadata': {
'description': 'High threshold for the temperature sensor, in Celsius',
'example': 75,
}
}
)
power_plugged: Optional[bool] = field(
metadata={
'metadata': {
'description': 'Whether the battery is plugged in or not',
'example': False,
}
}
)
value: Optional[float] = percent_field(
metadata={
'metadata': {
'description': 'Current charge left, as a percentage value '
'between 0 and 1',
'example': 0.5,
}
}
)

View file

@ -0,0 +1,7 @@
from marshmallow_dataclass import class_schema
from ._base import BatteryBaseSchema
from ._model import Battery
BatterySchema = class_schema(Battery, base_schema=BatteryBaseSchema)

View file

@ -1,6 +1,7 @@
from dataclasses import dataclass from dataclasses import dataclass
from typing import List from typing import List, Optional
from ._battery import Battery
from ._cpu import Cpu from ._cpu import Cpu
from ._disk import Disk from ._disk import Disk
from ._fan import Fan from ._fan import Fan
@ -22,3 +23,4 @@ class SystemInfo:
network: List[NetworkInterface] network: List[NetworkInterface]
temperature: List[Temperature] temperature: List[Temperature]
fans: List[Fan] fans: List[Fan]
battery: Optional[Battery]