# Monitor a Solplanet battery locally in Home Assistant, with fault emails you can trust

> Read a Solplanet hybrid inverter and battery straight from its Ai-Dongle, with no cloud in the path, and build a fault email that survives the dongle's quirks: one-poll "Wait" blips, dropouts and a rate limit.

Guide · Published 2026-10-14 · Updated 2026-10-14  
Web page: https://blog.jacq.cc/guides/solplanet-battery-home-assistant-local/

My home battery is a Solplanet system. Its Ai-Dongle, the inverter's network module, answers on the local network, and a Home Assistant integration reads it every 60 seconds, with no cloud account in the path.

[Image: A drawing: the sun shines on a solar panel that charges a home battery, next to an envelope with a green tick]

Getting the numbers in is easy. Trusting them is harder. The dongle reports the inverter as waiting while it's producing 2.8 kW, drops off the network for minutes at a time, and locks everyone out if it's asked too often. My first fault email sent 27 false alarms in 10 days. Here's the setup and the automation I ended up with.

:::note[Status, October 2026]
Running since September 2026. The fault email below is the third version, running since 13 October 2026.
:::

## My system

[Image: Diagram: solar panels feed a Fronius solar inverter. A Solplanet hybrid inverter with a 35 kWh battery sits on the same switchboard as the grid and the home loads. Two Eastron meters, one on the Fronius and one on the grid, report to the Solplanet inverter. Its Ai-Dongle talks to Home Assistant on a Raspberry Pi 5 over Wi-Fi and HTTPS, polled every 60 seconds, read-only, with no cloud.]

*Power on the left, data on the right. This guide is about the dongle-to-Home Assistant link.*

| Part | Model |
| --- | --- |
| Hybrid inverter | Solplanet ASW10kH-T3, 10 kW |
| Battery | Solplanet AP-H series, 35 kWh (7 modules) |
| Solar inverter | Fronius Primo 5.0-1, with 6.6 kW of panels |
| Meters | 2× Eastron SEM3: one on the Fronius output, one on the grid |
| Dongle | Solplanet Ai-Dongle, on Wi-Fi |

The system is AC-coupled. The panels feed the Fronius, which has its own built-in Home Assistant integration. The Solplanet inverter charges and discharges the battery on the AC side, and its two meters tell it what the Fronius makes and what goes to and from the grid.

## Step 1: Install the integration

You need Home Assistant with [HACS](https://hacs.xyz/), and the dongle on your network.

Search HACS for "Solplanet". Two integrations show up: the original by zbigniewmotyka, and [calvinbui's fork](https://github.com/calvinbui/home-assistant-solplanet), which focuses on the newer "v2" dongle firmware. I use the fork, v1.1.0. Restart Home Assistant, then add it in **Settings → Devices & services** with the dongle's IP address. Leave the update interval at 60 seconds (step 2 says why).

Mine speaks v2 over HTTPS on port 443. The integration finds that by itself.

:::caution[Some entities write to the inverter]
The integration also creates controls: battery work mode, charge and discharge schedules, grid power limits. Those change how your system runs. This guide only reads.
:::

## Step 2: Don't ask the dongle yourself

The Ai-Dongle has a rate limit. Ask it too often and it rejects every request, Home Assistant's included, for a while. Home Assistant already polls it every 60 seconds, so a script of yours on top is how you'd trip it.

[Image: Diagram: Home Assistant polls the Ai-Dongle every 60 seconds, one request at a time, and keeps the results in its database and log. You read history and raw replies from Home Assistant, not from the dongle. Direct requests only with Home Assistant's polling paused, one at a time, well apart.]

*Read from Home Assistant, not from the dongle.*

Everything is already in Home Assistant: the history of every entity, and, at debug level, every raw reply from the dongle. Turn that on without a restart, in **Developer tools → Actions**:

```yaml
action: logger.set_level
data:
  custom_components.solplanet: debug
```

The replies show up in **Settings → System → Logs**, base64-encoded (`b'eyJ...'`). Decode one with `echo 'eyJ...' | base64 -d | jq .`, then set the level back to `warning`.

## Step 3: Turn on the dongle's Wi-Fi signal sensor

The dongle device has a **WiFi signal strength** sensor, disabled by default. Enable it on the dongle's device page. It costs no extra requests: the integration fetches that value anyway, once an hour.

Mine reads -66 dBm, which is borderline (about -67 dBm is the usual limit for a reliable link). When the dongle drops out, its history shows whether Wi-Fi is to blame.

## Step 4: The fault email

Create an automation, switch to YAML (**⋮ → Edit in YAML**), and paste this. Replace the entity IDs with yours: the integration names them after your devices, so the inverter's start with its model, `asw10kh_t3_`, and `notify.email` is your notifier.

```yaml
alias: Solplanet battery/inverter fault email
description: >-
  Email me when the Solplanet inverter or battery reports a fault that lasts
  5 min. Separate email when the dongle is unreachable for 1 h.
triggers:
  - trigger: state
    id: fault
    entity_id:
      - sensor.battery_errors
      - sensor.battery_warnings
      - sensor.inverter_error_code
      - sensor.inverter_status
      - sensor.battery_communication_status
    not_to:
      - unavailable
      - unknown
    for: "00:05:00"
  - trigger: state
    id: unreachable
    entity_id:
      - sensor.inverter_status
      - sensor.battery_communication_status
    to: unavailable
    for: "01:00:00"
actions:
  - choose:
      - conditions:
          - condition: trigger
            id: unreachable
        sequence:
          - action: smtp.send_message
            target:
              entity_id: notify.email
            data:
              title: Solplanet dongle unreachable for 1 h
              message: >-
                Home Assistant can't read the Solplanet Ai-Dongle. This means
                no data, not a fault: the inverter and battery most likely keep
                running. Check the dongle's Wi-Fi signal, or reboot it.
    default:
      - if:
          - condition: not
            conditions:
              - condition: and
                conditions:
                  - condition: state
                    entity_id: sensor.battery_errors
                    state: [No errors, unavailable, unknown]
                  - condition: state
                    entity_id: sensor.battery_warnings
                    state: [No warnings, unavailable, unknown]
                  - condition: state
                    entity_id: sensor.inverter_error_code
                    state: [No error, unavailable, unknown]
                  - condition: state
                    entity_id: sensor.inverter_status
                    state: [Normal, unavailable, unknown]
                  - condition: state
                    entity_id: sensor.battery_communication_status
                    state: [Normal, unavailable, unknown]
        then:
          - action: smtp.send_message
            target:
              entity_id: notify.email
            data:
              title: Solplanet fault
              message: >-
                Inverter: {{ states('sensor.inverter_status') }},
                {{ states('sensor.inverter_error_code') }}.
                Battery: {{ states('sensor.battery_errors') }},
                {{ states('sensor.battery_warnings') }}, comms
                {{ states('sensor.battery_communication_status') }},
                {{ states('sensor.battery_state_of_charge') }}%.
mode: single
max_exceeded: silent
```

`smtp.send_message` needs Home Assistant 2026.8 or newer. With another notifier, use `notify.send_message` with the same target and data.

[Image: Flow chart: the "fault" trigger fires when one of the 5 entities changes to a real state and stays 5 minutes. The check then asks whether everything is OK, counting unavailable or unknown as OK. All OK: no email. Something isn't: a "Solplanet fault" email. The "unreachable" trigger fires when the inverter status or battery communication is unavailable for an hour, and sends a separate "dongle unreachable" email.]

*Two triggers, one check. A change back to Normal fires "fault" too; the check is what stops those emails.*

## Why it looks like this

### "Wait" for one poll

The first version emailed on any change to a bad state. In 10 days it sent 27 emails saying the inverter had gone from Normal to Wait. Each time, the dongle reported Wait for a single 60-second poll, and the next poll was Normal again. A real Wait means no output, but the inverter was producing 2,779 W during one of them. None of the 27 was real.

[Image: A row of 60-second polls: Normal, Normal, Normal, Wait, Normal and so on. Without a delay, the single Wait at 10:09 on 28 September, with 2,779 W of output, sends an email straight away. With for: 5 minutes, Wait would have to last 5 minutes; it's Normal again after one poll, so nothing is sent.]

*One poll of Wait, while the inverter produced 2.8 kW.*

Fix: `for: "00:05:00"`, so a bad state has to last 5 minutes. The trade-off: a real fault that clears within 5 minutes no longer emails.

### Unavailable is not a fault

On 12 October I got 11 fault emails in one afternoon. None was a fault. Home Assistant lost contact with the dongle from 14:15 to 21:51, and the entities kept flipping to `unavailable` for 1 to 10 minutes at a time.

Five minutes wasn't enough because of the integration's back-off. After 3 failed polls in a row, it only tries again every 10 minutes, so the entities stay unavailable for at least 10 minutes, and a 5-minute trigger fires every time.

[Image: Timeline: three polls fail, up to 30 seconds each. After the third, the next poll is 10 minutes away. The entity is unavailable for those 10 minutes, so a 5-minute trigger used to send a fault email. Now unavailable is ignored, and only an hour of it sends a "dongle unreachable" email.]

*Three failures, then a 10-minute wait: long enough to beat a 5-minute trigger.*

Fix: the `fault` trigger ignores changes to `unavailable` and `unknown` (`not_to`), and the check counts them as OK. A dongle that stays silent is a different problem, so the `unreachable` trigger sends one separate email after an hour. The longest gap that day was 38 minutes.

The system itself kept working the whole time:

[Image: Bar chart of battery power by hour on 12 October 2026: charging in the morning, up to 8.6 kW at noon, then discharging at about 4 kW from 17:00 to 21:00. The afternoon gap is marked: 14:15 to 21:51, Home Assistant kept losing the dongle.]

*Battery power on 12 October, from Home Assistant's history. Only the data link was down.*

I haven't found the cause yet. The dongle answered pings slowly the next day (64 ms on average, against 1 to 5 ms for the Wi-Fi access points), so its Wi-Fi link is my suspect. The automation doesn't need to know.

## Sources

- [calvinbui/home-assistant-solplanet](https://github.com/calvinbui/home-assistant-solplanet/tree/v1.1.0) v1.1.0: the back-off in [`coordinator.py`](https://github.com/calvinbui/home-assistant-solplanet/blob/v1.1.0/custom_components/solplanet/coordinator.py), and its [wiki](https://github.com/calvinbui/home-assistant-solplanet/wiki)
- The original integration: [zbigniewmotyka/home-assistant-solplanet](https://github.com/zbigniewmotyka/home-assistant-solplanet)
- Home Assistant docs: [state trigger](https://www.home-assistant.io/triggers/state/) (`not_to`), [logger.set_level](https://www.home-assistant.io/actions/logger.set_level/), [smtp.send_message](https://www.home-assistant.io/actions/smtp.send_message/)

## Change history

- 2026-10-14: First published.

Licence: CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/). Credit "HomeLab Guides (blog.jacq.cc)" with a link to https://blog.jacq.cc/guides/solplanet-battery-home-assistant-local/. Code downloads: MIT. Details: https://blog.jacq.cc/licence/
