Skip to content

Commit

Permalink
Merge pull request #1251 from LKuemmel/soc_pro
Browse files Browse the repository at this point in the history
Fix SoC openWB pro
  • Loading branch information
LKuemmel committed Nov 16, 2023
2 parents 81d26c9 + 710413a commit d44cb3e
Show file tree
Hide file tree
Showing 8 changed files with 471 additions and 46 deletions.
6 changes: 4 additions & 2 deletions packages/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@
from unittest.mock import MagicMock, Mock
import pytest

from helpermodules import pub
from helpermodules import pub, timecheck


@pytest.fixture(autouse=True)
def mock_today(monkeypatch) -> None:
datetime_mock = MagicMock(wraps=datetime.datetime)
# Montag 16.05.2022, 8:40:52 "05/16/2022, 08:40:52"
# Montag 16.05.2022, 8:40:52 "05/16/2022, 08:40:52" Unix: 1652683252
datetime_mock.today.return_value = datetime.datetime(2022, 5, 16, 8, 40, 52)
monkeypatch.setattr(datetime, "datetime", datetime_mock)
mock_today_timestamp = Mock(return_value=1652683252)
monkeypatch.setattr(timecheck, "create_timestamp_unix", mock_today_timestamp)


@pytest.fixture(autouse=True)
Expand Down
7 changes: 6 additions & 1 deletion packages/helpermodules/subdata.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,6 @@ def process_vehicle_topic(self, client: mqtt.Client, var: Dict[str, ev.Ev], msg:
if isinstance(var["ev"+index].soc_module.vehicle_config, ManualSoc):
if (calculated_soc_state.manual_soc and calculated_soc_state.manual_soc !=
var["ev"+index].soc_module.calculated_soc_state.manual_soc):
calculated_soc_state.request_start_soc = True
Pub().pub(f"openWB/vehicle/{index}/get/force_soc_update", True)
var["ev"+index].soc_module.calculated_soc_state = calculated_soc_state
elif re.search("/vehicle/[0-9]+/soc_module/config$", msg.topic) is not None:
Expand Down Expand Up @@ -417,6 +416,12 @@ def process_chargepoint_topic(self, var: Dict[str, chargepoint.Chargepoint], msg
if re.search("/chargepoint/[0-9]+/get/connected_vehicle/", msg.topic) is not None:
self.set_json_payload_class(var["cp"+index].chargepoint.data.get.connected_vehicle, msg)
elif re.search("/chargepoint/[0-9]+/get/", msg.topic) is not None:
if (re.search("/chargepoint/[0-9]+/get/soc$", msg.topic) is not None and
decode_payload(msg.payload) != var["cp"+index].chargepoint.data.get.soc):
# Wenn das Auto noch nicht zugeordnet ist, wird der SoC nach der Zuordnung aktualisiert
if var["cp"+index].chargepoint.data.set.charging_ev > -1:
Pub().pub(f'openWB/set/vehicle/{var["cp"+index].chargepoint.data.set.charging_ev}'
'/get/force_soc_update', True)
self.set_json_payload_class(var["cp"+index].chargepoint.data.get, msg)
elif re.search("/chargepoint/[0-9]+/config$", msg.topic) is not None:
self.process_chargepoint_config_topic(var, msg)
Expand Down
2 changes: 1 addition & 1 deletion packages/helpermodules/timecheck.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ def create_timestamp_unix() -> int:
""" Unix Zeitstempel
"""
try:
return int(datetime.datetime.now().timestamp())
return int(datetime.datetime.today().timestamp())
except Exception:
raise

Expand Down
12 changes: 11 additions & 1 deletion packages/helpermodules/update_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@


class UpdateConfig:
DATASTORE_VERSION = 28
DATASTORE_VERSION = 29
valid_topic = [
"^openWB/bat/config/configured$",
"^openWB/bat/set/charging_power_left$",
Expand Down Expand Up @@ -1037,3 +1037,13 @@ def upgrade(topic: str, payload) -> None:
Pub().pub(topic.replace("openWB/", "openWB/set/"), configuration_payload)
self._loop_all_received_topics(upgrade)
Pub().pub("openWB/system/datastore_version", 28)

def upgrade_datastore_28(self) -> None:
def upgrade(topic: str, payload) -> None:
if re.search("openWB/vehicle/[0-9]+/soc_module/calculated_soc_state", topic) is not None:
payload = decode_payload(payload)
if payload.get("request_start_soc"):
payload.pop("request_start_soc")
Pub().pub(topic.replace("openWB/", "openWB/set/"), payload)
self._loop_all_received_topics(upgrade)
Pub().pub("openWB/system/datastore_version", 29)
4 changes: 2 additions & 2 deletions packages/modules/common/abstract_vehicle.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ class VehicleUpdateData:
imported: float = 0
battery_capacity: float = 82
efficiency: float = 90
soc_from_cp: float = 0
soc_from_cp: Optional[float] = None
timestamp_soc_from_cp: Optional[int] = None


@dataclass
Expand All @@ -24,5 +25,4 @@ class GeneralVehicleConfig:
class CalculatedSocState:
imported_start: Optional[float] = 0 # don't show in UI
manual_soc: Optional[int] = None # don't show in UI
request_start_soc: bool = True # don't show in UI
soc_start: float = 0 # don't show in UI
103 changes: 66 additions & 37 deletions packages/modules/common/configurable_vehicle.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from enum import Enum
import logging
from typing import Optional, TypeVar, Generic, Callable
from helpermodules import timecheck

from helpermodules.pub import Pub
from dataclass_utils import asdict
Expand All @@ -8,14 +10,21 @@
from modules.common.component_context import SingleComponentUpdateContext
from modules.common.component_state import CarState
from modules.common.fault_state import ComponentInfo
from modules.vehicles.common.calc_soc.calc_soc import calc_soc
from modules.vehicles.common.calc_soc import calc_soc
from modules.vehicles.manual.config import ManualSoc

T_VEHICLE_CONFIG = TypeVar("T_VEHICLE_CONFIG")

log = logging.getLogger(__name__)


class SocSource(Enum):
API = "api"
CP = "chargepoint"
MANUAL = "manual"
CALCULATION = "calculation"


class ConfigurableVehicle(Generic[T_VEHICLE_CONFIG]):
def __init__(self,
vehicle_config: T_VEHICLE_CONFIG,
Expand Down Expand Up @@ -43,43 +52,63 @@ def __init__(self,

def update(self, vehicle_update_data: VehicleUpdateData):
with SingleComponentUpdateContext(self.component_info):
if (self.calc_while_charging or
self.general_config.use_soc_from_cp or
isinstance(self.vehicle_config, ManualSoc)):
if self.calculated_soc_state.request_start_soc or vehicle_update_data.plug_state is False:
# Nur wenn angesteckt ist, kann ein initialer SoC abgefragt werden.
if vehicle_update_data.soc_from_cp is None or self.calculated_soc_state.manual_soc:
if isinstance(self.vehicle_config, ManualSoc):
soc = self.calculated_soc_state.manual_soc
source_str = "manual"
else:
soc = self.__component_updater(vehicle_update_data).soc
source_str = "api"
else:
soc = vehicle_update_data.soc_from_cp
source_str = "chargepoint"
log.debug(f"Requested start soc from {source_str}: {soc}%")
self.calculated_soc_state.soc_start = soc
source = self._get_carstate_source(vehicle_update_data)
car_state = self._get_carstate_by_source(vehicle_update_data, source)
log.debug(f"Requested start soc from {source.value}: {car_state.soc}%")

# Flag zurücksetzen, dass ein initialer SoC als Berechnungsgrundlage gesetzt werden soll:
# - bei Berechnung während Ladung (zB PSA): SoC immer vom Server abfragen, wenn nicht geladen wird
# - bei Auslesen aus Auto: Initialen SoC setzen, wenn neu angesteckt wurde
if ((self.calc_while_charging and vehicle_update_data.charge_state is False) or
(self.general_config.use_soc_from_cp and vehicle_update_data.soc_from_cp is None) or
(self.calc_while_charging is False and self.general_config.use_soc_from_cp is False and
vehicle_update_data.plug_state is False)):
self.calculated_soc_state.request_start_soc = True
else:
self.calculated_soc_state.request_start_soc = False
if source != SocSource.CALCULATION:
# Wenn nicht berechnet wurde, SoC als Start merken.
if self.calculated_soc_state.soc_start != car_state.soc:
self.calculated_soc_state.imported_start = vehicle_update_data.imported
Pub().pub(f"openWB/set/vehicle/{self.vehicle}/soc_module/calculated_soc_state",
asdict(self.calculated_soc_state))
self.calculated_soc_state.soc_start = car_state.soc
Pub().pub(f"openWB/set/vehicle/{self.vehicle}/soc_module/calculated_soc_state",
asdict(self.calculated_soc_state))
self.store.set(car_state)

def _get_carstate_source(self, vehicle_update_data: VehicleUpdateData) -> SocSource:
# Kein SoC vom LP vorhanden oder erwünscht
if (vehicle_update_data.soc_from_cp is None or self.general_config.use_soc_from_cp is False or
# oder aktueller manueller SoC vorhanden (ausgelesenen SoC während der Ladung korrigieren)
self.calculated_soc_state.manual_soc):
if isinstance(self.vehicle_config, ManualSoc):
# Wenn ein manueller SoC gesetzt wurde, diesen als neuen Start merken.
if self.calculated_soc_state.manual_soc:
return SocSource.MANUAL
else:
soc = calc_soc(vehicle_update_data,
vehicle_update_data.efficiency,
self.calculated_soc_state.imported_start,
self.calculated_soc_state.soc_start,
vehicle_update_data.battery_capacity)
self.store.set(CarState(soc))
return SocSource.CALCULATION
else:
self.store.set(self.__component_updater(vehicle_update_data))
if vehicle_update_data.charge_state and self.calc_while_charging:
# Wenn während dem Laden berechnet werden soll und gerade geladen wird, berechnen.
return SocSource.CALCULATION
else:
return SocSource.API
else:
if self._is_soc_timestamp_valid(vehicle_update_data):
return SocSource.CP
else:
# Wenn SoC vom LP nicht mehr aktuell, dann berechnen.
return SocSource.CALCULATION

def _get_carstate_by_source(self, vehicle_update_data, source):
if source == SocSource.API:
return self.__component_updater(vehicle_update_data)
elif source == SocSource.CALCULATION:
return CarState(soc=calc_soc.calc_soc(vehicle_update_data,
vehicle_update_data.efficiency,
self.calculated_soc_state.imported_start,
self.calculated_soc_state.soc_start,
vehicle_update_data.battery_capacity))
elif source == SocSource.CP:
return CarState(vehicle_update_data.soc_from_cp)
elif source == SocSource.MANUAL:
soc = self.calculated_soc_state.manual_soc or self.calculated_soc_state.soc_start
self.calculated_soc_state.manual_soc = None
return CarState(soc)

def _is_soc_timestamp_valid(self, vehicle_update_data: VehicleUpdateData) -> bool:
if vehicle_update_data.timestamp_soc_from_cp:
soc_ts = vehicle_update_data.timestamp_soc_from_cp + 60
now_ts = timecheck.create_timestamp_unix()
return soc_ts > now_ts
else:
return False
Loading

0 comments on commit d44cb3e

Please sign in to comment.