diff --git a/platypush/plugins/system/__init__.py b/platypush/plugins/system/__init__.py
index 51566817..8933f486 100644
--- a/platypush/plugins/system/__init__.py
+++ b/platypush/plugins/system/__init__.py
@@ -154,6 +154,8 @@ class SystemPlugin(SensorPlugin, EntityManager):
 
     @staticmethod
     def _cpu_frequency_avg() -> CpuFrequency:
+        # Dummy call to ensure the CPU frequency is updated
+        psutil.cpu_freq(percpu=False)
         return CpuFrequencySchema().load(psutil.cpu_freq(percpu=False))  # type: ignore
 
     @staticmethod
diff --git a/platypush/schemas/dataclasses/__init__.py b/platypush/schemas/dataclasses/__init__.py
index 3a4eb9a8..22df0920 100644
--- a/platypush/schemas/dataclasses/__init__.py
+++ b/platypush/schemas/dataclasses/__init__.py
@@ -1,4 +1,3 @@
-from dataclasses import field
 from datetime import date, datetime
 from uuid import UUID
 
@@ -18,8 +17,7 @@ def percent_field(**kwargs):
     """
     Field used to model percentage float fields between 0 and 1.
     """
-    return field(
-        default_factory=float,
+    return fields.Float(
         metadata={
             'validate': Range(min=0, max=1),
             **kwargs,
diff --git a/platypush/schemas/system/_base.py b/platypush/schemas/system/_base.py
index 26c03a4d..9befdde3 100644
--- a/platypush/schemas/system/_base.py
+++ b/platypush/schemas/system/_base.py
@@ -5,7 +5,7 @@ from platypush.schemas.dataclasses import DataClassSchema
 
 class SystemBaseSchema(DataClassSchema):
     """
-    Base schema for system info.
+    Base schema for system information.
     """
 
     @pre_load
diff --git a/platypush/schemas/system/_battery/_base.py b/platypush/schemas/system/_battery/_base.py
deleted file mode 100644
index 33e1603c..00000000
--- a/platypush/schemas/system/_battery/_base.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from enum import Enum
-
-from marshmallow import pre_load
-
-from .._base import SystemBaseSchema
-
-
-class BatteryBaseSchema(SystemBaseSchema):
-    """
-    Base schema for system battery sensors.
-    """
-
-    @pre_load
-    def pre_load(self, data: dict, **_) -> dict:
-        data = super().pre_load(data)
-        percent = data.pop('percent', data.pop('value', None))
-        seconds_left = data.pop('secsleft', data.pop('seconds_left', None))
-        data['value'] = percent / 100 if percent is not None else None
-        data['seconds_left'] = None if isinstance(seconds_left, Enum) else seconds_left
-        return data
diff --git a/platypush/schemas/system/_battery/_model.py b/platypush/schemas/system/_battery/_model.py
index cd35e479..f329cc00 100644
--- a/platypush/schemas/system/_battery/_model.py
+++ b/platypush/schemas/system/_battery/_model.py
@@ -1,39 +1,13 @@
-from dataclasses import dataclass, field
+from dataclasses import dataclass
 from typing import Optional
 
-from platypush.schemas.dataclasses import percent_field
-
 
 @dataclass
 class Battery:
     """
-    System battery sensor wrapper.
+    System battery sensor representation.
     """
 
-    seconds_left: Optional[float] = field(
-        metadata={
-            'metadata': {
-                'description': 'High threshold for the temperature sensor, in Celsius',
-                'example': 75,
-            }
-        }
-    )
-
-    power_plugged: Optional[bool] = field(
-        metadata={
-            'metadata': {
-                'description': 'Whether the battery is plugged in or not',
-                'example': False,
-            }
-        }
-    )
-
-    value: Optional[float] = percent_field(
-        metadata={
-            'metadata': {
-                'description': 'Current charge left, as a percentage value '
-                'between 0 and 1',
-                'example': 0.5,
-            }
-        }
-    )
+    seconds_left: Optional[float] = None
+    power_plugged: Optional[bool] = None
+    value: Optional[float] = None
diff --git a/platypush/schemas/system/_battery/_schemas.py b/platypush/schemas/system/_battery/_schemas.py
index 7a6e4589..65da669a 100644
--- a/platypush/schemas/system/_battery/_schemas.py
+++ b/platypush/schemas/system/_battery/_schemas.py
@@ -1,7 +1,45 @@
-from marshmallow_dataclass import class_schema
+from enum import Enum
 
-from ._base import BatteryBaseSchema
-from ._model import Battery
+from marshmallow import fields, pre_load
+
+from platypush.schemas.dataclasses import percent_field
+
+from .._base import SystemBaseSchema
 
 
-BatterySchema = class_schema(Battery, base_schema=BatteryBaseSchema)
+class BatterySchema(SystemBaseSchema):
+    """
+    System battery sensor schema.
+    """
+
+    seconds_left = fields.Float(
+        allow_none=True,
+        metadata={
+            'description': 'Number of seconds left before the battery runs out',
+            'example': 7200,
+        },
+    )
+
+    power_plugged = fields.Boolean(
+        metadata={
+            'description': 'Whether the battery is currently plugged in',
+            'example': False,
+        }
+    )
+
+    value = percent_field(
+        allow_none=True,
+        metadata={
+            'description': 'Current battery charge level, as a percentage between 0 and 1',
+            'example': 0.5,
+        },
+    )
+
+    @pre_load
+    def pre_load(self, data: dict, **_) -> dict:
+        data = super().pre_load(data)
+        percent = data.pop('percent', data.pop('value', None))
+        seconds_left = data.pop('secsleft', data.pop('seconds_left', None))
+        data['value'] = percent / 100 if percent is not None else None
+        data['seconds_left'] = None if isinstance(seconds_left, Enum) else seconds_left
+        return data
diff --git a/platypush/schemas/system/_connection/_base.py b/platypush/schemas/system/_connection/_base.py
deleted file mode 100644
index 97977c57..00000000
--- a/platypush/schemas/system/_connection/_base.py
+++ /dev/null
@@ -1,39 +0,0 @@
-from marshmallow import pre_load
-
-from .._base import SystemBaseSchema
-
-
-class ConnectionBaseSchema(SystemBaseSchema):
-    """
-    Base schema for connections.
-    """
-
-    @pre_load
-    def pre_load(self, data, **_) -> dict:
-        data = super().pre_load(data)
-        addr_mapping = {
-            'laddr': ('local_address', 'local_port'),
-            'raddr': ('remote_address', 'remote_port'),
-        }
-
-        # Parse laddr/raddr attributes
-        for ext_attr, (addr_attr, port_attr) in addr_mapping.items():
-            value = data.pop(ext_attr, None)
-            if not value:
-                data[addr_attr] = data[port_attr] = None
-            elif isinstance(value, tuple):
-                data[addr_attr], data[port_attr] = value
-            elif isinstance(value, str):
-                data[addr_attr] = value
-                data[port_attr] = None
-
-        # Handle enum values
-        for attr in ['type', 'family']:
-            value = data.pop(attr, None)
-            if value is not None:
-                data[attr] = value.name
-
-        if data.get('status') == 'NONE':
-            data['status'] = None
-
-        return data
diff --git a/platypush/schemas/system/_connection/_model.py b/platypush/schemas/system/_connection/_model.py
index d4c4a760..1c7013da 100644
--- a/platypush/schemas/system/_connection/_model.py
+++ b/platypush/schemas/system/_connection/_model.py
@@ -1,4 +1,4 @@
-from dataclasses import dataclass, field
+from dataclasses import dataclass
 from socket import AddressFamily, SocketKind
 from typing import Optional
 
@@ -9,88 +9,12 @@ class Connection:
     Network/UNIX socket data class.
     """
 
-    fd: int = field(
-        metadata={
-            'metadata': {
-                'description': 'File descriptor',
-                'example': 3,
-            },
-        }
-    )
-
-    family: AddressFamily = field(
-        metadata={
-            'metadata': {
-                'description': 'Socket family',
-                'example': AddressFamily.AF_INET.name,
-            }
-        }
-    )
-
-    type: SocketKind = field(
-        metadata={
-            'metadata': {
-                'description': 'Socket type',
-                'example': SocketKind.SOCK_STREAM.name,
-            }
-        }
-    )
-
-    local_address: str = field(
-        metadata={
-            'metadata': {
-                'description': 'Local address, as an IP address for network '
-                'connections and a socket path for a UNIX socket',
-                'example': '192.168.1.2',
-            }
-        }
-    )
-
-    local_port: Optional[int] = field(
-        metadata={
-            'metadata': {
-                'description': 'Local port, if this is a TCP/UDP connection, '
-                'otherwise null',
-                'example': 12345,
-            }
-        }
-    )
-
-    remote_address: Optional[str] = field(
-        metadata={
-            'metadata': {
-                'description': 'Remote address, if this is a network '
-                'connection, otherwise null',
-                'example': '192.168.1.1',
-            }
-        }
-    )
-
-    remote_port: Optional[int] = field(
-        metadata={
-            'metadata': {
-                'description': 'Local port, if this is a TCP/UDP connection, '
-                'otherwise null',
-                'example': 443,
-            }
-        }
-    )
-
-    status: Optional[str] = field(
-        metadata={
-            'metadata': {
-                'description': 'Connection status, if this is a network '
-                'connection, otherise null',
-                'example': 'ESTABLISHED',
-            }
-        }
-    )
-
-    pid: Optional[int] = field(
-        metadata={
-            'metadata': {
-                'description': 'ID of the process that owns the connection',
-                'example': 4321,
-            }
-        }
-    )
+    fd: int
+    family: AddressFamily
+    type: SocketKind
+    local_address: str
+    local_port: Optional[int] = None
+    remote_address: Optional[str] = None
+    remote_port: Optional[int] = None
+    status: Optional[str] = None
+    pid: Optional[int] = None
diff --git a/platypush/schemas/system/_connection/_schemas.py b/platypush/schemas/system/_connection/_schemas.py
index 2ad33a7a..741cf40e 100644
--- a/platypush/schemas/system/_connection/_schemas.py
+++ b/platypush/schemas/system/_connection/_schemas.py
@@ -1,7 +1,111 @@
-from marshmallow_dataclass import class_schema
+from marshmallow import fields, pre_load
 
-from ._base import ConnectionBaseSchema
-from ._model import Connection
+from .._base import SystemBaseSchema
 
 
-ConnectionSchema = class_schema(Connection, base_schema=ConnectionBaseSchema)
+class ConnectionSchema(SystemBaseSchema):
+    """
+    Base schema for connections.
+    """
+
+    fd = fields.Int(
+        required=True,
+        metadata={
+            'description': 'File descriptor number.',
+            'example': 3,
+        },
+    )
+
+    family = fields.String(
+        allow_none=True,
+        metadata={
+            'description': 'Address family.',
+            'example': 'AF_INET',
+        },
+    )
+
+    type = fields.String(
+        allow_none=True,
+        metadata={
+            'description': 'Socket type.',
+            'example': 'SOCK_STREAM',
+        },
+    )
+
+    local_address = fields.String(
+        allow_none=True,
+        metadata={
+            'description': 'Local address.',
+            'example': '192.168.1.3',
+        },
+    )
+
+    local_port = fields.Int(
+        allow_none=True,
+        metadata={
+            'description': 'Local port.',
+            'example': 1234,
+        },
+    )
+
+    remote_address = fields.String(
+        allow_none=True,
+        metadata={
+            'description': 'Remote address.',
+            'example': '192.168.1.4',
+        },
+    )
+
+    remote_port = fields.Int(
+        allow_none=True,
+        metadata={
+            'description': 'Remote port.',
+            'example': 5678,
+        },
+    )
+
+    status = fields.String(
+        allow_none=True,
+        metadata={
+            'description': 'Connection status.',
+            'example': 'ESTABLISHED',
+        },
+    )
+
+    pid = fields.Int(
+        allow_none=True,
+        metadata={
+            'description': 'ID of the process that owns the connection.',
+            'example': 1234,
+        },
+    )
+
+    @pre_load
+    def pre_load(self, data, **_) -> dict:
+        data = super().pre_load(data)
+        addr_mapping = {
+            'laddr': ('local_address', 'local_port'),
+            'raddr': ('remote_address', 'remote_port'),
+        }
+
+        # Parse laddr/raddr attributes
+        for ext_attr, (addr_attr, port_attr) in addr_mapping.items():
+            value = data.pop(ext_attr, None)
+            if not value:
+                data[addr_attr] = data[port_attr] = None
+            elif isinstance(value, tuple):
+                data[addr_attr], data[port_attr] = value
+            elif isinstance(value, str):
+                data[addr_attr] = value
+                data[port_attr] = None
+
+        # Handle enum values
+        for attr in ['type', 'family']:
+            value = data.pop(attr, None)
+            if value is not None:
+                data[attr] = value.name
+
+        if data.get('status') == 'NONE':
+            data['status'] = None
+
+        return data
diff --git a/platypush/schemas/system/_cpu/__init__.py b/platypush/schemas/system/_cpu/__init__.py
index e2c2b01e..8b119e1a 100644
--- a/platypush/schemas/system/_cpu/__init__.py
+++ b/platypush/schemas/system/_cpu/__init__.py
@@ -1,5 +1,11 @@
 from ._model import Cpu, CpuFrequency, CpuInfo, CpuStats, CpuTimes
-from ._schemas import CpuFrequencySchema, CpuInfoSchema, CpuStatsSchema, CpuTimesSchema
+from ._schemas import (
+    CpuFrequencySchema,
+    CpuInfoSchema,
+    CpuSchema,
+    CpuStatsSchema,
+    CpuTimesSchema,
+)
 
 
 __all__ = [
@@ -8,6 +14,7 @@ __all__ = [
     "CpuFrequencySchema",
     "CpuInfo",
     "CpuInfoSchema",
+    "CpuSchema",
     "CpuStats",
     "CpuStatsSchema",
     "CpuTimes",
diff --git a/platypush/schemas/system/_cpu/_base.py b/platypush/schemas/system/_cpu/_base.py
deleted file mode 100644
index e5ab0279..00000000
--- a/platypush/schemas/system/_cpu/_base.py
+++ /dev/null
@@ -1,56 +0,0 @@
-from marshmallow import pre_load
-
-from .._base import SystemBaseSchema
-
-
-class CpuInfoBaseSchema(SystemBaseSchema):
-    """
-    Base schema for CPU info.
-    """
-
-    @pre_load
-    def pre_load(self, data: dict, **_) -> dict:
-        data = super().pre_load(data)
-        if data.get('hz_advertised'):
-            data['frequency_advertised'] = data.pop('hz_advertised')[0]
-        if data.get('hz_actual'):
-            data['frequency_actual'] = data.pop('hz_actual')[0]
-
-        for key, value in data.items():
-            if key.endswith("_cache_size") and isinstance(value, str):
-                tokens = value.split(" ")
-                unit = None
-                if len(tokens) > 1:
-                    unit = tokens[1]
-
-                value = int(tokens[0])
-                if unit == "KiB":
-                    value *= 1024
-                elif unit == "MiB":
-                    value *= 1024 * 1024
-                elif unit == "GiB":
-                    value *= 1024 * 1024 * 1024
-
-                data[key] = value
-
-        return data
-
-
-class CpuTimesBaseSchema(SystemBaseSchema):
-    """
-    Base schema for CPU times.
-    """
-
-    @pre_load
-    def pre_load(self, data, **_) -> dict:
-        """
-        Convert the underlying object to dict and normalize all the percentage
-        values from [0, 100] to [0, 1].
-        """
-        data = super().pre_load(data)
-        return {
-            key: value / 100.0
-            for key, value in (
-                data if isinstance(data, dict) else data._asdict()
-            ).items()
-        }
diff --git a/platypush/schemas/system/_cpu/_model.py b/platypush/schemas/system/_cpu/_model.py
index 63965282..f07a6ebe 100644
--- a/platypush/schemas/system/_cpu/_model.py
+++ b/platypush/schemas/system/_cpu/_model.py
@@ -1,8 +1,6 @@
 from dataclasses import dataclass, field
 from typing import List, Optional, Tuple
 
-from platypush.schemas.dataclasses import percent_field
-
 
 @dataclass
 class CpuInfo:
@@ -10,117 +8,18 @@ class CpuInfo:
     CPU info data class.
     """
 
-    architecture: Optional[str] = field(
-        metadata={
-            'data_key': 'arch_string_raw',
-            'metadata': {
-                'description': 'CPU architecture',
-                'example': 'x86_64',
-            },
-        }
-    )
-
-    bits: int = field(
-        metadata={
-            'metadata': {
-                'description': 'CPU bits / register size',
-                'example': 64,
-            }
-        }
-    )
-
-    cores: int = field(
-        metadata={
-            'data_key': 'count',
-            'metadata': {
-                'description': 'Number of cores',
-                'example': 4,
-            },
-        }
-    )
-
-    vendor: Optional[str] = field(
-        metadata={
-            'data_key': 'vendor_id_raw',
-            'metadata': {
-                'description': 'Vendor string',
-                'example': 'GenuineIntel',
-            },
-        }
-    )
-
-    brand: Optional[str] = field(
-        metadata={
-            'data_key': 'brand_raw',
-            'metadata': {
-                'description': 'CPU brand string',
-                'example': 'Intel(R) Core(TM) i7-5500U CPU @ 2.40GHz',
-            },
-        }
-    )
-
-    frequency_advertised: Optional[float] = field(
-        metadata={
-            'metadata': {
-                'description': 'Advertised CPU frequency, in Hz',
-                'example': 2400000000,
-            }
-        }
-    )
-
-    frequency_actual: Optional[float] = field(
-        metadata={
-            'metadata': {
-                'description': 'Actual CPU frequency, in Hz',
-                'example': 2350000000,
-            }
-        }
-    )
-
-    flags: List[str] = field(
-        metadata={
-            'metadata': {
-                'description': 'CPU flags',
-                'example': ['acpi', 'aes', 'cpuid'],
-            }
-        }
-    )
-
-    l1_instruction_cache_size: Optional[float] = field(
-        metadata={
-            'metadata': {
-                'description': 'Size of the L1 instruction cache, in bytes',
-                'example': 65536,
-            }
-        }
-    )
-
-    l1_data_cache_size: Optional[float] = field(
-        metadata={
-            'metadata': {
-                'description': 'Size of the L1 data cache, in bytes',
-                'example': 65536,
-            }
-        }
-    )
-
-    l2_cache_size: Optional[float] = field(
-        metadata={
-            'metadata': {
-                'description': 'Size of the L2 cache, in bytes',
-                'example': 524288,
-            }
-        }
-    )
-
-    l3_cache_size: Optional[float] = field(
-        metadata={
-            'metadata': {
-                'description': 'Size of the L2 cache, in bytes',
-                'example': 4194304,
-            }
-        }
-    )
+    bits: int
+    cores: int
+    architecture: Optional[str] = None
+    vendor: Optional[str] = None
+    brand: Optional[str] = None
+    frequency_advertised: Optional[float] = None
+    frequency_actual: Optional[float] = None
+    flags: List[str] = field(default_factory=list)
+    l1_instruction_cache_size: Optional[float] = None
+    l1_data_cache_size: Optional[float] = None
+    l2_cache_size: Optional[float] = None
+    l3_cache_size: Optional[float] = None
 
 
 @dataclass
@@ -129,16 +28,16 @@ class CpuTimes:
     CPU times data class.
     """
 
-    user: Optional[float] = percent_field()
-    nice: Optional[float] = percent_field()
-    system: Optional[float] = percent_field()
-    idle: Optional[float] = percent_field()
-    iowait: Optional[float] = percent_field()
-    irq: Optional[float] = percent_field()
-    softirq: Optional[float] = percent_field()
-    steal: Optional[float] = percent_field()
-    guest: Optional[float] = percent_field()
-    guest_nice: Optional[float] = percent_field()
+    user: Optional[float] = None
+    nice: Optional[float] = None
+    system: Optional[float] = None
+    idle: Optional[float] = None
+    iowait: Optional[float] = None
+    irq: Optional[float] = None
+    softirq: Optional[float] = None
+    steal: Optional[float] = None
+    guest: Optional[float] = None
+    guest_nice: Optional[float] = None
 
 
 @dataclass
@@ -175,4 +74,4 @@ class Cpu:
     frequency: CpuFrequency
     stats: CpuStats
     load_avg: Tuple[float, float, float]
-    percent: float = percent_field()
+    percent: float
diff --git a/platypush/schemas/system/_cpu/_schemas.py b/platypush/schemas/system/_cpu/_schemas.py
index bdfda432..0a8e08e7 100644
--- a/platypush/schemas/system/_cpu/_schemas.py
+++ b/platypush/schemas/system/_cpu/_schemas.py
@@ -1,11 +1,312 @@
-from marshmallow_dataclass import class_schema
+from marshmallow import fields, pre_load
 
+from platypush.schemas.dataclasses import percent_field
 from .._base import SystemBaseSchema
-from ._base import CpuInfoBaseSchema, CpuTimesBaseSchema
-from ._model import CpuFrequency, CpuInfo, CpuStats, CpuTimes
 
 
-CpuFrequencySchema = class_schema(CpuFrequency, base_schema=SystemBaseSchema)
-CpuInfoSchema = class_schema(CpuInfo, base_schema=CpuInfoBaseSchema)
-CpuTimesSchema = class_schema(CpuTimes, base_schema=CpuTimesBaseSchema)
-CpuStatsSchema = class_schema(CpuStats, base_schema=SystemBaseSchema)
+class CpuInfoSchema(SystemBaseSchema):
+    """
+    Base schema for CPU info.
+    """
+
+    architecture = fields.String(
+        data_key='arch_string_raw',
+        metadata={
+            'description': 'CPU architecture.',
+            'example': 'x86_64',
+        },
+    )
+
+    bits = fields.Int(
+        metadata={
+            'description': 'CPU architecture bits.',
+            'example': 64,
+        }
+    )
+
+    cores = fields.Int(
+        data_key='count',
+        metadata={
+            'description': 'Number of CPU cores.',
+            'example': 4,
+        },
+    )
+
+    vendor = fields.String(
+        data_key='vendor_id_raw',
+        metadata={
+            'description': 'CPU vendor.',
+            'example': 'GenuineIntel',
+        },
+    )
+
+    brand = fields.String(
+        data_key='brand_raw',
+        metadata={
+            'description': 'CPU brand.',
+            'example': 'Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz',
+        },
+    )
+
+    frequency_advertised = fields.Float(
+        metadata={
+            'description': 'CPU advertised frequency.',
+            'example': 2800000000.0,
+        }
+    )
+
+    frequency_actual = fields.Float(
+        metadata={
+            'description': 'CPU actual frequency.',
+            'example': 2650000000.0,
+        }
+    )
+
+    flags = fields.List(
+        fields.String(),
+        metadata={
+            'description': 'CPU flags.',
+            'example': ['fpu', 'vme', 'de', 'pse', 'tsc', 'msr', 'pae'],
+        },
+    )
+
+    l1_instruction_cache_size = fields.Int(
+        metadata={
+            'description': 'L1 instruction cache size.',
+            'example': 65536,
+        }
+    )
+
+    l1_data_cache_size = fields.Int(
+        metadata={
+            'description': 'L1 data cache size.',
+            'example': 65536,
+        }
+    )
+
+    l2_cache_size = fields.Int(
+        metadata={
+            'description': 'L2 cache size.',
+            'example': 524288,
+        }
+    )
+
+    l3_cache_size = fields.Int(
+        metadata={
+            'description': 'L3 cache size.',
+            'example': 6291456,
+        }
+    )
+
+    @pre_load
+    def pre_load(self, data: dict, **_) -> dict:
+        data = super().pre_load(data)
+        if data.get('hz_advertised'):
+            data['frequency_advertised'] = data.pop('hz_advertised')[0]
+        if data.get('hz_actual'):
+            data['frequency_actual'] = data.pop('hz_actual')[0]
+
+        for key, value in data.items():
+            if key.endswith("_cache_size") and isinstance(value, str):
+                tokens = value.split(" ")
+                unit = None
+                if len(tokens) > 1:
+                    unit = tokens[1]
+
+                value = int(tokens[0])
+                if unit == "KiB":
+                    value *= 1024
+                elif unit == "MiB":
+                    value *= 1024 * 1024
+                elif unit == "GiB":
+                    value *= 1024 * 1024 * 1024
+
+                data[key] = value
+
+        return data
+
+
+class CpuTimesSchema(SystemBaseSchema):
+    """
+    Base schema for CPU times.
+    """
+
+    user = percent_field(
+        metadata={
+            'description': 'Time spent in user mode, '
+            'as a percentage between 0 and 1 of the total CPU time.',
+            'example': 0.0,
+        }
+    )
+
+    nice = percent_field(
+        metadata={
+            'description': 'Time spent in nice mode, '
+            'as a percentage between 0 and 1 of the total CPU time.',
+            'example': 0.0,
+        }
+    )
+
+    system = percent_field(
+        metadata={
+            'description': 'Time spent in system mode, '
+            'as a percentage between 0 and 1 of the total CPU time.',
+            'example': 0.0,
+        }
+    )
+
+    idle = percent_field(
+        metadata={
+            'description': 'Time spent in idle mode, '
+            'as a percentage between 0 and 1 of the total CPU time.',
+            'example': 0.0,
+        }
+    )
+
+    iowait = percent_field(
+        metadata={
+            'description': 'Time spent in I/O wait mode, '
+            'as a percentage between 0 and 1 of the total CPU time.',
+            'example': 0.0,
+        }
+    )
+
+    irq = percent_field(
+        metadata={
+            'description': 'Time spent in IRQ mode, '
+            'as a percentage between 0 and 1 of the total CPU time.',
+            'example': 0.0,
+        }
+    )
+
+    softirq = percent_field(
+        metadata={
+            'description': 'Time spent in soft IRQ mode, '
+            'as a percentage between 0 and 1 of the total CPU time.',
+            'example': 0.0,
+        }
+    )
+
+    steal = percent_field(
+        metadata={
+            'description': 'Time spent in steal mode, '
+            'as a percentage between 0 and 1 of the total CPU time.',
+            'example': 0.0,
+        }
+    )
+
+    guest = percent_field(
+        metadata={
+            'description': 'Time spent in guest mode, '
+            'as a percentage between 0 and 1 of the total CPU time.',
+            'example': 0.0,
+        }
+    )
+
+    guest_nice = percent_field(
+        metadata={
+            'description': 'Time spent in guest nice mode, '
+            'as a percentage between 0 and 1 of the total CPU time.',
+            'example': 0.0,
+        }
+    )
+
+    @pre_load
+    def pre_load(self, data, **_) -> dict:
+        """
+        Convert the underlying object to dict and normalize all the percentage
+        values from [0, 100] to [0, 1].
+        """
+        data = super().pre_load(data)
+        return {
+            key: value / 100.0
+            for key, value in (
+                data if isinstance(data, dict) else data._asdict()
+            ).items()
+        }
+
+
+class CpuFrequencySchema(SystemBaseSchema):
+    """
+    Base schema for CPU frequency.
+    """
+
+    current = fields.Float(
+        metadata={
+            'description': 'Current CPU frequency.',
+            'example': 2800000000.0,
+        }
+    )
+
+    min = fields.Float(
+        metadata={
+            'description': 'Minimum CPU frequency.',
+            'example': 800000000.0,
+        }
+    )
+
+    max = fields.Float(
+        metadata={
+            'description': 'Maximum CPU frequency.',
+            'example': 2800000000.0,
+        }
+    )
+
+
+class CpuStatsSchema(SystemBaseSchema):
+    """
+    Base schema for CPU stats.
+    """
+
+    ctx_switches = fields.Int(
+        metadata={
+            'description': 'Number of context switches.',
+            'example': 1234,
+        }
+    )
+
+    interrupts = fields.Int(
+        metadata={
+            'description': 'Number of interrupts.',
+            'example': 1234,
+        }
+    )
+
+    soft_interrupts = fields.Int(
+        metadata={
+            'description': 'Number of soft interrupts.',
+            'example': 1234,
+        }
+    )
+
+    syscalls = fields.Int(
+        metadata={
+            'description': 'Number of system calls.',
+            'example': 1234,
+        }
+    )
+
+
+class CpuSchema(SystemBaseSchema):
+    """
+    Base schema for CPU results.
+    """
+
+    info = fields.Nested(CpuInfoSchema)
+    times = fields.Nested(CpuTimesSchema)
+    frequency = fields.Nested(CpuFrequencySchema)
+    stats = fields.Nested(CpuStatsSchema)
+    load_avg = fields.Tuple(
+        [fields.Float(), fields.Float(), fields.Float()],
+        metadata={
+            'description': 'CPU load average.',
+            'example': (0.0, 0.0, 0.0),
+        },
+    )
+
+    percent = percent_field(
+        metadata={
+            'description': 'CPU usage percentage, as a value between 0 and 1.',
+            'example': 0.0,
+        }
+    )
diff --git a/platypush/schemas/system/_disk/_base.py b/platypush/schemas/system/_disk/_base.py
deleted file mode 100644
index 3f55603f..00000000
--- a/platypush/schemas/system/_disk/_base.py
+++ /dev/null
@@ -1,24 +0,0 @@
-from marshmallow import pre_load
-
-from .._base import SystemBaseSchema
-
-
-class DiskBaseSchema(SystemBaseSchema):
-    """
-    Base schema for disk stats.
-    """
-
-    @pre_load
-    def pre_load(self, data: dict, **_) -> dict:
-        data = super().pre_load(data)
-
-        # Convert read/write/busy times from milliseconds to seconds
-        for attr in ['read_time', 'write_time', 'busy_time']:
-            if data.get(attr) is not None:
-                data[attr] /= 1000
-
-        # Normalize the percentage between 0 and 1
-        if data.get('percent') is not None:
-            data['percent'] /= 100
-
-        return data
diff --git a/platypush/schemas/system/_disk/_model.py b/platypush/schemas/system/_disk/_model.py
index 822ec998..e1fb8843 100644
--- a/platypush/schemas/system/_disk/_model.py
+++ b/platypush/schemas/system/_disk/_model.py
@@ -1,8 +1,6 @@
-from dataclasses import dataclass, field
+from dataclasses import dataclass
 from typing import Optional
 
-from platypush.schemas.dataclasses import percent_field
-
 
 @dataclass
 class Disk:
@@ -10,120 +8,18 @@ class Disk:
     Disk data class.
     """
 
-    device: str = field(
-        metadata={
-            'metadata': {
-                'description': 'Path/identifier of the disk/partition',
-                'example': '/dev/sda1',
-            }
-        }
-    )
-
-    mountpoint: Optional[str] = field(
-        metadata={
-            'metadata': {
-                'description': 'Where the disk is mounted',
-                'example': '/home',
-            }
-        }
-    )
-
-    fstype: Optional[str] = field(
-        metadata={
-            'metadata': {
-                'description': 'Filesystem type',
-                'example': 'ext4',
-            }
-        }
-    )
-
-    opts: Optional[str] = field(
-        metadata={
-            'metadata': {
-                'description': 'Extra mount options passed to the partition',
-                'example': 'rw,relatime,fmask=0022,dmask=0022,utf8',
-            }
-        }
-    )
-
-    total: int = field(
-        metadata={
-            'metadata': {
-                'description': 'Total available space, in bytes',
-            }
-        }
-    )
-
-    used: int = field(
-        metadata={
-            'metadata': {
-                'description': 'Used disk space, in bytes',
-            }
-        }
-    )
-
-    free: int = field(
-        metadata={
-            'metadata': {
-                'description': 'Free disk space, in bytes',
-            }
-        }
-    )
-
-    read_count: Optional[int] = field(
-        metadata={
-            'metadata': {
-                'description': 'Number of recorded read operations',
-            }
-        }
-    )
-
-    write_count: Optional[int] = field(
-        metadata={
-            'metadata': {
-                'description': 'Number of recorded write operations',
-            }
-        }
-    )
-
-    read_bytes: Optional[int] = field(
-        metadata={
-            'metadata': {
-                'description': 'Number of read bytes',
-            }
-        }
-    )
-
-    write_bytes: Optional[int] = field(
-        metadata={
-            'metadata': {
-                'description': 'Number of written bytes',
-            }
-        }
-    )
-
-    read_time: Optional[float] = field(
-        metadata={
-            'metadata': {
-                'description': 'Time spent reading, in seconds',
-            }
-        }
-    )
-
-    write_time: Optional[float] = field(
-        metadata={
-            'metadata': {
-                'description': 'Time spent writing, in seconds',
-            }
-        }
-    )
-
-    busy_time: Optional[float] = field(
-        metadata={
-            'metadata': {
-                'description': 'Total disk busy time, in seconds',
-            }
-        }
-    )
-
-    percent: float = percent_field()
+    device: str
+    mountpoint: Optional[str] = None
+    fstype: Optional[str] = None
+    opts: Optional[str] = None
+    total: Optional[int] = None
+    used: Optional[int] = None
+    free: Optional[int] = None
+    read_count: Optional[int] = None
+    write_count: Optional[int] = None
+    read_bytes: Optional[int] = None
+    write_bytes: Optional[int] = None
+    read_time: Optional[float] = None
+    write_time: Optional[float] = None
+    busy_time: Optional[float] = None
+    percent: Optional[float] = None
diff --git a/platypush/schemas/system/_disk/_schemas.py b/platypush/schemas/system/_disk/_schemas.py
index 564910ae..b74a4d9d 100644
--- a/platypush/schemas/system/_disk/_schemas.py
+++ b/platypush/schemas/system/_disk/_schemas.py
@@ -1,7 +1,131 @@
-from marshmallow_dataclass import class_schema
+from marshmallow import fields, pre_load
 
-from ._base import DiskBaseSchema
-from ._model import Disk
+from platypush.schemas.dataclasses import percent_field
+from .._base import SystemBaseSchema
 
 
-DiskSchema = class_schema(Disk, base_schema=DiskBaseSchema)
+class DiskSchema(SystemBaseSchema):
+    """
+    Base schema for disk stats.
+    """
+
+    device = fields.String(
+        required=True,
+        metadata={
+            'description': 'Path/identifier of the disk/partition',
+            'example': '/dev/sda1',
+        },
+    )
+
+    mountpoint = fields.String(
+        metadata={
+            'description': 'Mountpoint of the disk/partition',
+            'example': '/mnt/data',
+        }
+    )
+
+    fstype = fields.String(
+        metadata={
+            'description': 'Filesystem type',
+            'example': 'ext4',
+        }
+    )
+
+    opts = fields.String(  # type: ignore
+        metadata={
+            'description': 'Mount options',
+            'example': 'rw,relatime',
+        }
+    )
+
+    total = fields.Integer(
+        metadata={
+            'description': 'Total disk space in bytes',
+            'example': 1024**3,
+        }
+    )
+
+    used = fields.Integer(
+        metadata={
+            'description': 'Used disk space in bytes',
+            'example': 1024**2,
+        }
+    )
+
+    free = fields.Integer(
+        metadata={
+            'description': 'Free disk space in bytes',
+            'example': (1024**3) - (1024**2),
+        }
+    )
+
+    read_count = fields.Integer(
+        metadata={
+            'description': 'Number of read operations',
+            'example': 100,
+        }
+    )
+
+    write_count = fields.Integer(
+        metadata={
+            'description': 'Number of write operations',
+            'example': 50,
+        }
+    )
+
+    read_bytes = fields.Integer(
+        metadata={
+            'description': 'Number of bytes read',
+            'example': 1024**3,
+        }
+    )
+
+    write_bytes = fields.Integer(
+        metadata={
+            'description': 'Number of bytes written',
+            'example': 1024**2,
+        }
+    )
+
+    read_time = fields.Float(
+        metadata={
+            'description': 'Time spent reading in seconds',
+            'example': 10.5,
+        }
+    )
+
+    write_time = fields.Float(
+        metadata={
+            'description': 'Time spent writing in seconds',
+            'example': 5.5,
+        }
+    )
+
+    busy_time = fields.Float(
+        metadata={
+            'description': 'Time spent doing I/Os in seconds',
+            'example': 20.5,
+        }
+    )
+
+    percent = percent_field(
+        metadata={
+            'description': 'Percentage of disk space used, normalized between 0 and 1',
+            'example': 0.1,
+        }
+    )
+
+    @pre_load
+    def pre_load(self, data: dict, **_) -> dict:
+        data = super().pre_load(data)
+
+        # Convert read/write/busy times from milliseconds to seconds
+        for attr in ['read_time', 'write_time', 'busy_time']:
+            if data.get(attr) is not None:
+                data[attr] /= 1000
+
+        # Normalize the percentage between 0 and 1
+        if data.get('percent') is not None:
+            data['percent'] /= 100
+
+        return data
diff --git a/platypush/schemas/system/_fan/_base.py b/platypush/schemas/system/_fan/_base.py
deleted file mode 100644
index 2a3fc749..00000000
--- a/platypush/schemas/system/_fan/_base.py
+++ /dev/null
@@ -1,15 +0,0 @@
-from marshmallow import pre_load
-
-from .._base import SystemBaseSchema
-
-
-class FanBaseSchema(SystemBaseSchema):
-    """
-    Base schema for system fan sensors.
-    """
-
-    @pre_load
-    def pre_load(self, data: dict, **_) -> dict:
-        data = super().pre_load(data)
-        data['value'] = data.pop('current', data.pop('value', None))
-        return data
diff --git a/platypush/schemas/system/_fan/_model.py b/platypush/schemas/system/_fan/_model.py
index 17a6ff93..7550662a 100644
--- a/platypush/schemas/system/_fan/_model.py
+++ b/platypush/schemas/system/_fan/_model.py
@@ -1,4 +1,5 @@
-from dataclasses import dataclass, field
+from dataclasses import dataclass
+from typing import Optional
 
 
 @dataclass
@@ -7,29 +8,6 @@ class Fan:
     System fan sensor data class.
     """
 
-    id: str = field(
-        metadata={
-            'metadata': {
-                'description': 'Unique ID for the sensor',
-                'example': 'acpi_1',
-            }
-        }
-    )
-
-    label: str = field(
-        metadata={
-            'metadata': {
-                'description': 'Name of the sensor',
-                'example': 'CPU',
-            }
-        }
-    )
-
-    value: float = field(
-        metadata={
-            'metadata': {
-                'description': 'Current fan speed, in RPM',
-                'example': 3000,
-            }
-        }
-    )
+    id: str
+    label: Optional[str] = None
+    value: Optional[float] = None
diff --git a/platypush/schemas/system/_fan/_schemas.py b/platypush/schemas/system/_fan/_schemas.py
index 1c82d8b2..273022f3 100644
--- a/platypush/schemas/system/_fan/_schemas.py
+++ b/platypush/schemas/system/_fan/_schemas.py
@@ -1,7 +1,39 @@
-from marshmallow_dataclass import class_schema
+from marshmallow import fields, pre_load
 
-from ._base import FanBaseSchema
-from ._model import Fan
+from .._base import SystemBaseSchema
 
 
-FanSchema = class_schema(Fan, base_schema=FanBaseSchema)
+class FanSchema(SystemBaseSchema):
+    """
+    Base schema for system fan sensors.
+    """
+
+    id = fields.String(
+        required=True,
+        metadata={
+            'description': 'Unique ID for the sensor',
+            'example': 'acpi_1',
+        },
+    )
+
+    label = fields.String(
+        allow_none=True,
+        metadata={
+            'description': 'Label for the sensor',
+            'example': 'CPU Fan',
+        },
+    )
+
+    value = fields.Float(
+        allow_none=True,
+        metadata={
+            'description': 'Current fan speed in RPM',
+            'example': 1200,
+        },
+    )
+
+    @pre_load
+    def pre_load(self, data: dict, **_) -> dict:
+        data = super().pre_load(data)
+        data['value'] = data.pop('current', data.pop('value', None))
+        return data
diff --git a/platypush/schemas/system/_memory/_base.py b/platypush/schemas/system/_memory/_base.py
deleted file mode 100644
index 85e10d7d..00000000
--- a/platypush/schemas/system/_memory/_base.py
+++ /dev/null
@@ -1,18 +0,0 @@
-from marshmallow import pre_load
-
-from .._base import SystemBaseSchema
-
-
-class MemoryStatsBaseSchema(SystemBaseSchema):
-    """
-    Base schema for memory stats.
-    """
-
-    @pre_load
-    def pre_load(self, data: dict, **_) -> dict:
-        data = super().pre_load(data)
-
-        # Normalize the percentage between 0 and 1
-        if data.get('percent') is not None:
-            data['percent'] /= 100
-        return data
diff --git a/platypush/schemas/system/_memory/_model.py b/platypush/schemas/system/_memory/_model.py
index ab9ea68e..5fdcb4d7 100644
--- a/platypush/schemas/system/_memory/_model.py
+++ b/platypush/schemas/system/_memory/_model.py
@@ -1,6 +1,4 @@
-from dataclasses import dataclass, field
-
-from platypush.schemas.dataclasses import percent_field
+from dataclasses import dataclass
 
 
 @dataclass
@@ -9,79 +7,16 @@ class MemoryStats:
     Memory stats data class.
     """
 
-    total: int = field(
-        metadata={
-            'metadata': {
-                'description': 'Total available memory, in bytes',
-            }
-        }
-    )
-
-    available: int = field(
-        metadata={
-            'metadata': {
-                'description': 'Available memory, in bytes',
-            }
-        }
-    )
-
-    used: int = field(
-        metadata={
-            'metadata': {
-                'description': 'Used memory, in bytes',
-            }
-        }
-    )
-
-    free: int = field(
-        metadata={
-            'metadata': {
-                'description': 'Free memory, in bytes',
-            }
-        }
-    )
-
-    active: int = field(
-        metadata={
-            'metadata': {
-                'description': 'Size of the active memory, in bytes',
-            }
-        }
-    )
-
-    inactive: int = field(
-        metadata={
-            'metadata': {
-                'description': 'Size of the inactive memory, in bytes',
-            }
-        }
-    )
-
-    buffers: int = field(
-        metadata={
-            'metadata': {
-                'description': 'Size of the buffered memory, in bytes',
-            }
-        }
-    )
-
-    cached: int = field(
-        metadata={
-            'metadata': {
-                'description': 'Size of the cached memory, in bytes',
-            }
-        }
-    )
-
-    shared: int = field(
-        metadata={
-            'metadata': {
-                'description': 'Size of the shared memory, in bytes',
-            }
-        }
-    )
-
-    percent: float = percent_field()
+    total: int
+    available: int
+    used: int
+    free: int
+    active: int
+    inactive: int
+    buffers: int
+    cached: int
+    shared: int
+    percent: float
 
 
 @dataclass
@@ -90,28 +25,7 @@ class SwapStats:
     Swap memory stats data class.
     """
 
-    total: int = field(
-        metadata={
-            'metadata': {
-                'description': 'Total available memory, in bytes',
-            }
-        }
-    )
-
-    used: int = field(
-        metadata={
-            'metadata': {
-                'description': 'Used memory, in bytes',
-            }
-        }
-    )
-
-    free: int = field(
-        metadata={
-            'metadata': {
-                'description': 'Free memory, in bytes',
-            }
-        }
-    )
-
-    percent: float = percent_field()
+    total: int
+    used: int
+    free: int
+    percent: float
diff --git a/platypush/schemas/system/_memory/_schemas.py b/platypush/schemas/system/_memory/_schemas.py
index 9d7bc21d..15f828dd 100644
--- a/platypush/schemas/system/_memory/_schemas.py
+++ b/platypush/schemas/system/_memory/_schemas.py
@@ -1,8 +1,133 @@
-from marshmallow_dataclass import class_schema
+from marshmallow import fields, pre_load
 
-from ._base import MemoryStatsBaseSchema
-from ._model import MemoryStats, SwapStats
+from platypush.schemas.dataclasses import percent_field
+
+from .._base import SystemBaseSchema
 
 
-MemoryStatsSchema = class_schema(MemoryStats, base_schema=MemoryStatsBaseSchema)
-SwapStatsSchema = class_schema(SwapStats, base_schema=MemoryStatsBaseSchema)
+class MemoryStatsSchema(SystemBaseSchema):
+    """
+    Base schema for memory stats.
+    """
+
+    total = fields.Integer(
+        metadata={
+            'description': 'Total memory available in bytes.',
+            'example': 8589934592,
+        }
+    )
+
+    available = fields.Integer(
+        metadata={
+            'description': 'Memory available in bytes.',
+            'example': 2147483648,
+        }
+    )
+
+    used = fields.Integer(
+        metadata={
+            'description': 'Memory used in bytes.',
+            'example': 6442450944,
+        }
+    )
+
+    free = fields.Integer(
+        metadata={
+            'description': 'Memory free in bytes.',
+            'example': 2147483648,
+        }
+    )
+
+    active = fields.Integer(
+        metadata={
+            'description': 'Memory active in bytes.',
+            'example': 4294967296,
+        }
+    )
+
+    inactive = fields.Integer(
+        metadata={
+            'description': 'Memory inactive in bytes.',
+            'example': 2147483648,
+        }
+    )
+
+    buffers = fields.Integer(
+        metadata={
+            'description': 'Memory buffers in bytes.',
+            'example': 2147483648,
+        }
+    )
+
+    cached = fields.Integer(
+        metadata={
+            'description': 'Memory cached in bytes.',
+            'example': 2147483648,
+        }
+    )
+
+    shared = fields.Integer(
+        metadata={
+            'description': 'Memory shared in bytes.',
+            'example': 3221225472,
+        }
+    )
+
+    percent = percent_field(
+        metadata={
+            'description': 'Memory usage percentage between 0 and 1.',
+            'example': 0.75,
+        }
+    )
+
+    @pre_load
+    def pre_load(self, data: dict, **_) -> dict:
+        data = super().pre_load(data)
+
+        # Normalize the percentage between 0 and 1
+        if data.get('percent') is not None:
+            data['percent'] /= 100
+        return data
+
+
+class SwapStatsSchema(SystemBaseSchema):
+    """
+    Base schema for swap stats.
+    """
+
+    total = fields.Integer(
+        metadata={
+            'description': 'Total memory available in bytes.',
+            'example': 8589934592,
+        }
+    )
+
+    used = fields.Integer(
+        metadata={
+            'description': 'Memory used in bytes.',
+            'example': 6442450944,
+        }
+    )
+
+    free = fields.Integer(
+        metadata={
+            'description': 'Memory free in bytes.',
+            'example': 2147483648,
+        }
+    )
+
+    percent = percent_field(
+        metadata={
+            'description': 'Memory usage percentage between 0 and 1.',
+            'example': 0.75,
+        }
+    )
+
+    @pre_load
+    def pre_load(self, data: dict, **_) -> dict:
+        data = super().pre_load(data)
+
+        # Normalize the percentage between 0 and 1
+        if data.get('percent') is not None:
+            data['percent'] /= 100
+        return data
diff --git a/platypush/schemas/system/_network/_base.py b/platypush/schemas/system/_network/_base.py
deleted file mode 100644
index 97f3affc..00000000
--- a/platypush/schemas/system/_network/_base.py
+++ /dev/null
@@ -1,43 +0,0 @@
-from enum import Enum
-from socket import AddressFamily
-
-from marshmallow import pre_load
-
-from .._base import SystemBaseSchema
-
-
-class NetworkInterfaceBaseSchema(SystemBaseSchema):
-    """
-    Base schema for network interface stats.
-    """
-
-    @pre_load
-    def pre_load(self, data: dict, **_) -> dict:
-        data = super().pre_load(data)
-
-        # Custom attribute mappings
-        for in_attr, out_attr in {
-            'errin': 'errors_in',
-            'errout': 'errors_out',
-            'dropin': 'drop_in',
-            'dropout': 'drop_out',
-            'isup': 'is_up',
-        }.items():
-            if in_attr in data:
-                data[out_attr] = data.pop(in_attr)
-
-        # Serialize enum values
-        for i, addr in enumerate(data.get('addresses', [])):
-            if hasattr(addr, '_asdict'):
-                addr = addr._asdict()
-            if isinstance(addr.get('family'), AddressFamily):
-                addr['family'] = addr['family'].name
-            data['addresses'][i] = addr
-
-        if isinstance(data.get('duplex'), Enum):
-            data['duplex'] = data['duplex'].name.split('_')[-1]
-
-        # Split the flags string
-        data['flags'] = data.get('flags', '').split(',')
-
-        return data
diff --git a/platypush/schemas/system/_network/_model.py b/platypush/schemas/system/_network/_model.py
index 68b1f03e..080aa9e4 100644
--- a/platypush/schemas/system/_network/_model.py
+++ b/platypush/schemas/system/_network/_model.py
@@ -2,154 +2,6 @@ from dataclasses import dataclass, field
 from socket import AddressFamily
 from typing import List, Optional
 
-from marshmallow.validate import OneOf
-
-
-@dataclass
-class NetworkInterface:
-    """
-    Network interface statistics data class.
-    """
-
-    interface: Optional[str] = field(
-        metadata={
-            'metadata': {
-                'description': 'Network interface identifier',
-                'example': 'eth0',
-            }
-        }
-    )
-
-    bytes_sent: int = field(
-        metadata={
-            'metadata': {
-                'description': 'Number of bytes sent',
-            }
-        }
-    )
-
-    bytes_recv: int = field(
-        metadata={
-            'metadata': {
-                'description': 'Number of bytes received',
-            }
-        }
-    )
-
-    packets_sent: int = field(
-        metadata={
-            'metadata': {
-                'description': 'Number of packets sent',
-            }
-        }
-    )
-
-    packets_recv: int = field(
-        metadata={
-            'metadata': {
-                'description': 'Number of packets received',
-            }
-        }
-    )
-
-    errors_in: int = field(
-        metadata={
-            'metadata': {
-                'description': 'Number of errors on incoming traffic',
-            },
-        }
-    )
-
-    errors_out: int = field(
-        metadata={
-            'metadata': {
-                'description': 'Number of errors on outgoing traffic',
-            },
-        }
-    )
-
-    drop_in: int = field(
-        metadata={
-            'metadata': {
-                'description': 'Number of packets dropped on incoming traffic',
-            },
-        }
-    )
-
-    drop_out: int = field(
-        metadata={
-            'metadata': {
-                'description': 'Number of packets dropped on outgoing traffic',
-            },
-        }
-    )
-
-    is_up: bool = field(
-        metadata={
-            'metadata': {
-                'description': 'Whether the interface is active',
-                'example': True,
-            },
-        }
-    )
-
-    speed: int = field(
-        metadata={
-            'metadata': {
-                'description': 'Interface reported speed in Mbps',
-                'example': 10000,
-            },
-        }
-    )
-
-    mtu: int = field(
-        metadata={
-            'metadata': {
-                'description': 'Interface maximum transmission unit expressed '
-                'in bytes',
-                'example': 65535,
-            },
-        }
-    )
-
-    duplex: str = field(
-        metadata={
-            'validate': OneOf(['FULL', 'HALF', 'UNKNOWN']),
-            'metadata': {
-                'description': 'Interface duplex configuration. Can be FULL, '
-                'HALF or UNKNOWN',
-                'example': 'FULL',
-            },
-        }
-    )
-
-    flags: List[str] = field(
-        default_factory=list,
-        metadata={
-            'metadata': {
-                'description': 'List of flags associated to the interface',
-                'example': ['up', 'broadcast', 'running'],
-            }
-        },
-    )
-
-    addresses: List['NetworkInterfaceAddress'] = field(
-        default_factory=list,
-        metadata={
-            'metadata': {
-                'description': 'List of addresses associated to the interface',
-                'example': [
-                    {
-                        'family': AddressFamily.AF_INET.name,
-                        'address': '192.168.1.2',
-                        'netmask': '255.255.255.0',
-                        'broadcast': '192.168.1.255',
-                    }
-                ],
-            },
-        },
-    )
-
 
 @dataclass
 class NetworkInterfaceAddress:
@@ -157,38 +9,30 @@ class NetworkInterfaceAddress:
     Network interface address data class.
     """
 
-    family: AddressFamily = field(
-        metadata={
-            'metadata': {
-                'description': 'Address family',
-                'example': AddressFamily.AF_INET.name,
-            }
-        }
-    )
+    family: Optional[AddressFamily] = None
+    address: Optional[str] = None
+    netmask: Optional[str] = None
+    broadcast: Optional[str] = None
 
-    address: Optional[str] = field(
-        metadata={
-            'metadata': {
-                'description': 'IPv4 or IPv6 address of the interface',
-                'example': '192.168.1.2',
-            }
-        }
-    )
 
-    netmask: Optional[str] = field(
-        metadata={
-            'metadata': {
-                'description': 'Netmask for the interface address',
-                'example': '255.255.255.0',
-            }
-        }
-    )
+@dataclass
+class NetworkInterface:
+    """
+    Network interface statistics data class.
+    """
 
-    broadcast: Optional[str] = field(
-        metadata={
-            'metadata': {
-                'description': 'Broadcast address for the interface',
-                'example': '192.168.1.255',
-            }
-        }
-    )
+    interface: Optional[str] = None
+    bytes_sent: int = 0
+    bytes_recv: int = 0
+    packets_sent: int = 0
+    packets_recv: int = 0
+    errors_in: int = 0
+    errors_out: int = 0
+    drop_in: int = 0
+    drop_out: int = 0
+    is_up: bool = False
+    speed: int = 0
+    mtu: int = 0
+    duplex: Optional[str] = None
+    flags: List[str] = field(default_factory=list)
+    addresses: List[NetworkInterfaceAddress] = field(default_factory=list)
diff --git a/platypush/schemas/system/_network/_schemas.py b/platypush/schemas/system/_network/_schemas.py
index e960b111..341f2424 100644
--- a/platypush/schemas/system/_network/_schemas.py
+++ b/platypush/schemas/system/_network/_schemas.py
@@ -1,9 +1,207 @@
-from marshmallow_dataclass import class_schema
+from enum import Enum
+from socket import AddressFamily
 
-from ._base import NetworkInterfaceBaseSchema
-from ._model import NetworkInterface
+from marshmallow import fields, pre_load
+from marshmallow.validate import OneOf
+
+from .._base import SystemBaseSchema
 
 
-NetworkInterfaceSchema = class_schema(
-    NetworkInterface, base_schema=NetworkInterfaceBaseSchema
-)
+class NetworkInterfaceAddressSchema(SystemBaseSchema):
+    """
+    Schema for network interface address.
+    """
+
+    family = fields.String(
+        allow_none=True,
+        metadata={
+            'description': 'The address family.',
+            'example': 'AF_INET',
+        },
+    )
+
+    address = fields.String(
+        allow_none=True,
+        metadata={
+            'description': 'The IP address associated with the interface.',
+            'example': '192.168.1.4',
+        },
+    )
+
+    netmask = fields.String(
+        allow_none=True,
+        metadata={
+            'description': 'The netmask associated with the interface.',
+            'example': '255.255.255.0',
+        },
+    )
+
+    broadcast = fields.String(
+        allow_none=True,
+        metadata={
+            'description': 'The broadcast address associated with the interface.',
+            'example': '192.168.1.255',
+        },
+    )
+
+
+class NetworkInterfaceSchema(SystemBaseSchema):
+    """
+    Schema for network interface stats.
+    """
+
+    interface = fields.String(
+        allow_none=True,
+        metadata={
+            'description': 'The name of the network interface.',
+            'example': 'eth0',
+        },
+    )
+
+    bytes_sent = fields.Integer(
+        missing=0,
+        metadata={
+            'description': 'The number of bytes sent.',
+            'example': 123456,
+        },
+    )
+
+    bytes_recv = fields.Integer(
+        missing=0,
+        metadata={
+            'description': 'The number of bytes received.',
+            'example': 654321,
+        },
+    )
+
+    packets_sent = fields.Integer(
+        missing=0,
+        metadata={
+            'description': 'The number of packets sent.',
+            'example': 123,
+        },
+    )
+
+    packets_recv = fields.Integer(
+        missing=0,
+        metadata={
+            'description': 'The number of packets received.',
+            'example': 321,
+        },
+    )
+
+    errors_in = fields.Integer(
+        missing=0,
+        metadata={
+            'description': 'The number of errors on the input side.',
+            'example': 10,
+        },
+    )
+
+    errors_out = fields.Integer(
+        missing=0,
+        metadata={
+            'description': 'The number of errors on the output side.',
+            'example': 5,
+        },
+    )
+
+    drop_in = fields.Integer(
+        missing=0,
+        metadata={
+            'description': 'The number of dropped packets on the input side.',
+            'example': 1,
+        },
+    )
+
+    drop_out = fields.Integer(
+        missing=0,
+        metadata={
+            'description': 'The number of dropped packets on the output side.',
+            'example': 2,
+        },
+    )
+
+    is_up = fields.Boolean(
+        metadata={
+            'description': 'Whether the interface is up.',
+            'example': True,
+        },
+    )
+
+    speed = fields.Integer(
+        metadata={
+            'description': 'The advertised speed of the interface in Mbps.',
+            'example': 1000,
+        },
+    )
+
+    mtu = fields.Integer(
+        metadata={
+            'description': 'The maximum transmission unit of the interface in bytes.',
+            'example': 1500,
+        },
+    )
+
+    duplex = fields.String(
+        validate=OneOf(['FULL', 'HALF', 'UNKNOWN']),
+        metadata={
+            'description': 'Interface duplex configuration. Can be FULL, '
+            'HALF or UNKNOWN',
+            'example': 'FULL',
+        },
+    )
+
+    flags = fields.List(
+        fields.String(),
+        metadata={
+            'description': 'A list of flags associated with the interface.',
+            'example': ['up', 'broadcast', 'running', 'multicast'],
+        },
+    )
+
+    addresses = fields.List(
+        fields.Nested(NetworkInterfaceAddressSchema),
+        metadata={
+            'description': 'A list of addresses associated with the interface.',
+            'example': [
+                {
+                    'family': 'AF_INET',
+                    'address': '192.168.1.4',
+                    'netmask': '255.255.255.0',
+                    'broadcast': '192.168.1.255',
+                },
+            ],
+        },
+    )
+
+    @pre_load
+    def pre_load(self, data: dict, **_) -> dict:
+        data = super().pre_load(data)
+
+        # Custom attribute mappings
+        for in_attr, out_attr in {
+            'errin': 'errors_in',
+            'errout': 'errors_out',
+            'dropin': 'drop_in',
+            'dropout': 'drop_out',
+            'isup': 'is_up',
+        }.items():
+            if in_attr in data:
+                data[out_attr] = data.pop(in_attr)
+
+        # Serialize enum values
+        for i, addr in enumerate(data.get('addresses', [])):
+            if hasattr(addr, '_asdict'):
+                addr = addr._asdict()
+            if isinstance(addr.get('family'), AddressFamily):
+                addr['family'] = addr['family'].name
+            data['addresses'][i] = addr
+
+        if isinstance(data.get('duplex'), Enum):
+            data['duplex'] = data['duplex'].name.split('_')[-1]
+
+        # Split the flags string
+        data['flags'] = data.get('flags', '').split(',')
+
+        return data
diff --git a/platypush/schemas/system/_process/_base.py b/platypush/schemas/system/_process/_base.py
deleted file mode 100644
index 72b505e0..00000000
--- a/platypush/schemas/system/_process/_base.py
+++ /dev/null
@@ -1,28 +0,0 @@
-from datetime import datetime
-
-from dateutil.tz import gettz
-from marshmallow import pre_load
-
-from .._base import SystemBaseSchema
-
-
-class ProcessBaseSchema(SystemBaseSchema):
-    """
-    Base schema for system processes.
-    """
-
-    @pre_load
-    def pre_load(self, data, **_) -> dict:
-        if hasattr(data, 'as_dict'):
-            data = data.as_dict()
-
-        started_ts = data.pop('create_time', None)
-        if started_ts is not None:
-            data['started'] = datetime.fromtimestamp(started_ts).replace(tzinfo=gettz())
-
-        data['command_line'] = data.pop('cmdline', None)
-        data['cpu_percent'] = data.pop('cpu_percent') / 100
-        data['current_directory'] = data.pop('cwd', None)
-        data['memory_percent'] = data.pop('memory_percent') / 100
-        data['parent_pid'] = data.pop('ppid', None)
-        return data
diff --git a/platypush/schemas/system/_process/_model.py b/platypush/schemas/system/_process/_model.py
index 30513b54..5a32a3ea 100644
--- a/platypush/schemas/system/_process/_model.py
+++ b/platypush/schemas/system/_process/_model.py
@@ -2,8 +2,6 @@ from dataclasses import dataclass, field
 from datetime import datetime
 from typing import List, Optional
 
-from platypush.schemas.dataclasses import percent_field
-
 
 @dataclass
 class Process:
@@ -11,100 +9,14 @@ class Process:
     System process data class.
     """
 
-    pid: int = field(
-        metadata={
-            'metadata': {
-                'description': 'Process PID',
-                'example': 12345,
-            }
-        }
-    )
-
-    name: str = field(
-        metadata={
-            'metadata': {
-                'description': 'Process name',
-                'example': 'python',
-            }
-        }
-    )
-
-    parent_pid: Optional[int] = field(
-        metadata={
-            'metadata': {
-                'description': 'PID of the parent process',
-                'example': 1000,
-            }
-        }
-    )
-
-    username: str = field(
-        metadata={
-            'metadata': {
-                'description': 'Username that owns the process',
-                'example': 'root',
-            }
-        }
-    )
-
-    command_line: List[str] = field(
-        metadata={
-            'metadata': {
-                'description': 'Command line of the process',
-                'example': ['/usr/bin/python', '-m', 'platypush'],
-            }
-        }
-    )
-
-    status: str = field(
-        metadata={
-            'metadata': {
-                'description': 'Process status',
-                'example': 'running',
-            }
-        }
-    )
-
-    terminal: Optional[str] = field(
-        metadata={
-            'metadata': {
-                'description': 'Terminal the process is running on',
-                'example': 'pts/1',
-            }
-        }
-    )
-
-    current_directory: Optional[str] = field(
-        metadata={
-            'metadata': {
-                'description': 'Current directory of the process',
-                'example': '/root',
-            }
-        }
-    )
-
-    started: Optional[datetime] = field(
-        metadata={
-            'metadata': {
-                'description': 'When the process started',
-            }
-        }
-    )
-
-    cpu_percent: float = percent_field(
-        metadata={
-            'metadata': {
-                'description': 'Percentage of CPU used by the process, between 0 and 1',
-                'example': 0.1,
-            }
-        }
-    )
-
-    memory_percent: float = percent_field(
-        metadata={
-            'metadata': {
-                'description': 'Percentage of memory used by the process, between 0 and 1',
-                'example': 0.05,
-            }
-        }
-    )
+    pid: int
+    name: str
+    parent_pid: Optional[int] = None
+    username: Optional[str] = None
+    command_line: List[str] = field(default_factory=list)
+    status: Optional[str] = None
+    terminal: Optional[str] = None
+    current_directory: Optional[str] = None
+    started: Optional[datetime] = None
+    cpu_percent: float = 0
+    memory_percent: float = 0
diff --git a/platypush/schemas/system/_process/_schemas.py b/platypush/schemas/system/_process/_schemas.py
index c4cca1ab..4d2f7c80 100644
--- a/platypush/schemas/system/_process/_schemas.py
+++ b/platypush/schemas/system/_process/_schemas.py
@@ -1,7 +1,124 @@
-from marshmallow_dataclass import class_schema
+from datetime import datetime
 
-from ._base import ProcessBaseSchema
-from ._model import Process
+from dateutil.tz import gettz
+from marshmallow import fields, pre_load
+
+from platypush.schemas import DateTime
+from platypush.schemas.dataclasses import percent_field
+from .._base import SystemBaseSchema
 
 
-ProcessSchema = class_schema(Process, base_schema=ProcessBaseSchema)
+class ProcessSchema(SystemBaseSchema):
+    """
+    Schema for system processes.
+    """
+
+    pid = fields.Int(
+        missing=-1,
+        metadata={
+            'description': 'The process ID.',
+            'example': 1234,
+        },
+    )
+
+    name = fields.String(
+        allow_none=True,
+        metadata={
+            'description': 'The name of the process.',
+            'example': 'python',
+        },
+    )
+
+    parent_pid = fields.Int(
+        allow_none=True,
+        metadata={
+            'description': 'The parent process ID.',
+            'example': 1000,
+        },
+    )
+
+    username = fields.String(
+        allow_none=True,
+        metadata={
+            'description': 'The username of the process owner.',
+            'example': 'root',
+        },
+    )
+
+    command_line = fields.List(
+        fields.String(),
+        allow_none=True,
+        metadata={
+            'description': 'The command line arguments of the process.',
+            'example': ['python', 'script.py'],
+        },
+    )
+
+    status = fields.String(
+        allow_none=True,
+        metadata={
+            'description': 'The status of the process.',
+            'example': 'running',
+        },
+    )
+
+    terminal = fields.String(
+        allow_none=True,
+        metadata={
+            'description': 'The terminal of the process.',
+            'example': 'tty1',
+        },
+    )
+
+    current_directory = fields.String(
+        allow_none=True,
+        metadata={
+            'description': 'The current working directory of the process.',
+            'example': '/home/user',
+        },
+    )
+
+    started = DateTime(
+        allow_none=True,
+        metadata={
+            'description': 'The timestamp when the process was started.',
+            'example': '2021-01-01T00:00:00+00:00',
+        },
+    )
+
+    cpu_percent = percent_field(
+        missing=0,
+        metadata={
+            'description': 'The CPU usage percentage of the process, in the range [0, 1].',
+            'example': 0.5,
+        },
+    )
+
+    memory_percent = percent_field(
+        missing=0,
+        metadata={
+            'description': 'The memory usage percentage of the process, in the range [0, 1].',
+            'example': 0.5,
+        },
+    )
+
+    @pre_load
+    def pre_load(self, data, **_) -> dict:
+        import psutil
+
+        if hasattr(data, 'as_dict'):
+            try:
+                data = data.as_dict()
+            except psutil.NoSuchProcess:
+                return {}
+
+        started_ts = data.pop('create_time', None)
+        if started_ts is not None:
+            data['started'] = datetime.fromtimestamp(started_ts).replace(tzinfo=gettz())
+
+        data['command_line'] = data.pop('cmdline', None)
+        data['cpu_percent'] = data.pop('cpu_percent') / 100
+        data['current_directory'] = data.pop('cwd', None)
+        data['memory_percent'] = data.pop('memory_percent') / 100
+        data['parent_pid'] = data.pop('ppid', None)
+        return data
diff --git a/platypush/schemas/system/_schemas.py b/platypush/schemas/system/_schemas.py
index 0bd2992f..19e4defe 100644
--- a/platypush/schemas/system/_schemas.py
+++ b/platypush/schemas/system/_schemas.py
@@ -1,8 +1,25 @@
-from marshmallow_dataclass import class_schema
+from marshmallow import fields
 
-from platypush.schemas.dataclasses import DataClassSchema
-
-from ._model import SystemInfo
+from ._base import SystemBaseSchema
+from ._battery import BatterySchema
+from ._cpu import CpuSchema
+from ._disk import DiskSchema
+from ._fan import FanSchema
+from ._memory import MemoryStatsSchema, SwapStatsSchema
+from ._network import NetworkInterfaceSchema
+from ._temperature import TemperatureSchema
 
 
-SystemInfoSchema = class_schema(SystemInfo, base_schema=DataClassSchema)
+class SystemInfoSchema(SystemBaseSchema):
+    """
+    Schema for system info.
+    """
+
+    cpu = fields.Nested(CpuSchema)
+    memory = fields.Nested(MemoryStatsSchema)
+    swap = fields.Nested(SwapStatsSchema)
+    disks = fields.List(fields.Nested(DiskSchema))
+    network = fields.List(fields.Nested(NetworkInterfaceSchema))
+    temperature = fields.List(fields.Nested(TemperatureSchema))
+    fans = fields.List(fields.Nested(FanSchema))
+    battery = fields.Nested(BatterySchema)
diff --git a/platypush/schemas/system/_temperature/_base.py b/platypush/schemas/system/_temperature/_base.py
deleted file mode 100644
index a5ada9dc..00000000
--- a/platypush/schemas/system/_temperature/_base.py
+++ /dev/null
@@ -1,15 +0,0 @@
-from marshmallow import pre_load
-
-from .._base import SystemBaseSchema
-
-
-class TemperatureBaseSchema(SystemBaseSchema):
-    """
-    Base schema for system temperature sensors.
-    """
-
-    @pre_load
-    def pre_load(self, data: dict, **_) -> dict:
-        data = super().pre_load(data)
-        data['value'] = data.pop('current', data.pop('value', None))
-        return data
diff --git a/platypush/schemas/system/_temperature/_model.py b/platypush/schemas/system/_temperature/_model.py
index d53b405c..470e4a3f 100644
--- a/platypush/schemas/system/_temperature/_model.py
+++ b/platypush/schemas/system/_temperature/_model.py
@@ -1,54 +1,15 @@
-from dataclasses import dataclass, field
+from dataclasses import dataclass
 from typing import Optional
 
 
 @dataclass
 class Temperature:
     """
-    System temperature sensor wrapper.
+    System temperature sensor data class.
     """
 
-    id: str = field(
-        metadata={
-            'metadata': {
-                'description': 'Unique ID for the sensor',
-                'example': 'acpi_1',
-            }
-        }
-    )
-
-    label: str = field(
-        metadata={
-            'metadata': {
-                'description': 'Name of the sensor',
-                'example': 'CPU',
-            }
-        }
-    )
-
-    value: float = field(
-        metadata={
-            'metadata': {
-                'description': 'Current temperature value, in Celsius',
-                'example': 55,
-            }
-        }
-    )
-
-    high: Optional[float] = field(
-        metadata={
-            'metadata': {
-                'description': 'High threshold for the temperature sensor, in Celsius',
-                'example': 75,
-            }
-        }
-    )
-
-    critical: Optional[float] = field(
-        metadata={
-            'metadata': {
-                'description': 'Critical threshold for the temperature sensor, in Celsius',
-                'example': 95,
-            }
-        }
-    )
+    id: str
+    label: Optional[str] = None
+    value: Optional[float] = None
+    high: Optional[float] = None
+    critical: Optional[float] = None
diff --git a/platypush/schemas/system/_temperature/_schemas.py b/platypush/schemas/system/_temperature/_schemas.py
index 09bc1463..b6aa4f28 100644
--- a/platypush/schemas/system/_temperature/_schemas.py
+++ b/platypush/schemas/system/_temperature/_schemas.py
@@ -1,7 +1,54 @@
-from marshmallow_dataclass import class_schema
+from marshmallow import fields, pre_load
 
-from ._base import TemperatureBaseSchema
-from ._model import Temperature
+from .._base import SystemBaseSchema
 
 
-TemperatureSchema = class_schema(Temperature, base_schema=TemperatureBaseSchema)
+class TemperatureSchema(SystemBaseSchema):
+    """
+    Schema for system temperature sensors.
+    """
+
+    id = fields.String(
+        metadata={
+            'description': 'The unique identifier of the temperature sensor.',
+            'example': 'acpi_1',
+        }
+    )
+
+    label = fields.String(
+        allow_none=True,
+        metadata={
+            'description': 'The label of the temperature sensor.',
+            'example': 'CPU Temperature',
+        },
+    )
+
+    value = fields.Float(
+        allow_none=True,
+        metadata={
+            'description': 'The current temperature value of the sensor, in degrees Celsius.',
+            'example': 56.0,
+        },
+    )
+
+    high = fields.Float(
+        allow_none=True,
+        metadata={
+            'description': 'The high temperature threshold of the sensor, in degrees Celsius.',
+            'example': 90.0,
+        },
+    )
+
+    critical = fields.Float(
+        allow_none=True,
+        metadata={
+            'description': 'The critical temperature threshold of the sensor, in degrees Celsius.',
+            'example': 100.0,
+        },
+    )
+
+    @pre_load
+    def pre_load(self, data: dict, **_) -> dict:
+        data = super().pre_load(data)
+        data['value'] = data.pop('current', data.pop('value', None))
+        return data
diff --git a/platypush/schemas/system/_user/_base.py b/platypush/schemas/system/_user/_base.py
deleted file mode 100644
index be32bb4f..00000000
--- a/platypush/schemas/system/_user/_base.py
+++ /dev/null
@@ -1,22 +0,0 @@
-from datetime import datetime
-
-from dateutil.tz import gettz
-from marshmallow import pre_load
-
-from .._base import SystemBaseSchema
-
-
-class UserBaseSchema(SystemBaseSchema):
-    """
-    Base schema for system users.
-    """
-
-    @pre_load
-    def pre_load(self, data: dict, **_) -> dict:
-        data = super().pre_load(data)
-        started_ts = data.pop('started', None)
-        if started_ts is not None:
-            data['started'] = datetime.fromtimestamp(started_ts).replace(tzinfo=gettz())
-
-        data['username'] = data.pop('name', data.pop('username', None))
-        return data
diff --git a/platypush/schemas/system/_user/_model.py b/platypush/schemas/system/_user/_model.py
index 2fc9e974..b023bf26 100644
--- a/platypush/schemas/system/_user/_model.py
+++ b/platypush/schemas/system/_user/_model.py
@@ -1,4 +1,4 @@
-from dataclasses import dataclass, field
+from dataclasses import dataclass
 from datetime import datetime
 from typing import Optional
 
@@ -9,37 +9,7 @@ class User:
     System user wrapper.
     """
 
-    username: str = field(
-        metadata={
-            'metadata': {
-                'description': 'Username',
-                'example': 'root',
-            }
-        }
-    )
-
-    terminal: Optional[str] = field(
-        metadata={
-            'metadata': {
-                'description': 'Identifier of the terminal the user is connected to',
-                'example': 'pts/1',
-            }
-        }
-    )
-
-    started: Optional[datetime] = field(
-        metadata={
-            'metadata': {
-                'description': 'When the user session started',
-            }
-        }
-    )
-
-    pid: Optional[int] = field(
-        metadata={
-            'metadata': {
-                'description': 'PID of the process that holds the session',
-                'example': 12345,
-            }
-        }
-    )
+    username: str
+    terminal: Optional[str] = None
+    started: Optional[datetime] = None
+    pid: Optional[int] = None
diff --git a/platypush/schemas/system/_user/_schemas.py b/platypush/schemas/system/_user/_schemas.py
index ae3395db..2ba9c9f2 100644
--- a/platypush/schemas/system/_user/_schemas.py
+++ b/platypush/schemas/system/_user/_schemas.py
@@ -1,7 +1,47 @@
-from marshmallow_dataclass import class_schema
+from datetime import datetime
 
-from ._base import UserBaseSchema
-from ._model import User
+from dateutil.tz import gettz
+from marshmallow import fields, pre_load
+
+from platypush.schemas import DateTime
+
+from .._base import SystemBaseSchema
 
 
-UserSchema = class_schema(User, base_schema=UserBaseSchema)
+class UserSchema(SystemBaseSchema):
+    """
+    Schema for system users.
+    """
+
+    username = fields.String(
+        required=True,
+        metadata={'description': 'The username of the user.', 'example': 'johndoe'},
+    )
+
+    terminal = fields.String(
+        metadata={
+            'description': 'The terminal the user is currently using.',
+            'example': 'tty1',
+        }
+    )
+
+    started = DateTime(
+        metadata={
+            'description': 'The timestamp when the user session started.',
+            'example': '2021-01-01T00:00:00+00:00',
+        }
+    )
+
+    pid = fields.Integer(
+        metadata={'description': 'The PID of the user session.', 'example': 1234}
+    )
+
+    @pre_load
+    def pre_load(self, data: dict, **_) -> dict:
+        data = super().pre_load(data)
+        started_ts = data.pop('started', None)
+        if started_ts is not None:
+            data['started'] = datetime.fromtimestamp(started_ts).replace(tzinfo=gettz())
+
+        data['username'] = data.pop('name', data.pop('username', None))
+        return data