Describe the bug
The shared MQTT include drops the timeout key when its value is exactly 30s, which is the preset's own default. Templates that do not declare their own default therefore render no timeout at all, and nothing in the code supplies one afterwards. The timeout stays at zero, and zero is not treated as thirty seconds but as no expiry, so staleness detection is off.
The guard is in util/templates/includes/mqtt.tpl:9:
{{- if ne .timeout "30s" }}
timeout: {{ .timeout }}
{{- end }}
The parameter it guards is declared in util/templates/defaults.yaml with default: 30s and help text reading "Don't accept values older than this value".
Decoding does not repair it. In plugin/mqtt.go the config struct initialises only Scale: 1, leaving Timeout at its zero value, and cc.Timeout is passed to NewMqtt, which stores it unchecked. At runtime util/monitor.go takes the if m.timeout == 0 branch in GetFuncContext, which returns the cached value once one has been received and never returns api.ErrOutdated again.
Four of the five bundled templates using preset: mqtt inherit the default and are affected: charger/tinkerforge-warp, charger/tinkerforge-warp3, charger/tinkerforge-warp3-smart and meter/solaranzeige-mqtt. Rendering them with ordinary inputs produces zero timeout: lines. charger/onekey produces seven, because it declares its own default of 90s.
The consequence is that a value which stops arriving is reported indefinitely as though it were current, and evcc keeps deciding on it. Read plugins are affected; setters do not use the monitor.
I may be missing something. It is possible that client keepalive or a last will catches this elsewhere, that zero-as-no-expiry is intended and staleness is meant to be handled by the device implementation, or that the affected templates rely on it deliberately. I could not find any of those, but I do not know the codebase well.
Steps to reproduce
- Run an MQTT broker on localhost.
- Publish a single retained value and then stop, so the source goes quiet:
mosquitto_pub -t evcc-repro/pv_leistung -r -m 1234
- Run
evcc -c repro.yaml meter --repeat with the configuration below, which uses the solaranzeige template with usage: pv and inherits the preset timeout.
- Observe that the meter reports
Power: 1234W continuously for as long as it is left running, well past the 30s the parameter documents. In an 80 second run there were no outdated lines at all.
- Add
timeout: 45s to the same meter entry and repeat. The meter now reports Power: 1234W at first and switches to Power: outdated once the value ages past the limit. The same 80 second run produced 41 outdated lines.
A device-free way to see the cause is to render an affected template through Template.RenderResult(..., RenderModeInstance, ...) with only host, port, topic and usage set, and count the timeout: lines. There are none. Setting the parameter to 45s or 30000ms produces one per plugin, so the comparison is against the string rather than the duration.
Configuration details
meters:
- name: pv
type: template
template: solaranzeige
usage: pv
host: 127.0.0.1
port: 1883
topic: evcc-repro
Log details
There is no error output, and that is the symptom. The expected behaviour after
the source stops publishing is an outdated error for the device; instead the log
stays quiet and the stale value continues to be served.
Inherited default, 80 second run, source silent after one retained publish:
Power: 1234W
... unchanged throughout ...
Power: 1234W
outdated lines: 0
Same run with timeout: 45s declared on the template:
Power: 1234W 39ms
... becomes ...
Power: outdated 682ms
Power: outdated 764ms
outdated lines: 41
What type of operating system or environment does evcc run on?
Linux
External automation
Nightly build
Version
evcc version 0.314.0-72-g83d322b5e
Describe the bug
The shared MQTT include drops the
timeoutkey when its value is exactly30s, which is the preset's own default. Templates that do not declare their own default therefore render notimeoutat all, and nothing in the code supplies one afterwards. The timeout stays at zero, and zero is not treated as thirty seconds but as no expiry, so staleness detection is off.The guard is in
util/templates/includes/mqtt.tpl:9:The parameter it guards is declared in
util/templates/defaults.yamlwithdefault: 30sand help text reading "Don't accept values older than this value".Decoding does not repair it. In
plugin/mqtt.gothe config struct initialises onlyScale: 1, leavingTimeoutat its zero value, andcc.Timeoutis passed toNewMqtt, which stores it unchecked. At runtimeutil/monitor.gotakes theif m.timeout == 0branch inGetFuncContext, which returns the cached value once one has been received and never returnsapi.ErrOutdatedagain.Four of the five bundled templates using
preset: mqttinherit the default and are affected:charger/tinkerforge-warp,charger/tinkerforge-warp3,charger/tinkerforge-warp3-smartandmeter/solaranzeige-mqtt. Rendering them with ordinary inputs produces zerotimeout:lines.charger/onekeyproduces seven, because it declares its own default of 90s.The consequence is that a value which stops arriving is reported indefinitely as though it were current, and evcc keeps deciding on it. Read plugins are affected; setters do not use the monitor.
I may be missing something. It is possible that client keepalive or a last will catches this elsewhere, that zero-as-no-expiry is intended and staleness is meant to be handled by the device implementation, or that the affected templates rely on it deliberately. I could not find any of those, but I do not know the codebase well.
Steps to reproduce
mosquitto_pub -t evcc-repro/pv_leistung -r -m 1234evcc -c repro.yaml meter --repeatwith the configuration below, which uses thesolaranzeigetemplate withusage: pvand inherits the preset timeout.Power: 1234Wcontinuously for as long as it is left running, well past the 30s the parameter documents. In an 80 second run there were nooutdatedlines at all.timeout: 45sto the same meter entry and repeat. The meter now reportsPower: 1234Wat first and switches toPower: outdatedonce the value ages past the limit. The same 80 second run produced 41outdatedlines.A device-free way to see the cause is to render an affected template through
Template.RenderResult(..., RenderModeInstance, ...)with onlyhost,port,topicandusageset, and count thetimeout:lines. There are none. Setting the parameter to45sor30000msproduces one per plugin, so the comparison is against the string rather than the duration.Configuration details
Log details
What type of operating system or environment does evcc run on?
Linux
External automation
Nightly build
Version
evcc version 0.314.0-72-g83d322b5e