Architecture

System Overview

casectl follows a daemon-first, plugin-driven architecture. All hardware access goes through a central daemon that exposes a REST API. The CLI, web dashboard, and any future interfaces are all API clients – they never touch hardware directly.

INTERFACES
CLI(Click)
Web(HTMX)
TUI(casectl top)
GUI(soon)
▼
REST APIFastAPI :8420
PLUGIN_HOST
fan-control
led-control
oled-display
system-monitor
prometheus
mqtt
automation
alerting
your plugin
HARDWARE_ABSTRACTION
ExpansionBoardI2C 0x21
OledDeviceI2C 0x3C
SystemInfosysfs
▼
STM323x Fan · 4x LED · Temp
SSD1306128x64 OLED
CPU/MemDisk · Temp

Hardware Layer

The Freenove Computer Case Kit Pro includes an STM32-based expansion board connected to the Pi via I2C. This board controls:

casectl communicates with the STM32 at I2C address 0x21 using the smbus2 library. All I2C calls run via asyncio.to_thread() to prevent blocking the FastAPI event loop.

The SSD1306 OLED display (128x64 pixels, I2C address 0x3C) is driven separately via the luma.oled library.

System metrics (CPU temperature, memory usage, disk space) come from psutil and sysfs reads.

Plugin System

Plugins are the core extensibility mechanism. casectl uses Python’s typing.Protocol for structural subtyping – your plugin doesn’t need to inherit from any base class. It just needs to implement the right methods:

class CasePlugin(Protocol):
    async def setup(self, ctx: PluginContext) -> None: ...
    async def start(self) -> None: ...
    async def stop(self) -> None: ...
    def get_status(self) -> dict[str, Any]: ...

Plugins are discovered automatically via Python entry points (casectl.plugins group). The PluginHost manages their lifecycle: discovery, loading, starting, stopping.

Built-in Plugins

Plugin What it does
fan-control Temperature-curve PWM control with follow-temp/follow-rpi/manual/custom/off modes
led-control Rainbow, breathing, follow-temp, manual colour, off
oled-display 4 cycling screens: clock, metrics, temperatures, fan duty
system-monitor Periodic metrics collection + event bus emission
prometheus /metrics endpoint in Prometheus text format
mqtt Bidirectional MQTT with Home Assistant auto-discovery
automation Event-driven rules engine with conditions, actions, priority, cooldown
alerting Alert dispatch via webhook, ntfy.sh, and SMTP channels

Event Bus

The EventBus provides async pub/sub within the daemon. Plugins publish events (e.g. temperature readings, fan speed changes) and other plugins or the WebSocket endpoint subscribe. The web dashboard uses WebSocket for real-time updates.

Graceful Degradation

casectl is designed to work without hardware. When the I2C bus or specific devices aren’t available, plugins report DEGRADED status instead of crashing. The daemon, API, monitoring, and web dashboard all continue to function – only the hardware control features are disabled.

This means you can develop and test plugins on any machine, not just a Pi with the case attached.

Testing

casectl has a comprehensive test suite using mock hardware fixtures at the I2C/bus level – all tests run without real hardware. Run pytest --cov=casectl for current coverage.

pytest                    # run all tests
pytest --cov=casectl      # with coverage report