Files
jonas 71803418e5 Initial commit: Shelly Manager with Textual CLI, Streamlit UI, and comprehensive .gitignore
Shelly device management app with mDNS/subnet discovery, inventory,
configuration, and mass operations for Gen1/Gen2+ devices.

Includes .gitignore excluding runtime data (device DB, user config),
AI conversation history, build artifacts, and common Python/OS patterns.
2026-03-23 21:51:59 +01:00

9.0 KiB
Raw Permalink Blame History

Shelly Manager

Manage Shelly Gen1 and Gen2+ devices on your LAN: discovery (mDNS + subnet scan), inventory, configuration, and mass operations.

Requirements

  • Python 3.11+
  • uv recommended

Install

cd shelly-ui
uv sync

Tests

uv sync
uv run pytest

Pytest is configured to disable the unraisableexception plugin: Streamlits AppTest runs pages that call asyncio.run(), and teardown can emit ResourceWarning for sockets/event loops that would otherwise make pytest exit with an ExceptionGroup even when all tests pass.

  • Unit / async tests: models; subnet scan (fetch_shelly_json mocked); discovery helpers (IPv6 URL host brackets, probe_ip port); SQLite + Markdown storage; DeviceManager (mocked mDNS + enrich). Real mDNS/zeroconf is not run in CI (no devices/network dependency).
  • Streamlit UI tests: streamlit.testing.v1.AppTest runs the home page and multipage scripts in-process — no live browser or running streamlit run required.

To add browser E2E tests against your local UI (http://localhost:8501), use Playwright or Selenium in a separate optional suite; AppTest already covers UI structure without flakiness from a real server.

CLI (Textual TUI)

uv run shelly-manager
# or
uv run python -m shelly_manager.cli

Options:

uv run shelly-manager --help
uv run shelly-manager --storage sqlite --db-path ./data/devices.db
uv run shelly-manager --storage markdown --markdown-dir ./data/devices_md

The Textual TUI uses the same DeviceFilter as the web UI: generation, online/auth, combined name search, model & firmware substrings, IP prefix & MAC, comma-separated tags and capabilities, and dropdown presets for cached settings and status JSON. The Streamlit app adds exact model multiselect, tag multiselect, and custom dot- or |-separated paths for settings/status (use | when a key contains : e.g. switch:0|output).

Web UI (Streamlit)

uv run shelly-manager-ui
# or
uv run streamlit run src/shelly_manager/ui/Dashboard.py

Hot reload: shelly-manager-ui enables Streamlits Run on save (server.runOnSave), so edits to the app or imported modules trigger an automatic rerun. To disable (manual “Rerun” only), set SHELLY_UI_NO_RELOAD=1. Project .streamlit/config.toml also sets runOnSave when you use streamlit run from the repo root.

Settings persistence: The Settings page writes data/shelly_ui_config.json (by default, relative to the process working directory — run from the repo root so data/ is stable). Override the path with env SHELLY_UI_CONFIG. Without this file, settings lived only in browser session state and were lost on restart.

Text fields: Single-line inputs use a control on the right of the input row to clear the field in one click (Streamlit does not render a native in-field clear icon; multi-line JSON fields show on the top-right of the block). Implementation: shelly_manager.ui.components.clearable_input.

Each device shows an Open device web UI link using the devices HTTP URL (http://…/), with correct bracketing for IPv6. Non-default ports from mDNS discovery are stored on the device as http_port.

Discovery: The app probes mDNS (_shelly._tcp / _http._tcp) and an optional subnet CIDR (GET http://IP/shelly on each address — use 192.168.x.0/24 for a whole LAN, or 192.168.x.y/32 for one IP). To add a single device by address, use Dashboard → Add Shelly device manually (IP only). Each discovery run uses the latest saved settings (get_config()), not only the in-memory DeviceManager snapshot. Dashboard → Refresh inventory from network only re-fetches devices already in inventory; it does not scan the subnet and ignores Subnet CIDR for finding new addresses. Discovery details on the Dashboard shows a text log (devices found + source) and JSON stats. The Python logger shelly_manager.core.device_manager also emits INFO lines per device. Devices that require login may not expose /shelly without auth.

The Dashboard page (Dashboard.py) is the main inventory: an Inventory overview with metrics (totals, online/offline, Gen1/2/3, auth, reboot, firmware-check buckets, tags) plus bar charts for generation and firmware status, top models and capabilities tables. Click the numbers in the overview to set URL query parameters (?preset=…, or ?preset=model&model=… / ?cap=… for model/cap rows) so Filters match that slice (applied once, then cleared from the URL). Then discovery, Add Shelly device manually (single IP), the same inline filters as Mass Config (including Tag filter: any / has tags / untagged), visible columns (shared preference), and a read-only table (including Needs reboot from Shelly.GetStatussys.restart_required after a live refresh, plus Reboot filtered devices that need reboot) (device name / IP links, Config snapshots column with the last N stored GetConfig snapshot labels per device — N is Settings → Config snapshots shown per device). FW update uses Shelly.CheckForUpdate when you run Check firmware, and otherwise falls back to Shelly.GetStatussys.available_updates (same information the device web UI uses; updated periodically on the device). Stored CheckForUpdate results are kept across refresh (they used to be cleared). Use Check firmware or Refresh so the column stays current. Filter FW update = Has stable update (default “offered” meaning) for devices with a stable channel build; use Beta only or Stable or beta to include beta-only offers. Additional pages: Device, Mass Config, Settings (see src/shelly_manager/ui/pages/). Dark theme: .streamlit/config.toml (Streamlit theming).

Device page: Firmware section shows the installed version, runs Shelly.CheckForUpdate, and can start Shelly.Update OTA (stable or beta) on Gen2+ without auth (device must reach the internet; the device reboots when the install finishes). Builds a dynamic editor from the last Shelly.GetConfig snapshot (one expander per top-level key: sys, wifi, switch:0, …). Edit scalars and nested objects in the form; lists are edited as JSON. Save to device (Gen2+) sends each changed section via Component.SetConfig RPC; then the inventory is refreshed. The Last config apply expander lists each section with a Restart required column when the RPC response asks for a reboot; you can Reboot device now from the UI. Gen1 only saves to local inventory (apply on the device separately). Discard resets the form widgets. Authenticated-only devices show raw JSON until credentials are supported. From the Dashboard or Mass Config inventory table, click the device name to open the in-app Device page, or click the IP to open the devices http:// web UI (typically in a new tab). You can also open a device via ?device=<id> in the URL. Configuration history compares stored GetConfig snapshots as a unified diff (snapshot count per device is capped in Settings).

Mass configuration: Table-first: filters narrow which devices appear (same layout as the Dashboard). Include checkboxes + Select / deselect all choose which filtered rows receive an action — e.g. filter Needs reboot = yes, include those rows, then Bulk actions → Device control → Reboot devices. Action category groups Device control (reboot, identify), Diagnostics (live refresh, firmware check), and RPC configuration (BLE / MQTT / Cloud). Custom section config lets you paste a JSON object for one top-level GetConfig key (mqtt, wifi, coiot, sntp, sys, …) with merge or replace, for advanced bulk edits (same Component.SetConfig path as the Device page). Tags apply to selected rows only. Latest bulk operation results stays until dismissed. Refresh table live-refreshes listed devices. Settings → Mass Config: refresh table after bulk operation still applies after bulk runs. Name / IP are links. Gen2+-only actions skip Gen1 where appropriate.

Project layout

  • shelly_manager.core — models, discovery, device manager
  • shelly_manager.api — aioshelly-based Gen1/Gen2 clients
  • shelly_manager.storage — SQLite or Markdown persistence
  • shelly_manager.cli — Textual app
  • shelly_manager.ui — Streamlit multipage app

Docs

Note

Authentication on devices is not implemented in this version; unauthenticated devices are supported.