Skip to content

Commit d15d5c8

Browse files
Add HA add-on CI workflow and Home Assistant control entities
- Create `.github/workflows/addon.yml` for automated testing, version bumping, and Docker builds. - Add support for Home Assistant control entities: switches, numbers, and selects with MQTT/REST parity tests. - Expand testing suite with `parity_test.go` and `MQTTControlRoundTrip` to ensure consistency between MQTT and REST APIs. - Enhance SSE implementation with better error handling and formatting. - Update project dependencies in `go.sum` to add required libraries for new functionality. - Improve `run.sh` script in HA add-on for better handling of MQTT broker detection.
1 parent f6f3d7f commit d15d5c8

17 files changed

Lines changed: 1444 additions & 130 deletions

File tree

.github/workflows/addon.yml

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
name: HA Add-on
2+
3+
on:
4+
push:
5+
tags:
6+
- "v*"
7+
8+
permissions:
9+
contents: write
10+
packages: write
11+
12+
jobs:
13+
version:
14+
runs-on: ubuntu-latest
15+
outputs:
16+
version: ${{ steps.version.outputs.version }}
17+
steps:
18+
- uses: actions/checkout@v4
19+
20+
- name: Set version from tag
21+
id: version
22+
run: echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"
23+
24+
- name: Update config.yaml version
25+
run: sed -i "s/^version:.*/version: ${GITHUB_REF_NAME#v}/" ha-addon/config.yaml
26+
27+
- name: Commit version bump
28+
run: |
29+
git config user.name "github-actions[bot]"
30+
git config user.email "github-actions[bot]@users.noreply.github.com"
31+
git add ha-addon/config.yaml
32+
git diff --cached --quiet || git commit -m "Release ${GITHUB_REF_NAME}: update add-on version"
33+
git push origin HEAD:master
34+
35+
build:
36+
needs: version
37+
runs-on: ubuntu-latest
38+
strategy:
39+
matrix:
40+
include:
41+
- arch: amd64
42+
platform: linux/amd64
43+
base: ghcr.io/home-assistant/amd64-base:3.20
44+
- arch: aarch64
45+
platform: linux/arm64
46+
base: ghcr.io/home-assistant/aarch64-base:3.20
47+
- arch: armv7
48+
platform: linux/arm/v7
49+
base: ghcr.io/home-assistant/armv7-base:3.20
50+
steps:
51+
- uses: actions/checkout@v4
52+
53+
- name: Use version from tag
54+
id: version
55+
run: echo "version=${{ needs.version.outputs.version }}" >> "$GITHUB_OUTPUT"
56+
57+
- uses: docker/setup-qemu-action@v3
58+
59+
- uses: docker/setup-buildx-action@v3
60+
61+
- uses: docker/login-action@v3
62+
with:
63+
registry: ghcr.io
64+
username: ${{ github.actor }}
65+
password: ${{ secrets.GITHUB_TOKEN }}
66+
67+
- name: Build and push
68+
uses: docker/build-push-action@v6
69+
with:
70+
context: .
71+
file: ha-addon/Dockerfile
72+
platforms: ${{ matrix.platform }}
73+
push: true
74+
build-args: |
75+
BUILD_FROM=${{ matrix.base }}
76+
tags: |
77+
ghcr.io/${{ github.repository }}/${{ matrix.arch }}:${{ steps.version.outputs.version }}
78+
ghcr.io/${{ github.repository }}/${{ matrix.arch }}:latest
79+
cache-from: type=gha,scope=${{ matrix.arch }}
80+
cache-to: type=gha,mode=max,scope=${{ matrix.arch }}

README.md

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
# SRNE Solar Controller
2+
3+
Monitor and control SRNE ASP/ASF-series hybrid inverters via MODBUS over Solarman V5 wifi dongles.
4+
5+
Integrates with Home Assistant through MQTT auto-discovery, providing ~65 sensor entities and 7 writable controls. Also includes a live web dashboard and CLI for direct register access. Supports multi-inverter parallel systems with aggregated data and synchronized settings writes.
6+
7+
## Home Assistant Add-on
8+
9+
### Prerequisites
10+
11+
- A Solarman V5 wifi dongle (LSW-3, LSE-3, etc.) connected to your inverter
12+
- The [Mosquitto broker](https://github.com/home-assistant/addons/tree/master/mosquitto) add-on (or an external MQTT broker)
13+
14+
### Installation
15+
16+
Add this repository URL to your Home Assistant add-on store:
17+
18+
```
19+
https://github.com/daniel-sullivan/srne-solar-controller
20+
```
21+
22+
Install the add-on, then configure your inverter connection(s) in the add-on settings:
23+
24+
| Option | Description |
25+
|---|---|
26+
| `inverters[].host` | IP address of the Solarman dongle |
27+
| `inverters[].port` | TCP port (default 8899) |
28+
| `inverters[].slave_id` | MODBUS slave ID (default 1) |
29+
| `inverters[].serial` | Dongle serial number (0 = auto-detect) |
30+
| `poll_interval` | Seconds between polling cycles (default 10) |
31+
| `mqtt_topic_prefix` | HA discovery prefix (default `homeassistant`) |
32+
| `mqtt_broker` | Manual broker URL override (auto-detected from Mosquitto add-on) |
33+
| `mqtt_username` | Manual broker username override |
34+
| `mqtt_password` | Manual broker password override |
35+
36+
Start the add-on. Sensor and control entities appear automatically under the MQTT integration.
37+
38+
### Auto-Discovered Entities
39+
40+
**Sensors (~65 entities):** Battery SOC, voltage, current, power, temperature. PV1/PV2 voltage, current, power. Load power, apparent power, power factor. Grid voltage, current, frequency per phase (L1/L2). Inverter state, bus voltage, heatsink temperatures. Daily and lifetime energy statistics.
41+
42+
**Controls (7 entities):**
43+
44+
| Entity | Type | Description |
45+
|---|---|---|
46+
| Charge from Mains | Switch | Toggle grid charging. Remembers previous charger priority and restores it on OFF. |
47+
| Charger Priority | Select | CSO (PV Preferred) / CUB (Mains Preferred) / SNU (Hybrid) / OSO (PV Only) |
48+
| Output Priority | Select | SOL (Solar First) / UTI (Utility First) / SBU (Solar, Battery, Utility) |
49+
| Max Charge SOC | Number | Stop charging at this SOC (0-100%) |
50+
| Discharge Cutoff SOC | Number | Hard minimum battery SOC (0-100%) |
51+
| SOC Switch to Mains | Number | Switch to grid power below this SOC (0-100%) |
52+
| SOC Switch to Battery | Number | Switch back to battery above this SOC (0-100%) |
53+
54+
### Web Dashboard
55+
56+
The add-on includes a web dashboard accessible via the HA sidebar (ingress). It provides real-time monitoring with live-updating panels and a full settings editor for all inverter parameters.
57+
58+
## Supported Hardware
59+
60+
SRNE ASP and ASF series hybrid inverters connected via Solarman V5 wifi dongles. Communication is over TCP port 8899 using the Solarman V5 protocol wrapping MODBUS RTU.
61+
62+
Tested with ASP48100U200-H units in split-phase 120/240V parallel configuration.
63+
64+
## Standalone Usage
65+
66+
The controller can also run outside Home Assistant as a standalone service or CLI tool.
67+
68+
### Prerequisites
69+
70+
- [mise](https://mise.jdx.dev/) (manages the Go toolchain)
71+
72+
```sh
73+
mise install
74+
```
75+
76+
### Service Mode
77+
78+
Create a `srne.toml` config file:
79+
80+
```toml
81+
[server]
82+
poll_interval = "10s"
83+
web_port = 8080
84+
settings_refresh = "5m"
85+
86+
[[inverter]]
87+
host = "10.100.3.92"
88+
port = 8899
89+
slave_id = 1
90+
91+
[[inverter]]
92+
host = "10.100.5.55"
93+
port = 8899
94+
slave_id = 2
95+
96+
[mqtt]
97+
broker = "tcp://localhost:1883"
98+
client_id = "srne"
99+
topic_prefix = "homeassistant"
100+
```
101+
102+
```sh
103+
mise run serve
104+
```
105+
106+
The web dashboard is available at `http://localhost:8080`. The MQTT section is optional when running standalone.
107+
108+
### CLI
109+
110+
```sh
111+
# Display inverter product info
112+
mise exec -- go run . info --host 10.100.3.92
113+
114+
# Dump all register groups
115+
mise exec -- go run . dump --host 10.100.3.92 all
116+
117+
# Dump a specific group: battery, inverter, settings, stats, faults, timed
118+
mise exec -- go run . dump --host 10.100.3.92 battery
119+
120+
# Read specific registers
121+
mise exec -- go run . read --host 10.100.3.92 0x0100 10
122+
123+
# Write a register
124+
mise exec -- go run . write --host 10.100.3.92 0xE001 0x0036
125+
126+
# Probe for undocumented registers in a range
127+
mise exec -- go run . probe --host 10.100.3.92 0xE100 0xE150
128+
129+
# Passively scan dongle traffic
130+
mise exec -- go run . scan --host 10.100.3.92
131+
```
132+
133+
Global flags: `--host` (required), `--port` (default 8899), `--serial` (auto-detected if omitted), `--slave` (default 1), `--debug`.
134+
135+
### REST API
136+
137+
| Endpoint | Method | Description |
138+
|---|---|---|
139+
| `/api/snapshot` | GET | Current system snapshot (JSON) |
140+
| `/api/snapshot/stream` | GET | SSE stream of snapshots |
141+
| `/api/settings` | GET | Current inverter settings |
142+
| `/api/settings/write` | POST | Write settings (`{"changes": [{"field": "...", "value": "..."}]}`) |
143+
| `/api/faults` | GET | Fault history records |
144+
| `/api/entities` | GET | All entity metadata (sensors + controls) with current state |
145+
| `/api/controls/{key}` | POST | Write a control value (`{"value": "..."}`) |
146+
147+
## Architecture
148+
149+
```
150+
CLI/API -> modbus.Session (cache + retry) -> modbus.Client -> Solarman V5 (TCP:8899) -> MODBUS RTU -> Inverter
151+
```
152+
153+
| Package | Role |
154+
|---|---|
155+
| `modbus/` | MODBUS RTU framing, CRC16, `Client` interface, `Session` (register cache + exponential backoff retry) |
156+
| `interfaces/solarman/` | Solarman V5 TCP transport with serial auto-detection, interleaved response filtering, and automatic reconnection |
157+
| `interfaces/mock/` | Mock inverter and live simulator for testing |
158+
| `register/` | Register definitions, context-aware scaling (`ScaleFunc`), fault code lookup |
159+
| `inverter/` | Multi-inverter system management, typed snapshots, aggregation, settings encoding |
160+
| `serve/` | Polling hub, MQTT publisher with HA control entities, web dashboard (HTMX/SSE), REST API |
161+
| `cmd/` | Cobra CLI: `read`, `write`, `dump`, `info`, `scan`, `probe`, `serve` |
162+
163+
## Building
164+
165+
```sh
166+
mise exec -- go build ./...
167+
mise exec -- go test ./...
168+
```
169+
170+
## References
171+
172+
- [SRNE ASP 8-10kW User Manual](https://www.srnesolar.com/userfiles/files/2025/11/28/ASP%20_8-10kW_U_All-in-one%20solar%20charge%20inverter_V1.3[20250514].pdf)
173+
- [SRNE MODBUS Protocol PDFs](https://github.com/shakthisachintha/SRNE-Hybrid-Inverter-Monitor/tree/master/Resources)
174+
- [ha-solarman](https://github.com/davidrapan/ha-solarman) -- Solarman V5 protocol implementation, SRNE inverter profile
175+
- [V2.08 Protocol + ESPHome YAML](https://github.com/phinix-org/SRNE-inverters-by-modbus-rs485) -- Latest known protocol PDF
176+
177+
## License
178+
179+
See [LICENSE](LICENSE) for details.

go.mod

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,20 @@ module github.com/daniel-sullivan/srne-solar-controller
33
go 1.26.1
44

55
require (
6+
github.com/BurntSushi/toml v1.6.0
67
github.com/cenkalti/backoff/v5 v5.0.3
8+
github.com/eclipse/paho.mqtt.golang v1.5.1
9+
github.com/mochi-mqtt/server/v2 v2.7.9
710
github.com/spf13/cobra v1.10.2
811
github.com/stretchr/testify v1.11.1
912
)
1013

1114
require (
12-
github.com/BurntSushi/toml v1.6.0 // indirect
1315
github.com/davecgh/go-spew v1.1.1 // indirect
14-
github.com/eclipse/paho.mqtt.golang v1.5.1 // indirect
1516
github.com/gorilla/websocket v1.5.3 // indirect
1617
github.com/inconshreveable/mousetrap v1.1.0 // indirect
1718
github.com/pmezard/go-difflib v1.0.0 // indirect
19+
github.com/rs/xid v1.4.0 // indirect
1820
github.com/spf13/pflag v1.0.9 // indirect
1921
golang.org/x/net v0.44.0 // indirect
2022
golang.org/x/sync v0.17.0 // indirect

go.sum

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,14 @@ github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aN
1111
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
1212
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
1313
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
14+
github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg=
15+
github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
16+
github.com/mochi-mqtt/server/v2 v2.7.9 h1:y0g4vrSLAag7T07l2oCzOa/+nKVLoazKEWAArwqBNYI=
17+
github.com/mochi-mqtt/server/v2 v2.7.9/go.mod h1:lZD3j35AVNqJL5cezlnSkuG05c0FCHSsfAKSPBOSbqc=
1418
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
1519
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
20+
github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY=
21+
github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
1622
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
1723
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
1824
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=

ha-addon/config.yaml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ ports:
1818
ports_description:
1919
8080/tcp: Web dashboard (optional, ingress preferred)
2020
services:
21-
- mqtt:want
21+
- mqtt:need
2222
map:
2323
- config:rw
2424
options:
@@ -28,7 +28,6 @@ options:
2828
slave_id: 1
2929
serial: 0
3030
poll_interval: 10
31-
mqtt_enabled: true
3231
mqtt_topic_prefix: homeassistant
3332
mqtt_broker: ""
3433
mqtt_username: ""
@@ -40,7 +39,6 @@ schema:
4039
slave_id: "int(1,247)"
4140
serial: "int(0,)?"
4241
poll_interval: "int(1,3600)"
43-
mqtt_enabled: bool
4442
mqtt_topic_prefix: str
4543
mqtt_broker: url?
4644
mqtt_username: str?

0 commit comments

Comments
 (0)