Skip to content

fix(inverter): don't overwrite a genuinely configured time entity with a dummy sensor - #4745

Open
chalfontchubby wants to merge 1 commit into
mainfrom
fix/inverter-respect-configured-time-entity
Open

fix(inverter): don't overwrite a genuinely configured time entity with a dummy sensor#4745
chalfontchubby wants to merge 1 commit into
mainfrom
fix/inverter-respect-configured-time-entity

Conversation

@chalfontchubby

Copy link
Copy Markdown
Collaborator

Fixes #4738

Problem

For any charge_time_format other than "HH:MM:SS", Predbat unconditionally creates and assigns a self-owned sensor.predbat_<type>_<id>_<field> for charge_start_time/charge_end_time/discharge_start_time/discharge_end_time, discarding whatever the user configured — real entity or not.

That's correct for the two existing cases:

  • "H M" format (GS/GS_fb00) never expects these fields to be user-configured at all — the real writes go via separate hour/minute entities (confirmed against templates/ginlong_solis.yaml, which never sets discharge_start_time).
  • "S" format with no time window (SF/SE/etc) ships a bare placeholder string, not a real entity (confirmed against templates/sofar.yaml, which sets it to the literal "00:00:00"). has_time_window turns out to be read nowhere else in the codebase, so this placeholder is genuinely inert either way — it only ever talks to itself.

The reporter's custom inverter definition (GROWATTSPH) combines a non-HH:MM:SS format ("S") with a real time window (has_time_window: true, has_discharge_enable_time: true) and a real, directly user-configured time.growatt_battery_grid_first_time_period_9_start entity — a combination the existing check never anticipated. For this combination the write path already does a plain write straight to whatever discharge_start_time resolves to; the dummy creation is the only thing standing in the way of it actually working.

Confirmed directly against the reporter's log and predbat_debug.yaml:

  • args_from_apps_yaml.discharge_start_time = their real time.* entity
  • args.discharge_start_time (what's actually used) = sensor.predbat_GROWATTSPH_0_discharge_start_time, created at startup
  • Their quoted "successfully wrote" log line is a real, successful write — to Predbat's own placeholder sensor, which is why the underlying Growatt integration never sees it and the real inverter entity never changes.

Fix

is_real_entity_configured() distinguishes "the user pointed this at a real HA entity" (has a domain, e.g. time.foo) from "this is unset or a bare placeholder" (no dot, e.g. "23:59:00") — preserving both existing legitimate cases exactly while respecting a genuinely configured one.

Testing

New regression test covering all three cases:

  • real entity configured → kept, not overwritten (fails without the fix, reproducing the exact sensor.predbat_<type>_<id>_discharge_start_time naming pattern from the report)
  • nothing configured → dummy still created (GS_fb00-style, unaffected)
  • bare placeholder string, no domain → still replaced by a dummy (SF-style, unaffected)

Full --quick suite green.

🤖 Generated with Claude Code

For any charge_time_format other than "HH:MM:SS", Predbat unconditionally
creates and assigns a self-owned sensor.predbat_<type>_<id>_<field> for
charge_start_time/charge_end_time/discharge_start_time/discharge_end_time,
discarding whatever the user configured - real entity or not.

That's correct for the two existing cases: "H M" format (GS/GS_fb00) never
expects these to be user-configured at all, since the real writes go via
separate hour/minute entities (confirmed against templates/ginlong_solis.yaml);
"S" format with no time window (SF/SE/etc) ships a bare placeholder string, not
a real entity (confirmed against templates/sofar.yaml) - has_time_window turns
out to be read nowhere else, so the placeholder is genuinely inert either way.

A custom inverter definition can combine a non-HH:MM:SS format with a real time
window and a real, directly user-configured entity - the combination the
existing check never anticipated. For those the write path already does a
plain write straight to whatever discharge_start_time resolves to; the dummy
creation is the only thing standing in the way of it working (#4738).

is_real_entity_configured() distinguishes "the user pointed this at a real HA
entity" (has a domain, e.g. time.foo) from "this is unset or a bare placeholder"
- preserving both existing cases exactly while respecting a genuinely configured
one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant