SPAN Smart Panel
The span_panel driver polls a SPAN Smart Panel over its on-LAN REST API. It auto-discovers circuits, mirrors them as zones under the power subsystem, and exposes panel-level telemetry — main relay state, grid/feedthrough power, run config, DSM grid state, islanding state, and battery state-of-energy — as device attributes.
All traffic stays on the LAN. There is no SPAN cloud round-trip at runtime.
Status
Built against the SPAN documented REST API. Onboarding uses /api/v2/auth/register to exchange a passphrase for a JWT bearer; telemetry and control use /api/v1/*. The /api/v1 surface is documented to deprecate 2026-12-31 — when SPAN extends /api/v2 to cover telemetry, or we move to MQTT/eBus, the implementation swaps behind the existing SpanClient wrapper.
The panel's built-in MQTT/Homie 5.0 broker is intentionally not used.
Prerequisites
- SPAN panel reachable from the GEM controller on TCP/443 (HTTPS, self-signed cert accepted).
- A panel passphrase. There are two ways to mint one:
- SPAN Home app — issues a passphrase tied to your account.
- Door switch — press the panel door switch for proof-of-proximity, then complete the registration flow within the timeout window.
Setup
- System → Devices: add a device with driver
span_panel. Fill in:ip— panel LAN IPpassphrase— proof-of-proximity passphrase (secure)
- Save and enable. The driver:
- calls
/api/v2/auth/registerand caches the returned JWT as theaccess_tokendevice attribute, - reads
/api/v2/statusto populateserial_number,firmware_version,door_state, - calls
/api/v1/circuitsto enumerate circuits — each one is auto-created as a zone under thepowersubsystem (created on demand) withzone.address= the circuit UUID andcontrol=power_switch, - starts polling
/api/v1/panel,/api/v1/circuits,/api/v1/storage/soe,/api/v1/islanding-stateevery 5 s.
- calls
Attributes
Device
| Attribute | Required | Type | Notes |
|---|---|---|---|
ip | yes | string | Panel LAN IP. |
passphrase | yes | string (secure) | Proof-of-proximity passphrase from the SPAN Home app or door-switch flow. |
port | no | int | Override the panel port (default 443 for https, 80 for http). Useful when proxying. |
protocol | no | string | https (default) or http. Use http only behind a plaintext proxy. |
poll_interval | no | int | Telemetry poll interval in seconds. Default 5, range 1..300. |
access_token | no | string (secure, readonly) | JWT cached after registration. The driver auto-refreshes by re-registering on 401/403. |
Runtime-populated (read-only):
| Attribute | Type | Source |
|---|---|---|
serial_number | string | /api/v2/status |
firmware_version | string | /api/v2/status |
main_relay_state | string | /api/v1/panel.mainRelayState |
grid_power_w | float | /api/v1/panel.instantGridPowerW |
feedthrough_power_w | float | /api/v1/panel.feedthroughPowerW |
run_config | string | /api/v1/panel.currentRunConfig |
dsm_grid_state | string | /api/v1/panel.dsmGridState |
islanding_state | string | /api/v1/islanding-state.islanding_state |
battery_soe_percent | float | /api/v1/storage/soe.percentage |
door_state | string | /api/v2/status.system.doorState |
discovered_circuits | string (json) | Last circuits snapshot for diagnostics. |
Zone
Zone address is the circuit UUID from /api/v1/circuits. Per-circuit attributes populated by the poller:
| Attribute | Type |
|---|---|
state | string — on (relay closed) or off (relay open) |
relay_state | string — raw CLOSED / OPEN |
instant_power_w | float |
consumed_energy_wh | float |
produced_energy_wh | float |
priority | string — MUST_HAVE / NICE_TO_HAVE / NON_ESSENTIAL |
is_user_controllable | bool |
is_sheddable | bool |
is_never_backup | bool |
tabs | string (CSV) |
Commands
| Command | Args | Purpose |
|---|---|---|
on | address | Set the circuit's relay to CLOSED (zone on). Refused if the circuit is not user-controllable. |
off | address | Set the circuit's relay to OPEN (zone off). |
reboot | address, delay | Open the relay, wait delay seconds (default 5), close it again. Useful for power-cycling a non-essential load. |
Quirks
- SPAN's API serializes float32 telemetry as full float64, leaking artifacts like
1557.4998779296875for what was1557.5. The driver rounds to 1 decimal place — only ~7 significant digits are real. - The panel rejects relay writes for circuits where
isUserControllable=false. The driver checks the cached snapshot before sending and returns an error early. - After auth-related 401/403 errors the driver re-registers automatically using the stored passphrase. If the passphrase was rotated externally, registration will fail until the device row is updated.
Troubleshooting
| Symptom | Check |
|---|---|
registration returned no access token on first connect | Wrong passphrase, or the proof-of-proximity window expired. Mint a fresh passphrase from the SPAN Home app and retry. |
Repeated re-register failed warnings | Passphrase has been invalidated (e.g. rotated). Update the device's passphrase attribute with a freshly minted value. |
Circuit relay command returns circuit ... is not user-controllable | The circuit is locked at the panel level (e.g. main breakers, never-backup loads). Cannot be controlled remotely. |
| No new zones appear after wiring change | Run the connect action via the device page (or reload the device) to re-call /api/v1/circuits. The poller does not auto-create new zones — only refreshes telemetry on existing ones. |