6.6 KiB
Power profiles
A power profile is a plain YAML file linking one Anker SOLIX device to one Shelly switch channel. You can edit it in any text editor.
The Anker device is read-only. The engine reads its telemetry and never sends it a command. The only thing that gets switched is the Shelly.
Layout
device-profiles/anker/ generated, one file per Anker device
device-profiles/shelly/ generated, one file per Shelly device
power-profiles/ yours, hand-edited
state/runtime.json last known target state per profile
logs/automation.log action history
Workflow
solixauto discover-anker
solixauto discover-shelly
solixauto new-profile solar-failover --template solar
solixauto fields A1782-<serial>
solixauto run solar-failover --test
solixauto run solar-failover
Anatomy of a rule
- name: grid assist when solar drops
when: pv_total < 150
for: 90s
then: target.on
priority: 0
when is an expression over any field listed in the Anker device profile,
under readable or derived. solixauto fields <device> prints them with
current sample values.
for is the dwell time. The condition must stay true for this long before
anything happens. Without it, a cloud passing over your array would cycle the
relay repeatedly.
then is target.on, target.off, or none.
priority breaks ties. If two rules are ready at the same moment and disagree,
the higher number wins. A low-battery override should outrank normal solar
logic.
Use a deadband
This is the single most important thing to get right:
# WRONG - will chatter around 200W
- when: pv_total < 200
then: target.on
- when: pv_total > 200
then: target.off
# RIGHT - 150W of deadband between the two
- when: pv_total < 150
then: target.on
- when: pv_total > 300
then: target.off
--test warns when it detects the same threshold used for both directions.
Useful automations
Solar failover. Solar covers the load most of the day; pull from the wall only when production drops.
- name: grid assist when solar drops
when: pv_total < 150
for: 90s
then: target.on
- name: release grid when solar recovers
when: pv_total > 300
for: 2m
then: target.off
Low-battery charge. No solar involved.
- name: charge when battery is low
when: battery_soc <= 15
for: 30s
then: target.on
- name: stop charging when battery is healthy
when: battery_soc >= 60
for: 2m
then: target.off
Overnight top-up. Combine conditions.
- name: cheap overnight charging
when: battery_soc < 80 and pv_total < 20
for: 5m
then: target.on
Load shedding. Cut a non-essential circuit when the battery is draining.
- name: shed load
when: battery_soc < 30 and ac_input_power == 0
for: 2m
then: target.off
Thermal guard. Higher priority so it outranks everything else.
- name: stop charging when hot
when: temperature >= 45
for: 60s
then: target.off
priority: 200
Safety settings
stale_after and on_stale control what happens when telemetry stops
arriving. Rules are never evaluated against stale data.
holdkeeps the relay wherever it is. Default and safest.safe_statedrives the relay to thesafe_state:value.stopexits the engine.
limits is a hard backstop independent of dwell times:
limits:
min_seconds_between_actions: 60
max_actions_per_hour: 20
If a rule somehow oscillates, this caps the damage.
Notifications
Turn them on in the profile:
notifications:
enabled: true
channels: [ntfy]
title: "{profile}"
template: >-
{source_name} battery {battery_soc}%, solar {pv_total}W.
{target_name} turned {action}.
throttle: 5m
on:
- action
Then per rule, notify: on or notify: off to include or exclude it. Omit it
to inherit. A rule can also carry its own wording:
- name: emergency charge on low battery
when: battery_soc <= 15
for: 30s
then: target.on
priority: 100
notify:
template: >-
{source_name} has {battery_soc}% battery remaining.
{target_name} turned on AC power.
priority: high
Template fields
Any field from the device profile works, plus:
{profile} power profile name
{rule} rule that fired
{condition} the rule's when expression
{action} ON or OFF
{action_word} on or off
{source_name} Anker device name, falling back to model
{source_model} e.g. SOLIX F3000
{source_serial}
{target_name} Shelly device name, falling back to model
{target_model}
{target_host}
{time}
--test checks every field name in your templates against the device profile,
so a typo is caught before it ships a message reading battery ?%.
Channels
Credentials live in ../notifications.yaml, which is created with owner-only
permissions. Power profiles stay free of secrets.
- ntfy - recommended. Free, no account. Install the app, pick an unguessable topic name, subscribe. Anyone who knows the topic can read your alerts, so make it long.
- pushover - $5 once per platform. Priority 2 alerts repeat until you acknowledge them.
- email - SMTP. Gmail requires an App Password.
- telegram - free bot.
- webhook - Slack, Discord, or generic JSON.
- desktop - local notification on the machine running the engine.
Uses osascript on macOS, notify-send on Linux, PowerShell on Windows.
Run
solixauto doctorto see which backend was detected.
The Anker app and the Shelly app cannot receive custom push messages from an external program, so neither is an option here.
Test a channel:
solixauto notify-test
solixauto notify-test --channel ntfy
Throttling
throttle is per rule. An identical repeated message inside the window is
dropped. This is separate from the switching rate limits, so a stuck condition
cannot flood your phone even if the relay is behaving.
Other events
on:
- action
- stale
stale fires once when telemetry stops arriving and is worth enabling if you
depend on the automation.
Testing
solixauto run <profile> --test
Validates syntax, checks that every field you reference actually exists on the device, warns about missing deadbands and short dwell times, connects to both devices, and prints what each rule would do right now. Nothing is switched.
solixauto run <profile> --test --offline
Same checks without connecting. Rules are evaluated against the sample values captured during discovery.