anker-shelly-bridge
Automate a Shelly smart plug from live Anker SOLIX telemetry.
Your SOLIX device reports battery level, solar input, and load over Anker's cloud MQTT service. This reads that telemetry and switches a Shelly plug on your local network according to rules you write in a plain text file.
The common use is controlling grid charging: plug the SOLIX into a Shelly, and let the battery level decide when to draw from the wall.
The SOLIX device is read-only. This tool never sends it a command. The Shelly plug is the only thing it ever switches.
Unofficial and unaffiliated with Anker or Allterco Robotics. SOLIX and Shelly are trademarks of their respective owners. Built on the community anker-solix-api library.
Quick start
You need a SOLIX device that connects to WiFi, a Shelly plug on the same network, and your Anker account login.
git clone https://github.com/YOURNAME/anker-shelly-bridge.git
cd anker-shelly-bridge
./start.sh
On Windows, double-click start.bat.
That is the whole install. start.sh builds a Python environment for this
project at ~/solix-automation/venv, installs everything into it, and hands
over to a guided setup with eight steps:
- Dependencies: installs whatever is missing
- Anker account: your login, stored locally with owner-only permissions
- Find your SOLIX device: connects and captures every field it reports
- Find your Shelly plug: scans the local network
- Check the plug: finds schedules and timers already on it that would conflict, and offers to remove them
- Notifications: optional push to your phone, with a QR code to scan
- Rules: pick a strategy and answer a few questions in plain language
- Test, then start: a live dry run against your hardware that switches nothing, then offers to install a background service
Nothing touches your hardware until step 8 asks. Ctrl-C is safe at any point.
Needs Python 3.12+ and git. If they are missing, start.sh tells you how to
install them for your platform.
Everything the project needs lives in its own environment, so installing or removing packages elsewhere on the machine cannot break your automation. The background service is pinned to that same interpreter.
What it does
Reads your SOLIX device
Connects to Anker's cloud MQTT broker, captures a live telemetry snapshot, and writes a device profile you can read. A portable power station typically reports around 44 fields: battery percentage, per-string solar input, AC and DC output, temperature, port states.
It also computes derived fields, so rules can use pv_total instead of adding
pv_1_power and pv_2_power by hand:
| Field | Meaning |
|---|---|
pv_total |
all solar strings added together, in watts |
pv_surplus |
solar minus load; positive means the surplus is charging the battery |
pv_covers_load |
true when solar alone is carrying everything |
usb_total |
combined USB output |
soc_headroom |
percentage points above the configured floor |
solixauto fields <device> # every usable field name
solixauto status <device> --watch # live values
Controls your Shelly plug
Local HTTP only, no cloud. Gen1 and Gen2+ including Gen4. Discovery uses mDNS plus a subnet scan across every private network it can see, so machines with several interfaces work.
solixauto switch <plug> on|off|status
Every command reads the state back from the device afterwards rather than trusting the HTTP response.
Runs your rules
A power profile is a YAML file linking one SOLIX device to one Shelly channel:
rules:
- name: top up from grid when low
when: battery_soc <= 35
for: 2m
then: target.on
- name: stop charging when full enough
when: battery_soc >= 85
for: 2m
then: target.off
when is an expression over any field the device reports. for is how long
the condition must hold before anything happens, so a passing cloud or a
momentary load spike cannot toggle the relay. priority breaks ties when rules
disagree.
Expressions run in a sandbox allowing only comparisons, and/or/not, and
arithmetic. Function calls and attribute access are rejected at parse time, so
a power profile cannot execute code.
Full rule reference with worked examples: docs/RULES.md. The same document is written into your data directory during setup.
Live monitor
solixauto monitor
Opens a dashboard at http://127.0.0.1:8765 showing every device you have
saved: battery level, solar in, grid in, load out, and each Shelly's switch
state and draw. It refreshes every five seconds and keeps a rolling chart.
Each device gets a power bus: solar and grid drawn from the left, load from the right, all to one scale. If the left outweighs the right, the battery is filling. Light and dark themes, remembered between visits.
Add --host 0.0.0.0 to reach it from a phone on the same network.
Anker readings come from a running automation rather than a second cloud connection, so start a service first. Two MQTT sessions on one Anker account invite rate limiting. Shelly devices are polled directly over local HTTP and always show live.
Safety
Controlling the power supply to a battery has a specific failure mode: turn charging off, let the battery run flat, and the device drops off the network, at which point nothing can turn charging back on. The design is built around preventing that.
The battery floor
safety:
battery_floor:
at_or_below: 20
release_at: 40
then: target.on
Checked before any rule. It bypasses the rate limits, outranks every
rule, and latches once tripped, so nothing can turn the plug off again
until the battery reaches release_at. Without the latch, a solar rule could
release it at 21% straight back into whatever drained it.
release_at must be meaningfully above at_or_below; the profile is rejected
otherwise.
Failing in the safe direction
source:
stale_after: 300s
on_stale: safe_state
safe_state: on
If telemetry stops arriving, rules are never evaluated against stale values.
hold leaves the plug alone, safe_state drives it to a known state, stop
exits. When the plug supplies power to the SOLIX device, safe_state: on means
losing sight of it fails toward charging.
Rules are also never evaluated against partial telemetry. If a field a rule needs has not arrived yet, nothing is evaluated at all, including the floor.
Rate limits
limits:
min_seconds_between_actions: 60
max_actions_per_hour: 20
A hard backstop independent of dwell times. If a rule somehow oscillates, this caps the damage. The battery floor deliberately ignores it.
Conflict detection
A Shelly can hold schedules, auto-on/auto-off timers, and webhooks on the device itself. They run whether or not this tool is running, and they silently override it.
solixauto conflicts <plug> # list them
solixauto conflicts <plug> --fix # remove them, asking before each change
Checked at discovery, at --test, and again at engine startup.
One limitation: schedules created as Shelly Cloud scenes live in the cloud
rather than on the device and cannot be seen over local HTTP. If behaviour
still looks wrong after conflicts is clean, check the app's scenes.
Testing before you trust it
solixauto run <profile> --test
Validates syntax, checks every field name against what the device actually reports, warns about missing deadbands and short dwell times, connects to both devices, and prints what each rule would do right now. Switches nothing.
solixauto run <profile> --test --offline
Same checks without connecting, evaluated against values captured at discovery.
solixauto run <profile> --test --simulate battery_soc=12
Force a rule to fire without waiting for real conditions. Prove your low-battery logic works at 2pm on a sunny day, and see the exact notification text it would send.
solixauto run <profile> --dry-run
The full engine loop with real telemetry, narrating every cycle, but no switch command and no notification.
Notifications
solixauto notify-setup
Interactive: pick a channel, it configures and tests it. For ntfy it generates a random private topic, shows QR codes for the app store and the topic, and sends a test push.
Supported: ntfy (free, no account), Pushover, email, Telegram, webhook (Slack/Discord/custom), and desktop (osascript on macOS, notify-send on Linux, PowerShell on Windows).
Credentials live in notifications.yaml with owner-only permissions, never in
power profiles, so profiles stay safe to share.
Battery floor alerts fire even when notifications are otherwise disabled, at elevated priority, bypassing the throttle. Muting routine chatter should not silence a battery emergency.
The Anker app and the Shelly app cannot receive custom push messages from an external program, so neither is an option.
Running it unattended
solixauto service <profile> # install and start
solixauto service <profile> --status
solixauto service <profile> --uninstall
macOS gets a LaunchAgent, Linux a systemd user unit, both starting at login and restarting on crash. Windows prints the Task Scheduler command.
If the machine sleeps, the automation sleeps with it and the safety floor cannot protect anything. Use an always-on machine, or disable sleep.
Layout
Code lives where you cloned it. Everything else lives in ~/solix-automation:
~/solix-automation/
├── venv/ Python environment created by start.sh
├── device-profiles/
│ ├── anker/ what your SOLIX device reports
│ └── shelly/ your plugs and their capabilities
├── power-profiles/ your rules, hand-editable
│ └── README.md full rule reference with worked examples
├── notifications.yaml channel credentials, mode 0600
├── state/runtime.json last known target state
└── logs/automation.log every action taken
Override the root with SOLIXAUTO_HOME.
Commands
solixauto setup guided setup, start here
solixauto doctor check this machine is set up correctly
solixauto discover-anker find and profile SOLIX devices
solixauto discover-shelly find and profile Shelly devices
solixauto devices list saved device profiles
solixauto name <device> "<name>" name a device, rename its file
solixauto fields <device> field names usable in rules
solixauto status <device> live telemetry
solixauto switch <plug> on|off manual control
solixauto conflicts <plug> automation set on the plug itself
solixauto monitor live dashboard in your browser
solixauto new-profile <name> scaffold a power profile
solixauto profiles list power profiles
solixauto validate <profile> check without connecting
solixauto run <profile> --test test against real devices, switch nothing
solixauto run <profile> run it
solixauto notify-setup configure push notifications
solixauto notify-test send a test
solixauto notify-qr show the ntfy topic QR again
solixauto service <profile> run in the background at login
Profiles resolve by friendly name, serial, ID, MAC, or IP, so all of these reach the same device:
solixauto switch "Garage Plug" on
solixauto switch 192.168.1.50 on
solixauto switch garage-plug on
Device support
Anything the upstream anker-solix-api library supports and that holds a cloud connection.
Models that pair with the Anker app over Bluetooth only, where you press a button on the unit to make it discoverable, publish nothing to the MQTT broker and cannot be used. The F2000 / PowerHouse 767 works this way. Discovery detects and skips them.
Shelly Gen1 and Gen2+ including Gen4, over local HTTP.
Developed against a SOLIX F3000 (A1782) and a Shelly Plug US Gen4. Other combinations should work; reports welcome.
Known limitations
- Field mappings come from a community reverse-engineering effort, not from
Anker. Fields ending in
?or starting withunknown_are unconfirmed, so do not build rules on them. - Verify any field against the Anker app before trusting it. The solar fields in particular should be watched in daylight and compared to the app before writing solar rules.
- Shelly Cloud scenes and app-set device names are invisible over local HTTP.
- Requires a machine that stays awake.
- Anker rate-limits logins; avoid restarting the service in a tight loop.
Troubleshooting
A device reports no telemetry. It is powered off, asleep, off WiFi, or
Bluetooth-only. Confirm it shows online in the Anker app, then retry that
device alone with --sn <serial>.
Shelly discovery finds nothing. Try --host 192.168.1.50 or
--network 192.168.1.0/24. Give your plugs DHCP reservations; access.host is
a fixed address in the profile.
The automation does nothing. Check solixauto service <profile> --status
and the log. Rules only act after their for: dwell has fully elapsed.
Something switches the plug unexpectedly. Run solixauto conflicts <plug>,
then check the Shelly app for cloud scenes.
Anything else. solixauto doctor reports platform, dependencies,
credentials, network, and notification status in one place.
Documentation
- docs/RULES.md: power profile format, every option, worked automation examples
- docs/TESTING.md: staged checklist for validating a new setup before trusting it with real hardware
- examples/: a solar failover profile and an annotated notifications config
Contributing
Bug reports welcome, particularly for device models not listed above. Include
the output of solixauto doctor and the relevant section of
logs/automation.log.
Never paste notifications.yaml, your .env, or an ntfy topic into an
issue. Device profiles contain serial numbers; redact them if that matters to
you.
License
MIT. See LICENSE.
