Skip to content
Open
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ The key-value pairs in the JSON express the following:
| chargeStateTopic | topic indicating the charge state - **required** |
| chargingValue | payload that indicates the charging - **required** |
| socTopic | topic where the gateway publishes the SoC for the charging station - optional |
| socTsTopic | topic where the gateway publishes the SoC timestamp for the charging station - optional |
| rangeTopic | topic where the gateway publishes the range for the charging station - optional |
| chargerConnectedTopic | topic indicating that the vehicle is connected to the charging station - optional |
| chargerConnectedValue | payload that indicates that the charger is connected - optional |
Expand Down
10 changes: 6 additions & 4 deletions examples/charging-stations.json.sample_openWB_2.0
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,19 @@
{
"chargeStateTopic": "openWB/chargepoint/2/get/charge_state",
"chargingValue": "true",
"socTopic": "openWB/set/vehicle/2/get/soc",
"rangeTopic": "openWB/set/vehicle/2/get/range",
"socTopic": "openWB/set/mqtt/vehicle/2/get/soc",
"socTsTopic": "openWB/set/mqtt/vehicle/2/get/soc_timestamp",
"rangeTopic": "openWB/set/mqtt/vehicle/2/get/range",
"chargerConnectedTopic": "openWB/chargepoint/2/get/plug_state",
"chargerConnectedValue": "true",
"vin": "vin1"
},
{
"chargeStateTopic": "openWB/chargepoint/3/get/charge_state",
"chargingValue": "true",
"socTopic": "openWB/set/vehicle/3/get/soc",
"rangeTopic": "openWB/set/vehicle/3/get/range",
"socTopic": "openWB/set/mqtt/vehicle/3/get/soc",
"socTsTopic": "openWB/set/mqtt/vehicle/3/get/soc_timestamp",
"rangeTopic": "openWB/set/mqtt/vehicle/3/get/range",
"chargerConnectedTopic": "openWB/chargepoint/3/get/plug_state",
"chargerConnectedValue": "true",
"vin": "vin2"
Expand Down
46 changes: 44 additions & 2 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ pytest-asyncio = "^1.2.0"
pytest-mock = "^3.14.0"
mypy = "^1.15.0"
pylint = "^3.3.6"
freezegun = "^1.5.5"

[tool.poetry.dependencies]
saic-ismart-client-ng = { develop = true }
Expand Down
17 changes: 9 additions & 8 deletions src/configuration/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -550,15 +550,16 @@ def __process_charging_stations_file(config: Configuration, json_file: str) -> N
for item in data:
charge_state_topic = item["chargeStateTopic"]
charging_value = item["chargingValue"]
soc_topic = item.get("socTopic", None)
soc_ts_topic = item.get("socTsTopic", None)
vin = item["vin"]
if "socTopic" in item:
charging_station = ChargingStation(
vin, charge_state_topic, charging_value, item["socTopic"]
)
else:
charging_station = ChargingStation(
vin, charge_state_topic, charging_value
)
charging_station = ChargingStation(
vin,
charge_state_topic,
charging_value,
soc_topic,
soc_ts_topic,
)
if "rangeTopic" in item:
charging_station.range_topic = item["rangeTopic"]
if "chargerConnectedTopic" in item:
Expand Down
13 changes: 12 additions & 1 deletion src/integrations/openwb/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import datetime
import logging
from typing import TYPE_CHECKING

Expand Down Expand Up @@ -40,7 +41,7 @@ def update_openwb(
vehicle_status, charge_status
)
if electric_range is not None and range_topic is not None:
LOG.info("OpenWB Integration published range to %f", range_topic)
LOG.info("OpenWB Integration published range to %s", range_topic)
self.__publisher.publish_float(
key=range_topic,
value=electric_range,
Expand All @@ -56,3 +57,13 @@ def update_openwb(
value=soc,
no_prefix=True,
)

soc_ts_topic = self.__charging_station.soc_ts_topic
soc_ts = int(datetime.datetime.now().timestamp())
if soc_ts_topic is not None:
LOG.info("OpenWB Integration published SoC timestamp to %s", soc_ts_topic)
self.__publisher.publish_int(
key=soc_ts_topic,
value=soc_ts,
no_prefix=True,
)
2 changes: 2 additions & 0 deletions src/integrations/openwb/charging_station.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ def __init__(
charge_state_topic: str,
charging_value: str,
soc_topic: str | None = None,
soc_ts_topic: str | None = None
) -> None:
self.vin: str = vin
self.charge_state_topic: str = charge_state_topic
self.charging_value: str = charging_value
self.soc_topic: str | None = soc_topic
self.soc_ts_topic: str | None = soc_ts_topic
self.range_topic: str | None = None
self.connected_topic: str | None = None
self.connected_value: str | None = None
16 changes: 16 additions & 0 deletions tests/integrations/openwb/test_openwb_integration.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from __future__ import annotations

from datetime import datetime
from typing import Any
import unittest

from apscheduler.schedulers.blocking import BlockingScheduler
from freezegun import freeze_time
import pytest
from saic_ismart_client_ng.api.vehicle.schema import VinInfo

Expand All @@ -25,6 +27,7 @@
RANGE_TOPIC = "/mock/range"
CHARGE_STATE_TOPIC = "/mock/charge/state"
SOC_TOPIC = "/mock/soc/state"
SOC_TS_TOPIC = "/mock/soc/timestamp"
CHARGING_VALUE = "VehicleIsCharging"


Expand All @@ -46,13 +49,15 @@ def setUp(self) -> None:
charge_state_topic=CHARGE_STATE_TOPIC,
charging_value=CHARGING_VALUE,
soc_topic=SOC_TOPIC,
soc_ts_topic=SOC_TS_TOPIC,
)
charging_station.range_topic = RANGE_TOPIC
self.openwb_integration = OpenWBIntegration(
charging_station=charging_station,
publisher=self.publisher,
)

@freeze_time("2025-01-01 12:00:00")
async def test_update_soc_with_no_bms_data(self) -> None:
vehicle_status_resp = get_mock_vehicle_status_resp()
result = self.vehicle_state.handle_vehicle_status(vehicle_status_resp)
Expand All @@ -65,16 +70,22 @@ async def test_update_soc_with_no_bms_data(self) -> None:
SOC_TOPIC,
float(DRIVETRAIN_SOC_VEHICLE),
)
self.assert_mqtt_topic(
SOC_TS_TOPIC,
int(datetime(2025, 1, 1, 12, 0, 0).timestamp())
)
self.assert_mqtt_topic(
RANGE_TOPIC,
DRIVETRAIN_RANGE_VEHICLE,
)
expected_topics = {
SOC_TOPIC,
SOC_TS_TOPIC,
RANGE_TOPIC,
}
assert expected_topics == set(self.publisher.map.keys())

@freeze_time("2025-01-01 12:00:00")
async def test_update_soc_with_bms_data(self) -> None:
vehicle_status_resp = get_mock_vehicle_status_resp()
chrg_mgmt_data_resp = get_mock_charge_management_data_resp()
Expand All @@ -98,8 +109,13 @@ async def test_update_soc_with_bms_data(self) -> None:
RANGE_TOPIC,
DRIVETRAIN_RANGE_BMS,
)
self.assert_mqtt_topic(
SOC_TS_TOPIC,
int(datetime(2025, 1, 1, 12, 0, 0).timestamp())
)
expected_topics = {
SOC_TOPIC,
SOC_TS_TOPIC,
RANGE_TOPIC,
}
assert expected_topics == set(self.publisher.map.keys())
Expand Down