forked from platypush/platypush
Support for disk
entities in the system
integration.
This commit is contained in:
parent
6b03451386
commit
44b8fd4b34
6 changed files with 454 additions and 225 deletions
|
@ -0,0 +1,225 @@
|
|||
<template>
|
||||
<div class="entity disk-container">
|
||||
<div class="head" @click.stop="isCollapsed = !isCollapsed">
|
||||
<div class="col-1 icon">
|
||||
<EntityIcon
|
||||
:entity="value"
|
||||
:loading="loading"
|
||||
:error="error" />
|
||||
</div>
|
||||
|
||||
<div class="col-8 label">
|
||||
<div class="name" v-text="value.name" />
|
||||
</div>
|
||||
|
||||
<div class="col-2 value" v-text="Math.round(value.percent * 100, 1) + '%'" />
|
||||
|
||||
<div class="col-1 collapse-toggler" @click.stop="isCollapsed = !isCollapsed">
|
||||
<i class="fas"
|
||||
:class="{'fa-chevron-down': isCollapsed, 'fa-chevron-up': !isCollapsed}" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="body children attributes fade-in" v-if="!isCollapsed">
|
||||
<div class="child" v-if="value.mountpoint?.length">
|
||||
<div class="col-s-12 col-m-6 label">
|
||||
<div class="name">Mountpoint</div>
|
||||
</div>
|
||||
<div class="value">
|
||||
<div class="name" v-text="value.mountpoint" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="child" v-if="value.fstype?.length">
|
||||
<div class="col-s-12 col-m-6 label">
|
||||
<div class="name">Filesystem</div>
|
||||
</div>
|
||||
<div class="value">
|
||||
<div class="name" v-text="value.fstype" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="child" v-if="value.opts?.length">
|
||||
<div class="col-s-12 col-m-6 label">
|
||||
<div class="name">Mount options</div>
|
||||
</div>
|
||||
<div class="value">
|
||||
<div class="name" v-text="value.opts" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="child" v-if="value.total != null">
|
||||
<div class="col-s-12 col-m-6 label">
|
||||
<div class="name">Total space</div>
|
||||
</div>
|
||||
<div class="value">
|
||||
<div class="name" v-text="convertSize(value.total)" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="child" v-if="value.used != null">
|
||||
<div class="col-s-12 col-m-6 label">
|
||||
<div class="name">Used space</div>
|
||||
</div>
|
||||
<div class="value">
|
||||
<div class="name" v-text="convertSize(value.used)" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="child" v-if="value.free != null">
|
||||
<div class="col-s-12 col-m-6 label">
|
||||
<div class="name">Available space</div>
|
||||
</div>
|
||||
<div class="value">
|
||||
<div class="name" v-text="convertSize(value.free)" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="child" v-if="value.read_count != null">
|
||||
<div class="col-s-12 col-m-6 label">
|
||||
<div class="name">Number of reads</div>
|
||||
</div>
|
||||
<div class="value">
|
||||
<div class="name" v-text="value.read_count" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="child" v-if="value.write_count != null">
|
||||
<div class="col-s-12 col-m-6 label">
|
||||
<div class="name">Number of writes</div>
|
||||
</div>
|
||||
<div class="value">
|
||||
<div class="name" v-text="value.write_count" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="child" v-if="value.read_bytes != null">
|
||||
<div class="col-s-12 col-m-6 label">
|
||||
<div class="name">Bytes read</div>
|
||||
</div>
|
||||
<div class="value">
|
||||
<div class="name" v-text="convertSize(value.read_bytes)" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="child" v-if="value.write_bytes != null">
|
||||
<div class="col-s-12 col-m-6 label">
|
||||
<div class="name">Bytes written</div>
|
||||
</div>
|
||||
<div class="value">
|
||||
<div class="name" v-text="convertSize(value.write_bytes)" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="child" v-if="value.read_time != null">
|
||||
<div class="col-s-12 col-m-6 label">
|
||||
<div class="name">Read time</div>
|
||||
</div>
|
||||
<div class="value">
|
||||
<div class="name" v-text="convertTime(value.read_time)" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="child" v-if="value.write_time != null">
|
||||
<div class="col-s-12 col-m-6 label">
|
||||
<div class="name">Write time</div>
|
||||
</div>
|
||||
<div class="value">
|
||||
<div class="name" v-text="convertTime(value.write_time)" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="child" v-if="value.busy_time != null">
|
||||
<div class="col-s-12 col-m-6 label">
|
||||
<div class="name">Busy time</div>
|
||||
</div>
|
||||
<div class="value">
|
||||
<div class="name" v-text="convertTime(value.busy_time)" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import EntityMixin from "./EntityMixin"
|
||||
import EntityIcon from "./EntityIcon"
|
||||
|
||||
export default {
|
||||
name: 'Disk',
|
||||
components: {EntityIcon},
|
||||
mixins: [EntityMixin],
|
||||
|
||||
data() {
|
||||
return {
|
||||
isCollapsed: true,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "common";
|
||||
|
||||
.entity {
|
||||
.head {
|
||||
padding: 0.25em;
|
||||
|
||||
.value {
|
||||
text-align: right;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.icon {
|
||||
margin-right: 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.collapse-toggler {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
min-height: 3em;
|
||||
cursor: pointer;
|
||||
|
||||
@include from($tablet) {
|
||||
@include until($desktop) {
|
||||
margin-left: 3.25em;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: $default-hover-fg;
|
||||
}
|
||||
}
|
||||
|
||||
.attributes .child {
|
||||
margin: 0 -0.5em;
|
||||
padding: 0.5em 1em;
|
||||
|
||||
&:not(:last-child) {
|
||||
border-bottom: 1px solid $border-color-1;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
cursor: initial;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-weight: bold;
|
||||
@include from($tablet) {
|
||||
@extend .col-m-6;
|
||||
}
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 0.95em;
|
||||
text-align: right;
|
||||
|
||||
@include from($tablet) {
|
||||
@extend .col-m-6;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -63,6 +63,14 @@
|
|||
}
|
||||
},
|
||||
|
||||
"disk": {
|
||||
"name": "System",
|
||||
"name_plural": "System",
|
||||
"icon": {
|
||||
"class": "fas fa-hard-drive"
|
||||
}
|
||||
},
|
||||
|
||||
"current_sensor": {
|
||||
"name": "Sensor",
|
||||
"name_plural": "Sensors",
|
||||
|
|
|
@ -142,3 +142,36 @@ if 'swap_stats' not in Base.metadata:
|
|||
__mapper_args__ = {
|
||||
'polymorphic_identity': __tablename__,
|
||||
}
|
||||
|
||||
|
||||
if 'disk' not in Base.metadata:
|
||||
|
||||
class Disk(Entity):
|
||||
"""
|
||||
``Disk`` ORM model.
|
||||
"""
|
||||
|
||||
__tablename__ = 'disk'
|
||||
|
||||
id = Column(
|
||||
Integer, ForeignKey(Entity.id, ondelete='CASCADE'), primary_key=True
|
||||
)
|
||||
|
||||
mountpoint = Column(String)
|
||||
fstype = Column(String)
|
||||
opts = Column(String)
|
||||
total = Column(Integer)
|
||||
used = Column(Integer)
|
||||
free = Column(Integer)
|
||||
percent = Column(Float)
|
||||
read_count = Column(Integer)
|
||||
write_count = Column(Integer)
|
||||
read_bytes = Column(Integer)
|
||||
write_bytes = Column(Integer)
|
||||
read_time = Column(Float)
|
||||
write_time = Column(Float)
|
||||
busy_time = Column(Float)
|
||||
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity': __tablename__,
|
||||
}
|
||||
|
|
|
@ -28,86 +28,6 @@ class SensorResponse(SystemResponse):
|
|||
pass
|
||||
|
||||
|
||||
class DiskPartitionResponse(DiskResponse):
|
||||
def __init__(
|
||||
self,
|
||||
device: str,
|
||||
mount_point: str,
|
||||
fstype: Optional[str] = None,
|
||||
opts: Optional[str] = None,
|
||||
*args,
|
||||
**kwargs
|
||||
):
|
||||
super().__init__(
|
||||
*args,
|
||||
output={
|
||||
'device': device,
|
||||
'mount_point': mount_point,
|
||||
'fstype': fstype,
|
||||
'opts': opts,
|
||||
},
|
||||
**kwargs
|
||||
)
|
||||
|
||||
|
||||
class DiskUsageResponse(DiskResponse):
|
||||
def __init__(
|
||||
self,
|
||||
path: str,
|
||||
total: int,
|
||||
used: int,
|
||||
free: int,
|
||||
percent: float,
|
||||
*args,
|
||||
**kwargs
|
||||
):
|
||||
super().__init__(
|
||||
*args,
|
||||
output={
|
||||
'path': path,
|
||||
'total': total,
|
||||
'used': used,
|
||||
'free': free,
|
||||
'percent': percent,
|
||||
},
|
||||
**kwargs
|
||||
)
|
||||
|
||||
|
||||
class DiskIoCountersResponse(DiskResponse):
|
||||
def __init__(
|
||||
self,
|
||||
read_count: int,
|
||||
write_count: int,
|
||||
read_bytes: int,
|
||||
write_bytes: int,
|
||||
read_time: int,
|
||||
write_time: int,
|
||||
read_merged_count: int,
|
||||
write_merged_count: int,
|
||||
busy_time: int,
|
||||
disk: Optional[str] = None,
|
||||
*args,
|
||||
**kwargs
|
||||
):
|
||||
super().__init__(
|
||||
*args,
|
||||
output={
|
||||
'read_count': read_count,
|
||||
'write_count': write_count,
|
||||
'read_bytes': read_bytes,
|
||||
'write_bytes': write_bytes,
|
||||
'read_time': read_time,
|
||||
'write_time': write_time,
|
||||
'read_merged_count': read_merged_count,
|
||||
'write_merged_count': write_merged_count,
|
||||
'busy_time': busy_time,
|
||||
'disk': disk,
|
||||
},
|
||||
**kwargs
|
||||
)
|
||||
|
||||
|
||||
class NetworkIoCountersResponse(NetworkResponse):
|
||||
def __init__(
|
||||
self,
|
||||
|
@ -363,11 +283,6 @@ class SystemResponseList(SystemResponse):
|
|||
super().__init__(output=[r.output for r in responses], *args, **kwargs)
|
||||
|
||||
|
||||
class DiskResponseList(DiskResponse, SystemResponseList):
|
||||
def __init__(self, responses: List[DiskResponse], *args, **kwargs):
|
||||
super().__init__(responses=responses, *args, **kwargs)
|
||||
|
||||
|
||||
class NetworkResponseList(NetworkResponse, SystemResponseList):
|
||||
def __init__(self, responses: List[NetworkResponse], *args, **kwargs):
|
||||
super().__init__(responses=responses, *args, **kwargs)
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
import os
|
||||
import socket
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Tuple, Union, List, Optional, Dict
|
||||
from typing_extensions import override
|
||||
|
||||
import psutil
|
||||
|
||||
from platypush.entities import Entity
|
||||
from platypush.entities.devices import Device
|
||||
from platypush.entities.managers import EntityManager
|
||||
|
@ -13,14 +16,11 @@ from platypush.entities.system import (
|
|||
CpuInfo as CpuInfoModel,
|
||||
CpuStats as CpuStatsModel,
|
||||
CpuTimes as CpuTimesModel,
|
||||
Disk as DiskModel,
|
||||
MemoryStats as MemoryStatsModel,
|
||||
SwapStats as SwapStatsModel,
|
||||
)
|
||||
from platypush.message.response.system import (
|
||||
DiskResponseList,
|
||||
DiskPartitionResponse,
|
||||
DiskUsageResponse,
|
||||
DiskIoCountersResponse,
|
||||
NetworkIoCountersResponse,
|
||||
NetworkResponseList,
|
||||
NetworkConnectionResponse,
|
||||
|
@ -46,6 +46,8 @@ from platypush.schemas.system import (
|
|||
CpuStatsSchema,
|
||||
CpuTimes,
|
||||
CpuTimesSchema,
|
||||
Disk,
|
||||
DiskSchema,
|
||||
MemoryStats,
|
||||
MemoryStatsSchema,
|
||||
SwapStats,
|
||||
|
@ -95,16 +97,12 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
|||
|
||||
@classmethod
|
||||
def _cpu_times_avg(cls, percent=True) -> CpuTimes:
|
||||
import psutil
|
||||
|
||||
method = psutil.cpu_times_percent if percent else psutil.cpu_times
|
||||
times = method(percpu=False)
|
||||
return cls._load_cpu_times(times, many=False) # type: ignore
|
||||
|
||||
@classmethod
|
||||
def _cpu_times_per_cpu(cls, percent=True) -> List[CpuTimes]:
|
||||
import psutil
|
||||
|
||||
method = psutil.cpu_times_percent if percent else psutil.cpu_times
|
||||
times = method(percpu=True)
|
||||
return cls._load_cpu_times(times, many=True) # type: ignore
|
||||
|
@ -143,8 +141,6 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
|||
function be called with at least 0.1 seconds between calls.
|
||||
:return: float if ``per_cpu=False``, ``list[float]`` otherwise.
|
||||
"""
|
||||
import psutil
|
||||
|
||||
percent = psutil.cpu_percent(percpu=per_cpu, interval=interval)
|
||||
|
||||
if per_cpu:
|
||||
|
@ -152,8 +148,6 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
|||
return percent
|
||||
|
||||
def _cpu_stats(self) -> CpuStats:
|
||||
import psutil
|
||||
|
||||
stats = psutil.cpu_stats()
|
||||
return CpuStatsSchema().load(stats._asdict()) # type: ignore
|
||||
|
||||
|
@ -167,14 +161,10 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
|||
return CpuStatsSchema().dump(self._cpu_stats()) # type: ignore
|
||||
|
||||
def _cpu_frequency_avg(self) -> CpuFrequency:
|
||||
import psutil
|
||||
|
||||
freq = psutil.cpu_freq(percpu=False)
|
||||
return CpuFrequencySchema().load(freq._asdict()) # type: ignore
|
||||
|
||||
def _cpu_frequency_per_cpu(self) -> List[CpuFrequency]:
|
||||
import psutil
|
||||
|
||||
freq = psutil.cpu_freq(percpu=True)
|
||||
return CpuFrequencySchema().load(freq._asdict(), many=True) # type: ignore
|
||||
|
||||
|
@ -203,13 +193,9 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
|||
"""
|
||||
Get the average load as a vector that represents the load within the last 1, 5 and 15 minutes.
|
||||
"""
|
||||
import psutil
|
||||
|
||||
return psutil.getloadavg()
|
||||
|
||||
def _mem_virtual(self) -> MemoryStats:
|
||||
import psutil
|
||||
|
||||
return MemoryStatsSchema().load(
|
||||
psutil.virtual_memory()._asdict()
|
||||
) # type: ignore
|
||||
|
@ -224,8 +210,6 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
|||
return MemoryStatsSchema().dump(self._mem_virtual()) # type: ignore
|
||||
|
||||
def _mem_swap(self) -> SwapStats:
|
||||
import psutil
|
||||
|
||||
return SwapStatsSchema().load(psutil.swap_memory()._asdict()) # type: ignore
|
||||
|
||||
@action
|
||||
|
@ -237,111 +221,41 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
|||
"""
|
||||
return SwapStatsSchema().dump(self._mem_swap()) # type: ignore
|
||||
|
||||
@action
|
||||
def disk_partitions(self) -> DiskResponseList:
|
||||
"""
|
||||
Get the list of partitions mounted on the system.
|
||||
:return: list of :class:`platypush.message.response.system.DiskPartitionResponse`
|
||||
"""
|
||||
import psutil
|
||||
def _disk_info(self) -> List[Disk]:
|
||||
parts = {part.device: part._asdict() for part in psutil.disk_partitions()}
|
||||
basename_parts = {os.path.basename(part): part for part in parts}
|
||||
|
||||
parts = psutil.disk_partitions()
|
||||
return DiskResponseList(
|
||||
[
|
||||
DiskPartitionResponse(
|
||||
device=p.device,
|
||||
mount_point=p.mountpoint,
|
||||
fstype=p.fstype,
|
||||
opts=p.opts,
|
||||
)
|
||||
for p in parts
|
||||
]
|
||||
)
|
||||
|
||||
@action
|
||||
def disk_usage(
|
||||
self, path: Optional[str] = None
|
||||
) -> Union[DiskUsageResponse, DiskResponseList]:
|
||||
"""
|
||||
Get the usage of a mounted disk.
|
||||
|
||||
:param path: Path where the device is mounted (default: get stats for all mounted devices).
|
||||
:return: :class:`platypush.message.response.system.DiskUsageResponse` or list of
|
||||
:class:`platypush.message.response.system.DiskUsageResponse`.
|
||||
"""
|
||||
import psutil
|
||||
|
||||
if path:
|
||||
usage = psutil.disk_usage(path)
|
||||
return DiskUsageResponse(
|
||||
path=path,
|
||||
total=usage.total,
|
||||
used=usage.used,
|
||||
free=usage.free,
|
||||
percent=usage.percent,
|
||||
)
|
||||
else:
|
||||
disks = {
|
||||
p.mountpoint: psutil.disk_usage(p.mountpoint)
|
||||
for p in psutil.disk_partitions()
|
||||
io_stats = {
|
||||
basename_parts[disk]: stats._asdict()
|
||||
for disk, stats in psutil.disk_io_counters(perdisk=True).items()
|
||||
if disk in basename_parts
|
||||
}
|
||||
|
||||
return DiskResponseList(
|
||||
usage = {
|
||||
disk: psutil.disk_usage(info['mountpoint'])._asdict()
|
||||
for disk, info in parts.items()
|
||||
}
|
||||
|
||||
return DiskSchema().load( # type: ignore
|
||||
[
|
||||
DiskUsageResponse(
|
||||
path=path,
|
||||
total=disk.total,
|
||||
used=disk.used,
|
||||
free=disk.free,
|
||||
percent=disk.percent,
|
||||
)
|
||||
for path, disk in disks.items()
|
||||
]
|
||||
{
|
||||
**info,
|
||||
**io_stats[part],
|
||||
**usage[part],
|
||||
}
|
||||
for part, info in parts.items()
|
||||
],
|
||||
many=True,
|
||||
)
|
||||
|
||||
@action
|
||||
def disk_io_counters(
|
||||
self, disk: Optional[str] = None, per_disk: bool = False
|
||||
) -> Union[DiskIoCountersResponse, DiskResponseList]:
|
||||
def disk_info(self):
|
||||
"""
|
||||
Get the I/O counter stats for the mounted disks.
|
||||
Get information about the detected disks and partitions.
|
||||
|
||||
:param disk: Select the stats for a specific disk (e.g. 'sda1'). Default: get stats for all mounted disks.
|
||||
:param per_disk: Return the stats per disk (default: False).
|
||||
:return: :class:`platypush.message.response.system.DiskIoCountersResponse` or list of
|
||||
:class:`platypush.message.response.system.DiskIoCountersResponse`.
|
||||
:return: .. schema:: system.DiskSchema(many=True)
|
||||
"""
|
||||
import psutil
|
||||
|
||||
def _expand_response(_disk, _stats):
|
||||
return DiskIoCountersResponse(
|
||||
read_count=_stats.read_count,
|
||||
write_count=_stats.write_count,
|
||||
read_bytes=_stats.read_bytes,
|
||||
write_bytes=_stats.write_bytes,
|
||||
read_time=_stats.read_time,
|
||||
write_time=_stats.write_time,
|
||||
read_merged_count=_stats.read_merged_count,
|
||||
write_merged_count=_stats.write_merged_count,
|
||||
busy_time=_stats.busy_time,
|
||||
disk=_disk,
|
||||
)
|
||||
|
||||
if disk:
|
||||
per_disk = True
|
||||
|
||||
io = psutil.disk_io_counters(perdisk=per_disk)
|
||||
if disk:
|
||||
stats = [d for name, d in io.items() if name == disk]
|
||||
assert stats, 'No such disk: {}'.format(disk)
|
||||
return _expand_response(disk, stats[0])
|
||||
|
||||
if not per_disk:
|
||||
return _expand_response(None, io)
|
||||
|
||||
return DiskResponseList(
|
||||
[_expand_response(disk, stats) for disk, stats in io.items()]
|
||||
)
|
||||
return DiskSchema().dump(self._disk_info(), many=True)
|
||||
|
||||
@action
|
||||
def net_io_counters(
|
||||
|
@ -355,7 +269,6 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
|||
:return: :class:`platypush.message.response.system.NetIoCountersResponse` or list of
|
||||
:class:`platypush.message.response.system.NetIoCountersResponse`.
|
||||
"""
|
||||
import psutil
|
||||
|
||||
def _expand_response(_nic, _stats):
|
||||
return NetworkIoCountersResponse(
|
||||
|
@ -414,8 +327,6 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
|||
|
||||
:return: List of :class:`platypush.message.response.system.NetworkConnectionResponse`.
|
||||
"""
|
||||
import psutil
|
||||
|
||||
conns = psutil.net_connections(kind=type)
|
||||
|
||||
return NetworkResponseList(
|
||||
|
@ -446,8 +357,6 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
|||
:return: :class:`platypush.message.response.system.NetworkAddressResponse` or list of
|
||||
:class:`platypush.message.response.system.NetworkAddressResponse`.
|
||||
"""
|
||||
import psutil
|
||||
|
||||
addrs = psutil.net_if_addrs()
|
||||
|
||||
def _expand_addresses(_nic, _addrs):
|
||||
|
@ -504,8 +413,6 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
|||
:return: :class:`platypush.message.response.system.NetworkInterfaceStatsResponse` or list of
|
||||
:class:`platypush.message.response.system.NetworkInterfaceStatsResponse`.
|
||||
"""
|
||||
import psutil
|
||||
|
||||
stats = psutil.net_if_stats()
|
||||
|
||||
def _expand_stats(_nic, _stats):
|
||||
|
@ -540,8 +447,6 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
|||
:param sensor: Select the sensor name.
|
||||
:param fahrenheit: Return the temperature in Fahrenheit (default: Celsius).
|
||||
"""
|
||||
import psutil
|
||||
|
||||
stats = psutil.sensors_temperatures(fahrenheit=fahrenheit)
|
||||
|
||||
if sensor:
|
||||
|
@ -596,8 +501,6 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
|||
:param sensor: Select the sensor name.
|
||||
:return: List of :class:`platypush.message.response.system.SensorFanResponse`.
|
||||
"""
|
||||
import psutil
|
||||
|
||||
stats = psutil.sensors_fans()
|
||||
|
||||
def _expand_stats(name, _stats):
|
||||
|
@ -627,8 +530,6 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
|||
Get stats from the battery sensor.
|
||||
:return: List of :class:`platypush.message.response.system.SensorFanResponse`.
|
||||
"""
|
||||
import psutil
|
||||
|
||||
stats = psutil.sensors_battery()
|
||||
|
||||
return SensorBatteryResponse(
|
||||
|
@ -643,8 +544,6 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
|||
Get the list of connected users.
|
||||
:return: List of :class:`platypush.message.response.system.ConnectUserResponse`.
|
||||
"""
|
||||
import psutil
|
||||
|
||||
users = psutil.users()
|
||||
|
||||
return ConnectedUserResponseList(
|
||||
|
@ -668,8 +567,6 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
|||
:param filter: Filter the list by name.
|
||||
:return: List of :class:`platypush.message.response.system.ProcessResponse`.
|
||||
"""
|
||||
import psutil
|
||||
|
||||
processes = [psutil.Process(pid) for pid in psutil.pids()]
|
||||
p_list = []
|
||||
|
||||
|
@ -720,8 +617,6 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
|||
|
||||
@staticmethod
|
||||
def _get_process(pid: int):
|
||||
import psutil
|
||||
|
||||
return psutil.Process(pid)
|
||||
|
||||
@action
|
||||
|
@ -730,8 +625,6 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
|||
:param pid: Process PID.
|
||||
:return: ``True`` if the process exists, ``False`` otherwise.
|
||||
"""
|
||||
import psutil
|
||||
|
||||
return psutil.pid_exists(pid)
|
||||
|
||||
@action
|
||||
|
@ -794,6 +687,7 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
|||
},
|
||||
'memory': self._mem_virtual(),
|
||||
'swap': self._mem_swap(),
|
||||
'disks': self._disk_info(),
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -870,6 +764,14 @@ class SystemPlugin(SensorPlugin, EntityManager):
|
|||
name='Swap',
|
||||
**entities['swap'],
|
||||
),
|
||||
*[
|
||||
DiskModel(
|
||||
id=f'system:disk:{disk["device"]}',
|
||||
name=disk.pop('device'),
|
||||
**disk,
|
||||
)
|
||||
for disk in entities['disks']
|
||||
],
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -68,6 +68,25 @@ class CpuTimesBaseSchema(DataClassSchema):
|
|||
}
|
||||
|
||||
|
||||
class DiskBaseSchema(DataClassSchema):
|
||||
"""
|
||||
Base schema for disk stats.
|
||||
"""
|
||||
|
||||
@pre_load
|
||||
def pre_load(self, data: dict, **_) -> dict:
|
||||
# Convert read/write/busy times from milliseconds to seconds
|
||||
for attr in ['read_time', 'write_time', 'busy_time']:
|
||||
if data.get(attr) is not None:
|
||||
data[attr] /= 1000
|
||||
|
||||
# Normalize the percentage between 0 and 1
|
||||
if data.get('percent') is not None:
|
||||
data['percent'] /= 100
|
||||
|
||||
return data
|
||||
|
||||
|
||||
@dataclass
|
||||
class CpuInfo:
|
||||
"""
|
||||
|
@ -356,6 +375,131 @@ class SwapStats:
|
|||
percent: float = percent_field()
|
||||
|
||||
|
||||
@dataclass
|
||||
class Disk:
|
||||
"""
|
||||
Disk data class.
|
||||
"""
|
||||
|
||||
device: str = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Path/identifier of the disk/partition',
|
||||
'example': '/dev/sda1',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
mountpoint: Optional[str] = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Where the disk is mounted',
|
||||
'example': '/home',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
fstype: Optional[str] = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Filesystem type',
|
||||
'example': 'ext4',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
opts: Optional[str] = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Extra mount options passed to the partition',
|
||||
'example': 'rw,relatime,fmask=0022,dmask=0022,utf8',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
total: int = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Total available space, in bytes',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
used: int = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Used disk space, in bytes',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
free: int = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Free disk space, in bytes',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
read_count: int = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Number of recorded read operations',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
write_count: int = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Number of recorded write operations',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
read_bytes: int = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Number of read bytes',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
write_bytes: int = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Number of written bytes',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
read_time: float = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Time spent reading, in seconds',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
write_time: float = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Time spent writing, in seconds',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
busy_time: float = field(
|
||||
metadata={
|
||||
'metadata': {
|
||||
'description': 'Total disk busy time, in seconds',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
percent: float = percent_field()
|
||||
|
||||
|
||||
@dataclass
|
||||
class SystemInfo:
|
||||
"""
|
||||
|
@ -365,12 +509,14 @@ class SystemInfo:
|
|||
cpu: CpuData
|
||||
memory: MemoryStats
|
||||
swap: SwapStats
|
||||
disks: List[Disk]
|
||||
|
||||
|
||||
CpuFrequencySchema = class_schema(CpuFrequency, base_schema=DataClassSchema)
|
||||
CpuInfoSchema = class_schema(CpuInfo, base_schema=CpuInfoBaseSchema)
|
||||
CpuTimesSchema = class_schema(CpuTimes, base_schema=CpuTimesBaseSchema)
|
||||
CpuStatsSchema = class_schema(CpuStats, base_schema=DataClassSchema)
|
||||
DiskSchema = class_schema(Disk, base_schema=DiskBaseSchema)
|
||||
MemoryStatsSchema = class_schema(MemoryStats, base_schema=MemoryStatsBaseSchema)
|
||||
SwapStatsSchema = class_schema(SwapStats, base_schema=MemoryStatsBaseSchema)
|
||||
SystemInfoSchema = class_schema(SystemInfo, base_schema=DataClassSchema)
|
||||
|
|
Loading…
Reference in a new issue