|
9 | 9 | from datetime import datetime, time as dtime, timedelta |
10 | 10 |
|
11 | 11 | from homeassistant.config_entries import ConfigEntry |
12 | | -from homeassistant.core import HomeAssistant |
| 12 | +from homeassistant.core import Event, HomeAssistant, callback |
| 13 | +from homeassistant.helpers.event import async_track_state_change_event |
13 | 14 | from homeassistant.helpers.update_coordinator import DataUpdateCoordinator |
14 | 15 | from homeassistant.util import dt as dt_util |
15 | 16 |
|
@@ -166,6 +167,24 @@ def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None: |
166 | 167 | self.car_plan: P.Plan | None = None |
167 | 168 | self.status: dict = {} |
168 | 169 |
|
| 170 | + def async_watch_car(self): |
| 171 | + """Plug-in and charge start run a cycle right away instead of at the |
| 172 | + next full minute: a car that starts charging by itself is stopped |
| 173 | + within seconds (the home battery pays for every minute).""" |
| 174 | + c = self.car_cfg or {} |
| 175 | + ents = [e for e in (c.get(CONF_CAR_PLUGGED_SENSOR), c.get(CONF_CAR_PLUGGED_FALLBACK), |
| 176 | + c.get(CONF_CAR_CHARGING_SENSOR)) if e] |
| 177 | + if not ents: |
| 178 | + return None |
| 179 | + |
| 180 | + @callback |
| 181 | + def _changed(event: Event) -> None: |
| 182 | + old, new = event.data.get("old_state"), event.data.get("new_state") |
| 183 | + if old is not None and new is not None and old.state != new.state: |
| 184 | + self.hass.async_create_task(self.async_request_refresh()) |
| 185 | + |
| 186 | + return async_track_state_change_event(self.hass, ents, _changed) |
| 187 | + |
169 | 188 | async def async_setup(self) -> None: |
170 | 189 | await self.store.async_load() |
171 | 190 | await self.calibrator.async_load() |
@@ -554,7 +573,13 @@ def inputs(pv_kw: float, base_kw: float, car_fixed: float | None) -> P.Inputs: |
554 | 573 | pv5 = statistics.median(s[1] for s in win5) |
555 | 574 | base5 = statistics.median(s[2] for s in win5) |
556 | 575 | car5 = statistics.median(s[3] for s in win5) |
557 | | - self.device_plan = P.make_plan(inputs(pv5, base5, car5)) |
| 576 | + car_fixed = car5 |
| 577 | + cp = self.car_plan |
| 578 | + if cp is not None and not cp.car_first and not cp.car_grid and cp.car_reason != "pflicht_minimum": |
| 579 | + # the top-up ranks below the devices: they may claim it, the car |
| 580 | + # follows at its next decision (only the obligation stays fixed) |
| 581 | + car_fixed = min(car5, cp.car_must_kw) |
| 582 | + self.device_plan = P.make_plan(inputs(pv5, base5, car_fixed)) |
558 | 583 | await self._apply_devices(now, dev_on) |
559 | 584 |
|
560 | 585 | self.status = self._build_status(now, pv, load, base, soc, export, car_in, car_kw, charging, sun, |
|
0 commit comments