Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion packages/helpermodules/update_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@

class UpdateConfig:

DATASTORE_VERSION = 123
DATASTORE_VERSION = 124

valid_topic = [
"^openWB/bat/config/bat_control_activated$",
Expand Down Expand Up @@ -3120,3 +3120,15 @@ def upgrade(topic: str, payload) -> Optional[dict]:
return {topic: configuration_payload}
self._loop_all_received_topics(upgrade)
self._append_datastore_version(123)

def upgrade_datastore_124(self) -> None:
def upgrade(topic: str, payload) -> Optional[dict]:
if re.search("^openWB/vehicle/[0-9]+/soc_module/config$", topic) is not None:
configuration_payload = decode_payload(payload)
if configuration_payload.get("type") == "json":
json_config = configuration_payload.get("configuration", {})
if "odometer_pattern" not in json_config:
json_config["odometer_pattern"] = None
return {topic: configuration_payload}
self._loop_all_received_topics(upgrade)
self._append_datastore_version(124)
29 changes: 29 additions & 0 deletions packages/helpermodules/update_config_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,35 @@ def test_upgrade_datastore_94(index_test_template, expected_index):
assert plan_ids == expected_index


def test_upgrade_datastore_124_adds_missing_odometer_pattern_for_json_soc_module():
update_con = UpdateConfig()
update_con.all_received_topics = {
"openWB/system/datastore_version": list(range(124)),
"openWB/vehicle/0/soc_module/config": {
"type": "json",
"configuration": {
"url": "https://example.invalid/soc",
"soc_pattern": ".soc",
}
},
"openWB/vehicle/1/soc_module/config": {
"type": "homeassistant",
"configuration": {
"url": "http://ha.local"
}
}
}

update_con.upgrade_datastore_124()

json_config = update_con.all_received_topics["openWB/vehicle/0/soc_module/config"]["configuration"]
assert "odometer_pattern" in json_config
assert json_config["odometer_pattern"] is None

ha_config = update_con.all_received_topics["openWB/vehicle/1/soc_module/config"]["configuration"]
assert "odometer_pattern" not in ha_config


@pytest.mark.parametrize("name", [
"happy_path",
"missing_prices_dict",
Expand Down
2 changes: 2 additions & 0 deletions packages/modules/vehicles/json/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ def __init__(
soc_pattern: Optional[str] = None,
range_pattern: Optional[str] = None,
timestamp_pattern: Optional[str] = None,
odometer_pattern: Optional[str] = None,
timeout: Optional[int] = None,
calculate_soc: bool = False
):
self.url = url
self.soc_pattern = soc_pattern
self.range_pattern = range_pattern
self.timestamp_pattern = timestamp_pattern
self.odometer_pattern = odometer_pattern
self.timeout = timeout
self.calculate_soc = calculate_soc

Expand Down
14 changes: 11 additions & 3 deletions packages/modules/vehicles/json/soc.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,9 @@ def fetch_soc(config: JsonSocSetup, compiled_queries: Dict) -> CarState:
compiled_queries["timestamp"],
config.configuration.timestamp_pattern))
if compiled_queries["timestamp"] is not None else None)
return CarState(soc=soc, range=range, soc_timestamp=timestamp)
odometer = (float(parse_data(raw_data, compiled_queries["odometer"], config.configuration.odometer_pattern))
if compiled_queries["odometer"] is not None else None)
return CarState(soc=soc, range=range, soc_timestamp=timestamp, odometer=odometer)


def initialize_vehicle(vehicle_config: JsonSocSetup, compiled_queries: Dict) -> None:
Expand All @@ -71,13 +73,16 @@ def initialize_vehicle(vehicle_config: JsonSocSetup, compiled_queries: Dict) ->
compiled_queries["range"] = jq.compile(config.range_pattern) if config.range_pattern is not None else None
compiled_queries["timestamp"] = (jq.compile(config.timestamp_pattern)
if config.timestamp_pattern is not None else None)
compiled_queries["odometer"] = (jq.compile(config.odometer_pattern)
if config.odometer_pattern is not None else None)


def create_vehicle(vehicle_config: JsonSocSetup, vehicle: int):
compiled_queries = {
'soc': None,
'range': None,
'timestamp': None
'timestamp': None,
'odometer': None
}

def initializer() -> None:
Expand All @@ -97,14 +102,17 @@ def json_update(charge_point: int,
soc_pattern: str,
range_pattern: Optional[str] = None,
timestamp_pattern: Optional[str] = None,
odometer_pattern: Optional[str] = None,
calculate_soc: Optional[bool] = False):
log.debug(f'json-soc: charge_point={charge_point} url="{url}" soc-pattern="{soc_pattern}" '
f'range-pattern="{range_pattern}" timestamp-pattern="{timestamp_pattern}" calculate-soc={calculate_soc}')
f'range-pattern="{range_pattern}" timestamp-pattern="{timestamp_pattern}" '
f'odometer-pattern="{odometer_pattern}" calculate-soc={calculate_soc}')
store.get_car_value_store(charge_point).store.set(
fetch_soc(JsonSocSetup(configuration=JsonSocConfiguration(url=url,
soc_pattern=soc_pattern,
range_pattern=range_pattern,
timestamp_pattern=timestamp_pattern,
odometer_pattern=odometer_pattern,
calculate_soc=calculate_soc))))
Comment on lines 110 to 116

Copilot AI Apr 14, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

json_update calls fetch_soc(...) without passing a compiled_queries dict, but fetch_soc requires it and indexes keys like compiled_queries["soc"]/["odometer"]. This will raise a TypeError at runtime. Consider creating a local compiled_queries dict in json_update, calling initialize_vehicle(...), and then calling fetch_soc(config, compiled_queries) (or alternatively adjust fetch_soc to compile queries internally when none are provided).

Suggested change
store.get_car_value_store(charge_point).store.set(
fetch_soc(JsonSocSetup(configuration=JsonSocConfiguration(url=url,
soc_pattern=soc_pattern,
range_pattern=range_pattern,
timestamp_pattern=timestamp_pattern,
odometer_pattern=odometer_pattern,
calculate_soc=calculate_soc))))
vehicle_config = JsonSocSetup(configuration=JsonSocConfiguration(url=url,
soc_pattern=soc_pattern,
range_pattern=range_pattern,
timestamp_pattern=timestamp_pattern,
odometer_pattern=odometer_pattern,
calculate_soc=calculate_soc))
compiled_queries = {
'soc': None,
'range': None,
'timestamp': None,
'odometer': None
}
initialize_vehicle(vehicle_config, compiled_queries)
store.get_car_value_store(charge_point).store.set(fetch_soc(vehicle_config, compiled_queries))

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Der Code funktioniert. Anmerkung kann ignoriert werden.



Expand Down
34 changes: 30 additions & 4 deletions packages/modules/vehicles/json/test_json_soc.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@ def setUp(self):
'soc_pattern': '.energy[0].level',
'range_pattern': '.energy[0].autonomy',
'timestamp_pattern': '.energy[0].updated_at',
'odometer_pattern': None,
'expected_soc': 69,
'expected_range': 214,
'expected_timestamp': 1735984813
'expected_timestamp': 1735984813,
'expected_odometer': None
},
{
'sample_data': {
Expand All @@ -46,9 +48,30 @@ def setUp(self):
'soc_pattern': '.response.value',
'range_pattern': None,
'timestamp_pattern': '.response.timestamp',
'odometer_pattern': None,
'expected_soc': 20.2,
'expected_range': None,
'expected_timestamp': 1736108141
'expected_timestamp': 1736108141,
'expected_odometer': None
},
{
'sample_data': {
"response": {
"soc": "45.5",
"range": 300,
"timestamp": 1736108141,
"odometer": 12345.67
}
},
'url': "http://example.com/soc3",
'soc_pattern': '.response.soc',
'range_pattern': '.response.range',
'timestamp_pattern': '.response.timestamp',
'odometer_pattern': '.response.odometer',
'expected_soc': 45.5,
'expected_range': 300,
'expected_timestamp': 1736108141,
'expected_odometer': 12345.67
}
]

Expand All @@ -62,17 +85,20 @@ def test_fetch_soc(self, mock_get_http_session):
compiled_queries = {
'soc': None,
'range': None,
'timestamp': None
'timestamp': None,
'odometer': None
}
vehicle_config = JsonSocSetup(configuration=JsonSocConfiguration(
url=case['url'],
soc_pattern=case['soc_pattern'],
range_pattern=case['range_pattern'],
timestamp_pattern=case['timestamp_pattern']
timestamp_pattern=case['timestamp_pattern'],
odometer_pattern=case['odometer_pattern']
))
initialize_vehicle(vehicle_config, compiled_queries)
car_state = fetch_soc(vehicle_config, compiled_queries)

self.assertEqual(car_state.soc, case['expected_soc'])
self.assertEqual(car_state.range, case['expected_range'])
self.assertEqual(car_state.soc_timestamp, case['expected_timestamp'])
self.assertEqual(car_state.odometer, case['expected_odometer'])
1 change: 1 addition & 0 deletions packages/modules/vehicles/psacc/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ def __init__(self,
super().__init__(soc_pattern=prf + "level",
range_pattern=prf + "autonomy",
timestamp_pattern=prf + "updated_at",
odometer_pattern=".timed_odometer.mileage",
timeout=10,
calculate_soc=True)

Expand Down
Loading