Skip to content

Add an offline add-on mode that runs grottserver (works without internet) - #12

Open
FezVrasta wants to merge 1 commit into
Herbertmt978:masterfrom
FezVrasta:feat/offline-mode-grottserver-mqtt
Open

Add an offline add-on mode that runs grottserver (works without internet)#12
FezVrasta wants to merge 1 commit into
Herbertmt978:masterfrom
FezVrasta:feat/offline-mode-grottserver-mqtt

Conversation

@FezVrasta

Copy link
Copy Markdown

Adds an offline add-on mode that runs grottserver instead of the proxy, so the add-on keeps producing data with no internet connection and without contacting Growatt at all.

proxy remains the default and is unchanged.

Why proxy mode cannot do this

grottproxy.on_accept() connects to Growatt before accepting the datalogger, and drops the datalogger if that connection fails:

forward = Forward().start(self.forward_to[0], self.forward_to[1])
clientsock, clientaddr = self.server.accept()
if forward:
    ...
else:
    clientsock.close()

Keeping that socket open isn't sufficient either — I tried that first. The proxy never replies to the datalogger; acknowledgements come from the real Growatt server. With nothing upstream the datalogger is never acknowledged, retries, and its records arrive mis-framed:

- Grott - grottproxy - Invalid data record received, processing stopped for this record

So a patched proxy can't stand in for a server. grottserver already is one — it ACKs data records (rectype 03/04/50/1b/20), answers pings and handles time sync. Its only gap is that it has no MQTT output at all, so it can't feed Home Assistant.

What this does

Wires grottserver into Grott's existing publishing pipeline rather than giving it a parallel one:

  • builds a Conf at startup (so MQTT settings, extensions and the listen port come from the same configuration as the rest of Grott)
  • takes the listen port from that config
  • calls procdata(conf, data) for each data record

Because procdata() also runs extensions, grottext.ha discovery works unchanged — MQTT output and Home Assistant entities are identical in both modes. That's the main reason for routing through procdata() instead of publishing directly.

Publishing is wrapped in try/except so a failure can never prevent the acknowledgement below it; otherwise the datalogger would retry the same record indefinitely.

On the add-on side, mode becomes list(offline|proxy). Grott's own config only accepts proxy/sniff, so run.sh presents proxy to it and selects the grottserver entry point. The listen port is unchanged, so switching modes needs no datalogger reconfiguration.

Tradeoff

In offline mode the inverter no longer reports to Growatt, so ShinePhone and ShineServer stop updating. That's inherent to running without the cloud, not a defect — it's documented in DOCS.md, and proxy remains the default for anyone who wants the app.

Testing

Honest summary of what is and isn't covered:

Verified on real hardware — a Growatt SML0CGU03K (single string on MPPT2, single phase, no battery) behind a ShineLan-X datalogger, Home Assistant OS 18.2 on aarch64, Mosquitto add-on:

  • with the datalogger pointed at the HA host and no upstream reachable, grottserver acknowledges records and procdata() publishes them; sensors update continuously
  • the same setup in proxy mode with the upstream unreachable produces nothing, which is what motivated this
  • values cross-checked against the growatt_server cloud integration while both were running: local 2152.6 W vs cloud 2144.9 W (cloud lags by a poll)

Caveat: I validated the grottserver + procdata() change on my own build of this code, not by building this repository's exact add-on image — I don't have your release/UAT pipeline. The add-on plumbing here is verified only by simulating run.sh (mode=proxy/app/grott.py, mode=offline/app/grottserver.py, gmode=proxy in both), plus py_compile and a config schema check. It deserves a run through your normal gates before release.

libscrc is already in requirements.lock and grottserver.py is already copied into the image, so no packaging changes were needed.

I deliberately did not bump the add-on version, and filed the changelog entry under Unreleased, so this fits your existing release process rather than pre-empting it.

Note

Thanks for maintaining this fork, and for sorting out the redistribution permission with Johan — that's why I brought this here rather than publishing my own derivative. Happy to adjust naming, defaults or docs to fit your conventions.

Proxy mode cannot run without internet access. `on_accept()` opens the
connection to Growatt before accepting the datalogger and closes the
datalogger's socket when that fails:

    forward = Forward().start(self.forward_to[0], self.forward_to[1])
    clientsock, clientaddr = self.server.accept()
    if forward:
        ...
    else:
        clientsock.close()

Keeping that socket open is not sufficient either, because the proxy never
replies to the datalogger: acknowledgements come from the real Growatt
server. With nothing upstream the datalogger is never acknowledged, retries,
and its records arrive mis-framed and fail validation.

grottserver already solves this - it is a local stand-in for the Growatt
server and acknowledges data records itself - but it has no MQTT output at
all, so it cannot feed Home Assistant.

Wire grottserver into Grott's existing publishing pipeline: build a Conf at
startup, take the listen port from it, and call procdata() for each data
record. Because procdata() also runs extensions, the Home Assistant discovery
plugin works unchanged, and MQTT output is identical to proxy mode.

Publishing is wrapped so a failure can never prevent the acknowledgement;
otherwise the datalogger would retry the same record indefinitely.

Expose this as a new add-on mode `offline` alongside `proxy`. Grott's own
configuration only accepts proxy/sniff, so run.sh presents "proxy" to it and
selects the grottserver entry point. The listen port is unchanged, so
switching modes needs no datalogger reconfiguration.

In offline mode the inverter no longer reports to Growatt, so ShinePhone and
ShineServer stop updating. That is inherent to running without the cloud;
proxy remains the default.

The add-on version is deliberately not bumped, and the changelog entry is
filed under Unreleased, so the change fits the existing release process.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant