Files
goodwe-addon/goodwe_controller/translations/en.yaml
T
Claude dev agent d69fd8fbfd
checks / unit (pull_request) Successful in 10m59s
infra-pin / fresh (pull_request) Successful in 1s
OPT-11 P1: per-battery mode limits, on/off, solar order
A battery's mode is a soft unit limit (source `mode`, P_MODE 35) kept
apart from the hard limits in pre_resolve(). The allocator applies it
only while the site target is decided at or below 35 (new
Resolution.deciding_priority), so the peak floor releases it and a mode
`hi` never meets the site `lo`. At N=1 resolve() applies the same rule
to the site target after stage 2. A mode range without 0 W is dropped
as a software fault, so REFUSE_SIGN stays unreachable from a mode.

compute() gets the loop bound (sum over live units of hard limits with
modes applied), so the loop never asks for watts no battery may give
and the saturation freeze never parks on them. While charging, Leftover
solar weighs 0 so Solar first fills first. Store keys pack_use and
pack_mode per battery, set by two new /action values; Off stops and
skips maintenance with a log line. Minimal Batteries cards (EN/NL).

Off by default (battery_modes_enabled); off, commands are byte-identical
to a5dddad at N=1 and N=2 (test_modes.py part 1).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 18:51:29 +02:00

304 lines
19 KiB
YAML

# Home Assistant add-on translations - ENGLISH (UX-01)
#
# ⚠️ WHAT THIS FILE IS FOR, AND WHAT HOME ASSISTANT ACTUALLY DOES WITH IT.
# Without a `translations/` directory the Configuration tab renders the raw
# option KEYS - `unit_sign_disagree_w`, `saturation_cycles`, `integrator_max_w`.
# The frontend keeps a field when `e.name in options || e.required`, and all 72
# keys are in `options:` (59 of them are `required` as well), so ALL SEVENTY-TWO are
# rendered, always, in `schema:` order, with no description. This file is what
# turns that into sentences.
#
# HOW IT IS READ, measured on the Supervisor image this site runs
# (ghcr.io/home-assistant/amd64-hassio-supervisor:2026.08.0):
#
# * `supervisor/store/data.py:_read_app_translations()` reads every
# `.yaml`/`.yml`/`.json` in this directory, keyed by FILE STEM (so this file
# is the `en` translation), and validates each through
# `SCHEMA_APP_TRANSLATIONS`: `configuration: {str: {name (required),
# description, fields}}` plus `network: {str: str}`. ⚠️ A FILE THAT FAILS
# THAT SCHEMA IS DROPPED WITH A LOG WARNING AND NOTHING ELSE - the add-on
# goes on rendering raw keys and nobody is told twice. tools/
# supervisor_shape_probe.py section F is what says it loaded.
# * The frontend resolves each label as
# `translations[<user's language>]?...name || translations["en"]?...name ||
# schema.name`, and the helper text the same way with `description`. ⚠️ THE
# FALLBACK IS PER KEY, so a key missing from nl.yaml degrades to this file,
# while a key present but wrong does not degrade at all.
#
# ⚠️ ORDER IS NOT SET HERE. `App.schema_ui` is `UiOptions(...)(data["schema"])`,
# so the Configuration tab renders in the order of config.yaml's `schema:`
# block. The order below only mirrors it so the two files read alike.
#
# ⚠️ ADD-ON OPTIONS ARE A FLAT LIST. There is no section-header primitive, no
# per-option "advanced" flag, no units on the input and no sliders, so grouping
# is approximated by ORDER plus the `Group · ` prefix in each label. Do not
# promise the operator visual sections; there are none.
#
# Names and descriptions follow STYLE.md (UX-08): a short label with its unit,
# and one or two plain sentences. A measured or derived value says "leave it
# unchanged", and where it came from is in DOCS.md under "For installers:
# where the numbers come from".
configuration:
setpoint_entity:
name: "Hardware · Inverter setpoint entity"
description: "The ESPHome number entity the add-on writes the inverter's power to. Leave it empty when you fill in the inverter list."
soc_entity:
name: "Hardware · Battery level entity (%)"
description: "The sensor with the battery level in percent. Use the control board's own reading."
batt_entity:
name: "Hardware · Battery power entity (W)"
description: "The sensor with this inverter's battery power. Positive must mean the battery is giving power."
batt_invert:
name: "Hardware · Battery power sign is reversed"
description: "Switch this on only if your battery power sensor shows giving power as a negative number."
inverters:
name: "Hardware · Inverter list"
description: "Leave it empty if you have one inverter. With two or more, add one entry per inverter, which replaces the inverter settings above."
fields:
name:
name: "Name"
description: "A short, unique name for this inverter, for example inverter-a."
setpoint_entity:
name: "Setpoint entity"
description: "This inverter's own ESPHome setpoint entity. Two entries may not use the same one."
max_w:
name: "Inverter rating (W)"
description: "The most power the add-on may ask of this inverter. The site total is the sum of the list."
bms_soc_floor:
name: "Battery cut-out level (%)"
description: "The level at which this battery's own management system stops giving power. Read it off the battery that is installed."
soc_entity:
name: "Battery level entity (%) (optional)"
description: "This battery's own level sensor. Leave it empty if this inverter has none."
batt_entity:
name: "Battery power entity (W) (optional)"
description: "This inverter's own battery power sensor. Leave it empty if it has none."
batt_invert:
name: "Battery power sign is reversed (optional)"
description: "Switch this on if this inverter's sensor shows giving power as a negative number."
capacity_kwh:
name: "Usable battery capacity (kWh) (optional)"
description: "This battery's usable capacity from its datasheet. Fill it in on every inverter or on none. The add-on then shares the work by the energy each battery has left."
estop_fitted:
name: "Hardware · RS485 emergency stop is fitted"
description: "Switch this on if the emergency stop is installed. It only changes a warning on the status page."
battery_modes_enabled:
name: "Hardware · Battery modes"
description: "Give each battery an on and off switch and a mode, set on the Batteries screen."
pv_entity:
name: "Hardware · Solar production entity (W)"
description: "Optional. Your solar inverter's power sensor, positive while producing. The add-on records it and does not use it for control."
pv_entities:
name: "Hardware · Solar production entities, per inverter (W)"
description: "Optional. Use one row per solar inverter instead of “Solar production entity” when you have several. If one stops reporting, no total is recorded."
pv_invert_entities:
name: "Hardware · Reversed-sign solar entities"
description: "Optional. Add each solar entity above that reads negative while producing, spelled the same way. Its sign is flipped before adding up."
meter_source:
name: "Meter · Where the meter reading comes from"
description: "How the add-on reads your meter. 'off' uses “Grid power entity” below, and only homewizard_local can spot a stuck meter."
meter_entity:
name: "Meter · Grid power entity (W)"
description: "Used when the meter source is 'off'. Positive must mean drawing from the grid."
meter_invert:
name: "Meter · Grid power sign is reversed"
description: "Switch this on if your grid sensor shows drawing from the grid as a negative number."
meter_phases:
name: "Meter · Number of phases (1 or 3)"
description: "Must match your grid connection. Meter readings with a different number of phases are rejected."
p1_host:
name: "Meter · HomeWizard P1 address"
description: "For homewizard_local only. The meter's address on your network, as host or host:port."
meter_poll_s:
name: "Meter · Time between meter reads (s)"
description: "For homewizard_local only. The meter updates every 5 s, so lower gains nothing. Keep it well below “Reading too old after”."
meter_max_age_s:
name: "Meter · Reading too old after (s)"
description: "After this a meter reading counts as missing. The “Missing inputs time limit” starts only after this one runs out."
meter_mqtt_topic:
name: "Meter · MQTT topic"
description: "For mqtt_p1 only. The topic your meter readings are published on."
p1_import_entity:
name: "Meter · Grid import entity (W)"
description: "The sensor for power drawn from the grid, never negative. The add-on works out the net value itself."
p1_export_entity:
name: "Meter · Grid export entity (W)"
description: "The sensor for power fed into the grid, never negative. It pairs with “Grid import entity”."
p1_net_entity:
name: "Meter · Signed net power entity (W)"
description: "For ha_signed only. One sensor that is positive when drawing from the grid and negative when feeding in."
p1_phase_import_entities:
name: "Meter · Import entities per phase (L1-L3)"
description: "Optional, in L1, L2, L3 order. Needed for the peak limit on a three-phase connection, one entry per phase."
p1_phase_export_entities:
name: "Meter · Export entities per phase (L1-L3)"
description: "Optional, in L1, L2, L3 order, next to the import entities per phase. One entry per phase."
p1_phase_net_entities:
name: "Meter · Net entities per phase (L1-L3)"
description: "For ha_signed only. Optional, in L1, L2, L3 order, signed like “Signed net power entity”, one entry per phase."
max_w:
name: "Limits · Inverter rating (W)"
description: "Your own number: the most power the add-on may ever ask of the inverter. Start low and raise it after commissioning."
bms_soc_floor:
name: "Limits · Battery cut-out level (%)"
description: "The level where the battery's own management system stops giving power. Read it off your battery rather than copying a figure."
peak_cap_w:
name: "Limits · Peak limit (W)"
description: "Your own number: the 15-minute average power the add-on keeps the grid under. Take it from your grid contract."
peak_forecast_entity:
name: "Limits · 15-minute demand forecast entity (W)"
description: "Optional. A forecast of this 15-minute period's average power. Leave it empty and there is no peak limit."
peak_soc_floor:
name: "Limits · Peak reserve (%)"
description: "Peak limiting will not draw the battery below this level, but ordinary use can. Derived value, leave it unchanged."
price_provider:
name: "Tariff · Dynamic price source"
description: "Where prices come from. Choosing a source starts the price fetch, even with trading off."
price_markup_eur_kwh:
name: "Tariff · Supplier markup (EUR/kWh)"
description: "Your supplier's margin. It is added only when the price source does not already include it."
price_taxes_eur_kwh:
name: "Tariff · Distribution, levies and VAT (EUR/kWh)"
description: "Grid costs, levies and VAT per kWh, preset for Flanders. Check it against your own grid operator's rates."
export_price_eur_kwh:
name: "Tariff · Injection price (EUR/kWh)"
description: "What you are paid per kWh fed into the grid, all in. It may be negative."
static_profile:
name: "Tariff · Fixed or day/night tariff"
description: "Your fixed or day/night contract on one line, prices all in, as the documentation shows. Fill it in even on a dynamic contract, as the fallback."
price_taxes_profile:
name: "Tariff · Day/night levies and VAT"
description: "Leave it empty unless your grid operator charges a different day and night rate, as in Wallonia and Brussels. Set this or the flat figure above, not both."
price_formula:
name: "Tariff · Your own tariff formula"
description: "For custom_formula only. Type the numbers from your supplier's tariff card on one line, as the documentation shows. The add-on checks them against the card."
price_export_formula:
name: "Tariff · Your own injection formula"
description: "Optional. The injection formula from your tariff card, in the same shape as your own tariff formula. Leave it empty to use the flat injection price."
price_csv_path:
name: "Tariff · Price CSV file"
description: "For manual_csv only. Where in /share the add-on reads your price file."
price_feed_url:
name: "Tariff · Price feed URL override"
description: "Leave it empty to use the source's own feed. An override is sent exactly as typed and often returns only one day."
price_horizon_h:
name: "Tariff · Price look-ahead (hours)"
description: "How many hours of prices to ask the source for."
price_timeout_s:
name: "Tariff · Price request timeout (s)"
description: "How long one request to the price source may take before it is given up."
price_now_entity:
name: "Tariff · Current price entity (older)"
description: "The older way to get a price, used only to charge in a cheap window during maintenance. Do not combine it with a dynamic price source."
price_avg_entity:
name: "Tariff · Average price entity (older)"
description: "The average the current price is compared with. Use it only with the current price entity, not with a dynamic price source."
arbitrage_enabled:
name: "Tariff · Trade on the price spread"
description: "Charge in the cheapest hour and draw the battery down in the dearest, when the spread covers the cost. Turning it off does not turn the price feed off."
cycle_cost_threshold_eur:
name: "Tariff · Battery cycle cost (EUR/kWh)"
description: "What one full battery cycle costs per kWh. A smaller price spread is not traded, and the value is a product choice, not a measured one."
arbitrage_soc_floor:
name: "Tariff · Trading keeps at least (%)"
description: "Price trading will not draw the battery below this level. It is separate from the maintenance settings."
arbitrage_soc_ceiling:
name: "Tariff · Trading charges at most (%)"
description: "Price trading will not charge the battery above this level."
maintenance_enabled:
name: "Maintenance · Run the monthly battery cycle"
description: "Once a month, empty the battery deeply, charge it full and hold it there, so its level reading stays accurate. Off by default."
maintenance_interval_days:
name: "Maintenance · Minimum days between cycles"
description: "The shortest gap between two maintenance cycles."
maintenance_start_hour:
name: "Maintenance · Start hour"
description: "The local hour, 0 to 23, at which a due cycle starts."
maintenance_discharge_w:
name: "Maintenance · Draw-down rate (W)"
description: "How fast the cycle empties the battery. Above “Inverter rating” it is cut back to the rating, and the cycle just takes longer."
maintenance_charge_w:
name: "Maintenance · Recharge rate (W)"
description: "How fast the cycle recharges the battery. It must be at or below “Inverter rating”, or the add-on will not start."
maintenance_soc_floor:
name: "Maintenance · Draw down to (%)"
description: "The level the monthly cycle empties the battery to. It is not the “Peak reserve”, and the two differ on purpose."
maintenance_soc_target:
name: "Maintenance · Recharge up to (%)"
description: "The level the recharge aims for before the hold starts."
maintenance_hold_min:
name: "Maintenance · Hold at full for (minutes)"
description: "How long the battery stays full so its cells can balance."
sign_disagree_w:
name: "Safety · Wrong-direction threshold (W)"
description: "Part of the safety stop for an inverter pushing the wrong way. It counts only when command and meter reading both exceed this. Measured value, leave it unchanged."
sign_disagree_s:
name: "Safety · Wrong-direction time (s)"
description: "How long a wrong direction may last before a safety stop sets every inverter to 0 W. Measured value, leave it unchanged."
unit_sign_disagree_w:
name: "Safety · Per-inverter wrong-direction threshold (W)"
description: "For two or more inverters: the wrong-direction threshold for each inverter. Derived value, leave it unchanged."
unit_sign_disagree_s:
name: "Safety · Per-inverter wrong-direction time (s)"
description: "For two or more inverters: how long one inverter may push the wrong way before it is set to 0 W. Derived value, leave it unchanged."
site_sign_disagree_w:
name: "Safety · Site wrong-direction threshold (W)"
description: "For two or more inverters: the wrong-direction threshold for the whole site. Measured value, leave it unchanged."
site_sign_disagree_s:
name: "Safety · Site wrong-direction time (s)"
description: "For two or more inverters: how long the whole site may push the wrong way before a safety stop. Derived value, leave it unchanged."
meter_freeze_s:
name: "Safety · Stuck meter alarm after (s)"
description: "An alarm when the meter repeats one reading this long while the command is at full power, with homewizard_local only. Measured value, leave it unchanged."
circulation_w:
name: "Safety · Opposing inverters threshold (W)"
description: "For two or more inverters: how much power one may push against another before it counts. Measured value, leave it unchanged."
circulation_hold_s:
name: "Safety · Opposing inverters time (s)"
description: "For two or more inverters: how long they may push against each other before a safety stop. Derived value, leave it unchanged."
delivery_hold_s:
name: "Safety · Not delivering alarm after (s)"
description: "For two or more inverters: how long one may fall short of its share before an alarm, which changes nothing. Derived value, leave it unchanged."
shadow_mode:
name: "Safety · Shadow mode"
description: "The add-on makes every decision but keeps every inverter at 0 W. 'auto' does this while control is stopped, 'on' always, 'off' never."
auto_start:
name: "Advanced · Start control on boot"
description: "Off by default. Leave it off until the site is commissioned, so someone presses Start after each restart."
gain:
name: "Advanced · Control gain"
description: "How much of the remaining error control corrects each cycle. Measured on real hardware, do not raise it."
slew_w:
name: "Advanced · Largest change per cycle (W)"
description: "How far the command may move in one cycle. 0 follows “Inverter rating” and is recommended."
deadband_w:
name: "Advanced · Ignore errors below (W)"
description: "Errors smaller than this are ignored. Keep it small, since any resting point inside it holds."
target_grid_w:
name: "Advanced · Meter resting point (W)"
description: "What the meter should show when nothing happens. A slight export avoids paying for tiny imports all day."
step_w:
name: "Advanced · Command rounding (W)"
description: "The command is rounded to a multiple of this, so noise does not change it every cycle."
saturation_w:
name: "Advanced · At-limit shortfall (W)"
description: "When an inverter delivers this much less than asked, it counts as at its limit. The per-inverter wrong-direction threshold is half of this."
saturation_cycles:
name: "Advanced · Cycles at limit before freezing"
description: "How many cycles in a row at the limit before control stops building up error. Do not set it to 1."
integrator_max_w:
name: "Advanced · Catch-up limit (W)"
description: "Limits how hard control keeps pushing while the battery falls behind. 0 means “Inverter rating”. Do not raise it above the rating."
heartbeat_s:
name: "Advanced · Command refresh interval (s)"
description: "How often the command is sent again even when unchanged. Keep it well under the control board's own 30 s limit."
stale_input_s:
name: "Advanced · Missing inputs time limit (s)"
description: "How long inputs may be missing before every inverter is set to 0 W. With a P1 meter it starts after “Reading too old after”."
cycle_floor_s:
name: "Advanced · Longest time between cycles (s)"
description: "A deadline between control cycles that a healthy meter never reaches. Raise it only for a meter slower than about 10 s."
log_level:
name: "Advanced · Log level"
description: "How much detail the add-on writes to its log. 'debug' and 'trace' are for finding a problem."