While I was in Jamaica, I set up a PowMr inverter with a Siseli-compatible Wi-Fi dongle. The vendor app could see the inverter, but I wanted useful, current telemetry in Home Assistant without making the cloud app stop working.
I already had the Siseli Inverter Bridge integration in Home Assistant. The missing piece was a reliable local path for live readings. I used Codex as a pair programmer while I captured and inspected the traffic, worked out the Modbus request format, and built a small read-only polling path.
Start by watching the traffic
The first useful step was not guessing register meanings from a product page. It was observing what the dongle and cloud service actually exchanged.
The capture showed the cloud connection requesting configuration and status information. It did not show the live register blocks I needed for continuously updated inverter readings. That was an important result: passively decoding the existing traffic would not be enough. The local system needed to make its own Modbus read requests.
I also learned that the bridge was doing more than exposing a network port. It intercepted and forwarded the dongle’s traffic at the network layer. A setting that looked like it should enable a TCP listener did not, in fact, make a local Modbus TCP endpoint available. The existing cloud path and the new local polling path had to be treated as separate jobs.
The protocol detail that held things up
The dongle carries its messages over its MQTT connection. A first attempt to inject a request failed because the payload was not framed the way the device expects. The Siseli message has a leading NUL byte followed by JSON. Once I reproduced that framing and sent a standard Modbus function 03 read request, the inverter returned a valid reply.
Each request and response needed to be correlated and checked rather than trusted just because bytes came back. The reader verifies the transaction, unit, function, expected address and length, and Modbus CRC before making data available to Home Assistant.
There was another non-obvious device quirk: the two bytes packed inside each 16-bit register are low-byte first. Applying the usual assumption silently produces wrong values. I kept a captured, validated response as a regression fixture so future decoder changes can be checked against known data.
A read-only local path
The resulting design kept the vendor connection working and added a separate local read path:
- A small gateway transparently relays the dongle’s existing cloud traffic.
- The gateway sends periodic, read-only Modbus requests through that same device connection.
- It validates the replies and forwards them to a Home Assistant add-on over the local network.
- The add-on validates the datagram again and publishes the readings through its authenticated MQTT connection, where Home Assistant discovers the entities.
The main telemetry block reads 45 registers starting at address 4501 every minute. A second 16-register block starting at 4546 is polled every five minutes for slower-changing status, including charging and grid-versus-battery state. Those addresses and intervals are specific to the inverter and bridge combination I tested; they should not be copied blindly to a different model.
The gateway has no MQTT credential of its own, and the polling path has no Modbus write operation. The cloud connection remains available for the vendor app. This is local telemetry alongside the cloud service, not a claim that the whole inverter setup is cloud-free.
What showed up in Home Assistant
After decoding the register values and publishing them through MQTT discovery, Home Assistant could show live electrical readings and operating state. One captured sample showed 113.6 V at 50.1 Hz on the grid, a 26.7 V battery, 121.0 V at 50.1 Hz inverter output, and a 447 W load. That is an example from a single reading, not a promise about normal voltage or load.
The point was to make the data available to dashboards and automations using the same Home Assistant entities as the rest of the house. I could keep using the vendor app while also seeing the local readings in my own system.
Making it less fragile
A successful packet exchange is only a prototype. The polling logic was tightened so it permits one outstanding request at a time, validates replies before publishing, and backs off after repeated failures instead of flooding the device. Follow-up work added controlled reconnect behavior and a watchdog that can restart the relay if it becomes unhealthy.
The implementation and later maintenance are in my Siseli Home Assistant fork. The protocol-specific byte order, captured response fixture, and read-only boundary are the details I would preserve if adapting this to another inverter.
What Codex helped with
Codex was useful as an iterative debugging partner: I could bring it observations from a packet capture, ask it to reason through the request and response structure, then test the next small change against the actual device. The critical evidence still came from the wire and from validating the inverter’s reply. The model helped organize the investigation and implementation; it did not replace measurement.
The general lesson was to separate the layers. The bridge’s network behavior, Siseli’s MQTT framing, Modbus transaction format, register byte order, and Home Assistant’s MQTT discovery each had their own failure modes. Proving each boundary in turn made the final local telemetry path understandable and kept the control surface read-only.