Repository navigation
feat(car): manual car SoC for cars with no SoC sensor, tracked from charger energy and reset on unplug - #5678
Conversation
…rgy and unplugging - A car with no car_charging_soc sensor uses the manual SoC without the switch being turned on; the switch now only forces manual SoC for a car that has a sensor. The manual SoC kWh entity is shown for every car, and for a car on its sensor it shows the sensor's reading. - With car_charging_energy set for the car, the manual SoC rises by the energy the charger's history shows since the entity last changed, instead of the plan's prediction. - With a plugged-in sensor, the manual SoC resets to zero once the car has been unplugged since the entity last changed. - Both work from the entity's last_changed in Home Assistant, so nothing is lost over a restart. - New optional car_charging_plugged for a pure plugged-in sensor, used for the reset in preference to car_charging_planned. Ohme publishes binary_sensor.predbat_ohme_plugged and wires it automatically, as its connected sensor goes off when a charge finishes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ble it with energy and plug sensors - Drop last_changed as the reference. Predbat's own database (no Home Assistant) stores it with no timezone, which the parser rejected, and stamps it on every write rather than on a change. Each manually tracked car now keeps a record (value last seen or written, time energy is counted from, last plug reading), saved through the storage component so a restart picks up where it left off. - A value that differs from the record is a user edit: kept, and counted on from then. - Unplug reset on a plugged-in -> unplugged transition of the plug sensor; unavailable is no reading. - A car with no SoC sensor is only tracked automatically when it has both car_charging_energy and a plug sensor entity. Without them the plan write-back would climb to the charge limit with nothing to reset it, and a car modelled as full loses its Octopus Intelligent slots and battery hold. - Gateway: only wire car_charging_soc when the charger reports the car's SoC (the estimate restarts at 0% each session), so hub cars go on the tracked path; re-run auto-config when the car's charger starts reporting a SoC. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tead of the storage component Each tracked car's record now lives in sensor.predbat_car_manual_soc_tracked[_N]: the tracked kWh as the state, with since (time energy is counted from) and plugged (last plug reading) as attributes. A restart reads it back from Home Assistant or Predbat's database, which load every entity with its attributes at startup. Published only when the SoC or plug reading changes. No storage dependency, no expiry, and the record is visible to users and support. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Debounce the unplug reset: the plugged-in sensor must read unplugged for CAR_MANUAL_SOC_UNPLUG_MINUTES (10) before the SoC goes to zero. A pending unplug is dropped if the car reads plugged in again, kept through an unavailable reading, and published as the tracking sensor's unplugged_since attribute so it survives a restart. - Gateway: publish binary_sensor.<pfx>_plugged, unavailable while the charger itself is offline rather than off, and wire it as car_charging_plugged. The connected sensor (car_charging_planned) is unchanged. The hub synthesises "Available" on an OCPP reconnect and marks the known charger Offline until BootNotification (predbat-gateway#504), which read as an unplug several times a day. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ap counted energy Brings in what #5671 covered and #5678 did not: - Clamp on read: the plan uses the manual SoC within zero and the battery size, and clamps again once Octopus Intelligent reports a (larger) vehicle battery size. The entity keeps what was typed. - car_charging_now reading not charging holds the plan's predicted write-back for a manual car without an energy sensor; unavailable/unknown is no reading. - Charger energy counted per cycle is capped at car_charging_rate x elapsed x 1.1 (+0.1 kWh), so a counter that drops to zero and comes back - which the history counts as a new session - or jumps cannot fill the car. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
springfall2008
left a comment
There was a problem hiding this comment.
Automated comment from the triage bot.
Reviewed at head f80579e (manual car SoC for cars with no SoC sensor: auto-enable needs both an energy and a plugged-in sensor, energy-based tracking with a rate cap, reset on unplug after 10 minutes, a published record sensor read back on restart, plus the gateway/Ohme car_charging_plugged wiring). 1 finding posted inline, 0 in the body.
Checked: the new tracking flow end to end (auto-enable gating in car_manual_soc_config, edit detection, the 60-minute late cap, the pending-unplug wait, publish-on-change with restart read-back) against the live-cycle call order (fetch_config_options → load_car_energy → fetch_sensor_data_cars → plan → update_car_manual_soc); scalar-vs-list car-arg semantics and the enable/enable_condition gating (the same num_cars pattern the switch items already use); the energy add's consistency with car_charging_loss's effectiveness semantics (multiplied by 1 - loss, as prediction.py/plan.py use it); the record dict is YAML-safe for the debug dump; compare.py's save=False re-runs publish nothing; the docs (apps-yaml.md, car-charging.md, customisation.md) match the code, including the #predbat-led-charging anchors; and the test module is registered in TEST_REGISTRY. gh pr checks was denied in this session, so CI status was not independently confirmed.
mgazza
left a comment
There was a problem hiding this comment.
Reviewed at f80579e. The new commits address what we raised earlier (no more last_changed, auto-tracking gated on energy + plug sensors, clamp on read, rate cap, car_charging_now hold). Four issues below, each reproduced with a scratch test reusing test_car_manual_soc.py fixtures. A fifth note is a reply on the triage bot's gateway.py:2279 thread.
…address review - Only car_charging_plugged resets the manual SoC on unplug, and auto-tracking a car with no SoC sensor needs it. car_charging_planned turns off when a charge finishes on many chargers, so it reset a still-plugged-in car. It still holds the plan's write-back. A car with no car_charging_plugged is reset after 12 hours idle instead (no edit, no charge counted or written, car_charging_now off). - The tracked value is no longer clamped to the configured battery size when energy is counted: Octopus's larger size is only known later in the cycle. The plan still reads it clamped. - The kWh entity is only mirrored for cars with a SoC sensor, so a car that drops out of tracking no longer has its value overwritten with 0. - Gateway: session energy reads unavailable while the charger is offline rather than 0, which the history counted as a new session. car_charging_soc is no longer wired, and sensor.<pfx>_soc holds the last SoC the charger reported. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…oud, and add it to the templates Since d2951cf a car with no SoC sensor is only tracked automatically when car_charging_plugged is set, so the three charger components that wired only car_charging_planned no longer got their cars tracked. - myenergi, Wallbox and GE Cloud EVC each publish a _plugged binary sensor (GE: _evc_car_plugged) and wire it as car_charging_plugged. It is on with a car plugged in, including after a charge finishes, and off only when the charger positively says no car: Zappi "EV Disconnected", Wallbox Ready or Locked with no car, GE available/idle. Anything else - offline, a fault, Wallbox Disconnected (also what an unreported status reads as), a status not known - is unavailable, so a dropout never resets the manual SoC. - Every template with a car section gets a commented car_charging_plugged example, and those that had no car_charging_now get the commented car_charging_now example and its response list. - components.md and car-charging.md list the new sensors and wiring. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ive HA history Three weeks of history from an OCPP Pulsar Plus confirm the mapping behind binary_sensor.<prefix>_plugged: 209 is no car, 210/164/181 are plugged in, 161 is a no-car blip, and status 0 "Disconnected" is the charger dropping off the cloud (seen mid-charge too), so it is rightly unavailable rather than unplugged. Replaying the history gave one reset per real unplug and no others. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Summary
Reworks manual car SoC (
car_charging_manual_soc) so it can run without being switched on for cars that can be tracked from what really happens. It follows what the charger actually delivered, resets when the car is unplugged, and survives a restart.car_charging_socentry is tracked ininput_number.predbat_car_charging_manual_soc_kwh[_N]when it has both acar_charging_energysensor and acar_charging_pluggedsensor.car_charging_plannedis not enough, because many chargers turn it off when a charge finishes. A fixed value such as"on"doesn't count as a plugged-in sensor. Without both, the car is planned from empty as before. Tracking it from the plan's prediction with nothing to reset it would climb to the charge limit and stay there, and a car modelled as full has its Octopus Intelligent slots ignored and no battery hold during them. Predbat never turns the switch on; the switch forces manual SoC for any car.num_cars, like the switch). For a car on its sensor, Predbat writes the sensor's reading into it each live cycle, so it is clear that editing it does nothing, and turning the switch on starts from the car's real level.car_charging_energyset for the car, the manual SoC rises by the energy the charger's history shows (lesscar_charging_loss) instead of the plan's predicted charge. Each cycle's count is capped atcar_charging_rate× elapsed time × 1.1 (+0.1 kWh): the history counts a counter that drops to zero and comes back as a new session, and the cap stops one glitch or jump from filling the car. The end-of-cycle predicted write-back skips these cars so nothing is counted twice.car_charging_nowreads not charging, or whencar_charging_plugged(elsecar_charging_planned) reads unplugged. Unavailable/unknown is no reading, and the prediction is written as before.car_charging_pluggedgoes from plugged in to unplugged and stays unplugged for 10 minutes (CAR_MANUAL_SOC_UNPLUG_MINUTES), so a charger that reads unplugged for a few minutes while it reconnects doesn't reset it. A pending unplug is dropped if the car reads plugged in again and kept through an unavailable reading. Unavailable/unknown is never an unplug, and a value entered while unplugged is kept.car_charging_plugged(in practice one forced by the switch) resets to 0 after 12 hours idle (CAR_MANUAL_SOC_IDLE_MINUTES): no value entered, no charge counted or written by the plan, andcar_charging_nownot reading charging.car_charging_plannednever resets it.car_charging_socsensor has its reading shown in the kWh entity, so a car that drops out of tracking keeps its value.car_charging_plugged(per car) for a sensor that only reports whether the cable is in, used for the reset and needed for auto-tracking. Ohme publishesbinary_sensor.predbat_ohme_pluggedand wires it underohme_automatic, becausebinary_sensor.predbat_ohme_connecteddeliberately goes off atFINISHED_CHARGE._pluggedbinary sensor (GE:_evc_car_plugged) and wire it ascar_charging_plugged. It is on with a car plugged in, including after a charge finishes, and off only when the charger positively says there is no car (Zappi "EV Disconnected", Wallbox Ready / Locked with no car, GE available/idle). Offline, faulted, Wallbox "Disconnected" and unknown statuses readunavailable, so a dropout never resets the car.car_charging_pluggedexample. Those without acar_charging_nowexample now have one, with its response list.gateway_evc_automatic).car_charging_socis no longer wired: a charger rarely knows the car's SoC, and only during a session. The car is tracked from session energy instead. If a user maps it tosensor.<pfx>_socby hand, that sensor holds the last SoC the charger reported rather than falling back to the session estimate.sensor.<pfx>_session_energyreadsunavailablewhile the charger is offline, not 0, so the energy history doesn't count the session twice.binary_sensor.<pfx>_plugged, wired ascar_charging_plugged: whether a car is plugged in, butunavailablewhile the charger itself is offline, where_connectedreads off. The hub also synthesisesAvailableon an OCPP reconnect (Predictive-Cloud-Ltd/predbat-gateway#504), which the unplug wait above absorbs.Design: a published tracking record
Each tracked car's state is published as
sensor.predbat_car_manual_soc_tracked[_N]:sinceattribute: the UTC time charger energy has been counted up topluggedattribute: the plugged-in sensor's last real reading (true,falseornull)activeattribute: when the car was last active, for the idle resetunplugged_sinceattribute: when a pending unplug was first read, until it has lasted 10 minutesEach live cycle compares the kWh entity with the record (edit or not), adds the energy since
since, and checks for a plugged-in → unplugged transition. After a restart the record is read back from the sensor. Home Assistant and Predbat's own database both load every entity with its attributes at startup.It is published only when the SoC or plug reading changes. When nothing was charged,
sinceis kept at most 60 minutes back (CAR_MANUAL_SOC_LATE_MINUTES), to allow for energy the history records late, and that move stays in memory.Entity
last_changedis deliberately not used. Without Home Assistant, Predbat's database stores it with no timezone, whichstr2timerejects, and updates it on every write rather than on a change.Tracking only runs for the live plan (
fetch_sensor_data_cars(save=True)), not compare.py's re-runs.Covers what #5671 does, apart from its fall-back to the plan when an energy counter has been unreadable for an hour (see below).
Things for review
car_charging_rate. A rate set below the real one under-counts, the safe direction. Energy clamped in one cycle is not carried to the next.entity$attributeworks for the plug reading. Only the transition is used.fetch_sensor_data_carsandupdate_car_manual_socHIGH (main plan-cycle path, one direct caller each); the new behaviour only acts on manually tracked cars. The gateway changes rated LOW.Test plan
car_manual_soctest module (49 cases):car_charging_pluggedprecedencecar_charging_nowoff holds / on and unavailable let the write-back throughcar_charging_nowhold) fails a testcar_charging_nowkeep it active, read back after a restart); no store clamp; mirror only with a SoC sensor. Each fix mutation-checked.test_gateway: nocar_charging_socwired whether or not the charger reports one, no reconfigure on SoC reporting;_socholds the last reported value;_session_energyunavailable while offline;_pluggedpublished andunavailablewhile the charger is offline;car_charging_pluggedwiredtest_myenergi/test_wallbox/test_ge_cloud:_pluggedstates for every plug/status case andcar_charging_pluggedwiring order (bbc0494, mutation-checked); all templates still parsetest_ohme:plugged_sensoracross every Ohme status; automatic config setscar_charging_plugged./run_all --quickpasses; pre-commit clean🤖 Generated with Claude Code