From 6c17a650813d8b33b4cbc8c92f927dfda3587c55 Mon Sep 17 00:00:00 2001 From: Aidan Timson Date: Fri, 5 Aug 2022 17:51:28 +0100 Subject: [PATCH 01/38] Add SQLModel --- shared/requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/shared/requirements.txt b/shared/requirements.txt index 4963349e8..9bcb730bd 100644 --- a/shared/requirements.txt +++ b/shared/requirements.txt @@ -5,3 +5,4 @@ cryptography==37.0.4 incremental==21.3.0 pandas==1.4.3 pydantic==1.9.1 +sqlmodel==0.0.6 From 1420916021154dc55e5633807856bb5ce75a906e Mon Sep 17 00:00:00 2001 From: Aidan Timson Date: Fri, 5 Aug 2022 17:51:36 +0100 Subject: [PATCH 02/38] Add database models --- .../models/database_data.py | 15 +++++++++++++++ .../models/database_data_sensor.py | 19 +++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 shared/systembridgeshared/models/database_data.py create mode 100644 shared/systembridgeshared/models/database_data_sensor.py diff --git a/shared/systembridgeshared/models/database_data.py b/shared/systembridgeshared/models/database_data.py new file mode 100644 index 000000000..a54a782cf --- /dev/null +++ b/shared/systembridgeshared/models/database_data.py @@ -0,0 +1,15 @@ +"""System Bridge: Models - Database Data""" + +from __future__ import annotations + +from typing import Optional + +from sqlmodel import Field, SQLModel + + +class DatabaseData(SQLModel, table=True): + """Database Data""" + + key: str = Field(primary_key=True, nullable=False) + value: Optional[str] = Field(default=None, nullable=True) + timestamp: Optional[float] = Field(default=None, nullable=True) diff --git a/shared/systembridgeshared/models/database_data_sensor.py b/shared/systembridgeshared/models/database_data_sensor.py new file mode 100644 index 000000000..b393a6c10 --- /dev/null +++ b/shared/systembridgeshared/models/database_data_sensor.py @@ -0,0 +1,19 @@ +"""System Bridge: Models - Database Data Sensor""" + +from __future__ import annotations + +from typing import Optional + +from sqlmodel import Field, SQLModel + + +class DatabaseDataSensor(SQLModel, table=True): + """Database Data Sensor""" + + key: str = Field(primary_key=True, nullable=False) + type: str = Field(nullable=False) + name: Optional[str] = Field(default=None, nullable=True) + hardware_type: Optional[str] = Field(default=None, nullable=True) + hardware_name: Optional[str] = Field(default=None, nullable=True) + value: Optional[str] = Field(default=None, nullable=True) + timestamp: Optional[float] = Field(default=None, nullable=True) From 06caf4f970140fd36c52ccae8dbcade5c97ad1ee Mon Sep 17 00:00:00 2001 From: Aidan Timson Date: Fri, 5 Aug 2022 17:56:16 +0100 Subject: [PATCH 03/38] Add db bridge model --- .../models/database_data_bridge.py | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 shared/systembridgeshared/models/database_data_bridge.py diff --git a/shared/systembridgeshared/models/database_data_bridge.py b/shared/systembridgeshared/models/database_data_bridge.py new file mode 100644 index 000000000..677f44d74 --- /dev/null +++ b/shared/systembridgeshared/models/database_data_bridge.py @@ -0,0 +1,26 @@ +"""System Bridge: Models - Database Data Bridge""" + +from __future__ import annotations + +from typing import Optional + +from sqlmodel import Field, SQLModel + + +class DatabaseDataBridge(SQLModel, table=True): + """Database Data Bridge""" + + uuid: str = Field(primary_key=True, nullable=False) + name: str = Field(nullable=False) + address: str = Field(nullable=False) + fqdn: Optional[str] = Field(default=None, nullable=True) + host: str = Field(default=None, nullable=True) + ip: str = Field(nullable=False) + mac: str = Field(nullable=False) + port: str = Field(nullable=False) + version: str = Field(nullable=False) + websocket_address: str = Field(nullable=False) + websocket_port: str = Field(nullable=False) + active: Optional[int] = Field(default=None, nullable=True) + last_active_timestamp: Optional[float] = Field(default=None, nullable=True) + timestamp: Optional[float] = Field(default=None, nullable=True) From 8c5879194d2abe1ffad43ef47380cec1488a2f65 Mon Sep 17 00:00:00 2001 From: Aidan Timson Date: Sat, 6 Aug 2022 00:32:07 +0100 Subject: [PATCH 04/38] Rewrite --- backend/systembridgebackend/__main__.py | 8 +- .../systembridgebackend/autostart/__init__.py | 8 +- backend/systembridgebackend/data.py | 2 +- backend/systembridgebackend/modules/base.py | 9 - .../modules/battery/update.py | 21 +- .../modules/bridge/update.py | 125 ++---- .../systembridgebackend/modules/cpu/update.py | 91 ++++- .../modules/disk/update.py | 4 +- .../modules/display/update.py | 4 +- .../systembridgebackend/modules/gpu/update.py | 4 +- .../systembridgebackend/modules/listeners.py | 19 +- .../modules/memory/update.py | 5 +- .../modules/network/update.py | 4 +- .../modules/sensors/update.py | 22 +- .../modules/system/__init__.py | 2 +- .../modules/system/update.py | 4 +- backend/systembridgebackend/modules/update.py | 39 +- .../systembridgebackend/server/__init__.py | 41 +- backend/systembridgebackend/server/mdns.py | 2 +- backend/systembridgebackend/server/options.py | 2 +- .../systembridgebackend/server/websocket.py | 21 +- .../systembridgebackend/shortcut/__init__.py | 4 +- connector/systembridgeconnector/const.py | 4 +- shared/systembridgeshared/common.py | 4 +- shared/systembridgeshared/const.py | 4 +- shared/systembridgeshared/database.py | 376 ++++++++---------- shared/systembridgeshared/models/data.py | 9 +- .../models/database_data.py | 42 +- .../models/database_data_bridge.py | 2 +- .../models/database_data_sensor.py | 19 - .../models/database_data_sensors.py | 18 + shared/systembridgeshared/settings.py | 114 +++--- 32 files changed, 503 insertions(+), 530 deletions(-) delete mode 100644 shared/systembridgeshared/models/database_data_sensor.py create mode 100644 shared/systembridgeshared/models/database_data_sensors.py diff --git a/backend/systembridgebackend/__main__.py b/backend/systembridgebackend/__main__.py index e25afd5bb..9023a92b6 100644 --- a/backend/systembridgebackend/__main__.py +++ b/backend/systembridgebackend/__main__.py @@ -8,10 +8,10 @@ from systembridgeshared.logger import setup_logger from systembridgeshared.settings import Settings -from systembridgebackend.autostart import autostart_disable, autostart_enable -from systembridgebackend.modules.system import System -from systembridgebackend.server import Server -from systembridgebackend.shortcut import create_shortcuts +from .autostart import autostart_disable, autostart_enable +from .modules.system import System +from .server import Server +from .shortcut import create_shortcuts class Main(Base): diff --git a/backend/systembridgebackend/autostart/__init__.py b/backend/systembridgebackend/autostart/__init__.py index 52d2326e9..27227aa62 100644 --- a/backend/systembridgebackend/autostart/__init__.py +++ b/backend/systembridgebackend/autostart/__init__.py @@ -6,12 +6,12 @@ def autostart_disable(): """Disable autostart""" if "Windows" in platform.system(): # pylint: disable=import-error, import-outside-toplevel - from systembridgebackend.autostart.windows import autostart_windows_disable + from .windows import autostart_windows_disable autostart_windows_disable() elif "Linux" in platform.system(): # pylint: disable=import-error, import-outside-toplevel - from systembridgebackend.autostart.linux import autostart_linux_disable + from .linux import autostart_linux_disable autostart_linux_disable() @@ -20,11 +20,11 @@ def autostart_enable(): """Enable autostart""" if "Windows" in platform.system(): # pylint: disable=import-error, import-outside-toplevel - from systembridgebackend.autostart.windows import autostart_windows_enable + from .windows import autostart_windows_enable autostart_windows_enable() elif "Linux" in platform.system(): # pylint: disable=import-error, import-outside-toplevel - from systembridgebackend.autostart.linux import autostart_linux_enable + from .linux import autostart_linux_enable autostart_linux_enable() diff --git a/backend/systembridgebackend/data.py b/backend/systembridgebackend/data.py index b6d79ccf1..3aeba5f8a 100644 --- a/backend/systembridgebackend/data.py +++ b/backend/systembridgebackend/data.py @@ -6,7 +6,7 @@ from systembridgeshared.base import Base from systembridgeshared.database import Database -from systembridgebackend.modules.update import Update +from .modules.update import Update class UpdateThread(Thread): diff --git a/backend/systembridgebackend/modules/base.py b/backend/systembridgebackend/modules/base.py index 732611b93..3e72b4187 100644 --- a/backend/systembridgebackend/modules/base.py +++ b/backend/systembridgebackend/modules/base.py @@ -10,19 +10,10 @@ class ModuleUpdateBase(Base): def __init__( self, database: Database, - table: str, ): super().__init__() self._database = database - self._database.create_table( - table, - [ - (COLUMN_KEY, "TEXT PRIMARY KEY"), - (COLUMN_VALUE, "TEXT"), - (COLUMN_TIMESTAMP, "DOUBLE"), - ], - ) async def update_all_data(self) -> None: """Update data""" diff --git a/backend/systembridgebackend/modules/battery/update.py b/backend/systembridgebackend/modules/battery/update.py index d8dac332c..ced6436b0 100644 --- a/backend/systembridgebackend/modules/battery/update.py +++ b/backend/systembridgebackend/modules/battery/update.py @@ -3,9 +3,10 @@ from systembridgeshared.common import camel_to_snake from systembridgeshared.database import Database +from systembridgeshared.models.database_data import Battery as DatabaseModel -from systembridgebackend.modules.base import ModuleUpdateBase -from systembridgebackend.modules.battery import Battery +from . import Battery +from ..base import ModuleUpdateBase class BatteryUpdate(ModuleUpdateBase): @@ -16,7 +17,7 @@ def __init__( database: Database, ) -> None: """Initialize""" - super().__init__(database, "battery") + super().__init__(database) self._battery = Battery() async def update_sensors(self) -> None: @@ -26,12 +27,22 @@ async def update_sensors(self) -> None: # From status if key == "percent": continue - self._database.write("battery", f"sensors_{key}", value) + self._database.add_data( + self._database.create_data( + "sensors_{key}", + value, + ) + ) async def update_status(self) -> None: """Update Battery Status""" for key, value in self._battery.status().items(): - self._database.write("battery", camel_to_snake(key), value) + self._database.add_data( + self._database.create_data( + key=camel_to_snake(key), + value=value, + ) + ) async def update_all_data(self) -> None: """Update data""" diff --git a/backend/systembridgebackend/modules/bridge/update.py b/backend/systembridgebackend/modules/bridge/update.py index 69c28d11d..275cfc5ef 100644 --- a/backend/systembridgebackend/modules/bridge/update.py +++ b/backend/systembridgebackend/modules/bridge/update.py @@ -1,12 +1,12 @@ """System Bridge: Update Bridge""" import time -from systembridgeshared.base import Base -from systembridgeshared.const import COLUMN_TIMESTAMP from systembridgeshared.database import Database +from systembridgeshared.models.database_data_bridge import Bridge as DatabaseModel from zeroconf import ServiceStateChange, Zeroconf -from systembridgebackend.modules.bridge import Bridge +from . import Bridge +from ..base import ModuleUpdateBase COLUMN_NAME = "name" COLUMN_ADDRESS = "address" @@ -23,7 +23,7 @@ COLUMN_LAST_ACTIVE_TIMESTAMP = "last_active_timestamp" -class BridgeUpdate(Base): +class BridgeUpdate(ModuleUpdateBase): """Bridge Update""" def __init__( @@ -31,28 +31,7 @@ def __init__( database: Database, ) -> None: """Initialize""" - super().__init__() - - self._database = database - self._database.create_table( - "bridge", - [ - (COLUMN_UUID, "TEXT PRIMARY KEY"), - (COLUMN_NAME, "TEXT"), - (COLUMN_ADDRESS, "TEXT"), - (COLUMN_FQDN, "TEXT"), - (COLUMN_HOST, "TEXT"), - (COLUMN_IP, "TEXT"), - (COLUMN_MAC, "TEXT"), - (COLUMN_PORT, "TEXT"), - (COLUMN_VERSION, "TEXT"), - (COLUMN_WEBSOCKET_ADDRESS, "TEXT"), - (COLUMN_WEBSOCKET_PORT, "TEXT"), - (COLUMN_ACTIVE, "INTEGER"), - (COLUMN_LAST_ACTIVE_TIMESTAMP, "INTEGER"), - (COLUMN_TIMESTAMP, "DOUBLE"), - ], - ) + super().__init__(database) self._bridge = Bridge(self.service_changed) def service_changed( @@ -74,76 +53,26 @@ def service_changed( "Service %s %s: %s - %s", name, state_change, service_type, service_info ) if service_info and service_info.properties: - if state_change in (ServiceStateChange.Added, ServiceStateChange.Updated): - self.write_bridge(name, service_info.properties) - elif state_change == ServiceStateChange.Removed: - self.deactivate_bridge(name, service_info.properties) - - def deactivate_bridge( - self, - name: str, - info: dict, - ) -> None: - """Deactivate bridge""" - self._logger.info("Deactivate bridge %s", name) - self._database.execute_sql( - f"""UPDATE bridge SET {COLUMN_ACTIVE} = 0, {COLUMN_TIMESTAMP} = {time.time()} - WHERE {COLUMN_UUID}={info[b"uuid"].decode("utf-8")}""".replace( - "\n", "" - ).replace( - " ", "" - ), - ) - - def write_bridge( - self, - name: str, - info: dict, - ) -> None: - """Write bridge""" - values = { - COLUMN_UUID: info[b"uuid"].decode("utf-8"), - COLUMN_NAME: name, - COLUMN_ADDRESS: info[b"address"].decode("utf-8"), - COLUMN_FQDN: info[b"fqdn"].decode("utf-8"), - COLUMN_HOST: info[b"host"].decode("utf-8"), - COLUMN_IP: info[b"ip"].decode("utf-8"), - COLUMN_MAC: info[b"mac"].decode("utf-8"), - COLUMN_PORT: info[b"port"].decode("utf-8"), - COLUMN_VERSION: info[b"version"].decode("utf-8"), - COLUMN_WEBSOCKET_ADDRESS: info[b"websocketAddress"].decode("utf-8"), - COLUMN_WEBSOCKET_PORT: info[b"wsPort"].decode("utf-8"), - COLUMN_ACTIVE: 1, - COLUMN_LAST_ACTIVE_TIMESTAMP: time.time(), - COLUMN_TIMESTAMP: time.time(), - } - - self._database.execute_sql( - f"""INSERT INTO bridge ({COLUMN_UUID}, {COLUMN_NAME}, {COLUMN_ADDRESS}, {COLUMN_FQDN}, - {COLUMN_HOST}, {COLUMN_IP}, {COLUMN_MAC}, {COLUMN_PORT}, {COLUMN_VERSION}, - {COLUMN_WEBSOCKET_ADDRESS}, {COLUMN_WEBSOCKET_PORT}, {COLUMN_ACTIVE}, - {COLUMN_LAST_ACTIVE_TIMESTAMP}, {COLUMN_TIMESTAMP}) - VALUES("{values[COLUMN_UUID]}", "{values[COLUMN_NAME]}", "{values[COLUMN_ADDRESS]}", - "{values[COLUMN_FQDN]}", "{values[COLUMN_HOST]}", "{values[COLUMN_IP]}", - "{values[COLUMN_MAC]}", "{values[COLUMN_PORT]}", "{values[COLUMN_VERSION]}", - "{values[COLUMN_WEBSOCKET_ADDRESS]}", "{values[COLUMN_WEBSOCKET_PORT]}", - {values[COLUMN_ACTIVE]}, {values[COLUMN_LAST_ACTIVE_TIMESTAMP]}, - {values[COLUMN_TIMESTAMP]}) - ON CONFLICT({COLUMN_UUID}) DO - UPDATE SET {COLUMN_ADDRESS} = "{values[COLUMN_ADDRESS]}", - {COLUMN_FQDN} = "{values[COLUMN_FQDN]}", {COLUMN_HOST} = "{values[COLUMN_HOST]}", - {COLUMN_IP} = "{values[COLUMN_IP]}", {COLUMN_MAC} = "{values[COLUMN_MAC]}", - {COLUMN_PORT} = "{values[COLUMN_PORT]}", - {COLUMN_VERSION} = "{values[COLUMN_VERSION]}", - {COLUMN_WEBSOCKET_ADDRESS} = "{values[COLUMN_WEBSOCKET_ADDRESS]}", - {COLUMN_WEBSOCKET_PORT} = "{values[COLUMN_WEBSOCKET_PORT]}", - {COLUMN_ACTIVE} = {values[COLUMN_ACTIVE]}, - {COLUMN_LAST_ACTIVE_TIMESTAMP} = {values[COLUMN_LAST_ACTIVE_TIMESTAMP]}, - {COLUMN_TIMESTAMP} = {values[COLUMN_TIMESTAMP]} - WHERE {COLUMN_UUID} = "{values[COLUMN_UUID]}" - """.replace( - "\n", "" - ).replace( - " ", "" + self._database.add_data( + DatabaseModel( + uuid=service_info.properties[b"uuid"].decode("utf-8"), + name=name, + address=service_info.properties[b"address"].decode("utf-8"), + fqdn=service_info.properties[b"fqdn"].decode("utf-8"), + host=service_info.properties[b"host"].decode("utf-8"), + ip=service_info.properties[b"ip"].decode("utf-8"), + mac=service_info.properties[b"mac"].decode("utf-8"), + port=service_info.properties[b"port"].decode("utf-8"), + version=service_info.properties[b"version"].decode("utf-8"), + websocket_address=service_info.properties[ + b"websocketAddress" + ].decode("utf-8"), + websocket_port=service_info.properties[b"wsPort"].decode("utf-8"), + active=1 + if state_change + in (ServiceStateChange.Added, ServiceStateChange.Updated) + else 0, + last_active_timestamp=time.time(), + timestamp=time.time(), + ) ) - ) diff --git a/backend/systembridgebackend/modules/cpu/update.py b/backend/systembridgebackend/modules/cpu/update.py index f73ef6ec9..83a7aaffd 100644 --- a/backend/systembridgebackend/modules/cpu/update.py +++ b/backend/systembridgebackend/modules/cpu/update.py @@ -3,8 +3,8 @@ from systembridgeshared.database import Database -from systembridgebackend.modules.base import ModuleUpdateBase -from systembridgebackend.modules.cpu import CPU +from . import CPU +from ..base import ModuleUpdateBase class CPUUpdate(ModuleUpdateBase): @@ -15,52 +15,90 @@ def __init__( database: Database, ) -> None: """Initialize""" - super().__init__(database, "cpu") + super().__init__(database) self._cpu = CPU() async def update_count(self) -> None: """Update CPU count""" - self._database.write("cpu", "count", self._cpu.count()) + self._database.add_data( + self._database.create_data( + "count", + self._cpu.count(), + ) + ) async def update_frequency(self) -> None: """Update CPU frequency""" for key, value in self._cpu.freq()._asdict().items(): - self._database.write("cpu", f"frequency_{key}", value) + self._database.add_data( + self._database.create_data( + f"frequency_{key}", + value, + ) + ) async def update_frequency_per_cpu(self) -> None: """Update CPU frequency per CPU""" count = 0 for data in [freq._asdict() for freq in self._cpu.freq_per_cpu()]: for key, value in data.items(): - self._database.write("cpu", f"frequency_{count}_{key}", value) + self._database.add_data( + self._database.create_data( + f"frequency_{count}_{key}", + value, + ) + ) count += 1 async def update_load_average(self) -> None: """Update load average""" avg_tuple = self._cpu.load_average() result = sum([avg_tuple[0], avg_tuple[1], avg_tuple[2]]) / 3 - self._database.write("cpu", "load_average", result) + self._database.add_data( + self._database.create_data( + "load_average", + result, + ) + ) async def update_stats(self) -> None: """Update stats""" for key, value in self._cpu.stats()._asdict().items(): - self._database.write("cpu", f"stats_{key}", value) + self._database.add_data( + self._database.create_data( + f"stats_{key}", + value, + ) + ) async def update_temperature(self) -> None: """Update temperature""" - self._database.write( - "cpu", "temperature", self._cpu.temperature(self._database) + self._database.add_data( + self._database.create_data( + "temperature", + self._cpu.temperature(self._database), + ) ) async def update_times(self) -> None: """Update times""" for key, value in self._cpu.times()._asdict().items(): - self._database.write("cpu", f"times_{key}", value) + self._database.add_data( + self._database.create_data( + f"times_{key}", + value, + ) + ) async def update_times_percent(self) -> None: """Update times percent""" for key, value in self._cpu.times_percent()._asdict().items(): - self._database.write("cpu", f"times_percent_{key}", value) + self._database.add_data( + self._database.create_data( + f"times_percent_{key}", + value, + ) + ) async def update_times_per_cpu(self) -> None: """Update times per CPU""" @@ -75,25 +113,44 @@ async def update_times_per_cpu_percent(self) -> None: count = 0 for data in [freq._asdict() for freq in self._cpu.times_per_cpu_percent()]: for key, value in data.items(): - self._database.write( - "cpu", f"times_per_cpu_percent_{count}_{key}", value + self._database.add_data( + self._database.create_data( + "cpu", + f"times_per_cpu_percent_{count}_{key}", + value, + ) ) count += 1 async def update_usage(self) -> None: """Update usage""" - self._database.write("cpu", "usage", self._cpu.usage()) + self._database.add_data( + self._database.create_data( + "usage", + self._cpu.usage(), + ) + ) async def update_usage_per_cpu(self) -> None: """Update usage per CPU""" count = 0 for value in self._cpu.usage_per_cpu(): - self._database.write("cpu", f"usage_{count}", value) + self._database.add_data( + self._database.create_data( + f"usage_{count}", + value, + ) + ) count += 1 async def update_voltage(self) -> None: """Update voltage""" - self._database.write("cpu", "voltage", self._cpu.voltage(self._database)) + self._database.add_data( + self._database.create_data( + "voltage", + self._cpu.voltage(self._database), + ) + ) async def update_all_data(self) -> None: """Update data""" diff --git a/backend/systembridgebackend/modules/disk/update.py b/backend/systembridgebackend/modules/disk/update.py index 3d64aedca..513af83da 100644 --- a/backend/systembridgebackend/modules/disk/update.py +++ b/backend/systembridgebackend/modules/disk/update.py @@ -3,8 +3,8 @@ from systembridgeshared.database import Database -from systembridgebackend.modules.base import ModuleUpdateBase -from systembridgebackend.modules.disk import Disk +from . import Disk +from ..base import ModuleUpdateBase class DiskUpdate(ModuleUpdateBase): diff --git a/backend/systembridgebackend/modules/display/update.py b/backend/systembridgebackend/modules/display/update.py index b4dbb0373..7c25b1d01 100644 --- a/backend/systembridgebackend/modules/display/update.py +++ b/backend/systembridgebackend/modules/display/update.py @@ -4,8 +4,8 @@ from systembridgeshared.common import make_key from systembridgeshared.database import Database -from systembridgebackend.modules.base import ModuleUpdateBase -from systembridgebackend.modules.display import Display +from . import Display +from ..base import ModuleUpdateBase class DisplayUpdate(ModuleUpdateBase): diff --git a/backend/systembridgebackend/modules/gpu/update.py b/backend/systembridgebackend/modules/gpu/update.py index d427b34ee..80122c140 100644 --- a/backend/systembridgebackend/modules/gpu/update.py +++ b/backend/systembridgebackend/modules/gpu/update.py @@ -4,8 +4,8 @@ from systembridgeshared.common import make_key from systembridgeshared.database import Database -from systembridgebackend.modules.base import ModuleUpdateBase -from systembridgebackend.modules.gpu import GPU +from . import GPU +from ..base import ModuleUpdateBase class GPUUpdate(ModuleUpdateBase): diff --git a/backend/systembridgebackend/modules/listeners.py b/backend/systembridgebackend/modules/listeners.py index 70a6c4cfa..8ccdc2652 100644 --- a/backend/systembridgebackend/modules/listeners.py +++ b/backend/systembridgebackend/modules/listeners.py @@ -1,12 +1,12 @@ """System Bridge: Module Listeners""" import asyncio -from collections.abc import Callable -from collections.abc import Awaitable +from collections.abc import Awaitable, Callable from systembridgeshared.base import Base from systembridgeshared.const import MODEL_MAP -from systembridgeshared.database import Database +from systembridgeshared.database import TABLE_MAP, Database +from systembridgeshared.models.data import DataDict class Listener: @@ -15,7 +15,7 @@ class Listener: def __init__( self, listener_id: str, - data_changed_callback: Callable[[str, dict], Awaitable[None]], + data_changed_callback: Callable[[str, DataDict], Awaitable[None]], modules: list[str], # pylint: disable=unsubscriptable-object ) -> None: """Initialize""" @@ -34,8 +34,8 @@ def __init__( ) -> None: """Initialize""" super().__init__() - self._database = database - self._implemented_modules = implemented_modules + self._database: Database = database + self._implemented_modules: list[str] = implemented_modules self._data: dict = {module: {} for module in self._implemented_modules} # pylint: disable=unsubscriptable-object self._registered_listeners: list[Listener] = [] @@ -43,7 +43,7 @@ def __init__( async def add_listener( self, listener_id: str, - data_changed_callback: Callable[[str, dict], Awaitable[None]], + data_changed_callback: Callable[[str, DataDict], Awaitable[None]], modules: list[str], # pylint: disable=unsubscriptable-object ) -> bool: """Add modules to listener""" @@ -62,9 +62,6 @@ async def add_listener( async def refresh_data(self) -> None: """Refresh data""" - if not self._database.connected: - self._database.connect() - # Get modules from registered listeners modules = [] for listener in self._registered_listeners: @@ -94,7 +91,7 @@ async def refresh_data_by_module( self._logger.warning("Unknown model: %s", module) return - new_data = self._database.table_data_to_ordered_dict(module) + new_data = self._database.get_data_dict(TABLE_MAP[module]) if new_data is None: self._logger.warning("No data found for module: %s", module) return diff --git a/backend/systembridgebackend/modules/memory/update.py b/backend/systembridgebackend/modules/memory/update.py index 599b699ab..e676c7a8c 100644 --- a/backend/systembridgebackend/modules/memory/update.py +++ b/backend/systembridgebackend/modules/memory/update.py @@ -1,9 +1,10 @@ """System Bridge: Update Memory""" import asyncio + from systembridgeshared.database import Database -from systembridgebackend.modules.base import ModuleUpdateBase -from systembridgebackend.modules.memory import Memory +from . import Memory +from ..base import ModuleUpdateBase class MemoryUpdate(ModuleUpdateBase): diff --git a/backend/systembridgebackend/modules/network/update.py b/backend/systembridgebackend/modules/network/update.py index 9db3bbe42..522b33d6b 100644 --- a/backend/systembridgebackend/modules/network/update.py +++ b/backend/systembridgebackend/modules/network/update.py @@ -3,8 +3,8 @@ from systembridgeshared.database import Database -from systembridgebackend.modules.base import ModuleUpdateBase -from systembridgebackend.modules.network import Network +from . import Network +from ..base import ModuleUpdateBase class NetworkUpdate(ModuleUpdateBase): diff --git a/backend/systembridgebackend/modules/sensors/update.py b/backend/systembridgebackend/modules/sensors/update.py index 110152be5..746588d7a 100644 --- a/backend/systembridgebackend/modules/sensors/update.py +++ b/backend/systembridgebackend/modules/sensors/update.py @@ -1,7 +1,6 @@ """System Bridge: Update Sensors""" import asyncio -from systembridgeshared.base import Base from systembridgeshared.common import make_key from systembridgeshared.const import ( COLUMN_HARDWARE_NAME, @@ -14,10 +13,11 @@ ) from systembridgeshared.database import Database -from systembridgebackend.modules.sensors import Sensors +from . import Sensors +from ..base import ModuleUpdateBase -class SensorsUpdate(Base): +class SensorsUpdate(ModuleUpdateBase): """Sensors Update""" def __init__( @@ -25,21 +25,7 @@ def __init__( database: Database, ) -> None: """Initialize""" - super().__init__() - - self._database = database - self._database.create_table( - "sensors", - [ - (COLUMN_KEY, "TEXT PRIMARY KEY"), - (COLUMN_TYPE, "TEXT"), - (COLUMN_NAME, "TEXT"), - (COLUMN_HARDWARE_TYPE, "TEXT"), - (COLUMN_HARDWARE_NAME, "TEXT"), - (COLUMN_VALUE, "TEXT"), - (COLUMN_TIMESTAMP, "DOUBLE"), - ], - ) + super().__init__(database) self._sensors = Sensors() async def update_fans(self) -> None: diff --git a/backend/systembridgebackend/modules/system/__init__.py b/backend/systembridgebackend/modules/system/__init__.py index 5f64c9ea0..4402792db 100644 --- a/backend/systembridgebackend/modules/system/__init__.py +++ b/backend/systembridgebackend/modules/system/__init__.py @@ -22,7 +22,7 @@ from systembridgeshared.base import Base from systembridgeshared.database import Database -from systembridgebackend._version import __version__ +from ..._version import __version__ class System(Base): diff --git a/backend/systembridgebackend/modules/system/update.py b/backend/systembridgebackend/modules/system/update.py index b75aec110..c35ebd228 100644 --- a/backend/systembridgebackend/modules/system/update.py +++ b/backend/systembridgebackend/modules/system/update.py @@ -3,8 +3,8 @@ from systembridgeshared.database import Database -from systembridgebackend.modules.base import ModuleUpdateBase -from systembridgebackend.modules.system import System +from . import System +from ..base import ModuleUpdateBase class SystemUpdate(ModuleUpdateBase): diff --git a/backend/systembridgebackend/modules/update.py b/backend/systembridgebackend/modules/update.py index 99a6bc101..b689be81a 100644 --- a/backend/systembridgebackend/modules/update.py +++ b/backend/systembridgebackend/modules/update.py @@ -1,21 +1,20 @@ """System Bridge: Modules Update""" import asyncio -from collections.abc import Callable -from collections.abc import Awaitable +from collections.abc import Awaitable, Callable from systembridgeshared.base import Base from systembridgeshared.database import Database -from systembridgebackend.modules.battery.update import BatteryUpdate -from systembridgebackend.modules.bridge.update import BridgeUpdate -from systembridgebackend.modules.cpu.update import CPUUpdate -from systembridgebackend.modules.disk.update import DiskUpdate -from systembridgebackend.modules.display.update import DisplayUpdate -from systembridgebackend.modules.gpu.update import GPUUpdate -from systembridgebackend.modules.memory.update import MemoryUpdate -from systembridgebackend.modules.network.update import NetworkUpdate -from systembridgebackend.modules.sensors.update import SensorsUpdate -from systembridgebackend.modules.system.update import SystemUpdate +from .battery.update import BatteryUpdate +from .bridge.update import BridgeUpdate +from .cpu.update import CPUUpdate +from .disk.update import DiskUpdate +from .display.update import DisplayUpdate +from .gpu.update import GPUUpdate +from .memory.update import MemoryUpdate +from .network.update import NetworkUpdate +from .sensors.update import SensorsUpdate +from .system.update import SystemUpdate class Update(Base): @@ -31,15 +30,15 @@ def __init__( self._classes = [ {"name": "battery", "cls": BatteryUpdate(self._database)}, - {"name": "disk", "cls": DiskUpdate(self._database)}, - {"name": "system", "cls": SystemUpdate(self._database)}, + # {"name": "disk", "cls": DiskUpdate(self._database)}, + # {"name": "system", "cls": SystemUpdate(self._database)}, ] self._classes_frequent = [ {"name": "cpu", "cls": CPUUpdate(self._database)}, - {"name": "display", "cls": DisplayUpdate(self._database)}, - {"name": "gpu", "cls": GPUUpdate(self._database)}, - {"name": "memory", "cls": MemoryUpdate(self._database)}, - {"name": "network", "cls": NetworkUpdate(self._database)}, + # {"name": "display", "cls": DisplayUpdate(self._database)}, + # {"name": "gpu", "cls": GPUUpdate(self._database)}, + # {"name": "memory", "cls": MemoryUpdate(self._database)}, + # {"name": "network", "cls": NetworkUpdate(self._database)}, ] BridgeUpdate(self._database) @@ -58,8 +57,6 @@ async def update_data( ) -> None: """Update Data""" self._logger.info("Update data") - if not self._database.connected: - self._database.connect() tasks = [self._update(cls, updated_callback) for cls in self._classes] await asyncio.gather(*tasks) @@ -72,8 +69,6 @@ async def update_frequent_data( ) -> None: """Update Data""" self._logger.info("Update frequent data") - if not self._database.connected: - self._database.connect() sensors_update = SensorsUpdate(self._database) await sensors_update.update_all_data() diff --git a/backend/systembridgebackend/server/__init__.py b/backend/systembridgebackend/server/__init__.py index 0e47382ab..fde2069f7 100644 --- a/backend/systembridgebackend/server/__init__.py +++ b/backend/systembridgebackend/server/__init__.py @@ -22,14 +22,14 @@ from systembridgeshared.models.notification import Notification from systembridgeshared.settings import Settings -from systembridgebackend.data import Data -from systembridgebackend.gui import GUIAttemptsExceededException, start_gui_threaded -from systembridgebackend.modules.listeners import Listeners -from systembridgebackend.server.auth import ApiKeyAuthentication -from systembridgebackend.server.cors import add_cors_headers -from systembridgebackend.server.keyboard import handler_keyboard -from systembridgebackend.server.mdns import MDNSAdvertisement -from systembridgebackend.server.media import ( +from ..data import Data +from ..gui import GUIAttemptsExceededException, start_gui_threaded +from ..modules.listeners import Listeners +from ..server.auth import ApiKeyAuthentication +from ..server.cors import add_cors_headers +from ..server.keyboard import handler_keyboard +from ..server.mdns import MDNSAdvertisement +from ..server.media import ( handler_media_directories, handler_media_file, handler_media_file_data, @@ -37,10 +37,10 @@ handler_media_files, handler_media_play, ) -from systembridgebackend.server.notification import handler_notification -from systembridgebackend.server.open import handler_open -from systembridgebackend.server.options import setup_options -from systembridgebackend.server.power import ( +from ..server.notification import handler_notification +from ..server.open import handler_open +from ..server.options import setup_options +from ..server.power import ( handler_hibernate, handler_lock, handler_logout, @@ -48,8 +48,8 @@ handler_shutdown, handler_sleep, ) -from systembridgebackend.server.update import handler_update -from systembridgebackend.server.websocket import WebSocketHandler +from ..server.update import handler_update +from ..server.websocket import WebSocketHandler class ApplicationExitException(BaseException): @@ -127,7 +127,7 @@ async def _handler_data_all( """Data handler all""" if table not in implemented_modules: return json({"message": f"Data module {table} not found"}, status=404) - return json(self._database.table_data_to_ordered_dict(table)) + return json(self._database.get_data_dict(table)) @auth.key_required async def _handler_data_by_key( @@ -139,13 +139,14 @@ async def _handler_data_by_key( if table not in implemented_modules: return json({"message": f"Data module {table} not found"}, status=404) - data = self._database.read_table_by_key(table, key).to_dict( - orient="records" - )[0] + data = self._database.get_data_item_by_key(table, key) + if data is None: + return json({"message": f"Data item {key} not found"}, status=404) + return json( { - data["key"]: data["value"], - "last_updated": data["timestamp"], + data.key: data.value, + "last_updated": data.timestamp, } ) diff --git a/backend/systembridgebackend/server/mdns.py b/backend/systembridgebackend/server/mdns.py index c78e6d717..96c109f67 100644 --- a/backend/systembridgebackend/server/mdns.py +++ b/backend/systembridgebackend/server/mdns.py @@ -3,7 +3,7 @@ from systembridgeshared.settings import SETTING_PORT_API, Settings from zeroconf import InterfaceChoice, ServiceInfo, Zeroconf -from systembridgebackend.modules.system import System +from ..modules.system import System ZEROCONF_TYPE = "_system-bridge._tcp.local." diff --git a/backend/systembridgebackend/server/options.py b/backend/systembridgebackend/server/options.py index a7dca4e5e..e252050ab 100644 --- a/backend/systembridgebackend/server/options.py +++ b/backend/systembridgebackend/server/options.py @@ -4,7 +4,7 @@ from sanic import Sanic, response from sanic_routing.router import Route -from systembridgebackend.server.cors import _add_cors_headers +from .cors import _add_cors_headers def _compile_routes_needing_options(routes: dict[str, Route]) -> dict[str, frozenset]: diff --git a/backend/systembridgebackend/server/websocket.py b/backend/systembridgebackend/server/websocket.py index e37ad624e..106cdb370 100644 --- a/backend/systembridgebackend/server/websocket.py +++ b/backend/systembridgebackend/server/websocket.py @@ -100,20 +100,13 @@ from systembridgeshared.settings import SECRET_API_KEY, Settings from systembridgeshared.update import Update -from systembridgebackend.autostart import autostart_disable, autostart_enable -from systembridgebackend.gui import start_gui_threaded -from systembridgebackend.modules.listeners import Listeners -from systembridgebackend.server.keyboard import keyboard_keypress, keyboard_text -from systembridgebackend.server.media import get_directories, get_file, get_files -from systembridgebackend.server.open import open_path, open_url -from systembridgebackend.server.power import ( - hibernate, - lock, - logout, - restart, - shutdown, - sleep, -) +from ..autostart import autostart_disable, autostart_enable +from ..gui import start_gui_threaded +from ..modules.listeners import Listeners +from ..server.keyboard import keyboard_keypress, keyboard_text +from ..server.media import get_directories, get_file, get_files +from ..server.open import open_path, open_url +from ..server.power import hibernate, lock, logout, restart, shutdown, sleep class WebSocketHandler(Base): diff --git a/backend/systembridgebackend/shortcut/__init__.py b/backend/systembridgebackend/shortcut/__init__.py index de9ae8bb8..30c14d723 100644 --- a/backend/systembridgebackend/shortcut/__init__.py +++ b/backend/systembridgebackend/shortcut/__init__.py @@ -6,11 +6,11 @@ def create_shortcuts(): """Create shortcuts""" if "Windows" in platform.system(): # pylint: disable=import-error, import-outside-toplevel - from systembridgebackend.shortcut.windows import create_windows_shortcuts + from .windows import create_windows_shortcuts create_windows_shortcuts() elif "Linux" in platform.system(): # pylint: disable=import-error, import-outside-toplevel - from systembridgebackend.shortcut.linux import create_linux_shortcuts + from .linux import create_linux_shortcuts create_linux_shortcuts() diff --git a/connector/systembridgeconnector/const.py b/connector/systembridgeconnector/const.py index bed41d877..1362f7355 100644 --- a/connector/systembridgeconnector/const.py +++ b/connector/systembridgeconnector/const.py @@ -148,15 +148,17 @@ MODEL_KEYBOARD_KEY = "keyboard_key" MODEL_KEYBOARD_TEXT = "keyboard_text" MODEL_MEDIA_DIRECTORIES = "media_directories" -MODEL_MEDIA_FILES = "media_files" MODEL_MEDIA_FILE = "media_file" +MODEL_MEDIA_FILES = "media_files" MODEL_MEMORY = "memory" MODEL_NETWORK = "network" MODEL_NOTIFICATION = "notification" MODEL_OPEN_PATH = "open_path" MODEL_OPEN_URL = "open_url" MODEL_RESPONSE = "response" +MODEL_SECRETS = "secrets" MODEL_SENSORS = "sensors" +MODEL_SETTINGS = "settings" MODEL_SYSTEM = "system" MODEL_MAP = { diff --git a/shared/systembridgeshared/common.py b/shared/systembridgeshared/common.py index 34e956841..70aac88bf 100644 --- a/shared/systembridgeshared/common.py +++ b/shared/systembridgeshared/common.py @@ -4,7 +4,7 @@ import json import os import re -from typing import Union +from typing import Any, Union from appdirs import AppDirs @@ -26,7 +26,7 @@ def camel_to_snake(name): def convert_string_to_correct_type( value: str, -) -> Union[bool, float, int, str, list, dict, None]: +) -> Union[bool, float, int, str, list[Any], dict[str, Any], None]: """Convert string to correct data type""" try: if value.startswith("'") and value.endswith("'"): diff --git a/shared/systembridgeshared/const.py b/shared/systembridgeshared/const.py index 601fccc5d..092e74037 100644 --- a/shared/systembridgeshared/const.py +++ b/shared/systembridgeshared/const.py @@ -163,15 +163,17 @@ MODEL_KEYBOARD_KEY = "keyboard_key" MODEL_KEYBOARD_TEXT = "keyboard_text" MODEL_MEDIA_DIRECTORIES = "media_directories" -MODEL_MEDIA_FILES = "media_files" MODEL_MEDIA_FILE = "media_file" +MODEL_MEDIA_FILES = "media_files" MODEL_MEMORY = "memory" MODEL_NETWORK = "network" MODEL_NOTIFICATION = "notification" MODEL_OPEN_PATH = "open_path" MODEL_OPEN_URL = "open_url" MODEL_RESPONSE = "response" +MODEL_SECRETS = "secrets" MODEL_SENSORS = "sensors" +MODEL_SETTINGS = "settings" MODEL_SYSTEM = "system" MODEL_MAP = { diff --git a/shared/systembridgeshared/database.py b/shared/systembridgeshared/database.py index f972af819..37288fb0a 100644 --- a/shared/systembridgeshared/database.py +++ b/shared/systembridgeshared/database.py @@ -1,15 +1,13 @@ """System Bridge Shared: Database""" from __future__ import annotations -from collections import OrderedDict from collections.abc import Mapping import json import os -from sqlite3 import OperationalError, connect from time import time -from typing import Optional, Union +from typing import Any, List, Optional, Union -from pandas import DataFrame, read_sql_query +from sqlmodel import Column, Session, SQLModel, Table, create_engine, select from systembridgeshared.base import Base from systembridgeshared.common import ( @@ -17,14 +15,83 @@ get_user_data_directory, ) from systembridgeshared.const import ( - COLUMN_HARDWARE_NAME, - COLUMN_HARDWARE_TYPE, - COLUMN_KEY, - COLUMN_NAME, - COLUMN_TIMESTAMP, - COLUMN_TYPE, - COLUMN_VALUE, + MODEL_BATTERY, + MODEL_BRIDGE, + MODEL_CPU, + MODEL_DISK, + MODEL_DISPLAY, + MODEL_GPU, + MODEL_MEMORY, + MODEL_NETWORK, + MODEL_SECRETS, + MODEL_SENSORS, + MODEL_SETTINGS, + MODEL_SYSTEM, ) +from systembridgeshared.models.data import DataDict +from systembridgeshared.models.database_data import ( + CPU, + GPU, + Battery, + Data, + Disk, + Display, + Memory, + Network, + Secrets, + Settings, + System, +) +from systembridgeshared.models.database_data_bridge import Bridge +from systembridgeshared.models.database_data_sensors import Sensors + +# TABLES: List[Table] = [ +# Table(MODEL_BATTERY, Battery), +# Table(MODEL_BRIDGE, Bridge), +# Table(MODEL_CPU, CPU), +# Table(MODEL_DISK, Disk), +# Table(MODEL_DISPLAY, Display), +# Table(MODEL_GPU, GPU), +# Table(MODEL_MEMORY, Memory), +# Table(MODEL_NETWORK, Network), +# Table(MODEL_SECRETS, Secrets), +# Table(MODEL_SENSORS, Sensors), +# Table(MODEL_SETTINGS, Settings), +# Table(MODEL_SYSTEM, System), +# ] + +# TABLE_MAP: Mapping[str, Any] = {table.name: table.metadata for table in TABLES} + +TABLE_MAP: Mapping[str, Any] = { + MODEL_BATTERY: Battery, + MODEL_BRIDGE: Bridge, + MODEL_CPU: CPU, + MODEL_DISK: Disk, + MODEL_DISPLAY: Display, + MODEL_GPU: GPU, + MODEL_MEMORY: Memory, + MODEL_NETWORK: Network, + MODEL_SECRETS: Secrets, + MODEL_SENSORS: Sensors, + MODEL_SETTINGS: Settings, + MODEL_SYSTEM: System, +} + + +TableDataType = Union[ + Battery, + Bridge, + CPU, + Disk, + Display, + GPU, + Memory, + Network, + Secrets, + Sensors, + Settings, + System, +] class Database(Base): @@ -33,221 +100,116 @@ class Database(Base): def __init__(self): """Initialise""" super().__init__() - self._connection = connect( - os.path.join(get_user_data_directory(), "systembridge.db"), - check_same_thread=False, + self._engine = create_engine( + f"sqlite:///{os.path.join(get_user_data_directory(), 'systembridge.db')}" + ) + SQLModel.metadata.create_all( + self._engine, + # tables=TABLES, ) - @property - def connected(self) -> bool: - """Check if connected""" - return self._connection is not None - - def execute_sql( - self, - sql: str, - ) -> None: - """Execute SQL""" - if not self.connected: - self.connect() - self._logger.debug("Executing SQL: %s", sql) - try: - self._connection.commit() - except OperationalError: - pass - try: - self._connection.execute(sql) - self._connection.commit() - except OperationalError as error: - self._logger.warning( - "Error executing SQL: %s\n%s", - sql, - error, - ) - - def execute_sql_with_params( + def add_data( self, - sql: str, - params: Mapping, + data: Any, ) -> None: - """Execute SQL""" - self._logger.debug("Executing SQL: %s\n%s", sql, params) - try: - self._connection.commit() - except OperationalError: - pass - try: - self._connection.execute(sql, params) - self._connection.commit() - except OperationalError as error: - self._logger.warning( - "Error executing SQL: %s\n%s", - sql, - error, - ) - - def connect(self) -> None: - """Connect to database""" - self._connection = connect( - os.path.join(get_user_data_directory(), "systembridge.db"), - check_same_thread=False, - ) + """Add data to database""" + with Session(self._engine) as session: + session.add(data) + session.commit() - def close(self) -> None: - """Close connection""" - self._connection.close() - - def check_table_for_key( + def create_data( self, - table_name: str, key: str, - ) -> bool: - """Check if key exists in table""" - return self.read_table_by_key(table_name, key).empty + value: Union[bool, float, int, str, list, dict, None], + timestamp: Optional[float] = None, + ) -> Data: + """Create data""" + if timestamp is None: + timestamp = time() - def read_table( - self, - table_name: str, - ) -> DataFrame: - """Read table""" - return self.read_query( - f"SELECT * FROM {table_name}", - ) + # Convert list or dict to JSON + if isinstance(value, (dict, list)): + value = json.dumps(value) + else: + value = str(value) - def read_table_by_key( - self, - table_name: str, - key: str, - ) -> DataFrame: - """Read table by key""" - return self.read_query( - f"SELECT * FROM {table_name} WHERE {COLUMN_KEY} = '{key}'", + return Data( + key=key, + value=value, + timestamp=timestamp, ) - def read_query( - self, - query: str, - ) -> DataFrame: - """Read SQL""" - if not self.connected: - self.connect() - self._logger.debug("Reading SQL: %s", query) - return read_sql_query(query, self._connection) - - def create_table( - self, - table_name: str, - columns: list[tuple], - ) -> None: - """Create table""" - sql = f"CREATE TABLE IF NOT EXISTS {table_name} (" - for column in columns: - sql += f"{column[0]} {column[1]}," - sql = sql[:-1] + ")" - self.execute_sql(sql) - - def clear_table( - self, - table_name: str, - ) -> None: - """Clear table""" - self.execute_sql(f"DELETE FROM {table_name}") - - def clear_table_by_key( + def create_sensor_data( self, - table_name: str, key: str, - ) -> None: - """Clear table by key""" - self.execute_sql(f"DELETE FROM {table_name} WHERE {COLUMN_KEY} = '{key}'") - - def write( - self, - table_name: str, - data_key: str, - data_value: Union[bool, float, int, str, list, dict, None], - data_timestamp: Optional[float] = None, - ) -> None: - """Write to table""" - if data_timestamp is None: - data_timestamp = time() + type: str, + name: str, + hardware_type: str, + hardware_name: str, + value: Union[bool, float, int, str, list, dict, None], + timestamp: Optional[float] = None, + ) -> Sensors: + """Create data""" + if timestamp is None: + timestamp = time() # Convert list or dict to JSON - if isinstance(data_value, (dict, list)): - data_value = json.dumps(data_value) + if isinstance(value, (dict, list)): + value = json.dumps(value) else: - data_value = str(data_value) - - self.execute_sql( - f"""INSERT INTO {table_name} ({COLUMN_KEY}, {COLUMN_VALUE}, {COLUMN_TIMESTAMP}) - VALUES ('{data_key}', '{data_value}', {data_timestamp}) - ON CONFLICT({COLUMN_KEY}) DO - UPDATE SET {COLUMN_VALUE} = '{data_value}', {COLUMN_TIMESTAMP} = {data_timestamp} - WHERE {COLUMN_KEY} = '{data_key}' - """.replace( - "\n", "" - ).replace( - " ", "" - ), + value = str(value) + + return Sensors( + key=key, + type=type, + name=name, + hardware_type=hardware_type, + hardware_name=hardware_name, + value=value, + timestamp=timestamp, ) - def write_sensor( + def get_data( self, - table_name: str, - data_key: str, - data_type: str, - data_name: str, - data_hardware_type: str, - data_hardware_name: str, - data_value: Union[bool, float, int, str, list, dict, None], - data_timestamp: Optional[float] = None, - ) -> None: - """Write to table""" - if data_timestamp is None: - data_timestamp = time() + table, + ) -> List[Data]: + """Get data from database""" + with Session(self._engine) as session: + return session.exec(select(table)).all() - # Convert list or dict to JSON - if isinstance(data_value, (dict, list)): - data_value = json.dumps(data_value) - else: - data_value = str(data_value) - - self.execute_sql( - f"""INSERT INTO {table_name} ({COLUMN_KEY}, {COLUMN_TYPE}, {COLUMN_NAME}, - {COLUMN_HARDWARE_TYPE}, {COLUMN_HARDWARE_NAME}, {COLUMN_VALUE}, {COLUMN_TIMESTAMP}) - VALUES ('{data_key}', '{data_type}', '{data_name}', '{data_hardware_type}', - '{data_hardware_name}', '{data_value}', {data_timestamp}) - ON CONFLICT({COLUMN_KEY}) DO - UPDATE SET {COLUMN_VALUE} = '{data_value}', {COLUMN_TIMESTAMP} = {data_timestamp} - WHERE {COLUMN_KEY} = '{data_key}' - """.replace( - "\n", "" - ).replace( - " ", "" - ), - ) + def get_data_by_key( + self, + table, + key: str, + ) -> List[Data]: + """Get data from database by key""" + with Session(self._engine) as session: + return session.exec(select(table).where(table.key == key)).all() + + def get_data_item_by_key( + self, + table, + key: str, + ) -> Optional[Data]: + """Get data item from database by key""" + with Session(self._engine) as session: + return session.exec(select(table).where(table.key == key)).first() - def table_data_to_ordered_dict( + def get_data_dict( self, - table_name: str, - ) -> Optional[OrderedDict]: - """Convert table to Ordereddict""" - data_dict = self.read_table(table_name).to_dict(orient="records") - if len(data_dict) == 0: - return None - data: dict = { - "last_updated": {}, - } - for item in data_dict: - data = { - **data, - item[COLUMN_KEY]: convert_string_to_correct_type(item[COLUMN_VALUE]), - "last_updated": { - **data["last_updated"], # type: ignore - item[COLUMN_KEY]: item["timestamp"], - }, - } - output = OrderedDict(sorted(data.items())) - output.move_to_end("last_updated", last=True) - - return output + table, + ) -> DataDict: + """Get data from database as dictionary""" + data: dict[str, Any] = {"last_updated": {}} + result = self.get_data(table) + for item in result: + if item.value is None: + data[item.key] = None + else: + data[item.key] = convert_string_to_correct_type(item.value) + if item.timestamp is None: + data["last_updated"][item.key] = None + else: + data["last_updated"][item.key] = item.timestamp + + return DataDict(**data) diff --git a/shared/systembridgeshared/models/data.py b/shared/systembridgeshared/models/data.py index 27ced2ca5..62a4ca245 100644 --- a/shared/systembridgeshared/models/data.py +++ b/shared/systembridgeshared/models/data.py @@ -2,7 +2,7 @@ from __future__ import annotations -from pydantic import BaseModel, Field +from pydantic import BaseModel, Extra, Field from systembridgeshared.models.battery import Battery from systembridgeshared.models.bridge import Bridge @@ -29,3 +29,10 @@ class Data(BaseModel): network: Network = Field(..., alias="network") sensors: Sensors = Field(..., alias="sensors") system: System = Field(..., alias="system") + + +class DataDict(BaseModel): + class Config: + extra = Extra.allow + + last_updated: dict[str, float] = Field(..., description="Last updated") diff --git a/shared/systembridgeshared/models/database_data.py b/shared/systembridgeshared/models/database_data.py index a54a782cf..60f3e17ab 100644 --- a/shared/systembridgeshared/models/database_data.py +++ b/shared/systembridgeshared/models/database_data.py @@ -7,9 +7,49 @@ from sqlmodel import Field, SQLModel -class DatabaseData(SQLModel, table=True): +class Data(SQLModel): """Database Data""" key: str = Field(primary_key=True, nullable=False) value: Optional[str] = Field(default=None, nullable=True) timestamp: Optional[float] = Field(default=None, nullable=True) + + +class Battery(Data, table=True): + """Database Data Battery""" + + +class CPU(Data, table=True): + """Database Data CPU""" + + +class Disk(Data, table=True): + """Database Data Disk""" + + +class Display(Data, table=True): + """Database Data Display""" + + +class GPU(Data, table=True): + """Database Data GPU""" + + +class Memory(Data, table=True): + """Database Data Memory""" + + +class Network(Data, table=True): + """Database Data Network""" + + +class Secrets(Data, table=True): + """Database Data Secrets""" + + +class Settings(Data, table=True): + """Database Data Settings""" + + +class System(Data, table=True): + """System""" diff --git a/shared/systembridgeshared/models/database_data_bridge.py b/shared/systembridgeshared/models/database_data_bridge.py index 677f44d74..97ba48a6c 100644 --- a/shared/systembridgeshared/models/database_data_bridge.py +++ b/shared/systembridgeshared/models/database_data_bridge.py @@ -7,7 +7,7 @@ from sqlmodel import Field, SQLModel -class DatabaseDataBridge(SQLModel, table=True): +class Bridge(SQLModel, table=True): """Database Data Bridge""" uuid: str = Field(primary_key=True, nullable=False) diff --git a/shared/systembridgeshared/models/database_data_sensor.py b/shared/systembridgeshared/models/database_data_sensor.py deleted file mode 100644 index b393a6c10..000000000 --- a/shared/systembridgeshared/models/database_data_sensor.py +++ /dev/null @@ -1,19 +0,0 @@ -"""System Bridge: Models - Database Data Sensor""" - -from __future__ import annotations - -from typing import Optional - -from sqlmodel import Field, SQLModel - - -class DatabaseDataSensor(SQLModel, table=True): - """Database Data Sensor""" - - key: str = Field(primary_key=True, nullable=False) - type: str = Field(nullable=False) - name: Optional[str] = Field(default=None, nullable=True) - hardware_type: Optional[str] = Field(default=None, nullable=True) - hardware_name: Optional[str] = Field(default=None, nullable=True) - value: Optional[str] = Field(default=None, nullable=True) - timestamp: Optional[float] = Field(default=None, nullable=True) diff --git a/shared/systembridgeshared/models/database_data_sensors.py b/shared/systembridgeshared/models/database_data_sensors.py new file mode 100644 index 000000000..dfa11dd25 --- /dev/null +++ b/shared/systembridgeshared/models/database_data_sensors.py @@ -0,0 +1,18 @@ +"""System Bridge: Models - Database Data Sensors""" + +from __future__ import annotations + +from typing import Optional + +from sqlmodel import Field + +from systembridgeshared.models.database_data import Data + + +class Sensors(Data, table=True): + """Database Data Sensors""" + + type: str = Field(nullable=False) + name: Optional[str] = Field(default=None, nullable=True) + hardware_type: Optional[str] = Field(default=None, nullable=True) + hardware_name: Optional[str] = Field(default=None, nullable=True) diff --git a/shared/systembridgeshared/settings.py b/shared/systembridgeshared/settings.py index fb49cf0ba..ea3c19051 100644 --- a/shared/systembridgeshared/settings.py +++ b/shared/systembridgeshared/settings.py @@ -4,7 +4,7 @@ import io import os from os.path import exists -from typing import Union +from typing import Any, Union from uuid import uuid4 from appdirs import AppDirs @@ -13,18 +13,18 @@ from systembridgeshared.base import Base from systembridgeshared.common import convert_string_to_correct_type from systembridgeshared.const import ( - COLUMN_KEY, - COLUMN_TIMESTAMP, - COLUMN_VALUE, SECRET_API_KEY, SETTING_ADDITIONAL_MEDIA_DIRECTORIES, SETTING_AUTOSTART, SETTING_LOG_LEVEL, SETTING_PORT_API, - TABLE_SECRETS, - TABLE_SETTINGS, ) from systembridgeshared.database import Database +from systembridgeshared.models.database_data import ( + Data as DatabaseData, + Secrets as DatabaseSecrets, + Settings as DatabaseSettings, +) class Settings(Base): @@ -36,28 +36,10 @@ def __init__( ) -> None: """Initialize""" super().__init__() - self._database = database - self._database.create_table( - TABLE_SECRETS, - [ - (COLUMN_KEY, "TEXT PRIMARY KEY"), - (COLUMN_VALUE, "TEXT"), - (COLUMN_TIMESTAMP, "DOUBLE"), - ], - ) - - self._database.create_table( - TABLE_SETTINGS, - [ - (COLUMN_KEY, "TEXT PRIMARY KEY"), - (COLUMN_VALUE, "TEXT"), - (COLUMN_TIMESTAMP, "DOUBLE"), - ], - ) # Generate default encryption key - self._encryption_key = None + self._encryption_key: str = "" secret_key_path = os.path.join( AppDirs("systembridge", "timmo001").user_data_dir, "secret.key" ) @@ -65,69 +47,84 @@ def __init__( with io.open(secret_key_path, encoding="utf-8") as file: self._encryption_key = file.read().splitlines()[0] if not self._encryption_key: - self._encryption_key = Fernet.generate_key() + self._encryption_key = Fernet.generate_key().decode() with io.open(secret_key_path, "w", encoding="utf-8") as file: - file.write(self._encryption_key.decode()) + file.write(self._encryption_key) # Default Secrets - if self._database.check_table_for_key(TABLE_SECRETS, SECRET_API_KEY): + if ( + self._database.get_data_item_by_key(DatabaseSecrets, SECRET_API_KEY) + is not None + ): self.set_secret(SECRET_API_KEY, str(uuid4())) # Default Settings - if self._database.check_table_for_key(TABLE_SETTINGS, SETTING_AUTOSTART): + if ( + self._database.get_data_item_by_key(DatabaseSettings, SETTING_AUTOSTART) + is not None + ): self.set(SETTING_AUTOSTART, False) - if self._database.check_table_for_key(TABLE_SETTINGS, SETTING_LOG_LEVEL): + if ( + self._database.get_data_item_by_key(DatabaseSettings, SETTING_LOG_LEVEL) + is not None + ): self.set(SETTING_LOG_LEVEL, "INFO") - if self._database.check_table_for_key(TABLE_SETTINGS, SETTING_PORT_API): + if ( + self._database.get_data_item_by_key(DatabaseSettings, SETTING_PORT_API) + is not None + ): self.set(SETTING_PORT_API, 9170) - if self._database.check_table_for_key( - TABLE_SETTINGS, SETTING_ADDITIONAL_MEDIA_DIRECTORIES + if ( + self._database.get_data_item_by_key( + DatabaseSettings, SETTING_ADDITIONAL_MEDIA_DIRECTORIES + ) + is not None ): self.set(SETTING_ADDITIONAL_MEDIA_DIRECTORIES, []) - def get_all(self) -> list[dict]: + def get_all(self) -> list[DatabaseData]: """Get settings""" - records = self._database.read_table(TABLE_SETTINGS).to_dict(orient="records") - for record in records: - record[COLUMN_VALUE] = convert_string_to_correct_type(record[COLUMN_VALUE]) + records = self._database.get_data(DatabaseSettings) + # for record in records: + # if record.value is not None: + # record.value = convert_string_to_correct_type(record.value) return records def get( self, key: str, - ) -> Union[bool, float, int, str, list, dict, None]: + ) -> Union[bool, float, int, str, list[Any], dict[str, Any], None]: """Get setting""" - record = self._database.read_table_by_key(TABLE_SETTINGS, key).to_dict( - orient="records" - ) - return ( - convert_string_to_correct_type(record[0]["value"]) - if record and len(record) > 0 - else None - ) + record = self._database.get_data_item_by_key(DatabaseSettings, key) + if record is None or record.value is None: + return None + return convert_string_to_correct_type(record.value) def get_secret( self, key: str, ) -> str: """Get secret""" - record = self._database.read_table_by_key(TABLE_SECRETS, key).to_dict( - orient="records" - ) - if not record or len(record) < 1: + record = self._database.get_data_item_by_key(DatabaseSecrets, key) + if record is None or record.value is None: raise KeyError(f"Secret {key} not found") - secret = record[0]["value"] - fernet = Fernet(self._encryption_key) # type: ignore + secret = record.value + fernet = Fernet(self._encryption_key) return fernet.decrypt(secret.encode()).decode() def set( self, key: str, - value: Union[bool, float, int, str, list, dict, None], + value: Union[bool, float, int, str, list[Any], dict[str, Any], None], ) -> None: """Set setting""" - self._database.write(TABLE_SETTINGS, key, value) + self._database.add_data( + self._database.create_data( + key, + value, + ) + ) def set_secret( self, @@ -135,8 +132,11 @@ def set_secret( value: str, ) -> None: """Set secret""" - fernet = Fernet(self._encryption_key) # type: ignore + fernet = Fernet(self._encryption_key) - self._database.write( - TABLE_SECRETS, key, fernet.encrypt(value.encode()).decode() + self._database.add_data( + self._database.create_data( + key, + fernet.encrypt(value.encode()).decode(), + ) ) From 5b48e1c561cad37d37c6ec57e59fc4484e644b2e Mon Sep 17 00:00:00 2001 From: Aidan Timson Date: Sat, 6 Aug 2022 01:02:27 +0100 Subject: [PATCH 05/38] Better --- .../systembridgebackend/modules/listeners.py | 2 +- gui/systembridgegui/system_tray.py | 32 ++++++---- shared/systembridgeshared/database.py | 64 ++++++++++--------- shared/systembridgeshared/settings.py | 34 +++++----- 4 files changed, 72 insertions(+), 60 deletions(-) diff --git a/backend/systembridgebackend/modules/listeners.py b/backend/systembridgebackend/modules/listeners.py index 8ccdc2652..bc7e57235 100644 --- a/backend/systembridgebackend/modules/listeners.py +++ b/backend/systembridgebackend/modules/listeners.py @@ -98,7 +98,7 @@ async def refresh_data_by_module( if new_data != self._data[module]: self._logger.info("Data changed for module: %s", module) - self._data[module] = model(**new_data).dict() + self._data[module] = model(**new_data.dict()) for listener in self._registered_listeners: self._logger.info("Listener: %s - %s", listener.id, listener.modules) if module in listener.modules: diff --git a/gui/systembridgegui/system_tray.py b/gui/systembridgegui/system_tray.py index beb301e52..961969554 100644 --- a/gui/systembridgegui/system_tray.py +++ b/gui/systembridgegui/system_tray.py @@ -12,6 +12,7 @@ from systembridgeshared.base import Base from systembridgeshared.common import get_user_data_directory from systembridgeshared.database import Database +from systembridgeshared.models.database_data import System as DatabaseSystem from systembridgeshared.settings import Settings PATH_BRIDGES_OPEN_ON = "/app/bridges/openon.html" @@ -70,19 +71,26 @@ def __init__( menu.addSeparator() latest_version_text = "Latest Version" - version_current = self._database.read_table_by_key("system", "version").to_dict( - orient="records" - )[0]["value"] - version_latest = self._database.read_table_by_key( - "system", "version_latest" - ).to_dict(orient="records")[0]["value"] - record_version_newer_available = self._database.read_table_by_key( - "system", "version_newer_available" + result_version_current = self._database.get_data_item_by_key( + DatabaseSystem, "version" ) - version_newer_available = ( - record_version_newer_available.to_dict(orient="records")[0]["value"] - if not record_version_newer_available.empty - else "False" + version_current = ( + result_version_current.value if result_version_current is not None else None + ) + result_version_latest = self._database.get_data_item_by_key( + DatabaseSystem, "version_latest" + ) + version_latest = ( + result_version_latest.value if result_version_latest is not None else None + ) + result_version_newer_available = self._database.get_data_item_by_key( + DatabaseSystem, "version_newer_available" + ) + version_newer_available: str = ( + result_version_newer_available.value + if result_version_newer_available is not None + and result_version_newer_available.value is not None + else "" ) if version_newer_available.lower() == "true": diff --git a/shared/systembridgeshared/database.py b/shared/systembridgeshared/database.py index 37288fb0a..85bf82e3c 100644 --- a/shared/systembridgeshared/database.py +++ b/shared/systembridgeshared/database.py @@ -108,36 +108,36 @@ def __init__(self): # tables=TABLES, ) - def add_data( - self, - data: Any, - ) -> None: - """Add data to database""" - with Session(self._engine) as session: - session.add(data) - session.commit() - - def create_data( - self, - key: str, - value: Union[bool, float, int, str, list, dict, None], - timestamp: Optional[float] = None, - ) -> Data: - """Create data""" - if timestamp is None: - timestamp = time() - - # Convert list or dict to JSON - if isinstance(value, (dict, list)): - value = json.dumps(value) - else: - value = str(value) - - return Data( - key=key, - value=value, - timestamp=timestamp, - ) + # def add_data( + # self, + # data: TableDataType, + # ) -> None: + # """Add data to database""" + # with Session(self._engine) as session: + # session.add(data) + # session.commit() + + # def create_data( + # self, + # key: str, + # value: Union[bool, float, int, str, list, dict, None], + # timestamp: Optional[float] = None, + # ) -> Data: + # """Create data""" + # if timestamp is None: + # timestamp = time() + + # # Convert list or dict to JSON + # if isinstance(value, (dict, list)): + # value = json.dumps(value) + # else: + # value = str(value) + + # return Data( + # key=key, + # value=value, + # timestamp=timestamp, + # ) def create_sensor_data( self, @@ -213,3 +213,7 @@ def get_data_dict( data["last_updated"][item.key] = item.timestamp return DataDict(**data) + + def get_session(self) -> Session: + """Get session""" + return Session(self._engine) diff --git a/shared/systembridgeshared/settings.py b/shared/systembridgeshared/settings.py index ea3c19051..a76dfdd47 100644 --- a/shared/systembridgeshared/settings.py +++ b/shared/systembridgeshared/settings.py @@ -10,17 +10,17 @@ from appdirs import AppDirs from cryptography.fernet import Fernet -from systembridgeshared.base import Base -from systembridgeshared.common import convert_string_to_correct_type -from systembridgeshared.const import ( +from .base import Base +from .common import convert_string_to_correct_type +from .const import ( SECRET_API_KEY, SETTING_ADDITIONAL_MEDIA_DIRECTORIES, SETTING_AUTOSTART, SETTING_LOG_LEVEL, SETTING_PORT_API, ) -from systembridgeshared.database import Database -from systembridgeshared.models.database_data import ( +from .database import Database +from .models.database_data import ( Data as DatabaseData, Secrets as DatabaseSecrets, Settings as DatabaseSettings, @@ -63,7 +63,7 @@ def __init__( self._database.get_data_item_by_key(DatabaseSettings, SETTING_AUTOSTART) is not None ): - self.set(SETTING_AUTOSTART, False) + self.set(SETTING_AUTOSTART, str(False)) if ( self._database.get_data_item_by_key(DatabaseSettings, SETTING_LOG_LEVEL) is not None @@ -73,14 +73,14 @@ def __init__( self._database.get_data_item_by_key(DatabaseSettings, SETTING_PORT_API) is not None ): - self.set(SETTING_PORT_API, 9170) + self.set(SETTING_PORT_API, str(9170)) if ( self._database.get_data_item_by_key( DatabaseSettings, SETTING_ADDITIONAL_MEDIA_DIRECTORIES ) is not None ): - self.set(SETTING_ADDITIONAL_MEDIA_DIRECTORIES, []) + self.set(SETTING_ADDITIONAL_MEDIA_DIRECTORIES, str([])) def get_all(self) -> list[DatabaseData]: """Get settings""" @@ -116,13 +116,13 @@ def get_secret( def set( self, key: str, - value: Union[bool, float, int, str, list[Any], dict[str, Any], None], + value: str, ) -> None: """Set setting""" - self._database.add_data( - self._database.create_data( - key, - value, + self._database.get_session().add( + DatabaseSettings( + key=key, + value=value, ) ) @@ -134,9 +134,9 @@ def set_secret( """Set secret""" fernet = Fernet(self._encryption_key) - self._database.add_data( - self._database.create_data( - key, - fernet.encrypt(value.encode()).decode(), + self._database.get_session().add( + DatabaseSecrets( + key=key, + value=fernet.encrypt(value.encode()).decode(), ) ) From e0b874e1f143e95fc2a3a647f5b644fc9cc2d81a Mon Sep 17 00:00:00 2001 From: Aidan Timson Date: Sat, 6 Aug 2022 01:18:02 +0100 Subject: [PATCH 06/38] Better --- .../modules/battery/update.py | 30 ++-- .../modules/sensors/update.py | 129 ++++++++++-------- shared/systembridgeshared/settings.py | 10 +- 3 files changed, 99 insertions(+), 70 deletions(-) diff --git a/backend/systembridgebackend/modules/battery/update.py b/backend/systembridgebackend/modules/battery/update.py index ced6436b0..aef1b6904 100644 --- a/backend/systembridgebackend/modules/battery/update.py +++ b/backend/systembridgebackend/modules/battery/update.py @@ -1,6 +1,7 @@ """System Bridge: Update Battery""" import asyncio +from sqlmodel import Session from systembridgeshared.common import camel_to_snake from systembridgeshared.database import Database from systembridgeshared.models.database_data import Battery as DatabaseModel @@ -20,25 +21,31 @@ def __init__( super().__init__(database) self._battery = Battery() - async def update_sensors(self) -> None: + async def update_sensors( + self, + session: Session, + ) -> None: """Update Battery Sensors""" if data := self._battery.sensors(): for key, value in data._asdict().items(): # From status if key == "percent": continue - self._database.add_data( - self._database.create_data( - "sensors_{key}", - value, + session.add( + DatabaseModel( + key="sensors_{key}", + value=value, ) ) - async def update_status(self) -> None: + async def update_status( + self, + session: Session, + ) -> None: """Update Battery Status""" for key, value in self._battery.status().items(): - self._database.add_data( - self._database.create_data( + session.add( + DatabaseModel( key=camel_to_snake(key), value=value, ) @@ -46,9 +53,12 @@ async def update_status(self) -> None: async def update_all_data(self) -> None: """Update data""" + session = self._database.get_session() await asyncio.gather( *[ - self.update_sensors(), - self.update_status(), + self.update_sensors(session), + self.update_status(session), ] ) + session.commit() + session.close() diff --git a/backend/systembridgebackend/modules/sensors/update.py b/backend/systembridgebackend/modules/sensors/update.py index 746588d7a..c5d7519ff 100644 --- a/backend/systembridgebackend/modules/sensors/update.py +++ b/backend/systembridgebackend/modules/sensors/update.py @@ -1,17 +1,10 @@ """System Bridge: Update Sensors""" import asyncio +from sqlmodel import Session, select from systembridgeshared.common import make_key -from systembridgeshared.const import ( - COLUMN_HARDWARE_NAME, - COLUMN_HARDWARE_TYPE, - COLUMN_KEY, - COLUMN_NAME, - COLUMN_TIMESTAMP, - COLUMN_TYPE, - COLUMN_VALUE, -) from systembridgeshared.database import Database +from systembridgeshared.models.database_data_sensors import Sensors as DatabaseModel from . import Sensors from ..base import ModuleUpdateBase @@ -28,39 +21,50 @@ def __init__( super().__init__(database) self._sensors = Sensors() - async def update_fans(self) -> None: + async def update_fans( + self, + session: Session, + ) -> None: """Update Fan Sensors""" if data := self._sensors.fans(): for key, value in data.items(): for item in value: for subkey, subvalue in item._asdict().items(): - self._database.write_sensor( - "sensors", - f"fans_{key}_{subkey}", - subkey, - subkey, - key, - key, - subvalue, + session.add( + DatabaseModel( + key=f"fans_{key}_{subkey}", + type=subkey, + name=subkey, + hardware_type=key, + hardware_name=key, + value=subvalue, + ) ) - async def update_temperatures(self) -> None: + async def update_temperatures( + self, + session: Session, + ) -> None: """Update Temperature Sensors""" if data := self._sensors.temperatures(): for key, value in data.items(): for item in value: for subkey, subvalue in item._asdict().items(): - self._database.write_sensor( - "sensors", - f"temperatures_{key}_{subkey}", - subkey, - subkey, - key, - key, - subvalue, + session.add( + DatabaseModel( + key=f"temperatures_{key}_{subkey}", + type=subkey, + name=subkey, + hardware_type=key, + hardware_name=key, + value=subvalue, + ) ) - async def update_windows_sensors(self) -> None: + async def update_windows_sensors( + self, + session: Session, + ) -> None: """Update Windows Sensors""" if not (data := self._sensors.windows_sensors()): return @@ -69,18 +73,19 @@ async def update_windows_sensors(self) -> None: key_hardware = ( make_key(f"_{hardware['name']}") if "name" in hardware else None ) - for sensor in hardware["sensors"] or []: + for sensor in hardware[DatabaseModel] or []: key_sensor_name = make_key(sensor["name"]) key_sensor_type = make_key(sensor["type"]) - self._database.write_sensor( - "sensors", - f"windows_hardware{key_hardware}_{key_sensor_name}_{key_sensor_type}", - sensor["type"], - sensor["name"], - hardware["type"], - hardware["name"], - sensor["value"] if "value" in sensor else None, + session.add( + DatabaseModel( + key=f"windows_hardware{key_hardware}_{key_sensor_name}_{key_sensor_type}", + type=sensor["type"], + name=sensor["name"], + hardware_type=hardware["type"], + hardware_name=hardware["name"], + value=sensor["value"] if "value" in sensor else None, + ) ) if "nvidia" in data and data["nvidia"] is not None: @@ -103,38 +108,46 @@ async def update_windows_sensors(self) -> None: f"Display {name_hardware.split('DISPLAY')[1]}" ) for subkey, subvalue in hardware.items(): - self._database.write_sensor( - "sensors", - f"windows_nvidia{key_hardware}_{sensor}_{counter}_{subkey}", - sensor, - subkey, - type_hardware, - name_hardware, - subvalue, + session.add( + DatabaseModel( + key=f"windows_nvidia{key_hardware}_{sensor}_{counter}_{subkey}", + type=sensor, + name=subkey, + hardware_type=type_hardware, + hardware_name=name_hardware, + value=subvalue, + ) ) counter += 1 else: for subkey, subvalue in value.items(): - self._database.write_sensor( - "sensors", - f"windows_nvidia_{sensor}_{subkey}", - sensor, - subkey, - value["type"] if "type" in value else "NVIDIA", - value["name"] if "name" in value else "NVIDIA", - subvalue, + session.add( + DatabaseModel( + key=f"windows_nvidia_{sensor}_{subkey}", + type=sensor, + name=subkey, + hardware_type=value["type"] + if "type" in value + else "NVIDIA", + hardware_name=value["name"] + if "name" in value + else "NVIDIA", + value=subvalue, + ) ) async def update_all_data(self) -> None: """Update data""" # Clear table in case of hardware changes since last run - self._database.clear_table("sensors") - + session = self._database.get_session() + session.delete(select(DatabaseModel)) await asyncio.gather( *[ - self.update_fans(), - self.update_temperatures(), - self.update_windows_sensors(), + self.update_fans(session), + self.update_temperatures(session), + self.update_windows_sensors(session), ] ) + session.commit() + session.close() diff --git a/shared/systembridgeshared/settings.py b/shared/systembridgeshared/settings.py index a76dfdd47..1e5617c38 100644 --- a/shared/systembridgeshared/settings.py +++ b/shared/systembridgeshared/settings.py @@ -119,12 +119,15 @@ def set( value: str, ) -> None: """Set setting""" - self._database.get_session().add( + session = self._database.get_session() + session.add( DatabaseSettings( key=key, value=value, ) ) + session.commit() + session.close() def set_secret( self, @@ -134,9 +137,12 @@ def set_secret( """Set secret""" fernet = Fernet(self._encryption_key) - self._database.get_session().add( + session = self._database.get_session() + session.add( DatabaseSecrets( key=key, value=fernet.encrypt(value.encode()).decode(), ) ) + session.commit() + session.close() From 63422a4ad61a41e20aecadc5220e98d2704f0561 Mon Sep 17 00:00:00 2001 From: Aidan Timson Date: Sat, 6 Aug 2022 12:35:03 +0100 Subject: [PATCH 07/38] Renive bridge module (unused) --- .../modules/bridge/__init__.py | 23 ------ .../modules/bridge/update.py | 78 ------------------- 2 files changed, 101 deletions(-) delete mode 100644 backend/systembridgebackend/modules/bridge/__init__.py delete mode 100644 backend/systembridgebackend/modules/bridge/update.py diff --git a/backend/systembridgebackend/modules/bridge/__init__.py b/backend/systembridgebackend/modules/bridge/__init__.py deleted file mode 100644 index a9882c685..000000000 --- a/backend/systembridgebackend/modules/bridge/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -"""System Bridge: Bridge""" -from collections.abc import Callable - -from systembridgeshared.base import Base -from zeroconf import InterfaceChoice, ServiceBrowser, Zeroconf - - -class Bridge(Base): - """Bridge""" - - def __init__( - self, - callback: Callable, - ): - super().__init__() - ServiceBrowser( - Zeroconf( - interfaces=InterfaceChoice.All, - unicast=True, - ), - "_system-bridge._tcp.local.", - handlers=[callback], - ) diff --git a/backend/systembridgebackend/modules/bridge/update.py b/backend/systembridgebackend/modules/bridge/update.py deleted file mode 100644 index 275cfc5ef..000000000 --- a/backend/systembridgebackend/modules/bridge/update.py +++ /dev/null @@ -1,78 +0,0 @@ -"""System Bridge: Update Bridge""" -import time - -from systembridgeshared.database import Database -from systembridgeshared.models.database_data_bridge import Bridge as DatabaseModel -from zeroconf import ServiceStateChange, Zeroconf - -from . import Bridge -from ..base import ModuleUpdateBase - -COLUMN_NAME = "name" -COLUMN_ADDRESS = "address" -COLUMN_FQDN = "fqdn" -COLUMN_HOST = "host" -COLUMN_IP = "ip" -COLUMN_MAC = "mac" -COLUMN_PORT = "port" -COLUMN_UUID = "uuid" -COLUMN_VERSION = "version" -COLUMN_WEBSOCKET_ADDRESS = "websocket_address" -COLUMN_WEBSOCKET_PORT = "websocket_port" -COLUMN_ACTIVE = "active" -COLUMN_LAST_ACTIVE_TIMESTAMP = "last_active_timestamp" - - -class BridgeUpdate(ModuleUpdateBase): - """Bridge Update""" - - def __init__( - self, - database: Database, - ) -> None: - """Initialize""" - super().__init__(database) - self._bridge = Bridge(self.service_changed) - - def service_changed( - self, - zeroconf: Zeroconf, - service_type: str, - name: str, - state_change: ServiceStateChange, - ) -> None: - """Service changed""" - self._logger.info( - "service_changed: type=%s name=%s state_change=%s", - service_type, - name, - state_change, - ) - service_info = zeroconf.get_service_info(service_type, name) - self._logger.info( - "Service %s %s: %s - %s", name, state_change, service_type, service_info - ) - if service_info and service_info.properties: - self._database.add_data( - DatabaseModel( - uuid=service_info.properties[b"uuid"].decode("utf-8"), - name=name, - address=service_info.properties[b"address"].decode("utf-8"), - fqdn=service_info.properties[b"fqdn"].decode("utf-8"), - host=service_info.properties[b"host"].decode("utf-8"), - ip=service_info.properties[b"ip"].decode("utf-8"), - mac=service_info.properties[b"mac"].decode("utf-8"), - port=service_info.properties[b"port"].decode("utf-8"), - version=service_info.properties[b"version"].decode("utf-8"), - websocket_address=service_info.properties[ - b"websocketAddress" - ].decode("utf-8"), - websocket_port=service_info.properties[b"wsPort"].decode("utf-8"), - active=1 - if state_change - in (ServiceStateChange.Added, ServiceStateChange.Updated) - else 0, - last_active_timestamp=time.time(), - timestamp=time.time(), - ) - ) From e4e208d4f2495f19cd7721a55c3b7344e2d24f35 Mon Sep 17 00:00:00 2001 From: Aidan Timson Date: Sat, 6 Aug 2022 12:35:06 +0100 Subject: [PATCH 08/38] Fix --- .../systembridgebackend/modules/sensors/update.py | 4 +++- shared/systembridgeshared/settings.py | 13 +++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/backend/systembridgebackend/modules/sensors/update.py b/backend/systembridgebackend/modules/sensors/update.py index c5d7519ff..00bccbcdf 100644 --- a/backend/systembridgebackend/modules/sensors/update.py +++ b/backend/systembridgebackend/modules/sensors/update.py @@ -141,7 +141,9 @@ async def update_all_data(self) -> None: # Clear table in case of hardware changes since last run session = self._database.get_session() - session.delete(select(DatabaseModel)) + for sensor in session.exec(select(DatabaseModel)).all(): + session.delete(sensor) + session.commit() await asyncio.gather( *[ self.update_fans(session), diff --git a/shared/systembridgeshared/settings.py b/shared/systembridgeshared/settings.py index 1e5617c38..d798b616e 100644 --- a/shared/systembridgeshared/settings.py +++ b/shared/systembridgeshared/settings.py @@ -52,33 +52,30 @@ def __init__( file.write(self._encryption_key) # Default Secrets - if ( - self._database.get_data_item_by_key(DatabaseSecrets, SECRET_API_KEY) - is not None - ): + if self._database.get_data_item_by_key(DatabaseSecrets, SECRET_API_KEY) is None: self.set_secret(SECRET_API_KEY, str(uuid4())) # Default Settings if ( self._database.get_data_item_by_key(DatabaseSettings, SETTING_AUTOSTART) - is not None + is None ): self.set(SETTING_AUTOSTART, str(False)) if ( self._database.get_data_item_by_key(DatabaseSettings, SETTING_LOG_LEVEL) - is not None + is None ): self.set(SETTING_LOG_LEVEL, "INFO") if ( self._database.get_data_item_by_key(DatabaseSettings, SETTING_PORT_API) - is not None + is None ): self.set(SETTING_PORT_API, str(9170)) if ( self._database.get_data_item_by_key( DatabaseSettings, SETTING_ADDITIONAL_MEDIA_DIRECTORIES ) - is not None + is None ): self.set(SETTING_ADDITIONAL_MEDIA_DIRECTORIES, str([])) From 20d79595d0184c9eec1ea853fcce333848749f8e Mon Sep 17 00:00:00 2001 From: Aidan Timson Date: Sat, 6 Aug 2022 12:35:47 +0100 Subject: [PATCH 09/38] Renive bridge module (unused) --- backend/systembridgebackend/modules/update.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/backend/systembridgebackend/modules/update.py b/backend/systembridgebackend/modules/update.py index b689be81a..68afcca2a 100644 --- a/backend/systembridgebackend/modules/update.py +++ b/backend/systembridgebackend/modules/update.py @@ -6,7 +6,6 @@ from systembridgeshared.database import Database from .battery.update import BatteryUpdate -from .bridge.update import BridgeUpdate from .cpu.update import CPUUpdate from .disk.update import DiskUpdate from .display.update import DisplayUpdate @@ -40,7 +39,6 @@ def __init__( # {"name": "memory", "cls": MemoryUpdate(self._database)}, # {"name": "network", "cls": NetworkUpdate(self._database)}, ] - BridgeUpdate(self._database) async def _update( self, From 624b46a300091648fc3a804cf7565526125e20fa Mon Sep 17 00:00:00 2001 From: Aidan Timson Date: Sat, 6 Aug 2022 12:42:15 +0100 Subject: [PATCH 10/38] Fix --- .../systembridgebackend/modules/cpu/update.py | 200 +++++++++++------- backend/systembridgebackend/modules/update.py | 13 +- 2 files changed, 131 insertions(+), 82 deletions(-) diff --git a/backend/systembridgebackend/modules/cpu/update.py b/backend/systembridgebackend/modules/cpu/update.py index 83a7aaffd..e391b3664 100644 --- a/backend/systembridgebackend/modules/cpu/update.py +++ b/backend/systembridgebackend/modules/cpu/update.py @@ -1,7 +1,9 @@ """System Bridge: Update CPU""" import asyncio +from sqlmodel import Session from systembridgeshared.database import Database +from systembridgeshared.models.database_data import CPU as DatabaseModel from . import CPU from ..base import ModuleUpdateBase @@ -18,156 +20,202 @@ def __init__( super().__init__(database) self._cpu = CPU() - async def update_count(self) -> None: + async def update_count( + self, + session: Session, + ) -> None: """Update CPU count""" - self._database.add_data( - self._database.create_data( - "count", - self._cpu.count(), + session.add( + DatabaseModel( + key="count", + value=str(self._cpu.count()), ) ) - async def update_frequency(self) -> None: + async def update_frequency( + self, + session: Session, + ) -> None: """Update CPU frequency""" for key, value in self._cpu.freq()._asdict().items(): - self._database.add_data( - self._database.create_data( - f"frequency_{key}", - value, + session.add( + DatabaseModel( + key=f"frequency_{key}", + value=value, ) ) - async def update_frequency_per_cpu(self) -> None: + async def update_frequency_per_cpu( + self, + session: Session, + ) -> None: """Update CPU frequency per CPU""" count = 0 for data in [freq._asdict() for freq in self._cpu.freq_per_cpu()]: for key, value in data.items(): - self._database.add_data( - self._database.create_data( - f"frequency_{count}_{key}", - value, + session.add( + DatabaseModel( + key=f"frequency_{count}_{key}", + value=value, ) ) count += 1 - async def update_load_average(self) -> None: + async def update_load_average( + self, + session: Session, + ) -> None: """Update load average""" avg_tuple = self._cpu.load_average() result = sum([avg_tuple[0], avg_tuple[1], avg_tuple[2]]) / 3 - self._database.add_data( - self._database.create_data( - "load_average", - result, + session.add( + DatabaseModel( + key="load_average", + value=str(result), ) ) - async def update_stats(self) -> None: + async def update_stats( + self, + session: Session, + ) -> None: """Update stats""" for key, value in self._cpu.stats()._asdict().items(): - self._database.add_data( - self._database.create_data( - f"stats_{key}", - value, + session.add( + DatabaseModel( + key=f"stats_{key}", + value=value, ) ) - async def update_temperature(self) -> None: + async def update_temperature( + self, + session: Session, + ) -> None: """Update temperature""" - self._database.add_data( - self._database.create_data( - "temperature", - self._cpu.temperature(self._database), + session.add( + DatabaseModel( + key="temperature", + value=str(self._cpu.temperature(self._database)), ) ) - async def update_times(self) -> None: + async def update_times( + self, + session: Session, + ) -> None: """Update times""" for key, value in self._cpu.times()._asdict().items(): - self._database.add_data( - self._database.create_data( - f"times_{key}", - value, + session.add( + DatabaseModel( + key=f"times_{key}", + value=value, ) ) - async def update_times_percent(self) -> None: + async def update_times_percent( + self, + session: Session, + ) -> None: """Update times percent""" for key, value in self._cpu.times_percent()._asdict().items(): - self._database.add_data( - self._database.create_data( - f"times_percent_{key}", - value, + session.add( + DatabaseModel( + key=f"times_percent_{key}", + value=value, ) ) - async def update_times_per_cpu(self) -> None: + async def update_times_per_cpu( + self, + session: Session, + ) -> None: """Update times per CPU""" count = 0 for data in [freq._asdict() for freq in self._cpu.times_per_cpu()]: for key, value in data.items(): - self._database.write("cpu", f"times_per_cpu_{count}_{key}", value) + session.add( + DatabaseModel( + key=f"times_per_cpu_{count}_{key}", + value=value, + ) + ) count += 1 - async def update_times_per_cpu_percent(self) -> None: + async def update_times_per_cpu_percent( + self, + session: Session, + ) -> None: """Update times per CPU percent""" count = 0 for data in [freq._asdict() for freq in self._cpu.times_per_cpu_percent()]: for key, value in data.items(): - self._database.add_data( - self._database.create_data( - "cpu", - f"times_per_cpu_percent_{count}_{key}", - value, + session.add( + DatabaseModel( + key=f"times_per_cpu_percent_{count}_{key}", + value=value, ) ) count += 1 - async def update_usage(self) -> None: + async def update_usage( + self, + session: Session, + ) -> None: """Update usage""" - self._database.add_data( - self._database.create_data( - "usage", - self._cpu.usage(), + session.add( + DatabaseModel( + key="usage", + value=str(self._cpu.usage()), ) ) - async def update_usage_per_cpu(self) -> None: + async def update_usage_per_cpu( + self, + session: Session, + ) -> None: """Update usage per CPU""" count = 0 for value in self._cpu.usage_per_cpu(): - self._database.add_data( - self._database.create_data( - f"usage_{count}", - value, + session.add( + DatabaseModel( + key=f"usage_{count}", + value=str(value), ) ) count += 1 - async def update_voltage(self) -> None: + async def update_voltage( + self, + session: Session, + ) -> None: """Update voltage""" - self._database.add_data( - self._database.create_data( - "voltage", - self._cpu.voltage(self._database), + session.add( + DatabaseModel( + key="voltage", + value=str(self._cpu.voltage(self._database)), ) ) async def update_all_data(self) -> None: """Update data""" + session = self._database.get_session() await asyncio.gather( *[ - self.update_count(), - self.update_frequency(), - self.update_frequency_per_cpu(), - self.update_load_average(), - self.update_stats(), - self.update_temperature(), - self.update_times(), - self.update_times_percent(), - self.update_times_per_cpu(), - self.update_times_per_cpu_percent(), - self.update_usage(), - self.update_usage_per_cpu(), - self.update_voltage(), + self.update_count(session), + self.update_frequency(session), + self.update_frequency_per_cpu(session), + self.update_load_average(session), + self.update_stats(session), + self.update_temperature(session), + self.update_times(session), + self.update_times_percent(session), + self.update_times_per_cpu(session), + self.update_times_per_cpu_percent(session), + self.update_usage(session), + self.update_usage_per_cpu(session), + self.update_voltage(session), ] ) + session.commit() + session.close() diff --git a/backend/systembridgebackend/modules/update.py b/backend/systembridgebackend/modules/update.py index 68afcca2a..084214e67 100644 --- a/backend/systembridgebackend/modules/update.py +++ b/backend/systembridgebackend/modules/update.py @@ -7,13 +7,14 @@ from .battery.update import BatteryUpdate from .cpu.update import CPUUpdate -from .disk.update import DiskUpdate -from .display.update import DisplayUpdate -from .gpu.update import GPUUpdate -from .memory.update import MemoryUpdate -from .network.update import NetworkUpdate from .sensors.update import SensorsUpdate -from .system.update import SystemUpdate + +# from .disk.update import DiskUpdate +# from .display.update import DisplayUpdate +# from .gpu.update import GPUUpdate +# from .memory.update import MemoryUpdate +# from .network.update import NetworkUpdate +# from .system.update import SystemUpdate class Update(Base): From 972fa1f72a48f3c7a5175a16d5553e855a78bc17 Mon Sep 17 00:00:00 2001 From: Aidan Timson Date: Sat, 6 Aug 2022 13:03:00 +0100 Subject: [PATCH 11/38] Fix --- .../modules/sensors/update.py | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/backend/systembridgebackend/modules/sensors/update.py b/backend/systembridgebackend/modules/sensors/update.py index 00bccbcdf..f9754f3ed 100644 --- a/backend/systembridgebackend/modules/sensors/update.py +++ b/backend/systembridgebackend/modules/sensors/update.py @@ -21,6 +21,14 @@ def __init__( super().__init__(database) self._sensors = Sensors() + def _update_data( + self, + session: Session, + data: DatabaseModel, + ) -> None: + """Update data""" + session.add(data) + async def update_fans( self, session: Session, @@ -30,7 +38,8 @@ async def update_fans( for key, value in data.items(): for item in value: for subkey, subvalue in item._asdict().items(): - session.add( + self._update_data( + session, DatabaseModel( key=f"fans_{key}_{subkey}", type=subkey, @@ -38,7 +47,7 @@ async def update_fans( hardware_type=key, hardware_name=key, value=subvalue, - ) + ), ) async def update_temperatures( @@ -50,7 +59,8 @@ async def update_temperatures( for key, value in data.items(): for item in value: for subkey, subvalue in item._asdict().items(): - session.add( + self._update_data( + session, DatabaseModel( key=f"temperatures_{key}_{subkey}", type=subkey, @@ -58,7 +68,7 @@ async def update_temperatures( hardware_type=key, hardware_name=key, value=subvalue, - ) + ), ) async def update_windows_sensors( @@ -73,11 +83,12 @@ async def update_windows_sensors( key_hardware = ( make_key(f"_{hardware['name']}") if "name" in hardware else None ) - for sensor in hardware[DatabaseModel] or []: + for sensor in hardware["sensors"] or []: key_sensor_name = make_key(sensor["name"]) key_sensor_type = make_key(sensor["type"]) - session.add( + self._update_data( + session, DatabaseModel( key=f"windows_hardware{key_hardware}_{key_sensor_name}_{key_sensor_type}", type=sensor["type"], @@ -85,7 +96,7 @@ async def update_windows_sensors( hardware_type=hardware["type"], hardware_name=hardware["name"], value=sensor["value"] if "value" in sensor else None, - ) + ), ) if "nvidia" in data and data["nvidia"] is not None: @@ -108,7 +119,8 @@ async def update_windows_sensors( f"Display {name_hardware.split('DISPLAY')[1]}" ) for subkey, subvalue in hardware.items(): - session.add( + self._update_data( + session, DatabaseModel( key=f"windows_nvidia{key_hardware}_{sensor}_{counter}_{subkey}", type=sensor, @@ -116,12 +128,13 @@ async def update_windows_sensors( hardware_type=type_hardware, hardware_name=name_hardware, value=subvalue, - ) + ), ) counter += 1 else: for subkey, subvalue in value.items(): - session.add( + self._update_data( + session, DatabaseModel( key=f"windows_nvidia_{sensor}_{subkey}", type=sensor, @@ -133,7 +146,7 @@ async def update_windows_sensors( if "name" in value else "NVIDIA", value=subvalue, - ) + ), ) async def update_all_data(self) -> None: From de6e815608c308c75c23ce354fd77f06021b0c0c Mon Sep 17 00:00:00 2001 From: Aidan Timson Date: Sat, 6 Aug 2022 13:03:12 +0100 Subject: [PATCH 12/38] Update function --- .../modules/battery/update.py | 27 +++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/backend/systembridgebackend/modules/battery/update.py b/backend/systembridgebackend/modules/battery/update.py index aef1b6904..bc421f291 100644 --- a/backend/systembridgebackend/modules/battery/update.py +++ b/backend/systembridgebackend/modules/battery/update.py @@ -1,7 +1,7 @@ """System Bridge: Update Battery""" import asyncio -from sqlmodel import Session +from sqlmodel import Session, select from systembridgeshared.common import camel_to_snake from systembridgeshared.database import Database from systembridgeshared.models.database_data import Battery as DatabaseModel @@ -21,6 +21,21 @@ def __init__( super().__init__(database) self._battery = Battery() + def _update_data( + self, + session: Session, + data: DatabaseModel, + ) -> None: + """Update data""" + result = session.exec( + select(DatabaseModel).where(DatabaseModel.key == data.key) + ) + old_data = result.first() + if old_data is None: + session.add(data) + else: + session.refresh(data) + async def update_sensors( self, session: Session, @@ -31,11 +46,12 @@ async def update_sensors( # From status if key == "percent": continue - session.add( + self._update_data( + session, DatabaseModel( key="sensors_{key}", value=value, - ) + ), ) async def update_status( @@ -44,11 +60,12 @@ async def update_status( ) -> None: """Update Battery Status""" for key, value in self._battery.status().items(): - session.add( + self._update_data( + session, DatabaseModel( key=camel_to_snake(key), value=value, - ) + ), ) async def update_all_data(self) -> None: From 7220a0a66967023bc2840bf1479b4e1fd87b6ac0 Mon Sep 17 00:00:00 2001 From: Aidan Timson Date: Sat, 6 Aug 2022 13:47:12 +0100 Subject: [PATCH 13/38] Fix --- backend/systembridgebackend/modules/base.py | 5 +- .../modules/battery/update.py | 42 +---- .../modules/cpu/__init__.py | 7 +- .../systembridgebackend/modules/cpu/update.py | 159 +++++++----------- .../modules/sensors/update.py | 56 ++---- .../systembridgebackend/server/websocket.py | 9 +- shared/systembridgeshared/database.py | 114 ++++--------- 7 files changed, 138 insertions(+), 254 deletions(-) diff --git a/backend/systembridgebackend/modules/base.py b/backend/systembridgebackend/modules/base.py index 3e72b4187..12560aa0a 100644 --- a/backend/systembridgebackend/modules/base.py +++ b/backend/systembridgebackend/modules/base.py @@ -1,6 +1,9 @@ """System Bridge: Module Base""" +from time import time +from typing import Any + +from sqlmodel import Session, select from systembridgeshared.base import Base -from systembridgeshared.const import COLUMN_KEY, COLUMN_TIMESTAMP, COLUMN_VALUE from systembridgeshared.database import Database diff --git a/backend/systembridgebackend/modules/battery/update.py b/backend/systembridgebackend/modules/battery/update.py index bc421f291..c2eff252e 100644 --- a/backend/systembridgebackend/modules/battery/update.py +++ b/backend/systembridgebackend/modules/battery/update.py @@ -1,7 +1,7 @@ """System Bridge: Update Battery""" import asyncio -from sqlmodel import Session, select +from sqlmodel import Session from systembridgeshared.common import camel_to_snake from systembridgeshared.database import Database from systembridgeshared.models.database_data import Battery as DatabaseModel @@ -21,47 +21,26 @@ def __init__( super().__init__(database) self._battery = Battery() - def _update_data( - self, - session: Session, - data: DatabaseModel, - ) -> None: - """Update data""" - result = session.exec( - select(DatabaseModel).where(DatabaseModel.key == data.key) - ) - old_data = result.first() - if old_data is None: - session.add(data) - else: - session.refresh(data) - - async def update_sensors( - self, - session: Session, - ) -> None: + async def update_sensors(self) -> None: """Update Battery Sensors""" if data := self._battery.sensors(): for key, value in data._asdict().items(): # From status if key == "percent": continue - self._update_data( - session, + self._database.update_data( + DatabaseModel, DatabaseModel( key="sensors_{key}", value=value, ), ) - async def update_status( - self, - session: Session, - ) -> None: + async def update_status(self) -> None: """Update Battery Status""" for key, value in self._battery.status().items(): - self._update_data( - session, + self._database.update_data( + DatabaseModel, DatabaseModel( key=camel_to_snake(key), value=value, @@ -70,12 +49,9 @@ async def update_status( async def update_all_data(self) -> None: """Update data""" - session = self._database.get_session() await asyncio.gather( *[ - self.update_sensors(session), - self.update_status(session), + self.update_sensors(), + self.update_status(), ] ) - session.commit() - session.close() diff --git a/backend/systembridgebackend/modules/cpu/__init__.py b/backend/systembridgebackend/modules/cpu/__init__.py index 8f4c4b693..980c2087a 100644 --- a/backend/systembridgebackend/modules/cpu/__init__.py +++ b/backend/systembridgebackend/modules/cpu/__init__.py @@ -21,6 +21,9 @@ COLUMN_VALUE, ) from systembridgeshared.database import Database +from systembridgeshared.models.database_data_sensors import ( + Sensors as SensorsDatabaseModel, +) class CPU(Base): @@ -53,7 +56,7 @@ def temperature( database: Database, ) -> Optional[float]: """CPU temperature""" - for item in database.read_table("sensors").to_dict(orient="records"): + for item in database.get_data(SensorsDatabaseModel): if item[COLUMN_HARDWARE_TYPE] is not None and ( ( "cpu" in item[COLUMN_HARDWARE_TYPE].lower() @@ -105,7 +108,7 @@ def voltage( database: Database, ) -> Optional[float]: """CPU voltage""" - for item in database.read_table("sensors").to_dict(orient="records"): + for item in database.get_data(SensorsDatabaseModel): if ( item[COLUMN_HARDWARE_TYPE] is not None and "cpu" in item[COLUMN_HARDWARE_TYPE].lower() diff --git a/backend/systembridgebackend/modules/cpu/update.py b/backend/systembridgebackend/modules/cpu/update.py index e391b3664..38b78204a 100644 --- a/backend/systembridgebackend/modules/cpu/update.py +++ b/backend/systembridgebackend/modules/cpu/update.py @@ -20,202 +20,173 @@ def __init__( super().__init__(database) self._cpu = CPU() - async def update_count( - self, - session: Session, - ) -> None: + async def update_count(self) -> None: """Update CPU count""" - session.add( + self._database.update_data( + DatabaseModel, DatabaseModel( key="count", value=str(self._cpu.count()), - ) + ), ) - async def update_frequency( - self, - session: Session, - ) -> None: + async def update_frequency(self) -> None: """Update CPU frequency""" for key, value in self._cpu.freq()._asdict().items(): - session.add( + self._database.update_data( + DatabaseModel, DatabaseModel( key=f"frequency_{key}", value=value, - ) + ), ) - async def update_frequency_per_cpu( - self, - session: Session, - ) -> None: + async def update_frequency_per_cpu(self) -> None: """Update CPU frequency per CPU""" count = 0 for data in [freq._asdict() for freq in self._cpu.freq_per_cpu()]: for key, value in data.items(): - session.add( + self._database.update_data( + DatabaseModel, DatabaseModel( key=f"frequency_{count}_{key}", value=value, - ) + ), ) count += 1 - async def update_load_average( - self, - session: Session, - ) -> None: + async def update_load_average(self) -> None: """Update load average""" avg_tuple = self._cpu.load_average() result = sum([avg_tuple[0], avg_tuple[1], avg_tuple[2]]) / 3 - session.add( + self._database.update_data( + DatabaseModel, DatabaseModel( key="load_average", value=str(result), - ) + ), ) - async def update_stats( - self, - session: Session, - ) -> None: + async def update_stats(self) -> None: """Update stats""" for key, value in self._cpu.stats()._asdict().items(): - session.add( + self._database.update_data( + DatabaseModel, DatabaseModel( key=f"stats_{key}", value=value, - ) + ), ) - async def update_temperature( - self, - session: Session, - ) -> None: + async def update_temperature(self) -> None: """Update temperature""" - session.add( + self._database.update_data( + DatabaseModel, DatabaseModel( key="temperature", value=str(self._cpu.temperature(self._database)), - ) + ), ) - async def update_times( - self, - session: Session, - ) -> None: + async def update_times(self) -> None: """Update times""" for key, value in self._cpu.times()._asdict().items(): - session.add( + self._database.update_data( + DatabaseModel, DatabaseModel( key=f"times_{key}", value=value, - ) + ), ) - async def update_times_percent( - self, - session: Session, - ) -> None: + async def update_times_percent(self) -> None: """Update times percent""" for key, value in self._cpu.times_percent()._asdict().items(): - session.add( + self._database.update_data( + DatabaseModel, DatabaseModel( key=f"times_percent_{key}", value=value, - ) + ), ) - async def update_times_per_cpu( - self, - session: Session, - ) -> None: + async def update_times_per_cpu(self) -> None: """Update times per CPU""" count = 0 for data in [freq._asdict() for freq in self._cpu.times_per_cpu()]: for key, value in data.items(): - session.add( + self._database.update_data( + DatabaseModel, DatabaseModel( key=f"times_per_cpu_{count}_{key}", value=value, - ) + ), ) count += 1 - async def update_times_per_cpu_percent( - self, - session: Session, - ) -> None: + async def update_times_per_cpu_percent(self) -> None: """Update times per CPU percent""" count = 0 for data in [freq._asdict() for freq in self._cpu.times_per_cpu_percent()]: for key, value in data.items(): - session.add( + self._database.update_data( + DatabaseModel, DatabaseModel( key=f"times_per_cpu_percent_{count}_{key}", value=value, - ) + ), ) count += 1 - async def update_usage( - self, - session: Session, - ) -> None: + async def update_usage(self) -> None: """Update usage""" - session.add( + self._database.update_data( + DatabaseModel, DatabaseModel( key="usage", value=str(self._cpu.usage()), - ) + ), ) - async def update_usage_per_cpu( - self, - session: Session, - ) -> None: + async def update_usage_per_cpu(self) -> None: """Update usage per CPU""" count = 0 for value in self._cpu.usage_per_cpu(): - session.add( + self._database.update_data( + DatabaseModel, DatabaseModel( key=f"usage_{count}", value=str(value), - ) + ), ) count += 1 - async def update_voltage( - self, - session: Session, - ) -> None: + async def update_voltage(self) -> None: """Update voltage""" - session.add( + self._database.update_data( + DatabaseModel, DatabaseModel( key="voltage", value=str(self._cpu.voltage(self._database)), - ) + ), ) async def update_all_data(self) -> None: """Update data""" - session = self._database.get_session() await asyncio.gather( *[ - self.update_count(session), - self.update_frequency(session), - self.update_frequency_per_cpu(session), - self.update_load_average(session), - self.update_stats(session), - self.update_temperature(session), - self.update_times(session), - self.update_times_percent(session), - self.update_times_per_cpu(session), - self.update_times_per_cpu_percent(session), - self.update_usage(session), - self.update_usage_per_cpu(session), - self.update_voltage(session), + self.update_count(), + self.update_frequency(), + self.update_frequency_per_cpu(), + self.update_load_average(), + self.update_stats(), + self.update_temperature(), + self.update_times(), + self.update_times_percent(), + self.update_times_per_cpu(), + self.update_times_per_cpu_percent(), + self.update_usage(), + self.update_usage_per_cpu(), + self.update_voltage(), ] ) - session.commit() - session.close() diff --git a/backend/systembridgebackend/modules/sensors/update.py b/backend/systembridgebackend/modules/sensors/update.py index f9754f3ed..0e7ffcd93 100644 --- a/backend/systembridgebackend/modules/sensors/update.py +++ b/backend/systembridgebackend/modules/sensors/update.py @@ -21,25 +21,14 @@ def __init__( super().__init__(database) self._sensors = Sensors() - def _update_data( - self, - session: Session, - data: DatabaseModel, - ) -> None: - """Update data""" - session.add(data) - - async def update_fans( - self, - session: Session, - ) -> None: + async def update_fans(self) -> None: """Update Fan Sensors""" if data := self._sensors.fans(): for key, value in data.items(): for item in value: for subkey, subvalue in item._asdict().items(): - self._update_data( - session, + self._database.update_data( + DatabaseModel, DatabaseModel( key=f"fans_{key}_{subkey}", type=subkey, @@ -50,17 +39,14 @@ async def update_fans( ), ) - async def update_temperatures( - self, - session: Session, - ) -> None: + async def update_temperatures(self) -> None: """Update Temperature Sensors""" if data := self._sensors.temperatures(): for key, value in data.items(): for item in value: for subkey, subvalue in item._asdict().items(): - self._update_data( - session, + self._database.update_data( + DatabaseModel, DatabaseModel( key=f"temperatures_{key}_{subkey}", type=subkey, @@ -71,10 +57,7 @@ async def update_temperatures( ), ) - async def update_windows_sensors( - self, - session: Session, - ) -> None: + async def update_windows_sensors(self) -> None: """Update Windows Sensors""" if not (data := self._sensors.windows_sensors()): return @@ -87,8 +70,8 @@ async def update_windows_sensors( key_sensor_name = make_key(sensor["name"]) key_sensor_type = make_key(sensor["type"]) - self._update_data( - session, + self._database.update_data( + DatabaseModel, DatabaseModel( key=f"windows_hardware{key_hardware}_{key_sensor_name}_{key_sensor_type}", type=sensor["type"], @@ -119,8 +102,8 @@ async def update_windows_sensors( f"Display {name_hardware.split('DISPLAY')[1]}" ) for subkey, subvalue in hardware.items(): - self._update_data( - session, + self._database.update_data( + DatabaseModel, DatabaseModel( key=f"windows_nvidia{key_hardware}_{sensor}_{counter}_{subkey}", type=sensor, @@ -133,8 +116,8 @@ async def update_windows_sensors( counter += 1 else: for subkey, subvalue in value.items(): - self._update_data( - session, + self._database.update_data( + DatabaseModel, DatabaseModel( key=f"windows_nvidia_{sensor}_{subkey}", type=sensor, @@ -153,16 +136,11 @@ async def update_all_data(self) -> None: """Update data""" # Clear table in case of hardware changes since last run - session = self._database.get_session() - for sensor in session.exec(select(DatabaseModel)).all(): - session.delete(sensor) - session.commit() + self._database.clear_table(DatabaseModel) await asyncio.gather( *[ - self.update_fans(session), - self.update_temperatures(session), - self.update_windows_sensors(session), + self.update_fans(), + self.update_temperatures(), + self.update_windows_sensors(), ] ) - session.commit() - session.close() diff --git a/backend/systembridgebackend/server/websocket.py b/backend/systembridgebackend/server/websocket.py index 106cdb370..0302d43c5 100644 --- a/backend/systembridgebackend/server/websocket.py +++ b/backend/systembridgebackend/server/websocket.py @@ -4,6 +4,7 @@ import os from uuid import uuid4 +from shared.systembridgeshared.database import TABLE_MAP from systembridgeshared.base import Base from systembridgeshared.const import ( EVENT_BASE, @@ -83,6 +84,7 @@ TYPE_UPDATE_SETTING, ) from systembridgeshared.database import Database +from systembridgeshared.models.data import DataDict from systembridgeshared.models.get_data import GetData from systembridgeshared.models.get_setting import GetSetting from systembridgeshared.models.keyboard_key import KeyboardKey @@ -145,7 +147,7 @@ async def _send_response( async def _data_changed( self, module: str, - data: dict, + data: DataDict, ) -> None: """Data changed""" if module not in self._implemented_modules: @@ -157,7 +159,7 @@ async def _data_changed( EVENT_TYPE: TYPE_DATA_UPDATE, EVENT_MESSAGE: "Data changed", EVENT_MODULE: module, - EVENT_DATA: data, + EVENT_DATA: data.json(), } ) ) @@ -602,7 +604,8 @@ async def _handler( ) for module in model.modules: - data = self._database.table_data_to_ordered_dict(module) + table = TABLE_MAP.get(module) + data = self._database.get_data_dict(table) if data is not None: await self._send_response( Response( diff --git a/shared/systembridgeshared/database.py b/shared/systembridgeshared/database.py index 85bf82e3c..93f524786 100644 --- a/shared/systembridgeshared/database.py +++ b/shared/systembridgeshared/database.py @@ -7,7 +7,7 @@ from time import time from typing import Any, List, Optional, Union -from sqlmodel import Column, Session, SQLModel, Table, create_engine, select +from sqlmodel import Session, SQLModel, create_engine, select from systembridgeshared.base import Base from systembridgeshared.common import ( @@ -45,23 +45,6 @@ from systembridgeshared.models.database_data_bridge import Bridge from systembridgeshared.models.database_data_sensors import Sensors -# TABLES: List[Table] = [ -# Table(MODEL_BATTERY, Battery), -# Table(MODEL_BRIDGE, Bridge), -# Table(MODEL_CPU, CPU), -# Table(MODEL_DISK, Disk), -# Table(MODEL_DISPLAY, Display), -# Table(MODEL_GPU, GPU), -# Table(MODEL_MEMORY, Memory), -# Table(MODEL_NETWORK, Network), -# Table(MODEL_SECRETS, Secrets), -# Table(MODEL_SENSORS, Sensors), -# Table(MODEL_SETTINGS, Settings), -# Table(MODEL_SYSTEM, System), -# ] - -# TABLE_MAP: Mapping[str, Any] = {table.name: table.metadata for table in TABLES} - TABLE_MAP: Mapping[str, Any] = { MODEL_BATTERY: Battery, MODEL_BRIDGE: Bridge, @@ -108,78 +91,27 @@ def __init__(self): # tables=TABLES, ) - # def add_data( - # self, - # data: TableDataType, - # ) -> None: - # """Add data to database""" - # with Session(self._engine) as session: - # session.add(data) - # session.commit() - - # def create_data( - # self, - # key: str, - # value: Union[bool, float, int, str, list, dict, None], - # timestamp: Optional[float] = None, - # ) -> Data: - # """Create data""" - # if timestamp is None: - # timestamp = time() - - # # Convert list or dict to JSON - # if isinstance(value, (dict, list)): - # value = json.dumps(value) - # else: - # value = str(value) - - # return Data( - # key=key, - # value=value, - # timestamp=timestamp, - # ) - - def create_sensor_data( + def clear_table( self, - key: str, - type: str, - name: str, - hardware_type: str, - hardware_name: str, - value: Union[bool, float, int, str, list, dict, None], - timestamp: Optional[float] = None, - ) -> Sensors: - """Create data""" - if timestamp is None: - timestamp = time() - - # Convert list or dict to JSON - if isinstance(value, (dict, list)): - value = json.dumps(value) - else: - value = str(value) - - return Sensors( - key=key, - type=type, - name=name, - hardware_type=hardware_type, - hardware_name=hardware_name, - value=value, - timestamp=timestamp, - ) + table: Any, + ) -> None: + """Clear table""" + with Session(self._engine) as session: + for sensor in session.exec(select(table)).all(): + session.delete(sensor) + session.commit() def get_data( self, - table, - ) -> List[Data]: + table: Any, + ) -> List[Any]: """Get data from database""" with Session(self._engine) as session: return session.exec(select(table)).all() def get_data_by_key( self, - table, + table: Any, key: str, ) -> List[Data]: """Get data from database by key""" @@ -188,7 +120,7 @@ def get_data_by_key( def get_data_item_by_key( self, - table, + table: Any, key: str, ) -> Optional[Data]: """Get data item from database by key""" @@ -197,7 +129,7 @@ def get_data_item_by_key( def get_data_dict( self, - table, + table: Any, ) -> DataDict: """Get data from database as dictionary""" data: dict[str, Any] = {"last_updated": {}} @@ -217,3 +149,21 @@ def get_data_dict( def get_session(self) -> Session: """Get session""" return Session(self._engine) + + def update_data( + self, + table, + data: Any, + ) -> None: + """Update data""" + with Session(self._engine) as session: + result = session.exec(select(table).where(table.key == data.key)) + old_data = result.first() + if old_data is None: + data.timestamp = time() + session.add(data) + else: + old_data.value = data.value + old_data.timestamp = time() + session.add(old_data) + # session.refresh(old_data) From 7b923fa1dbc25d187fbee3553fb6a38239c18d01 Mon Sep 17 00:00:00 2001 From: Aidan Timson Date: Sat, 6 Aug 2022 13:47:45 +0100 Subject: [PATCH 14/38] Fix import --- backend/systembridgebackend/server/websocket.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/backend/systembridgebackend/server/websocket.py b/backend/systembridgebackend/server/websocket.py index 0302d43c5..6c61e8b98 100644 --- a/backend/systembridgebackend/server/websocket.py +++ b/backend/systembridgebackend/server/websocket.py @@ -4,7 +4,6 @@ import os from uuid import uuid4 -from shared.systembridgeshared.database import TABLE_MAP from systembridgeshared.base import Base from systembridgeshared.const import ( EVENT_BASE, @@ -83,7 +82,7 @@ TYPE_UNREGISTER_DATA_LISTENER, TYPE_UPDATE_SETTING, ) -from systembridgeshared.database import Database +from systembridgeshared.database import TABLE_MAP, Database from systembridgeshared.models.data import DataDict from systembridgeshared.models.get_data import GetData from systembridgeshared.models.get_setting import GetSetting From 2aa5672fff285f5909d3e84d6fbae813472b3383 Mon Sep 17 00:00:00 2001 From: Aidan Timson Date: Sat, 6 Aug 2022 13:50:27 +0100 Subject: [PATCH 15/38] Remove bridge --- frontend/components/Data/Data.tsx | 2 -- shared/systembridgeshared/const.py | 1 - shared/systembridgeshared/models/data.py | 1 - 3 files changed, 4 deletions(-) diff --git a/frontend/components/Data/Data.tsx b/frontend/components/Data/Data.tsx index 95b3a2dc1..69405ef23 100644 --- a/frontend/components/Data/Data.tsx +++ b/frontend/components/Data/Data.tsx @@ -15,7 +15,6 @@ import DataItems from "components/Data/DataItems"; const modules = [ "battery", - "bridge", "cpu", "disk", "display", @@ -28,7 +27,6 @@ const modules = [ const moduleMap: { [key: string]: string } = { battery: "Battery", - bridge: "Bridge", cpu: "CPU", disk: "Disk", display: "Display", diff --git a/shared/systembridgeshared/const.py b/shared/systembridgeshared/const.py index 092e74037..b2b565bcf 100644 --- a/shared/systembridgeshared/const.py +++ b/shared/systembridgeshared/const.py @@ -153,7 +153,6 @@ # Model MODEL_BATTERY = "battery" -MODEL_BRIDGE = "bridge" MODEL_CPU = "cpu" MODEL_DATA = "data" MODEL_DISK = "disk" diff --git a/shared/systembridgeshared/models/data.py b/shared/systembridgeshared/models/data.py index 62a4ca245..793a5d21f 100644 --- a/shared/systembridgeshared/models/data.py +++ b/shared/systembridgeshared/models/data.py @@ -20,7 +20,6 @@ class Data(BaseModel): """Data""" battery: Battery = Field(..., alias="battery") - bridge: Bridge = Field(..., alias="bridge") cpu: Cpu = Field(..., alias="cpu") disk: Disk = Field(..., alias="disk") display: Display = Field(..., alias="display") From d567763ebf2569c7c40845a61484d727bada357f Mon Sep 17 00:00:00 2001 From: Aidan Timson Date: Sat, 6 Aug 2022 13:51:26 +0100 Subject: [PATCH 16/38] Remove bridge --- .../systembridgeconnector/models/bridge.py | 20 ----------------- schemas/data/bridge.json | 22 ------------------- shared/systembridgeshared/models/bridge.py | 20 ----------------- shared/systembridgeshared/models/data.py | 1 - 4 files changed, 63 deletions(-) delete mode 100644 connector/systembridgeconnector/models/bridge.py delete mode 100644 schemas/data/bridge.json delete mode 100644 shared/systembridgeshared/models/bridge.py diff --git a/connector/systembridgeconnector/models/bridge.py b/connector/systembridgeconnector/models/bridge.py deleted file mode 100644 index 1afbefce5..000000000 --- a/connector/systembridgeconnector/models/bridge.py +++ /dev/null @@ -1,20 +0,0 @@ -# generated by datamodel-codegen: -# filename: bridge.json - -from __future__ import annotations - -from typing import Optional - -from pydantic import BaseModel, Extra, Field - - -class Bridge(BaseModel): - """ - Bridge - """ - - class Config: - extra = Extra.allow - - id: Optional[str] = Field(None, description="Event ID") - last_updated: dict[str, float] = Field(..., description="Last updated") diff --git a/schemas/data/bridge.json b/schemas/data/bridge.json deleted file mode 100644 index 23ca76ce8..000000000 --- a/schemas/data/bridge.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "type": "object", - "description": "Bridge", - "properties": { - "id": { - "type": "string", - "description": "Event ID" - }, - "last_updated": { - "type": "object", - "description": "Last updated", - "additionalProperties": { - "type": "number" - } - } - }, - "additionalProperties": { - "type": "string" - }, - "required": ["last_updated"] -} diff --git a/shared/systembridgeshared/models/bridge.py b/shared/systembridgeshared/models/bridge.py deleted file mode 100644 index 1afbefce5..000000000 --- a/shared/systembridgeshared/models/bridge.py +++ /dev/null @@ -1,20 +0,0 @@ -# generated by datamodel-codegen: -# filename: bridge.json - -from __future__ import annotations - -from typing import Optional - -from pydantic import BaseModel, Extra, Field - - -class Bridge(BaseModel): - """ - Bridge - """ - - class Config: - extra = Extra.allow - - id: Optional[str] = Field(None, description="Event ID") - last_updated: dict[str, float] = Field(..., description="Last updated") diff --git a/shared/systembridgeshared/models/data.py b/shared/systembridgeshared/models/data.py index 793a5d21f..36aee820e 100644 --- a/shared/systembridgeshared/models/data.py +++ b/shared/systembridgeshared/models/data.py @@ -5,7 +5,6 @@ from pydantic import BaseModel, Extra, Field from systembridgeshared.models.battery import Battery -from systembridgeshared.models.bridge import Bridge from systembridgeshared.models.cpu import Cpu from systembridgeshared.models.disk import Disk from systembridgeshared.models.display import Display From cf56ae1d03e8f783217754d2708706b56c14205a Mon Sep 17 00:00:00 2001 From: Aidan Timson Date: Sat, 6 Aug 2022 13:52:38 +0100 Subject: [PATCH 17/38] Update models in connector --- .../systembridgeconnector/models/data.py | 11 +++- .../models/database_data.py | 55 +++++++++++++++++++ .../models/database_data_bridge.py | 26 +++++++++ .../models/database_data_sensors.py | 18 ++++++ 4 files changed, 107 insertions(+), 3 deletions(-) create mode 100644 connector/systembridgeconnector/models/database_data.py create mode 100644 connector/systembridgeconnector/models/database_data_bridge.py create mode 100644 connector/systembridgeconnector/models/database_data_sensors.py diff --git a/connector/systembridgeconnector/models/data.py b/connector/systembridgeconnector/models/data.py index fbe44f3a6..f9a2f58e0 100644 --- a/connector/systembridgeconnector/models/data.py +++ b/connector/systembridgeconnector/models/data.py @@ -2,10 +2,9 @@ from __future__ import annotations -from pydantic import BaseModel, Field +from pydantic import BaseModel, Extra, Field from systembridgeconnector.models.battery import Battery -from systembridgeconnector.models.bridge import Bridge from systembridgeconnector.models.cpu import Cpu from systembridgeconnector.models.disk import Disk from systembridgeconnector.models.display import Display @@ -20,7 +19,6 @@ class Data(BaseModel): """Data""" battery: Battery = Field(..., alias="battery") - bridge: Bridge = Field(..., alias="bridge") cpu: Cpu = Field(..., alias="cpu") disk: Disk = Field(..., alias="disk") display: Display = Field(..., alias="display") @@ -29,3 +27,10 @@ class Data(BaseModel): network: Network = Field(..., alias="network") sensors: Sensors = Field(..., alias="sensors") system: System = Field(..., alias="system") + + +class DataDict(BaseModel): + class Config: + extra = Extra.allow + + last_updated: dict[str, float] = Field(..., description="Last updated") diff --git a/connector/systembridgeconnector/models/database_data.py b/connector/systembridgeconnector/models/database_data.py new file mode 100644 index 000000000..60f3e17ab --- /dev/null +++ b/connector/systembridgeconnector/models/database_data.py @@ -0,0 +1,55 @@ +"""System Bridge: Models - Database Data""" + +from __future__ import annotations + +from typing import Optional + +from sqlmodel import Field, SQLModel + + +class Data(SQLModel): + """Database Data""" + + key: str = Field(primary_key=True, nullable=False) + value: Optional[str] = Field(default=None, nullable=True) + timestamp: Optional[float] = Field(default=None, nullable=True) + + +class Battery(Data, table=True): + """Database Data Battery""" + + +class CPU(Data, table=True): + """Database Data CPU""" + + +class Disk(Data, table=True): + """Database Data Disk""" + + +class Display(Data, table=True): + """Database Data Display""" + + +class GPU(Data, table=True): + """Database Data GPU""" + + +class Memory(Data, table=True): + """Database Data Memory""" + + +class Network(Data, table=True): + """Database Data Network""" + + +class Secrets(Data, table=True): + """Database Data Secrets""" + + +class Settings(Data, table=True): + """Database Data Settings""" + + +class System(Data, table=True): + """System""" diff --git a/connector/systembridgeconnector/models/database_data_bridge.py b/connector/systembridgeconnector/models/database_data_bridge.py new file mode 100644 index 000000000..97ba48a6c --- /dev/null +++ b/connector/systembridgeconnector/models/database_data_bridge.py @@ -0,0 +1,26 @@ +"""System Bridge: Models - Database Data Bridge""" + +from __future__ import annotations + +from typing import Optional + +from sqlmodel import Field, SQLModel + + +class Bridge(SQLModel, table=True): + """Database Data Bridge""" + + uuid: str = Field(primary_key=True, nullable=False) + name: str = Field(nullable=False) + address: str = Field(nullable=False) + fqdn: Optional[str] = Field(default=None, nullable=True) + host: str = Field(default=None, nullable=True) + ip: str = Field(nullable=False) + mac: str = Field(nullable=False) + port: str = Field(nullable=False) + version: str = Field(nullable=False) + websocket_address: str = Field(nullable=False) + websocket_port: str = Field(nullable=False) + active: Optional[int] = Field(default=None, nullable=True) + last_active_timestamp: Optional[float] = Field(default=None, nullable=True) + timestamp: Optional[float] = Field(default=None, nullable=True) diff --git a/connector/systembridgeconnector/models/database_data_sensors.py b/connector/systembridgeconnector/models/database_data_sensors.py new file mode 100644 index 000000000..dfa11dd25 --- /dev/null +++ b/connector/systembridgeconnector/models/database_data_sensors.py @@ -0,0 +1,18 @@ +"""System Bridge: Models - Database Data Sensors""" + +from __future__ import annotations + +from typing import Optional + +from sqlmodel import Field + +from systembridgeshared.models.database_data import Data + + +class Sensors(Data, table=True): + """Database Data Sensors""" + + type: str = Field(nullable=False) + name: Optional[str] = Field(default=None, nullable=True) + hardware_type: Optional[str] = Field(default=None, nullable=True) + hardware_name: Optional[str] = Field(default=None, nullable=True) From 5065352d58d94377177f9b269c67fc07f5787704 Mon Sep 17 00:00:00 2001 From: Aidan Timson Date: Sat, 6 Aug 2022 13:55:59 +0100 Subject: [PATCH 18/38] Remove bridge --- connector/systembridgeconnector/const.py | 2 -- shared/systembridgeshared/const.py | 1 - shared/systembridgeshared/database.py | 2 -- 3 files changed, 5 deletions(-) diff --git a/connector/systembridgeconnector/const.py b/connector/systembridgeconnector/const.py index 1362f7355..9ad83feb6 100644 --- a/connector/systembridgeconnector/const.py +++ b/connector/systembridgeconnector/const.py @@ -138,7 +138,6 @@ # Model MODEL_BATTERY = "battery" -MODEL_BRIDGE = "bridge" MODEL_CPU = "cpu" MODEL_DATA = "data" MODEL_DISK = "disk" @@ -163,7 +162,6 @@ MODEL_MAP = { MODEL_BATTERY: Battery, - MODEL_BRIDGE: Bridge, MODEL_CPU: Cpu, MODEL_DATA: Data, MODEL_DISK: Disk, diff --git a/shared/systembridgeshared/const.py b/shared/systembridgeshared/const.py index b2b565bcf..a67de7ed4 100644 --- a/shared/systembridgeshared/const.py +++ b/shared/systembridgeshared/const.py @@ -177,7 +177,6 @@ MODEL_MAP = { MODEL_BATTERY: Battery, - MODEL_BRIDGE: Bridge, MODEL_CPU: Cpu, MODEL_DATA: Data, MODEL_DISK: Disk, diff --git a/shared/systembridgeshared/database.py b/shared/systembridgeshared/database.py index 93f524786..ae1fd0602 100644 --- a/shared/systembridgeshared/database.py +++ b/shared/systembridgeshared/database.py @@ -16,7 +16,6 @@ ) from systembridgeshared.const import ( MODEL_BATTERY, - MODEL_BRIDGE, MODEL_CPU, MODEL_DISK, MODEL_DISPLAY, @@ -47,7 +46,6 @@ TABLE_MAP: Mapping[str, Any] = { MODEL_BATTERY: Battery, - MODEL_BRIDGE: Bridge, MODEL_CPU: CPU, MODEL_DISK: Disk, MODEL_DISPLAY: Display, From 2d5f88676d9761865ed372ee63f98cc38f145081 Mon Sep 17 00:00:00 2001 From: Aidan Timson Date: Sat, 6 Aug 2022 14:21:05 +0100 Subject: [PATCH 19/38] Fixes --- .../systembridgebackend/server/__init__.py | 15 ++++++----- .../systembridgebackend/server/websocket.py | 2 +- .../systembridgeconnector/models/data.py | 4 ++- shared/systembridgeshared/common.py | 4 ++- shared/systembridgeshared/database.py | 27 +++++++++---------- shared/systembridgeshared/models/data.py | 4 ++- 6 files changed, 31 insertions(+), 25 deletions(-) diff --git a/backend/systembridgebackend/server/__init__.py b/backend/systembridgebackend/server/__init__.py index fde2069f7..205de4212 100644 --- a/backend/systembridgebackend/server/__init__.py +++ b/backend/systembridgebackend/server/__init__.py @@ -11,13 +11,14 @@ from sanic.response import HTTPResponse, json from sanic_scheduler import SanicScheduler, task from systembridgeshared.base import Base +from systembridgeshared.common import convert_string_to_correct_type from systembridgeshared.const import ( QUERY_API_KEY, SECRET_API_KEY, SETTING_LOG_LEVEL, SETTING_PORT_API, ) -from systembridgeshared.database import Database +from systembridgeshared.database import TABLE_MAP, Database from systembridgeshared.models.media_play import MediaPlay from systembridgeshared.models.notification import Notification from systembridgeshared.settings import Settings @@ -125,9 +126,10 @@ async def _handler_data_all( table: str, ) -> HTTPResponse: """Data handler all""" - if table not in implemented_modules: + table_module = TABLE_MAP.get(table) + if table not in implemented_modules or table_module is None: return json({"message": f"Data module {table} not found"}, status=404) - return json(self._database.get_data_dict(table)) + return json(self._database.get_data_dict(table_module).dict()) @auth.key_required async def _handler_data_by_key( @@ -136,16 +138,17 @@ async def _handler_data_by_key( key: str, ) -> HTTPResponse: """Data handler by key""" - if table not in implemented_modules: + table_module = TABLE_MAP.get(table) + if table not in implemented_modules or table_module is None: return json({"message": f"Data module {table} not found"}, status=404) - data = self._database.get_data_item_by_key(table, key) + data = self._database.get_data_item_by_key(table_module, key) if data is None: return json({"message": f"Data item {key} not found"}, status=404) return json( { - data.key: data.value, + data.key: convert_string_to_correct_type(data.value), "last_updated": data.timestamp, } ) diff --git a/backend/systembridgebackend/server/websocket.py b/backend/systembridgebackend/server/websocket.py index 6c61e8b98..33c8b070d 100644 --- a/backend/systembridgebackend/server/websocket.py +++ b/backend/systembridgebackend/server/websocket.py @@ -613,7 +613,7 @@ async def _handler( EVENT_TYPE: TYPE_DATA_UPDATE, EVENT_MESSAGE: "Data received", EVENT_MODULE: module, - EVENT_DATA: data, + EVENT_DATA: data.json(), } ) ) diff --git a/connector/systembridgeconnector/models/data.py b/connector/systembridgeconnector/models/data.py index f9a2f58e0..d841324fb 100644 --- a/connector/systembridgeconnector/models/data.py +++ b/connector/systembridgeconnector/models/data.py @@ -2,6 +2,8 @@ from __future__ import annotations +from typing import Optional + from pydantic import BaseModel, Extra, Field from systembridgeconnector.models.battery import Battery @@ -33,4 +35,4 @@ class DataDict(BaseModel): class Config: extra = Extra.allow - last_updated: dict[str, float] = Field(..., description="Last updated") + last_updated: dict[str, Optional[float]] = Field(..., description="Last updated") diff --git a/shared/systembridgeshared/common.py b/shared/systembridgeshared/common.py index 70aac88bf..6515a085c 100644 --- a/shared/systembridgeshared/common.py +++ b/shared/systembridgeshared/common.py @@ -25,9 +25,11 @@ def camel_to_snake(name): def convert_string_to_correct_type( - value: str, + value: str | None, ) -> Union[bool, float, int, str, list[Any], dict[str, Any], None]: """Convert string to correct data type""" + if value is None: + return None try: if value.startswith("'") and value.endswith("'"): return convert_string_to_correct_type(value[1:-1]) diff --git a/shared/systembridgeshared/database.py b/shared/systembridgeshared/database.py index ae1fd0602..0dc0c47b8 100644 --- a/shared/systembridgeshared/database.py +++ b/shared/systembridgeshared/database.py @@ -2,19 +2,15 @@ from __future__ import annotations from collections.abc import Mapping -import json import os from time import time from typing import Any, List, Optional, Union from sqlmodel import Session, SQLModel, create_engine, select -from systembridgeshared.base import Base -from systembridgeshared.common import ( - convert_string_to_correct_type, - get_user_data_directory, -) -from systembridgeshared.const import ( +from .base import Base +from .common import convert_string_to_correct_type, get_user_data_directory +from .const import ( MODEL_BATTERY, MODEL_CPU, MODEL_DISK, @@ -27,8 +23,8 @@ MODEL_SETTINGS, MODEL_SYSTEM, ) -from systembridgeshared.models.data import DataDict -from systembridgeshared.models.database_data import ( +from .models.data import DataDict +from .models.database_data import ( CPU, GPU, Battery, @@ -41,8 +37,8 @@ Settings, System, ) -from systembridgeshared.models.database_data_bridge import Bridge -from systembridgeshared.models.database_data_sensors import Sensors +from .models.database_data_bridge import Bridge +from .models.database_data_sensors import Sensors TABLE_MAP: Mapping[str, Any] = { MODEL_BATTERY: Battery, @@ -130,7 +126,8 @@ def get_data_dict( table: Any, ) -> DataDict: """Get data from database as dictionary""" - data: dict[str, Any] = {"last_updated": {}} + data: dict[str, Any] = {} + data_last_updated: dict[str, float | None] = {} result = self.get_data(table) for item in result: if item.value is None: @@ -138,11 +135,11 @@ def get_data_dict( else: data[item.key] = convert_string_to_correct_type(item.value) if item.timestamp is None: - data["last_updated"][item.key] = None + data_last_updated[item.key] = None else: - data["last_updated"][item.key] = item.timestamp + data_last_updated[item.key] = item.timestamp - return DataDict(**data) + return DataDict(**data, last_updated=data_last_updated) def get_session(self) -> Session: """Get session""" diff --git a/shared/systembridgeshared/models/data.py b/shared/systembridgeshared/models/data.py index 36aee820e..2ccffef65 100644 --- a/shared/systembridgeshared/models/data.py +++ b/shared/systembridgeshared/models/data.py @@ -2,6 +2,8 @@ from __future__ import annotations +from typing import Optional + from pydantic import BaseModel, Extra, Field from systembridgeshared.models.battery import Battery @@ -33,4 +35,4 @@ class DataDict(BaseModel): class Config: extra = Extra.allow - last_updated: dict[str, float] = Field(..., description="Last updated") + last_updated: dict[str, Optional[float]] = Field(..., description="Last updated") From 97937ca16593f6bd378a8d97e5535fa6e4f74bfd Mon Sep 17 00:00:00 2001 From: Aidan Timson Date: Sat, 6 Aug 2022 14:28:08 +0100 Subject: [PATCH 20/38] Fix --- backend/systembridgebackend/server/websocket.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/systembridgebackend/server/websocket.py b/backend/systembridgebackend/server/websocket.py index 33c8b070d..495543216 100644 --- a/backend/systembridgebackend/server/websocket.py +++ b/backend/systembridgebackend/server/websocket.py @@ -158,7 +158,7 @@ async def _data_changed( EVENT_TYPE: TYPE_DATA_UPDATE, EVENT_MESSAGE: "Data changed", EVENT_MODULE: module, - EVENT_DATA: data.json(), + EVENT_DATA: data, } ) ) @@ -613,7 +613,7 @@ async def _handler( EVENT_TYPE: TYPE_DATA_UPDATE, EVENT_MESSAGE: "Data received", EVENT_MODULE: module, - EVENT_DATA: data.json(), + EVENT_DATA: data, } ) ) From 8672d5d24bb9031ad046be9321ce66668a35c00b Mon Sep 17 00:00:00 2001 From: Aidan Timson Date: Sat, 6 Aug 2022 14:45:17 +0100 Subject: [PATCH 21/38] Update --- backend/systembridgebackend/modules/base.py | 3 - .../modules/battery/update.py | 1 - .../modules/disk/update.py | 54 +++++++-- .../modules/display/update.py | 64 +++++++--- .../systembridgebackend/modules/gpu/update.py | 1 + .../modules/memory/update.py | 19 ++- .../modules/network/update.py | 19 ++- .../modules/sensors/update.py | 1 - .../modules/system/update.py | 114 ++++++++++++++---- backend/systembridgebackend/modules/update.py | 25 ++-- 10 files changed, 223 insertions(+), 78 deletions(-) diff --git a/backend/systembridgebackend/modules/base.py b/backend/systembridgebackend/modules/base.py index 12560aa0a..345a97689 100644 --- a/backend/systembridgebackend/modules/base.py +++ b/backend/systembridgebackend/modules/base.py @@ -1,8 +1,5 @@ """System Bridge: Module Base""" -from time import time -from typing import Any -from sqlmodel import Session, select from systembridgeshared.base import Base from systembridgeshared.database import Database diff --git a/backend/systembridgebackend/modules/battery/update.py b/backend/systembridgebackend/modules/battery/update.py index c2eff252e..3bc4497d2 100644 --- a/backend/systembridgebackend/modules/battery/update.py +++ b/backend/systembridgebackend/modules/battery/update.py @@ -1,7 +1,6 @@ """System Bridge: Update Battery""" import asyncio -from sqlmodel import Session from systembridgeshared.common import camel_to_snake from systembridgeshared.database import Database from systembridgeshared.models.database_data import Battery as DatabaseModel diff --git a/backend/systembridgebackend/modules/disk/update.py b/backend/systembridgebackend/modules/disk/update.py index 513af83da..b25b41428 100644 --- a/backend/systembridgebackend/modules/disk/update.py +++ b/backend/systembridgebackend/modules/disk/update.py @@ -1,7 +1,9 @@ """System Bridge: Update Disk""" import asyncio +from json import dumps from systembridgeshared.database import Database +from systembridgeshared.models.database_data import Disk as DatabaseModel from . import Disk from ..base import ModuleUpdateBase @@ -15,20 +17,30 @@ def __init__( database: Database, ) -> None: """Initialize""" - super().__init__(database, "disk") + super().__init__(database) self._disk = Disk() async def update_io_counters(self) -> None: """Update IO counters""" for key, value in self._disk.io_counters()._asdict().items(): - self._database.write("disk", f"io_counters_{key}", value) + self._database.update_data( + DatabaseModel, + DatabaseModel( + key=f"io_counters_{key}", + value=value, + ), + ) async def update_io_counters_per_disk(self) -> None: """Update IO counters per disk""" for key, value in self._disk.io_counters_per_disk().items(): # type: ignore for subkey, subvalue in value._asdict().items(): - self._database.write( - "disk", f"io_counters_per_disk_{key}_{subkey}", subvalue + self._database.update_data( + DatabaseModel, + DatabaseModel( + key=f"io_counters_per_disk_{key}_{subkey}", + value=subvalue, + ), ) async def update_partitions(self) -> None: @@ -40,21 +52,39 @@ async def update_partitions(self) -> None: device_list.append(partition.device) partition_list.append(partition.mountpoint) for key, value in partition._asdict().items(): - self._database.write( - "disk", - f"partitions_{partition.mountpoint}_{key}", - value, + self._database.update_data( + DatabaseModel, + DatabaseModel( + key=f"partitions_{partition.mountpoint}_{key}", + value=value, + ), ) - self._database.write("disk", "devices", device_list) - self._database.write("disk", "partitions", partition_list) + self._database.update_data( + DatabaseModel, + DatabaseModel( + key="devices", + value=dumps(device_list), + ), + ) + self._database.update_data( + DatabaseModel, + DatabaseModel( + key="partitions", + value=dumps(partition_list), + ), + ) async def update_usage(self) -> None: """Update usage""" for partition in self._disk.partitions(): if data := self._disk.usage(partition.mountpoint): for key, value in data._asdict().items(): - self._database.write( - "disk", f"usage_{partition.mountpoint}_{key}", value + self._database.update_data( + DatabaseModel, + DatabaseModel( + key=f"usage_{partition.mountpoint}_{key}", + value=value, + ), ) async def update_all_data(self) -> None: diff --git a/backend/systembridgebackend/modules/display/update.py b/backend/systembridgebackend/modules/display/update.py index 7c25b1d01..40b950fba 100644 --- a/backend/systembridgebackend/modules/display/update.py +++ b/backend/systembridgebackend/modules/display/update.py @@ -1,8 +1,10 @@ """System Bridge: Update Display""" import asyncio +from json import dumps from systembridgeshared.common import make_key from systembridgeshared.database import Database +from systembridgeshared.models.database_data import Display as DatabaseModel from . import Display from ..base import ModuleUpdateBase @@ -16,7 +18,7 @@ def __init__( database: Database, ) -> None: """Initialize""" - super().__init__(database, "display") + super().__init__(database) self._display = Display() async def update_name( @@ -25,17 +27,26 @@ async def update_name( display_name: str, ) -> None: """Update name""" - self._database.write("display", f"{display_key}_name", display_name) + self._database.update_data( + DatabaseModel, + DatabaseModel( + key=f"{display_key}_name", + value=display_name, + ), + ) async def update_pixel_clock( self, display_key: str, ) -> None: """Update pixel clock""" - self._database.write( - "display", - f"{display_key}_pixel_clock", - self._display.pixel_clock(self._database, display_key), + value = self._display.pixel_clock(self._database, display_key) + self._database.update_data( + DatabaseModel, + DatabaseModel( + key=f"{display_key}_pixel_clock", + value=str(value) if value is not None else None, + ), ) async def update_refresh_rate( @@ -43,10 +54,13 @@ async def update_refresh_rate( display_key: str, ) -> None: """Update refresh rate""" - self._database.write( - "display", - f"{display_key}_refresh_rate", - self._display.refresh_rate(self._database, display_key), + value = self._display.refresh_rate(self._database, display_key) + self._database.update_data( + DatabaseModel, + DatabaseModel( + key=f"{display_key}_refresh_rate", + value=str(value) if value is not None else None, + ), ) async def update_resolution_horizontal( @@ -54,10 +68,13 @@ async def update_resolution_horizontal( display_key: str, ) -> None: """Update resolution horizontal""" - self._database.write( - "display", - f"{display_key}_resolution_horizontal", - self._display.resolution_horizontal(self._database, display_key), + value = self._display.resolution_horizontal(self._database, display_key) + self._database.update_data( + DatabaseModel, + DatabaseModel( + key=f"{display_key}_resolution_horizontal", + value=str(value) if value is not None else None, + ), ) async def update_resolution_vertical( @@ -65,10 +82,13 @@ async def update_resolution_vertical( display_key: str, ) -> None: """Update resolution vertical""" - self._database.write( - "display", - f"{display_key}_resolution_vertical", - self._display.resolution_vertical(self._database, display_key), + value = self._display.resolution_vertical(self._database, display_key) + self._database.update_data( + DatabaseModel, + DatabaseModel( + key=f"{display_key}_resolution_vertical", + value=str(value) if value is not None else None, + ), ) async def update_all_data(self) -> None: @@ -90,4 +110,10 @@ async def update_all_data(self) -> None: self.update_resolution_vertical(display_key), ] ) - self._database.write("display", "displays", display_list) + self._database.update_data( + DatabaseModel, + DatabaseModel( + key="displays", + value=dumps(display_list), + ), + ) diff --git a/backend/systembridgebackend/modules/gpu/update.py b/backend/systembridgebackend/modules/gpu/update.py index 80122c140..aa416ccbe 100644 --- a/backend/systembridgebackend/modules/gpu/update.py +++ b/backend/systembridgebackend/modules/gpu/update.py @@ -3,6 +3,7 @@ from systembridgeshared.common import make_key from systembridgeshared.database import Database +from systembridgeshared.models.database_data import GPU as DatabaseModel from . import GPU from ..base import ModuleUpdateBase diff --git a/backend/systembridgebackend/modules/memory/update.py b/backend/systembridgebackend/modules/memory/update.py index e676c7a8c..03f69e3ef 100644 --- a/backend/systembridgebackend/modules/memory/update.py +++ b/backend/systembridgebackend/modules/memory/update.py @@ -2,6 +2,7 @@ import asyncio from systembridgeshared.database import Database +from systembridgeshared.models.database_data import Memory as DatabaseModel from . import Memory from ..base import ModuleUpdateBase @@ -15,18 +16,30 @@ def __init__( database: Database, ) -> None: """Initialize""" - super().__init__(database, "memory") + super().__init__(database) self._memory = Memory() async def update_swap(self) -> None: """Update Swap Memory""" for key, value in self._memory.swap()._asdict().items(): - self._database.write("memory", f"swap_{key}", value) + self._database.update_data( + DatabaseModel, + DatabaseModel( + key=f"swap_{key}", + value=value, + ), + ) async def update_virtual(self) -> None: """Update Virtual Memory""" for key, value in self._memory.virtual()._asdict().items(): - self._database.write("memory", f"virtual_{key}", value) + self._database.update_data( + DatabaseModel, + DatabaseModel( + key=f"virtual_{key}", + value=value, + ), + ) async def update_all_data(self) -> None: """Update data""" diff --git a/backend/systembridgebackend/modules/network/update.py b/backend/systembridgebackend/modules/network/update.py index 522b33d6b..b7ce6a3cf 100644 --- a/backend/systembridgebackend/modules/network/update.py +++ b/backend/systembridgebackend/modules/network/update.py @@ -2,6 +2,7 @@ import asyncio from systembridgeshared.database import Database +from systembridgeshared.models.database_data import Network as DatabaseModel from . import Network from ..base import ModuleUpdateBase @@ -15,21 +16,31 @@ def __init__( database: Database, ) -> None: """Initialize""" - super().__init__(database, "network") + super().__init__(database) self._network = Network() async def update_stats(self) -> None: """Update stats""" for key, value in self._network.stats().items(): for subkey, subvalue in value._asdict().items(): - self._database.write( - "network", f"stat_{key.replace(' ', '')}_{subkey}", subvalue + self._database.update_data( + DatabaseModel, + DatabaseModel( + key=f"stat_{key.replace(' ', '')}_{subkey}", + value=subvalue, + ), ) async def update_io_counters(self) -> None: """Update IO counters""" for key, value in self._network.io_counters()._asdict().items(): - self._database.write("network", f"io_counters_{key}", value) + self._database.update_data( + DatabaseModel, + DatabaseModel( + key=f"io_counters_{key}", + value=value, + ), + ) async def update_all_data(self) -> None: """Update data""" diff --git a/backend/systembridgebackend/modules/sensors/update.py b/backend/systembridgebackend/modules/sensors/update.py index 0e7ffcd93..e4ee9476d 100644 --- a/backend/systembridgebackend/modules/sensors/update.py +++ b/backend/systembridgebackend/modules/sensors/update.py @@ -1,7 +1,6 @@ """System Bridge: Update Sensors""" import asyncio -from sqlmodel import Session, select from systembridgeshared.common import make_key from systembridgeshared.database import Database from systembridgeshared.models.database_data_sensors import Sensors as DatabaseModel diff --git a/backend/systembridgebackend/modules/system/update.py b/backend/systembridgebackend/modules/system/update.py index c35ebd228..0ce9e5244 100644 --- a/backend/systembridgebackend/modules/system/update.py +++ b/backend/systembridgebackend/modules/system/update.py @@ -2,6 +2,7 @@ import asyncio from systembridgeshared.database import Database +from systembridgeshared.models.database_data import System as DatabaseModel from . import System from ..base import ModuleUpdateBase @@ -15,75 +16,144 @@ def __init__( database: Database, ) -> None: """Initialize""" - super().__init__(database, "system") + super().__init__(database) self._system = System() async def update_boot_time(self) -> None: """Update boot time""" - self._database.write("system", "boot_time", self._system.boot_time()) + self._database.update_data( + DatabaseModel, + DatabaseModel( + key="boot_time", + value=str(self._system.boot_time()), + ), + ) async def update_fqdn(self) -> None: """Update FQDN""" - self._database.write("system", "fqdn", self._system.fqdn()) + self._database.update_data( + DatabaseModel, + DatabaseModel( + key="fqdn", + value=self._system.fqdn(), + ), + ) async def update_hostname(self) -> None: """Update hostname""" - self._database.write("system", "hostname", self._system.hostname()) + self._database.update_data( + DatabaseModel, + DatabaseModel( + key="hostname", + value=self._system.hostname(), + ), + ) async def update_ip_address_4(self) -> None: """Update IP address 4""" - self._database.write("system", "ip_address_4", self._system.ip_address_4()) + self._database.update_data( + DatabaseModel, + DatabaseModel( + key="ip_address_4", + value=self._system.ip_address_4(), + ), + ) async def update_mac_address(self) -> None: """Update MAC address""" - self._database.write("system", "mac_address", self._system.mac_address()) + self._database.update_data( + DatabaseModel, + DatabaseModel( + key="mac_address", + value=self._system.mac_address(), + ), + ) async def update_platform(self) -> None: """Update platform""" - self._database.write("system", "platform", self._system.platform()) + self._database.update_data( + DatabaseModel, + DatabaseModel( + key="platform", + value=self._system.platform(), + ), + ) async def update_platform_version(self) -> None: """Update platform version""" - self._database.write( - "system", "platform_version", self._system.platform_version() + self._database.update_data( + DatabaseModel, + DatabaseModel( + key="platform_version", + value=self._system.platform_version(), + ), ) async def update_uptime(self) -> None: """Update uptime""" - self._database.write("system", "uptime", self._system.uptime()) + self._database.update_data( + DatabaseModel, + DatabaseModel( + key="uptime", + value=str(self._system.uptime()), + ), + ) async def update_users(self) -> None: """Update users""" for user in self._system.users(): for key, value in user._asdict().items(): - self._database.write( - "system", f"user_{user.name.replace(' ','_').lower()}_{key}", value + self._database.update_data( + DatabaseModel, + DatabaseModel( + key=f"user_{user.name.replace(' ','_').lower()}_{key}", + value=value, + ), ) async def update_uuid(self) -> None: """Update UUID""" - self._database.write("system", "uuid", self._system.uuid()) + self._database.update_data( + DatabaseModel, + DatabaseModel( + key="uuid", + value=self._system.uuid(), + ), + ) async def update_version(self) -> None: """Update version""" - self._database.write("system", "version", self._system.version()) + self._database.update_data( + DatabaseModel, + DatabaseModel( + key="version", + value=self._system.version(), + ), + ) async def update_version_latest(self) -> None: """Update latest version""" release = await self._system.version_latest() if release and release.tag_name: - self._database.write( - "system", - "version_latest", - release.tag_name.replace("v", "") if release is not None else None, + self._database.update_data( + DatabaseModel, + DatabaseModel( + key="version_latest", + value=release.tag_name.replace("v", "") + if release is not None + else None, + ), ) async def update_version_newer_available(self) -> None: """Update newer version available""" - self._database.write( - "system", - "version_newer_available", - self._system.version_newer_available(self._database), + value = self._system.version_newer_available(self._database) + self._database.update_data( + DatabaseModel, + DatabaseModel( + key="version_newer_available", + value=str(value) if value else None, + ), ) async def update_all_data(self) -> None: diff --git a/backend/systembridgebackend/modules/update.py b/backend/systembridgebackend/modules/update.py index 084214e67..1a6e1cf0e 100644 --- a/backend/systembridgebackend/modules/update.py +++ b/backend/systembridgebackend/modules/update.py @@ -7,14 +7,13 @@ from .battery.update import BatteryUpdate from .cpu.update import CPUUpdate +from .disk.update import DiskUpdate +from .display.update import DisplayUpdate +from .gpu.update import GPUUpdate +from .memory.update import MemoryUpdate +from .network.update import NetworkUpdate from .sensors.update import SensorsUpdate - -# from .disk.update import DiskUpdate -# from .display.update import DisplayUpdate -# from .gpu.update import GPUUpdate -# from .memory.update import MemoryUpdate -# from .network.update import NetworkUpdate -# from .system.update import SystemUpdate +from .system.update import SystemUpdate class Update(Base): @@ -30,15 +29,15 @@ def __init__( self._classes = [ {"name": "battery", "cls": BatteryUpdate(self._database)}, - # {"name": "disk", "cls": DiskUpdate(self._database)}, - # {"name": "system", "cls": SystemUpdate(self._database)}, + {"name": "disk", "cls": DiskUpdate(self._database)}, + {"name": "system", "cls": SystemUpdate(self._database)}, ] self._classes_frequent = [ {"name": "cpu", "cls": CPUUpdate(self._database)}, - # {"name": "display", "cls": DisplayUpdate(self._database)}, - # {"name": "gpu", "cls": GPUUpdate(self._database)}, - # {"name": "memory", "cls": MemoryUpdate(self._database)}, - # {"name": "network", "cls": NetworkUpdate(self._database)}, + {"name": "display", "cls": DisplayUpdate(self._database)}, + {"name": "gpu", "cls": GPUUpdate(self._database)}, + {"name": "memory", "cls": MemoryUpdate(self._database)}, + {"name": "network", "cls": NetworkUpdate(self._database)}, ] async def _update( From 81aeae87df2ebf6e3b21611aef4883fe09272ef8 Mon Sep 17 00:00:00 2001 From: Aidan Timson Date: Sat, 6 Aug 2022 14:53:05 +0100 Subject: [PATCH 22/38] Update --- .../systembridgebackend/modules/gpu/update.py | 123 ++++++++++++------ 1 file changed, 86 insertions(+), 37 deletions(-) diff --git a/backend/systembridgebackend/modules/gpu/update.py b/backend/systembridgebackend/modules/gpu/update.py index aa416ccbe..14883702b 100644 --- a/backend/systembridgebackend/modules/gpu/update.py +++ b/backend/systembridgebackend/modules/gpu/update.py @@ -1,5 +1,6 @@ """System Bridge: Update GPU""" import asyncio +from json import dumps from systembridgeshared.common import make_key from systembridgeshared.database import Database @@ -17,7 +18,7 @@ def __init__( database: Database, ) -> None: """Initialize""" - super().__init__(database, "gpu") + super().__init__(database) self._gpu = GPU() async def update_name( @@ -26,17 +27,26 @@ async def update_name( gpu_name: str, ) -> None: """Update name""" - self._database.write("gpu", f"{gpu_key}_name", gpu_name) + self._database.update_data( + DatabaseModel, + DatabaseModel( + key=f"{gpu_key}_name", + value=gpu_name, + ), + ) async def update_core_clock( self, gpu_key: str, ) -> None: """Update core clock""" - self._database.write( - "gpu", - f"{gpu_key}_core_clock", - self._gpu.core_clock(self._database, gpu_key), + value = self._gpu.core_clock(self._database, gpu_key) + self._database.update_data( + DatabaseModel, + DatabaseModel( + key=f"{gpu_key}_core_clock", + value=str(value) if value else None, + ), ) async def update_core_load( @@ -44,8 +54,13 @@ async def update_core_load( gpu_key: str, ) -> None: """Update core load""" - self._database.write( - "gpu", f"{gpu_key}_core_load", self._gpu.core_load(self._database, gpu_key) + value = self._gpu.core_load(self._database, gpu_key) + self._database.update_data( + DatabaseModel, + DatabaseModel( + key=f"{gpu_key}_core_load", + value=str(value) if value else None, + ), ) async def update_fan_speed( @@ -53,19 +68,27 @@ async def update_fan_speed( gpu_key: str, ) -> None: """Update fan speed""" - self._database.write( - "gpu", f"{gpu_key}_fan_speed", self._gpu.fan_speed(self._database, gpu_key) + value = self._gpu.fan_speed(self._database, gpu_key) + self._database.update_data( + DatabaseModel, + DatabaseModel( + key=f"{gpu_key}_fan_speed", + value=str(value) if value else None, + ), ) async def update_memory_clock( self, gpu_key: str, ) -> None: + value = self._gpu.memory_clock(self._database, gpu_key) """Update memory clock""" - self._database.write( - "gpu", - f"{gpu_key}_memory_clock", - self._gpu.memory_clock(self._database, gpu_key), + self._database.update_data( + DatabaseModel, + DatabaseModel( + key=f"{gpu_key}_memory_clock", + value=str(value) if value else None, + ), ) async def update_memory_load( @@ -73,10 +96,13 @@ async def update_memory_load( gpu_key: str, ) -> None: """Update memory load""" - self._database.write( - "gpu", - f"{gpu_key}_memory_load", - self._gpu.memory_load(self._database, gpu_key), + value = self._gpu.memory_load(self._database, gpu_key) + self._database.update_data( + DatabaseModel, + DatabaseModel( + key=f"{gpu_key}_memory_load", + value=str(value) if value else None, + ), ) async def update_memory_free( @@ -84,10 +110,13 @@ async def update_memory_free( gpu_key: str, ) -> None: """Update memory free""" - self._database.write( - "gpu", - f"{gpu_key}_memory_free", - self._gpu.memory_free(self._database, gpu_key), + value = self._gpu.memory_free(self._database, gpu_key) + self._database.update_data( + DatabaseModel, + DatabaseModel( + key=f"{gpu_key}_memory_free", + value=str(value) if value else None, + ), ) async def update_memory_used( @@ -95,10 +124,13 @@ async def update_memory_used( gpu_key: str, ) -> None: """Update memory used""" - self._database.write( - "gpu", - f"{gpu_key}_memory_used", - self._gpu.memory_used(self._database, gpu_key), + value = self._gpu.memory_used(self._database, gpu_key) + self._database.update_data( + DatabaseModel, + DatabaseModel( + key=f"{gpu_key}_memory_used", + value=str(value) if value else None, + ), ) async def update_memory_total( @@ -106,10 +138,13 @@ async def update_memory_total( gpu_key: str, ) -> None: """Update memory total""" - self._database.write( - "gpu", - f"{gpu_key}_memory_total", - self._gpu.memory_total(self._database, gpu_key), + value = self._gpu.memory_total(self._database, gpu_key) + self._database.update_data( + DatabaseModel, + DatabaseModel( + key=f"{gpu_key}_memory_total", + value=str(value) if value else None, + ), ) async def update_power( @@ -117,8 +152,13 @@ async def update_power( gpu_key: str, ) -> None: """Update power""" - self._database.write( - "gpu", f"{gpu_key}_power", self._gpu.power(self._database, gpu_key) + value = self._gpu.power(self._database, gpu_key) + self._database.update_data( + DatabaseModel, + DatabaseModel( + key=f"{gpu_key}_power", + value=str(value) if value else None, + ), ) async def update_temperature( @@ -126,10 +166,13 @@ async def update_temperature( gpu_key: str, ) -> None: """Update temperature""" - self._database.write( - "gpu", - f"{gpu_key}_temperature", - self._gpu.temperature(self._database, gpu_key), + value = self._gpu.temperature(self._database, gpu_key) + self._database.update_data( + DatabaseModel, + DatabaseModel( + key=f"{gpu_key}_temperature", + value=str(value) if value else None, + ), ) async def update_all_data(self) -> None: @@ -157,4 +200,10 @@ async def update_all_data(self) -> None: self.update_temperature(gpu_key), ] ) - self._database.write("gpu", "gpus", gpu_list) + self._database.update_data( + DatabaseModel, + DatabaseModel( + key="gpus", + value=dumps(gpu_list), + ), + ) From 733710f3798c22043accc25647baba07f2aba757 Mon Sep 17 00:00:00 2001 From: Aidan Timson Date: Sat, 6 Aug 2022 14:56:35 +0100 Subject: [PATCH 23/38] Fix --- .../modules/system/__init__.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/backend/systembridgebackend/modules/system/__init__.py b/backend/systembridgebackend/modules/system/__init__.py index 4402792db..10b2ac410 100644 --- a/backend/systembridgebackend/modules/system/__init__.py +++ b/backend/systembridgebackend/modules/system/__init__.py @@ -21,6 +21,7 @@ from psutil._common import suser from systembridgeshared.base import Base from systembridgeshared.database import Database +from systembridgeshared.models.database_data import System as DatabaseModel from ..._version import __version__ @@ -98,12 +99,16 @@ def version_newer_available( database: Database, ) -> Optional[bool]: """Check if newer version is available""" - version = database.read_table_by_key("system", "version").to_dict( - orient="records" - )[0]["value"] - latest_version = database.read_table_by_key("system", "version_latest").to_dict( - orient="records" - )[0]["value"] + version_record = database.get_data_item_by_key(DatabaseModel, "version") + if version_record is None: + return None + version = version_record.value + latest_version_record = database.get_data_item_by_key( + DatabaseModel, "version_latest" + ) + if latest_version_record is None: + return None + latest_version = latest_version_record.value if version is not None and latest_version is not None: return parse_version(latest_version) > parse_version(version) return None From cd2017be380cc0e40e7bfaffa249a2973c62dd2e Mon Sep 17 00:00:00 2001 From: Aidan Timson Date: Sat, 6 Aug 2022 14:58:23 +0100 Subject: [PATCH 24/38] Fixes --- backend/systembridgebackend/modules/display/update.py | 2 +- backend/systembridgebackend/modules/gpu/update.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/systembridgebackend/modules/display/update.py b/backend/systembridgebackend/modules/display/update.py index 40b950fba..e5d2c3de4 100644 --- a/backend/systembridgebackend/modules/display/update.py +++ b/backend/systembridgebackend/modules/display/update.py @@ -95,7 +95,7 @@ async def update_all_data(self) -> None: """Update data""" # Clear table in case of hardware changes since last run - self._database.clear_table("display") + self._database.clear_table(DatabaseModel) display_list = [] for display_name in self._display.get_displays(self._database): diff --git a/backend/systembridgebackend/modules/gpu/update.py b/backend/systembridgebackend/modules/gpu/update.py index 14883702b..7c0dfdb69 100644 --- a/backend/systembridgebackend/modules/gpu/update.py +++ b/backend/systembridgebackend/modules/gpu/update.py @@ -179,7 +179,7 @@ async def update_all_data(self) -> None: """Update data""" # Clear table in case of hardware changes since last run - self._database.clear_table("gpu") + self._database.clear_table(DatabaseModel) gpu_list = [] for gpu_name in self._gpu.get_gpus(self._database): From 302acec5331dc802b0fdfbd0414b9ea1d76767b9 Mon Sep 17 00:00:00 2001 From: Aidan Timson Date: Sat, 6 Aug 2022 15:02:40 +0100 Subject: [PATCH 25/38] Fixes --- .../modules/display/__init__.py | 13 ++++++---- .../modules/gpu/__init__.py | 25 +++++++++++-------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/backend/systembridgebackend/modules/display/__init__.py b/backend/systembridgebackend/modules/display/__init__.py index adc2e4924..d3d6d09a8 100644 --- a/backend/systembridgebackend/modules/display/__init__.py +++ b/backend/systembridgebackend/modules/display/__init__.py @@ -13,6 +13,9 @@ COLUMN_VALUE, ) from systembridgeshared.database import Database +from systembridgeshared.models.database_data_sensors import ( + Sensors as SensorsDatabaseModel, +) class Display(Base): @@ -24,7 +27,7 @@ def get_displays( ) -> list[str]: """Get Displays""" displays = [] - for item in database.read_table("sensors").to_dict(orient="records"): + for item in database.get_data(SensorsDatabaseModel): if ( item[COLUMN_HARDWARE_TYPE] is not None and "display" in item[COLUMN_HARDWARE_TYPE].lower() @@ -40,7 +43,7 @@ def pixel_clock( display_key: str, ) -> Optional[float]: """Display pixel clock""" - for item in database.read_table("sensors").to_dict(orient="records"): + for item in database.get_data(SensorsDatabaseModel): if ( item[COLUMN_HARDWARE_TYPE] is not None and "display" in item[COLUMN_HARDWARE_TYPE].lower() @@ -62,7 +65,7 @@ def refresh_rate( display_key: str, ) -> Optional[float]: """Display refresh rate""" - for item in database.read_table("sensors").to_dict(orient="records"): + for item in database.get_data(SensorsDatabaseModel): if ( item[COLUMN_HARDWARE_TYPE] is not None and "display" in item[COLUMN_HARDWARE_TYPE].lower() @@ -84,7 +87,7 @@ def resolution_horizontal( display_key: str, ) -> Optional[int]: """Display resolution horizontal""" - for item in database.read_table("sensors").to_dict(orient="records"): + for item in database.get_data(SensorsDatabaseModel): if ( item[COLUMN_HARDWARE_TYPE] is not None and "display" in item[COLUMN_HARDWARE_TYPE].lower() @@ -106,7 +109,7 @@ def resolution_vertical( display_key: str, ) -> Optional[int]: """Display resolution vertical""" - for item in database.read_table("sensors").to_dict(orient="records"): + for item in database.get_data(SensorsDatabaseModel): if ( item[COLUMN_HARDWARE_TYPE] is not None and "display" in item[COLUMN_HARDWARE_TYPE].lower() diff --git a/backend/systembridgebackend/modules/gpu/__init__.py b/backend/systembridgebackend/modules/gpu/__init__.py index ec129070b..8d9e887ac 100644 --- a/backend/systembridgebackend/modules/gpu/__init__.py +++ b/backend/systembridgebackend/modules/gpu/__init__.py @@ -14,6 +14,9 @@ COLUMN_VALUE, ) from systembridgeshared.database import Database +from systembridgeshared.models.database_data_sensors import ( + Sensors as SensorsDatabaseModel, +) class GPU(Base): @@ -25,7 +28,7 @@ def get_gpus( ) -> list[str]: """Get GPUs""" gpus = [] - for item in database.read_table("sensors").to_dict(orient="records"): + for item in database.get_data(SensorsDatabaseModel): if ( item[COLUMN_HARDWARE_TYPE] is not None and "gpu" in item[COLUMN_HARDWARE_TYPE].lower() @@ -40,7 +43,7 @@ def core_clock( gpu_key: str, ) -> Optional[float]: """GPU core clock""" - for item in database.read_table("sensors").to_dict(orient="records"): + for item in database.get_data(SensorsDatabaseModel): if ( item[COLUMN_HARDWARE_TYPE] is not None and "gpu" in item[COLUMN_HARDWARE_TYPE].lower() @@ -62,7 +65,7 @@ def core_load( gpu_key: str, ) -> Optional[float]: """GPU core load""" - for item in database.read_table("sensors").to_dict(orient="records"): + for item in database.get_data(SensorsDatabaseModel): if ( item[COLUMN_HARDWARE_TYPE] is not None and "gpu" in item[COLUMN_HARDWARE_TYPE].lower() @@ -82,7 +85,7 @@ def fan_speed( gpu_key: str, ) -> Optional[float]: """GPU fan speed""" - for item in database.read_table("sensors").to_dict(orient="records"): + for item in database.get_data(SensorsDatabaseModel): if ( item[COLUMN_HARDWARE_TYPE] is not None and "gpu" in item[COLUMN_HARDWARE_TYPE].lower() @@ -101,7 +104,7 @@ def memory_clock( gpu_key: str, ) -> Optional[float]: """GPU memory clock""" - for item in database.read_table("sensors").to_dict(orient="records"): + for item in database.get_data(SensorsDatabaseModel): if ( item[COLUMN_HARDWARE_TYPE] is not None and "gpu" in item[COLUMN_HARDWARE_TYPE].lower() @@ -123,7 +126,7 @@ def memory_load( gpu_key: str, ) -> Optional[float]: """GPU memory load""" - for item in database.read_table("sensors").to_dict(orient="records"): + for item in database.get_data(SensorsDatabaseModel): if ( item[COLUMN_HARDWARE_TYPE] is not None and "gpu" in item[COLUMN_HARDWARE_TYPE].lower() @@ -145,7 +148,7 @@ def memory_free( gpu_key: str, ) -> Optional[float]: """GPU memory free""" - for item in database.read_table("sensors").to_dict(orient="records"): + for item in database.get_data(SensorsDatabaseModel): if ( item[COLUMN_HARDWARE_TYPE] is not None and "gpu" in item[COLUMN_HARDWARE_TYPE].lower() @@ -167,7 +170,7 @@ def memory_used( gpu_key: str, ) -> Optional[float]: """GPU memory used""" - for item in database.read_table("sensors").to_dict(orient="records"): + for item in database.get_data(SensorsDatabaseModel): if ( item[COLUMN_HARDWARE_TYPE] is not None and "gpu" in item[COLUMN_HARDWARE_TYPE].lower() @@ -189,7 +192,7 @@ def memory_total( gpu_key: str, ) -> Optional[float]: """GPU memory total""" - for item in database.read_table("sensors").to_dict(orient="records"): + for item in database.get_data(SensorsDatabaseModel): if ( item[COLUMN_HARDWARE_TYPE] is not None and "gpu" in item[COLUMN_HARDWARE_TYPE].lower() @@ -211,7 +214,7 @@ def power( gpu_key: str, ) -> Optional[float]: """GPU power usage""" - for item in database.read_table("sensors").to_dict(orient="records"): + for item in database.get_data(SensorsDatabaseModel): if ( item[COLUMN_HARDWARE_TYPE] is not None and "gpu" in item[COLUMN_HARDWARE_TYPE].lower() @@ -230,7 +233,7 @@ def temperature( gpu_key: str, ) -> Optional[float]: """GPU temperature""" - for item in database.read_table("sensors").to_dict(orient="records"): + for item in database.get_data(SensorsDatabaseModel): if ( item[COLUMN_HARDWARE_TYPE] is not None and "gpu" in item[COLUMN_HARDWARE_TYPE].lower() From 43ff68ed563a39c3836de995f0bbbab527f7c5bb Mon Sep 17 00:00:00 2001 From: Aidan Timson Date: Sat, 6 Aug 2022 15:07:31 +0100 Subject: [PATCH 26/38] Optional displays and gpus --- connector/systembridgeconnector/models/display.py | 4 ++-- connector/systembridgeconnector/models/gpu.py | 4 ++-- schemas/data/display.json | 3 +-- schemas/data/gpu.json | 3 +-- shared/systembridgeshared/models/display.py | 4 ++-- shared/systembridgeshared/models/gpu.py | 4 ++-- 6 files changed, 10 insertions(+), 12 deletions(-) diff --git a/connector/systembridgeconnector/models/display.py b/connector/systembridgeconnector/models/display.py index c08be42f2..c668615d0 100644 --- a/connector/systembridgeconnector/models/display.py +++ b/connector/systembridgeconnector/models/display.py @@ -28,5 +28,5 @@ class Config: extra = Extra.allow id: Optional[str] = Field(None, description="Event ID") - displays: list - last_updated: LastUpdated = Field(..., description="Last updated") + displays: Optional[list] = None + last_updated: Optional[LastUpdated] = Field(None, description="Last updated") diff --git a/connector/systembridgeconnector/models/gpu.py b/connector/systembridgeconnector/models/gpu.py index 51e573f57..01ebccc6a 100644 --- a/connector/systembridgeconnector/models/gpu.py +++ b/connector/systembridgeconnector/models/gpu.py @@ -28,5 +28,5 @@ class Config: extra = Extra.allow id: Optional[str] = Field(None, description="Event ID") - gpus: list - last_updated: LastUpdated = Field(..., description="Last updated") + gpus: Optional[list] = None + last_updated: Optional[LastUpdated] = Field(None, description="Last updated") diff --git a/schemas/data/display.json b/schemas/data/display.json index 08e3277da..e4a322cc4 100644 --- a/schemas/data/display.json +++ b/schemas/data/display.json @@ -24,6 +24,5 @@ "required": ["displays"] } }, - "additionalProperties": {}, - "required": ["displays", "last_updated"] + "additionalProperties": {} } diff --git a/schemas/data/gpu.json b/schemas/data/gpu.json index 5046871e2..b86666626 100644 --- a/schemas/data/gpu.json +++ b/schemas/data/gpu.json @@ -24,6 +24,5 @@ "required": ["gpus"] } }, - "additionalProperties": {}, - "required": ["gpus", "last_updated"] + "additionalProperties": {} } diff --git a/shared/systembridgeshared/models/display.py b/shared/systembridgeshared/models/display.py index c08be42f2..c668615d0 100644 --- a/shared/systembridgeshared/models/display.py +++ b/shared/systembridgeshared/models/display.py @@ -28,5 +28,5 @@ class Config: extra = Extra.allow id: Optional[str] = Field(None, description="Event ID") - displays: list - last_updated: LastUpdated = Field(..., description="Last updated") + displays: Optional[list] = None + last_updated: Optional[LastUpdated] = Field(None, description="Last updated") diff --git a/shared/systembridgeshared/models/gpu.py b/shared/systembridgeshared/models/gpu.py index 51e573f57..01ebccc6a 100644 --- a/shared/systembridgeshared/models/gpu.py +++ b/shared/systembridgeshared/models/gpu.py @@ -28,5 +28,5 @@ class Config: extra = Extra.allow id: Optional[str] = Field(None, description="Event ID") - gpus: list - last_updated: LastUpdated = Field(..., description="Last updated") + gpus: Optional[list] = None + last_updated: Optional[LastUpdated] = Field(None, description="Last updated") From 54a49b7e8c80379ac86485a9df1f5f5b4af629b7 Mon Sep 17 00:00:00 2001 From: Aidan Timson Date: Sat, 6 Aug 2022 15:14:42 +0100 Subject: [PATCH 27/38] Fix --- .../modules/cpu/__init__.py | 34 ++-- .../modules/display/__init__.py | 81 ++++----- .../modules/gpu/__init__.py | 172 ++++++++---------- shared/systembridgeshared/database.py | 4 +- 4 files changed, 132 insertions(+), 159 deletions(-) diff --git a/backend/systembridgebackend/modules/cpu/__init__.py b/backend/systembridgebackend/modules/cpu/__init__.py index 980c2087a..9ca4eb75b 100644 --- a/backend/systembridgebackend/modules/cpu/__init__.py +++ b/backend/systembridgebackend/modules/cpu/__init__.py @@ -14,12 +14,6 @@ ) from psutil._common import pcputimes, scpufreq, scpustats from systembridgeshared.base import Base -from systembridgeshared.const import ( - COLUMN_HARDWARE_TYPE, - COLUMN_KEY, - COLUMN_TYPE, - COLUMN_VALUE, -) from systembridgeshared.database import Database from systembridgeshared.models.database_data_sensors import ( Sensors as SensorsDatabaseModel, @@ -57,22 +51,22 @@ def temperature( ) -> Optional[float]: """CPU temperature""" for item in database.get_data(SensorsDatabaseModel): - if item[COLUMN_HARDWARE_TYPE] is not None and ( + if item.hardware_type is not None and ( ( - "cpu" in item[COLUMN_HARDWARE_TYPE].lower() - and "temperature" in item[COLUMN_TYPE].lower() + "cpu" in item.hardware_type.lower() + and "temperature" in item.type.lower() ) or ( - "k10temp" in item[COLUMN_HARDWARE_TYPE].lower() - and "current" in item[COLUMN_TYPE].lower() + "k10temp" in item.hardware_type.lower() + and "current" in item.type.lower() ) ): self._logger.debug( "Found CPU temperature: %s = %s", - item[COLUMN_KEY], - item[COLUMN_VALUE], + item.key, + item.value, ) - return item[COLUMN_VALUE] + return item.value return None def times(self) -> pcputimes: @@ -110,12 +104,10 @@ def voltage( """CPU voltage""" for item in database.get_data(SensorsDatabaseModel): if ( - item[COLUMN_HARDWARE_TYPE] is not None - and "cpu" in item[COLUMN_HARDWARE_TYPE].lower() - and "voltage" in item[COLUMN_TYPE].lower() + item.hardware_type is not None + and "cpu" in item.hardware_type.lower() + and "voltage" in item.type.lower() ): - self._logger.debug( - "Found CPU voltage: %s = %s", item[COLUMN_KEY], item[COLUMN_VALUE] - ) - return item[COLUMN_VALUE] + self._logger.debug("Found CPU voltage: %s = %s", item.key, item.value) + return item.value return None diff --git a/backend/systembridgebackend/modules/display/__init__.py b/backend/systembridgebackend/modules/display/__init__.py index d3d6d09a8..b6052e53e 100644 --- a/backend/systembridgebackend/modules/display/__init__.py +++ b/backend/systembridgebackend/modules/display/__init__.py @@ -5,13 +5,6 @@ from systembridgeshared.base import Base from systembridgeshared.common import make_key -from systembridgeshared.const import ( - COLUMN_HARDWARE_NAME, - COLUMN_HARDWARE_TYPE, - COLUMN_KEY, - COLUMN_NAME, - COLUMN_VALUE, -) from systembridgeshared.database import Database from systembridgeshared.models.database_data_sensors import ( Sensors as SensorsDatabaseModel, @@ -29,12 +22,12 @@ def get_displays( displays = [] for item in database.get_data(SensorsDatabaseModel): if ( - item[COLUMN_HARDWARE_TYPE] is not None - and "display" in item[COLUMN_HARDWARE_TYPE].lower() - and item[COLUMN_HARDWARE_NAME] is not None - and item[COLUMN_HARDWARE_NAME] not in displays + item.hardware_type is not None + and "display" in item.hardware_type.lower() + and item.hardware_name is not None + and item.hardware_name not in displays ): - displays.append(item[COLUMN_HARDWARE_NAME]) + displays.append(item.hardware_name) return displays def pixel_clock( @@ -45,18 +38,18 @@ def pixel_clock( """Display pixel clock""" for item in database.get_data(SensorsDatabaseModel): if ( - item[COLUMN_HARDWARE_TYPE] is not None - and "display" in item[COLUMN_HARDWARE_TYPE].lower() - and "pixel" in item[COLUMN_NAME].lower() - and "clock" in item[COLUMN_NAME].lower() - and make_key(item[COLUMN_HARDWARE_NAME]) == display_key + item.hardware_type is not None + and "display" in item.hardware_type.lower() + and "pixel" in item.name.lower() + and "clock" in item.name.lower() + and make_key(item.hardware_name) == display_key ): self._logger.debug( "Found display pixel clock: %s = %s", - item[COLUMN_KEY], - item[COLUMN_VALUE], + item.key, + item.value, ) - return item[COLUMN_VALUE] + return item.value return None def refresh_rate( @@ -67,18 +60,18 @@ def refresh_rate( """Display refresh rate""" for item in database.get_data(SensorsDatabaseModel): if ( - item[COLUMN_HARDWARE_TYPE] is not None - and "display" in item[COLUMN_HARDWARE_TYPE].lower() - and "refresh" in item[COLUMN_NAME].lower() - and "rate" in item[COLUMN_NAME].lower() - and make_key(item[COLUMN_HARDWARE_NAME]) == display_key + item.hardware_type is not None + and "display" in item.hardware_type.lower() + and "refresh" in item.name.lower() + and "rate" in item.name.lower() + and make_key(item.hardware_name) == display_key ): self._logger.debug( "Found display refresh rate: %s = %s", - item[COLUMN_KEY], - item[COLUMN_VALUE], + item.key, + item.value, ) - return item[COLUMN_VALUE] + return item.value return None def resolution_horizontal( @@ -89,18 +82,18 @@ def resolution_horizontal( """Display resolution horizontal""" for item in database.get_data(SensorsDatabaseModel): if ( - item[COLUMN_HARDWARE_TYPE] is not None - and "display" in item[COLUMN_HARDWARE_TYPE].lower() - and "resolution" in item[COLUMN_NAME].lower() - and "horizontal" in item[COLUMN_NAME].lower() - and make_key(item[COLUMN_HARDWARE_NAME]) == display_key + item.hardware_type is not None + and "display" in item.hardware_type.lower() + and "resolution" in item.name.lower() + and "horizontal" in item.name.lower() + and make_key(item.hardware_name) == display_key ): self._logger.debug( "Found display resolution horizontal: %s = %s", - item[COLUMN_KEY], - item[COLUMN_VALUE], + item.key, + item.value, ) - return item[COLUMN_VALUE] + return item.value return None def resolution_vertical( @@ -111,16 +104,16 @@ def resolution_vertical( """Display resolution vertical""" for item in database.get_data(SensorsDatabaseModel): if ( - item[COLUMN_HARDWARE_TYPE] is not None - and "display" in item[COLUMN_HARDWARE_TYPE].lower() - and "resolution" in item[COLUMN_NAME].lower() - and "vertical" in item[COLUMN_NAME].lower() - and make_key(item[COLUMN_HARDWARE_NAME]) == display_key + item.hardware_type is not None + and "display" in item.hardware_type.lower() + and "resolution" in item.name.lower() + and "vertical" in item.name.lower() + and make_key(item.hardware_name) == display_key ): self._logger.debug( "Found display resolution vertical: %s = %s", - item[COLUMN_KEY], - item[COLUMN_VALUE], + item.key, + item.value, ) - return item[COLUMN_VALUE] + return item.value return None diff --git a/backend/systembridgebackend/modules/gpu/__init__.py b/backend/systembridgebackend/modules/gpu/__init__.py index 8d9e887ac..add64e3ff 100644 --- a/backend/systembridgebackend/modules/gpu/__init__.py +++ b/backend/systembridgebackend/modules/gpu/__init__.py @@ -5,14 +5,6 @@ from systembridgeshared.base import Base from systembridgeshared.common import make_key -from systembridgeshared.const import ( - COLUMN_HARDWARE_NAME, - COLUMN_HARDWARE_TYPE, - COLUMN_KEY, - COLUMN_NAME, - COLUMN_TYPE, - COLUMN_VALUE, -) from systembridgeshared.database import Database from systembridgeshared.models.database_data_sensors import ( Sensors as SensorsDatabaseModel, @@ -30,11 +22,11 @@ def get_gpus( gpus = [] for item in database.get_data(SensorsDatabaseModel): if ( - item[COLUMN_HARDWARE_TYPE] is not None - and "gpu" in item[COLUMN_HARDWARE_TYPE].lower() - and item[COLUMN_HARDWARE_NAME] not in gpus + item.hardware_type is not None + and "gpu" in item.hardware_type.lower() + and item.hardware_name not in gpus ): - gpus.append(item[COLUMN_HARDWARE_NAME]) + gpus.append(item.hardware_name) return gpus def core_clock( @@ -45,18 +37,18 @@ def core_clock( """GPU core clock""" for item in database.get_data(SensorsDatabaseModel): if ( - item[COLUMN_HARDWARE_TYPE] is not None - and "gpu" in item[COLUMN_HARDWARE_TYPE].lower() - and "clock" in item[COLUMN_TYPE].lower() - and "core" in item[COLUMN_NAME].lower() - and make_key(item[COLUMN_HARDWARE_NAME]) == gpu_key + item.hardware_type is not None + and "gpu" in item.hardware_type.lower() + and "clock" in item.type.lower() + and "core" in item.name.lower() + and make_key(item.hardware_name) == gpu_key ): self._logger.debug( "Found GPU core clock: %s = %s", - item[COLUMN_KEY], - item[COLUMN_VALUE], + item.key, + item.value, ) - return item[COLUMN_VALUE] + return item.value return None def core_load( @@ -67,16 +59,14 @@ def core_load( """GPU core load""" for item in database.get_data(SensorsDatabaseModel): if ( - item[COLUMN_HARDWARE_TYPE] is not None - and "gpu" in item[COLUMN_HARDWARE_TYPE].lower() - and "load" in item[COLUMN_TYPE].lower() - and "core" in item[COLUMN_NAME].lower() - and make_key(item[COLUMN_HARDWARE_NAME]) == gpu_key + item.hardware_type is not None + and "gpu" in item.hardware_type.lower() + and "load" in item.type.lower() + and "core" in item.name.lower() + and make_key(item.hardware_name) == gpu_key ): - self._logger.debug( - "Found GPU core load: %s = %s", item[COLUMN_KEY], item[COLUMN_VALUE] - ) - return item[COLUMN_VALUE] + self._logger.debug("Found GPU core load: %s = %s", item.key, item.value) + return item.value return None def fan_speed( @@ -87,15 +77,13 @@ def fan_speed( """GPU fan speed""" for item in database.get_data(SensorsDatabaseModel): if ( - item[COLUMN_HARDWARE_TYPE] is not None - and "gpu" in item[COLUMN_HARDWARE_TYPE].lower() - and "fan" in item[COLUMN_TYPE].lower() - and make_key(item[COLUMN_HARDWARE_NAME]) == gpu_key + item.hardware_type is not None + and "gpu" in item.hardware_type.lower() + and "fan" in item.type.lower() + and make_key(item.hardware_name) == gpu_key ): - self._logger.debug( - "Found GPU fan speed: %s = %s", item[COLUMN_KEY], item[COLUMN_VALUE] - ) - return item[COLUMN_VALUE] + self._logger.debug("Found GPU fan speed: %s = %s", item.key, item.value) + return item.value return None def memory_clock( @@ -106,18 +94,18 @@ def memory_clock( """GPU memory clock""" for item in database.get_data(SensorsDatabaseModel): if ( - item[COLUMN_HARDWARE_TYPE] is not None - and "gpu" in item[COLUMN_HARDWARE_TYPE].lower() - and "clock" in item[COLUMN_TYPE].lower() - and "memory" in item[COLUMN_NAME].lower() - and make_key(item[COLUMN_HARDWARE_NAME]) == gpu_key + item.hardware_type is not None + and "gpu" in item.hardware_type.lower() + and "clock" in item.type.lower() + and "memory" in item.name.lower() + and make_key(item.hardware_name) == gpu_key ): self._logger.debug( "Found GPU memory clock: %s = %s", - item[COLUMN_KEY], - item[COLUMN_VALUE], + item.key, + item.value, ) - return item[COLUMN_VALUE] + return item.value return None def memory_load( @@ -128,18 +116,18 @@ def memory_load( """GPU memory load""" for item in database.get_data(SensorsDatabaseModel): if ( - item[COLUMN_HARDWARE_TYPE] is not None - and "gpu" in item[COLUMN_HARDWARE_TYPE].lower() - and "load" in item[COLUMN_TYPE].lower() - and "memory" in item[COLUMN_NAME].lower() - and make_key(item[COLUMN_HARDWARE_NAME]) == gpu_key + item.hardware_type is not None + and "gpu" in item.hardware_type.lower() + and "load" in item.type.lower() + and "memory" in item.name.lower() + and make_key(item.hardware_name) == gpu_key ): self._logger.debug( "Found GPU memory load: %s = %s", - item[COLUMN_KEY], - item[COLUMN_VALUE], + item.key, + item.value, ) - return item[COLUMN_VALUE] + return item.value return None def memory_free( @@ -150,18 +138,18 @@ def memory_free( """GPU memory free""" for item in database.get_data(SensorsDatabaseModel): if ( - item[COLUMN_HARDWARE_TYPE] is not None - and "gpu" in item[COLUMN_HARDWARE_TYPE].lower() - and "memory" in item[COLUMN_NAME].lower() - and "free" in item[COLUMN_NAME].lower() - and make_key(item[COLUMN_HARDWARE_NAME]) == gpu_key + item.hardware_type is not None + and "gpu" in item.hardware_type.lower() + and "memory" in item.name.lower() + and "free" in item.name.lower() + and make_key(item.hardware_name) == gpu_key ): self._logger.debug( "Found GPU memory free: %s = %s", - item[COLUMN_KEY], - item[COLUMN_VALUE], + item.key, + item.value, ) - return item[COLUMN_VALUE] + return item.value return None def memory_used( @@ -172,18 +160,18 @@ def memory_used( """GPU memory used""" for item in database.get_data(SensorsDatabaseModel): if ( - item[COLUMN_HARDWARE_TYPE] is not None - and "gpu" in item[COLUMN_HARDWARE_TYPE].lower() - and "memory" in item[COLUMN_NAME].lower() - and "used" in item[COLUMN_NAME].lower() - and make_key(item[COLUMN_HARDWARE_NAME]) == gpu_key + item.hardware_type is not None + and "gpu" in item.hardware_type.lower() + and "memory" in item.name.lower() + and "used" in item.name.lower() + and make_key(item.hardware_name) == gpu_key ): self._logger.debug( "Found GPU memory used: %s = %s", - item[COLUMN_KEY], - item[COLUMN_VALUE], + item.key, + item.value, ) - return item[COLUMN_VALUE] + return item.value return None def memory_total( @@ -194,18 +182,18 @@ def memory_total( """GPU memory total""" for item in database.get_data(SensorsDatabaseModel): if ( - item[COLUMN_HARDWARE_TYPE] is not None - and "gpu" in item[COLUMN_HARDWARE_TYPE].lower() - and "memory" in item[COLUMN_NAME].lower() - and "total" in item[COLUMN_NAME].lower() - and make_key(item[COLUMN_HARDWARE_NAME]) == gpu_key + item.hardware_type is not None + and "gpu" in item.hardware_type.lower() + and "memory" in item.name.lower() + and "total" in item.name.lower() + and make_key(item.hardware_name) == gpu_key ): self._logger.debug( "Found GPU memory total: %s = %s", - item[COLUMN_KEY], - item[COLUMN_VALUE], + item.key, + item.value, ) - return item[COLUMN_VALUE] + return item.value return None def power( @@ -216,15 +204,13 @@ def power( """GPU power usage""" for item in database.get_data(SensorsDatabaseModel): if ( - item[COLUMN_HARDWARE_TYPE] is not None - and "gpu" in item[COLUMN_HARDWARE_TYPE].lower() - and "power" in item[COLUMN_TYPE].lower() - and make_key(item[COLUMN_HARDWARE_NAME]) == gpu_key + item.hardware_type is not None + and "gpu" in item.hardware_type.lower() + and "power" in item.type.lower() + and make_key(item.hardware_name) == gpu_key ): - self._logger.debug( - "Found GPU power: %s = %s", item[COLUMN_KEY], item[COLUMN_VALUE] - ) - return item[COLUMN_VALUE] + self._logger.debug("Found GPU power: %s = %s", item.key, item.value) + return item.value return None def temperature( @@ -235,16 +221,16 @@ def temperature( """GPU temperature""" for item in database.get_data(SensorsDatabaseModel): if ( - item[COLUMN_HARDWARE_TYPE] is not None - and "gpu" in item[COLUMN_HARDWARE_TYPE].lower() - and "temperature" in item[COLUMN_TYPE].lower() - and "core" in item[COLUMN_NAME].lower() - and make_key(item[COLUMN_HARDWARE_NAME]) == gpu_key + item.hardware_type is not None + and "gpu" in item.hardware_type.lower() + and "temperature" in item.type.lower() + and "core" in item.name.lower() + and make_key(item.hardware_name) == gpu_key ): self._logger.debug( "Found GPU temperature: %s = %s", - item[COLUMN_KEY], - item[COLUMN_VALUE], + item.key, + item.value, ) - return item[COLUMN_VALUE] + return item.value return None diff --git a/shared/systembridgeshared/database.py b/shared/systembridgeshared/database.py index 0dc0c47b8..91ead3750 100644 --- a/shared/systembridgeshared/database.py +++ b/shared/systembridgeshared/database.py @@ -161,4 +161,6 @@ def update_data( old_data.value = data.value old_data.timestamp = time() session.add(old_data) - # session.refresh(old_data) + session.commit() + if old_data is not None: + session.refresh(old_data) From 259f16a6f82f0ceb67ff2468c9da904faf1af01e Mon Sep 17 00:00:00 2001 From: Aidan Timson Date: Sat, 6 Aug 2022 15:23:01 +0100 Subject: [PATCH 28/38] Lint --- backend/systembridgebackend/modules/cpu/update.py | 1 - backend/systembridgebackend/modules/gpu/update.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/backend/systembridgebackend/modules/cpu/update.py b/backend/systembridgebackend/modules/cpu/update.py index 38b78204a..0618d4314 100644 --- a/backend/systembridgebackend/modules/cpu/update.py +++ b/backend/systembridgebackend/modules/cpu/update.py @@ -1,7 +1,6 @@ """System Bridge: Update CPU""" import asyncio -from sqlmodel import Session from systembridgeshared.database import Database from systembridgeshared.models.database_data import CPU as DatabaseModel diff --git a/backend/systembridgebackend/modules/gpu/update.py b/backend/systembridgebackend/modules/gpu/update.py index 7c0dfdb69..d152b1001 100644 --- a/backend/systembridgebackend/modules/gpu/update.py +++ b/backend/systembridgebackend/modules/gpu/update.py @@ -81,8 +81,8 @@ async def update_memory_clock( self, gpu_key: str, ) -> None: - value = self._gpu.memory_clock(self._database, gpu_key) """Update memory clock""" + value = self._gpu.memory_clock(self._database, gpu_key) self._database.update_data( DatabaseModel, DatabaseModel( From 48c86ce282df6123d983914a13700371d2b9628a Mon Sep 17 00:00:00 2001 From: Aidan Timson Date: Sat, 6 Aug 2022 15:27:30 +0100 Subject: [PATCH 29/38] Fixes --- cli/systembridgecli/__main__.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/cli/systembridgecli/__main__.py b/cli/systembridgecli/__main__.py index 601bd53d6..0d021e547 100644 --- a/cli/systembridgecli/__main__.py +++ b/cli/systembridgecli/__main__.py @@ -4,12 +4,13 @@ import os import subprocess import sys -from uuid import uuid4 from typing import Optional +from uuid import uuid4 from systembridgeshared.common import get_user_data_directory -from systembridgeshared.const import SECRET_API_KEY, SETTING_PORT_API, TABLE_SETTINGS -from systembridgeshared.database import Database +from systembridgeshared.const import SECRET_API_KEY, SETTING_PORT_API +from systembridgeshared.database import TABLE_MAP, Database +from systembridgeshared.models.database_data import Settings as SettingsDatabaseModule from systembridgeshared.settings import Settings from tabulate import tabulate import typer @@ -39,10 +40,11 @@ def api_port() -> None: @app.command(name="data", short_help="Get data") def data(module: str, key=None) -> None: """Get data""" + table_module = TABLE_MAP.get(module) if key: - output = database.read_table_by_key(module, key) + output = database.get_data_by_key(table_module, key) else: - output = database.read_table(module) + output = database.get_data(table_module) table_data = tabulate(output, headers="keys", tablefmt="psql") typer.secho(table_data, fg=typer.colors.GREEN) @@ -53,17 +55,16 @@ def data_value( key: str, ) -> None: """Get data value""" - output = database.read_table_by_key(module, key).to_dict(orient="records")[0][ - "value" - ] - typer.secho(output, fg=typer.colors.GREEN) + table_module = TABLE_MAP.get(module) + output = database.get_data_item_by_key(table_module, key) + typer.secho(output.value if output else None, fg=typer.colors.GREEN) @app.command(name="settings", short_help="Get all settings") def settings_all(): """Get all Settings""" table_data = tabulate( - database.read_table(TABLE_SETTINGS), headers="keys", tablefmt="psql" + database.get_data(SettingsDatabaseModule), headers="keys", tablefmt="psql" ) typer.secho(table_data, fg=typer.colors.CYAN) From 8ae265d2554eb2c51c86ef918249d6098a963f95 Mon Sep 17 00:00:00 2001 From: Aidan Timson Date: Sat, 6 Aug 2022 15:41:03 +0100 Subject: [PATCH 30/38] Fix --- cli/systembridgecli/__main__.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/cli/systembridgecli/__main__.py b/cli/systembridgecli/__main__.py index 0d021e547..e0f28f33b 100644 --- a/cli/systembridgecli/__main__.py +++ b/cli/systembridgecli/__main__.py @@ -42,9 +42,12 @@ def data(module: str, key=None) -> None: """Get data""" table_module = TABLE_MAP.get(module) if key: - output = database.get_data_by_key(table_module, key) + result = database.get_data_by_key(table_module, key) else: - output = database.get_data(table_module) + result = database.get_data(table_module) + + output = [item.dict() for item in result] + table_data = tabulate(output, headers="keys", tablefmt="psql") typer.secho(table_data, fg=typer.colors.GREEN) @@ -64,7 +67,9 @@ def data_value( def settings_all(): """Get all Settings""" table_data = tabulate( - database.get_data(SettingsDatabaseModule), headers="keys", tablefmt="psql" + [item.dict() for item in database.get_data(SettingsDatabaseModule)], + headers="keys", + tablefmt="psql", ) typer.secho(table_data, fg=typer.colors.CYAN) From c6a8620da99cb4e9391308a03688abe7f082d54f Mon Sep 17 00:00:00 2001 From: Aidan Timson Date: Sat, 6 Aug 2022 15:43:33 +0100 Subject: [PATCH 31/38] Lint --- connector/systembridgeconnector/const.py | 39 +++++++++---------- .../systembridgeconnector/models/data.py | 18 ++++----- shared/systembridgeshared/const.py | 39 +++++++++---------- shared/systembridgeshared/models/data.py | 18 ++++----- 4 files changed, 56 insertions(+), 58 deletions(-) diff --git a/connector/systembridgeconnector/const.py b/connector/systembridgeconnector/const.py index 9ad83feb6..2c6d16bd0 100644 --- a/connector/systembridgeconnector/const.py +++ b/connector/systembridgeconnector/const.py @@ -1,24 +1,23 @@ """System Bridge Connector: Constants""" -from systembridgeconnector.models.battery import Battery -from systembridgeconnector.models.bridge import Bridge -from systembridgeconnector.models.cpu import Cpu -from systembridgeconnector.models.data import Data -from systembridgeconnector.models.disk import Disk -from systembridgeconnector.models.display import Display -from systembridgeconnector.models.generic import Generic -from systembridgeconnector.models.gpu import Gpu -from systembridgeconnector.models.keyboard_key import KeyboardKey -from systembridgeconnector.models.keyboard_text import KeyboardText -from systembridgeconnector.models.media_directories import MediaDirectories -from systembridgeconnector.models.media_files import File, MediaFiles -from systembridgeconnector.models.memory import Memory -from systembridgeconnector.models.network import Network -from systembridgeconnector.models.notification import Notification -from systembridgeconnector.models.open_path import OpenPath -from systembridgeconnector.models.open_url import OpenUrl -from systembridgeconnector.models.response import Response -from systembridgeconnector.models.sensors import Sensors -from systembridgeconnector.models.system import System +from .models.battery import Battery +from .models.cpu import Cpu +from .models.data import Data +from .models.disk import Disk +from .models.display import Display +from .models.generic import Generic +from .models.gpu import Gpu +from .models.keyboard_key import KeyboardKey +from .models.keyboard_text import KeyboardText +from .models.media_directories import MediaDirectories +from .models.media_files import File, MediaFiles +from .models.memory import Memory +from .models.network import Network +from .models.notification import Notification +from .models.open_path import OpenPath +from .models.open_url import OpenUrl +from .models.response import Response +from .models.sensors import Sensors +from .models.system import System # Settings SETTING_ADDITIONAL_MEDIA_DIRECTORIES = "additional_media_directories" diff --git a/connector/systembridgeconnector/models/data.py b/connector/systembridgeconnector/models/data.py index d841324fb..6aebb3361 100644 --- a/connector/systembridgeconnector/models/data.py +++ b/connector/systembridgeconnector/models/data.py @@ -6,15 +6,15 @@ from pydantic import BaseModel, Extra, Field -from systembridgeconnector.models.battery import Battery -from systembridgeconnector.models.cpu import Cpu -from systembridgeconnector.models.disk import Disk -from systembridgeconnector.models.display import Display -from systembridgeconnector.models.gpu import Gpu -from systembridgeconnector.models.memory import Memory -from systembridgeconnector.models.network import Network -from systembridgeconnector.models.sensors import Sensors -from systembridgeconnector.models.system import System +from .battery import Battery +from .cpu import Cpu +from .disk import Disk +from .display import Display +from .gpu import Gpu +from .memory import Memory +from .network import Network +from .sensors import Sensors +from .system import System class Data(BaseModel): diff --git a/shared/systembridgeshared/const.py b/shared/systembridgeshared/const.py index a67de7ed4..0b90022d8 100644 --- a/shared/systembridgeshared/const.py +++ b/shared/systembridgeshared/const.py @@ -1,24 +1,23 @@ """System Bridge Shared: Constants""" -from systembridgeshared.models.battery import Battery -from systembridgeshared.models.bridge import Bridge -from systembridgeshared.models.cpu import Cpu -from systembridgeshared.models.data import Data -from systembridgeshared.models.disk import Disk -from systembridgeshared.models.display import Display -from systembridgeshared.models.generic import Generic -from systembridgeshared.models.gpu import Gpu -from systembridgeshared.models.keyboard_key import KeyboardKey -from systembridgeshared.models.keyboard_text import KeyboardText -from systembridgeshared.models.media_directories import MediaDirectories -from systembridgeshared.models.media_files import File, MediaFiles -from systembridgeshared.models.memory import Memory -from systembridgeshared.models.network import Network -from systembridgeshared.models.notification import Notification -from systembridgeshared.models.open_path import OpenPath -from systembridgeshared.models.open_url import OpenUrl -from systembridgeshared.models.response import Response -from systembridgeshared.models.sensors import Sensors -from systembridgeshared.models.system import System +from .models.battery import Battery +from .models.cpu import Cpu +from .models.data import Data +from .models.disk import Disk +from .models.display import Display +from .models.generic import Generic +from .models.gpu import Gpu +from .models.keyboard_key import KeyboardKey +from .models.keyboard_text import KeyboardText +from .models.media_directories import MediaDirectories +from .models.media_files import File, MediaFiles +from .models.memory import Memory +from .models.network import Network +from .models.notification import Notification +from .models.open_path import OpenPath +from .models.open_url import OpenUrl +from .models.response import Response +from .models.sensors import Sensors +from .models.system import System # Logging DATE_FORMAT = "%Y-%m-%d %H:%M:%S" diff --git a/shared/systembridgeshared/models/data.py b/shared/systembridgeshared/models/data.py index 2ccffef65..6aebb3361 100644 --- a/shared/systembridgeshared/models/data.py +++ b/shared/systembridgeshared/models/data.py @@ -6,15 +6,15 @@ from pydantic import BaseModel, Extra, Field -from systembridgeshared.models.battery import Battery -from systembridgeshared.models.cpu import Cpu -from systembridgeshared.models.disk import Disk -from systembridgeshared.models.display import Display -from systembridgeshared.models.gpu import Gpu -from systembridgeshared.models.memory import Memory -from systembridgeshared.models.network import Network -from systembridgeshared.models.sensors import Sensors -from systembridgeshared.models.system import System +from .battery import Battery +from .cpu import Cpu +from .disk import Disk +from .display import Display +from .gpu import Gpu +from .memory import Memory +from .network import Network +from .sensors import Sensors +from .system import System class Data(BaseModel): From a848601d0656a3f78dc069124321e899ac1cf6aa Mon Sep 17 00:00:00 2001 From: Aidan Timson Date: Sat, 6 Aug 2022 15:47:10 +0100 Subject: [PATCH 32/38] Cleanup --- .../systembridgeconnector/http_client.py | 7 ++-- connector/systembridgeconnector/version.py | 11 +++--- .../systembridgeconnector/websocket_client.py | 34 +++++++++--------- shared/systembridgeshared/logger.py | 4 +-- shared/systembridgeshared/update.py | 4 +-- shared/systembridgeshared/websocket_client.py | 36 +++++++++---------- 6 files changed, 47 insertions(+), 49 deletions(-) diff --git a/connector/systembridgeconnector/http_client.py b/connector/systembridgeconnector/http_client.py index 078ad9600..29dcece56 100644 --- a/connector/systembridgeconnector/http_client.py +++ b/connector/systembridgeconnector/http_client.py @@ -8,11 +8,8 @@ from aiohttp.client_exceptions import ClientConnectorError, ServerDisconnectedError import async_timeout -from systembridgeconnector.base import Base -from systembridgeconnector.exceptions import ( - AuthenticationException, - ConnectionErrorException, -) +from .base import Base +from .exceptions import AuthenticationException, ConnectionErrorException BASE_HEADERS = { "Accept": "application/json", diff --git a/connector/systembridgeconnector/version.py b/connector/systembridgeconnector/version.py index aa692cc18..a26c927a9 100644 --- a/connector/systembridgeconnector/version.py +++ b/connector/systembridgeconnector/version.py @@ -1,14 +1,15 @@ """System Bridge Connector: Version""" from __future__ import annotations +from typing import Optional + from aiohttp import ClientSession from pkg_resources import parse_version -from typing import Optional -from systembridgeconnector.base import Base -from systembridgeconnector.exceptions import ConnectionErrorException -from systembridgeconnector.http_client import HTTPClient -from systembridgeconnector.models.system import System +from .base import Base +from .exceptions import ConnectionErrorException +from .http_client import HTTPClient +from .models.system import System SUPPORTED_VERSION = "3.1.2" diff --git a/connector/systembridgeconnector/websocket_client.py b/connector/systembridgeconnector/websocket_client.py index e831283a6..6716ddcee 100644 --- a/connector/systembridgeconnector/websocket_client.py +++ b/connector/systembridgeconnector/websocket_client.py @@ -9,8 +9,8 @@ import aiohttp -from systembridgeconnector.base import Base -from systembridgeconnector.const import ( +from .base import Base +from .const import ( EVENT_API_KEY, EVENT_DATA, EVENT_EVENT, @@ -43,26 +43,26 @@ TYPE_POWER_SLEEP, TYPE_REGISTER_DATA_LISTENER, ) -from systembridgeconnector.exceptions import ( +from .exceptions import ( AuthenticationException, BadMessageException, ConnectionClosedException, ConnectionErrorException, ) -from systembridgeconnector.models.get_data import GetData -from systembridgeconnector.models.keyboard_key import KeyboardKey -from systembridgeconnector.models.keyboard_text import KeyboardText -from systembridgeconnector.models.media_directories import MediaDirectories -from systembridgeconnector.models.media_files import File as MediaFile, MediaFiles -from systembridgeconnector.models.media_get_file import MediaGetFile -from systembridgeconnector.models.media_get_files import MediaGetFiles -from systembridgeconnector.models.notification import Notification -from systembridgeconnector.models.open_path import OpenPath -from systembridgeconnector.models.open_url import OpenUrl -from systembridgeconnector.models.register_data_listener import RegisterDataListener -from systembridgeconnector.models.request import Request -from systembridgeconnector.models.response import Response -from systembridgeconnector.models.update import Update +from .models.get_data import GetData +from .models.keyboard_key import KeyboardKey +from .models.keyboard_text import KeyboardText +from .models.media_directories import MediaDirectories +from .models.media_files import File as MediaFile, MediaFiles +from .models.media_get_file import MediaGetFile +from .models.media_get_files import MediaGetFiles +from .models.notification import Notification +from .models.open_path import OpenPath +from .models.open_url import OpenUrl +from .models.register_data_listener import RegisterDataListener +from .models.request import Request +from .models.response import Response +from .models.update import Update class WebSocketClient(Base): diff --git a/shared/systembridgeshared/logger.py b/shared/systembridgeshared/logger.py index a06dd6078..5cb931814 100644 --- a/shared/systembridgeshared/logger.py +++ b/shared/systembridgeshared/logger.py @@ -5,8 +5,8 @@ from colorlog import ColoredFormatter -from systembridgeshared.common import get_user_data_directory -from systembridgeshared.const import DATE_FORMAT, FORMAT +from .common import get_user_data_directory +from .const import DATE_FORMAT, FORMAT def setup_logger( diff --git a/shared/systembridgeshared/update.py b/shared/systembridgeshared/update.py index 846a94130..b13a99ab9 100644 --- a/shared/systembridgeshared/update.py +++ b/shared/systembridgeshared/update.py @@ -8,10 +8,10 @@ import platform import subprocess import sys -import urllib.request from typing import Optional +import urllib.request -from systembridgeshared.base import Base +from .base import Base class Update(Base): diff --git a/shared/systembridgeshared/websocket_client.py b/shared/systembridgeshared/websocket_client.py index 9e5468496..b32ef8778 100644 --- a/shared/systembridgeshared/websocket_client.py +++ b/shared/systembridgeshared/websocket_client.py @@ -9,8 +9,8 @@ import aiohttp -from systembridgeshared.base import Base -from systembridgeshared.const import ( +from .base import Base +from .const import ( EVENT_API_KEY, EVENT_DATA, EVENT_EVENT, @@ -45,27 +45,27 @@ TYPE_POWER_SLEEP, TYPE_REGISTER_DATA_LISTENER, ) -from systembridgeshared.exceptions import ( +from .exceptions import ( AuthenticationException, BadMessageException, ConnectionClosedException, ConnectionErrorException, ) -from systembridgeshared.models.get_data import GetData -from systembridgeshared.models.keyboard_key import KeyboardKey -from systembridgeshared.models.keyboard_text import KeyboardText -from systembridgeshared.models.media_directories import MediaDirectories -from systembridgeshared.models.media_files import File as MediaFile, MediaFiles -from systembridgeshared.models.media_get_file import MediaGetFile -from systembridgeshared.models.media_get_files import MediaGetFiles -from systembridgeshared.models.notification import Notification -from systembridgeshared.models.open_path import OpenPath -from systembridgeshared.models.open_url import OpenUrl -from systembridgeshared.models.register_data_listener import RegisterDataListener -from systembridgeshared.models.request import Request -from systembridgeshared.models.response import Response -from systembridgeshared.models.update import Update -from systembridgeshared.settings import Settings +from .models.get_data import GetData +from .models.keyboard_key import KeyboardKey +from .models.keyboard_text import KeyboardText +from .models.media_directories import MediaDirectories +from .models.media_files import File as MediaFile, MediaFiles +from .models.media_get_file import MediaGetFile +from .models.media_get_files import MediaGetFiles +from .models.notification import Notification +from .models.open_path import OpenPath +from .models.open_url import OpenUrl +from .models.register_data_listener import RegisterDataListener +from .models.request import Request +from .models.response import Response +from .models.update import Update +from .settings import Settings class WebSocketClient(Base): From de31a26379bbf38f714c65310359f83d0b65b15b Mon Sep 17 00:00:00 2001 From: Aidan Timson Date: Sat, 6 Aug 2022 15:48:40 +0100 Subject: [PATCH 33/38] Lint --- shared/systembridgeshared/database.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/shared/systembridgeshared/database.py b/shared/systembridgeshared/database.py index 91ead3750..e673c1fed 100644 --- a/shared/systembridgeshared/database.py +++ b/shared/systembridgeshared/database.py @@ -4,7 +4,7 @@ from collections.abc import Mapping import os from time import time -from typing import Any, List, Optional, Union +from typing import Any, Optional, Union from sqlmodel import Session, SQLModel, create_engine, select @@ -98,7 +98,7 @@ def clear_table( def get_data( self, table: Any, - ) -> List[Any]: + ) -> list[Any]: """Get data from database""" with Session(self._engine) as session: return session.exec(select(table)).all() @@ -107,7 +107,7 @@ def get_data_by_key( self, table: Any, key: str, - ) -> List[Data]: + ) -> list[Data]: """Get data from database by key""" with Session(self._engine) as session: return session.exec(select(table).where(table.key == key)).all() @@ -153,8 +153,7 @@ def update_data( """Update data""" with Session(self._engine) as session: result = session.exec(select(table).where(table.key == data.key)) - old_data = result.first() - if old_data is None: + if (old_data := result.first()) is None: data.timestamp = time() session.add(data) else: From b34d82d1bfe645784f8bc88f93400bf38e0aa956 Mon Sep 17 00:00:00 2001 From: Aidan Timson Date: Sat, 6 Aug 2022 15:50:52 +0100 Subject: [PATCH 34/38] Relative imports --- cli/systembridgecli/__main__.py | 2 +- gui/systembridgegui/__main__.py | 12 ++++++------ .../models/database_data_sensors.py | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cli/systembridgecli/__main__.py b/cli/systembridgecli/__main__.py index e0f28f33b..c7e4eebd8 100644 --- a/cli/systembridgecli/__main__.py +++ b/cli/systembridgecli/__main__.py @@ -15,7 +15,7 @@ from tabulate import tabulate import typer -from systembridgecli._version import __version__ +from ._version import __version__ app = typer.Typer() database = Database() diff --git a/gui/systembridgegui/__main__.py b/gui/systembridgegui/__main__.py index a95e0bd81..161a93822 100644 --- a/gui/systembridgegui/__main__.py +++ b/gui/systembridgegui/__main__.py @@ -24,12 +24,12 @@ from systembridgeshared.websocket_client import WebSocketClient from typer import Typer -from systembridgegui._version import __version__ -from systembridgegui.system_tray import SystemTray -from systembridgegui.widgets.timed_message_box import TimedMessageBox -from systembridgegui.window.main import MainWindow -from systembridgegui.window.notification import NotificationWindow -from systembridgegui.window.player import PlayerWindow +from ._version import __version__ +from .system_tray import SystemTray +from .widgets.timed_message_box import TimedMessageBox +from .window.main import MainWindow +from .window.notification import NotificationWindow +from .window.player import PlayerWindow class Main(Base): diff --git a/shared/systembridgeshared/models/database_data_sensors.py b/shared/systembridgeshared/models/database_data_sensors.py index dfa11dd25..fff2f54b1 100644 --- a/shared/systembridgeshared/models/database_data_sensors.py +++ b/shared/systembridgeshared/models/database_data_sensors.py @@ -6,7 +6,7 @@ from sqlmodel import Field -from systembridgeshared.models.database_data import Data +from .database_data import Data class Sensors(Data, table=True): From dfa62e717a129086689cc95b77ac147f5481d99a Mon Sep 17 00:00:00 2001 From: Aidan Timson Date: Sat, 6 Aug 2022 15:53:16 +0100 Subject: [PATCH 35/38] Fix --- shared/systembridgeshared/settings.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/shared/systembridgeshared/settings.py b/shared/systembridgeshared/settings.py index d798b616e..444224a5f 100644 --- a/shared/systembridgeshared/settings.py +++ b/shared/systembridgeshared/settings.py @@ -116,15 +116,13 @@ def set( value: str, ) -> None: """Set setting""" - session = self._database.get_session() - session.add( + self._database.update_data( + DatabaseSettings, DatabaseSettings( key=key, value=value, - ) + ), ) - session.commit() - session.close() def set_secret( self, @@ -134,12 +132,10 @@ def set_secret( """Set secret""" fernet = Fernet(self._encryption_key) - session = self._database.get_session() - session.add( + self._database.update_data( + DatabaseSecrets, DatabaseSecrets( key=key, value=fernet.encrypt(value.encode()).decode(), - ) + ), ) - session.commit() - session.close() From f9b524a8bb57d655ce51080619b800010057686d Mon Sep 17 00:00:00 2001 From: Aidan Timson Date: Sat, 6 Aug 2022 16:05:58 +0100 Subject: [PATCH 36/38] Autoflush --- shared/systembridgeshared/database.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/shared/systembridgeshared/database.py b/shared/systembridgeshared/database.py index e673c1fed..55c99d762 100644 --- a/shared/systembridgeshared/database.py +++ b/shared/systembridgeshared/database.py @@ -90,7 +90,7 @@ def clear_table( table: Any, ) -> None: """Clear table""" - with Session(self._engine) as session: + with Session(self._engine, autoflush=True) as session: for sensor in session.exec(select(table)).all(): session.delete(sensor) session.commit() @@ -100,7 +100,7 @@ def get_data( table: Any, ) -> list[Any]: """Get data from database""" - with Session(self._engine) as session: + with Session(self._engine, autoflush=True) as session: return session.exec(select(table)).all() def get_data_by_key( @@ -109,7 +109,7 @@ def get_data_by_key( key: str, ) -> list[Data]: """Get data from database by key""" - with Session(self._engine) as session: + with Session(self._engine, autoflush=True) as session: return session.exec(select(table).where(table.key == key)).all() def get_data_item_by_key( @@ -118,7 +118,7 @@ def get_data_item_by_key( key: str, ) -> Optional[Data]: """Get data item from database by key""" - with Session(self._engine) as session: + with Session(self._engine, autoflush=True) as session: return session.exec(select(table).where(table.key == key)).first() def get_data_dict( @@ -141,17 +141,13 @@ def get_data_dict( return DataDict(**data, last_updated=data_last_updated) - def get_session(self) -> Session: - """Get session""" - return Session(self._engine) - def update_data( self, table, data: Any, ) -> None: """Update data""" - with Session(self._engine) as session: + with Session(self._engine, autoflush=True) as session: result = session.exec(select(table).where(table.key == data.key)) if (old_data := result.first()) is None: data.timestamp = time() From 779d6cc65857c344c3b6281f637f935f61ad6c52 Mon Sep 17 00:00:00 2001 From: Aidan Timson Date: Sat, 6 Aug 2022 16:09:32 +0100 Subject: [PATCH 37/38] Remove warnings --- shared/systembridgeshared/database.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/shared/systembridgeshared/database.py b/shared/systembridgeshared/database.py index 55c99d762..0409aec01 100644 --- a/shared/systembridgeshared/database.py +++ b/shared/systembridgeshared/database.py @@ -7,6 +7,7 @@ from typing import Any, Optional, Union from sqlmodel import Session, SQLModel, create_engine, select +from sqlmodel.sql.expression import Select, SelectOfScalar from .base import Base from .common import convert_string_to_correct_type, get_user_data_directory @@ -71,6 +72,10 @@ ] +SelectOfScalar.inherit_cache = True # type: ignore +Select.inherit_cache = True # type: ignore + + class Database(Base): """Database""" From 716e2ae8760aca41a3e599cfc2ec204efbad2497 Mon Sep 17 00:00:00 2001 From: Aidan Timson Date: Sat, 6 Aug 2022 16:13:14 +0100 Subject: [PATCH 38/38] Fix version_newer_available --- backend/systembridgebackend/modules/system/update.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/systembridgebackend/modules/system/update.py b/backend/systembridgebackend/modules/system/update.py index 0ce9e5244..6186c75db 100644 --- a/backend/systembridgebackend/modules/system/update.py +++ b/backend/systembridgebackend/modules/system/update.py @@ -152,7 +152,7 @@ async def update_version_newer_available(self) -> None: DatabaseModel, DatabaseModel( key="version_newer_available", - value=str(value) if value else None, + value=str(value) if value else str(False), ), )