From fc03367265c120f92f06ec958e267ae34f15f871 Mon Sep 17 00:00:00 2001 From: nlindn <95648815+nlindn@users.noreply.github.com> Date: Mon, 9 Oct 2023 22:33:35 +0300 Subject: [PATCH] bugfix meter value logic --- custom_components/ocpp/api.py | 41 +++++++++++++++---------- tests/test_charge_point.py | 56 ++++++++++++++++++++++++++++++++++- 2 files changed, 81 insertions(+), 16 deletions(-) diff --git a/custom_components/ocpp/api.py b/custom_components/ocpp/api.py index 5184b6cc..f434ea21 100644 --- a/custom_components/ocpp/api.py +++ b/custom_components/ocpp/api.py @@ -1126,29 +1126,40 @@ def on_meter_values(self, connector_id: int, meter_value: dict, **kwargs): measurand = DEFAULT_MEASURAND unit = DEFAULT_ENERGY_UNIT + if measurand == DEFAULT_MEASURAND and unit is None: + unit = DEFAULT_ENERGY_UNIT + + if self._metrics[csess.meter_start.value].value == 0: + # Charger reports Energy.Active.Import.Register directly as Session energy for transactions. + self._charger_reports_session_energy = True + if phase is None: if unit == DEFAULT_POWER_UNIT: self._metrics[measurand].value = float(value) / 1000 self._metrics[measurand].unit = HA_POWER_UNIT - elif unit == DEFAULT_ENERGY_UNIT or "Energy" in str(measurand): - if self._metrics[csess.meter_start.value].value == 0: - # Charger reports Energy.Active.Import.Register directly as Session energy for transactions - self._charger_reports_session_energy = True - if ( - transaction_matches - and self._charger_reports_session_energy - and measurand == DEFAULT_MEASURAND - and connector_id - ): - self._metrics[csess.session_energy.value].value = ( - float(value) / 1000 + elif ( + measurand == DEFAULT_MEASURAND + and self._charger_reports_session_energy + ): + if transaction_matches: + if unit == DEFAULT_ENERGY_UNIT: + value = float(value) / 1000 + unit = HA_ENERGY_UNIT + self._metrics[csess.session_energy.value].value = float( + value ) + self._metrics[csess.session_energy.value].unit = unit self._metrics[csess.session_energy.value].extra_attr[ cstat.id_tag.name ] = self._metrics[cstat.id_tag.value].value - elif ( - transaction_matches or self._charger_reports_session_energy - ): + else: + if unit == DEFAULT_ENERGY_UNIT: + value = float(value) / 1000 + unit = HA_ENERGY_UNIT + self._metrics[measurand].value = float(value) + self._metrics[measurand].unit = unit + elif unit == DEFAULT_ENERGY_UNIT: + if transaction_matches: self._metrics[measurand].value = float(value) / 1000 self._metrics[measurand].unit = HA_ENERGY_UNIT else: diff --git a/tests/test_charge_point.py b/tests/test_charge_point.py index c5abe69e..b14bff2c 100644 --- a/tests/test_charge_point.py +++ b/tests/test_charge_point.py @@ -308,7 +308,6 @@ async def test_services(hass, socket_enabled): cp.send_meter_err_phases(), cp.send_meter_line_voltage(), cp.send_meter_periodic_data(), - cp.send_main_meter_clock_data(), # add delay to allow meter data to be processed cp.send_stop_transaction(2), ), @@ -370,6 +369,9 @@ async def test_services(hass, socket_enabled): pass await ws.close() assert int(cs.get_metric("test_cpid", "Frequency")) == int(50) + assert float(cs.get_metric("test_cpid", "Energy.Active.Import.Register")) == float( + 1101.452 + ) await asyncio.sleep(1) @@ -410,6 +412,33 @@ async def test_services(hass, socket_enabled): await asyncio.sleep(1) + # test ocpp messages sent from charger that don't support errata 3.9 with meter values with kWh as energy unit + async with websockets.connect( + "ws://127.0.0.1:9000/CP_1_non_er_3.9", + subprotocols=["ocpp1.6"], + ) as ws: + # use a different id for debugging + cp = ChargePoint("CP_1_non_errata_3.9", ws) + try: + await asyncio.wait_for( + asyncio.gather( + cp.start(), + cp.send_start_transaction(0), + cp.send_meter_energy_kwh(), + cp.send_meter_clock_data(), + # add delay to allow meter data to be processed + cp.send_stop_transaction(2), + ), + timeout=5, + ) + except asyncio.TimeoutError: + pass + await ws.close() + + assert int(cs.get_metric("test_cpid", "Energy.Active.Import.Register")) == int(1101) + assert int(cs.get_metric("test_cpid", "Energy.Session")) == int(11) + assert cs.get_unit("test_cpid", "Energy.Active.Import.Register") == "kWh" + # test ocpp rejection messages sent from charger to cms cs.charge_points["test_cpid"].received_boot_notification = False cs.charge_points["test_cpid"].post_connect_success = False @@ -971,6 +1000,31 @@ async def send_meter_err_phases(self): resp = await self.call(request) assert resp is not None + async def send_meter_energy_kwh(self): + """Send periodic energy meter value with kWh unit.""" + while self.active_transactionId == 0: + await asyncio.sleep(1) + request = call.MeterValuesPayload( + connector_id=1, + transaction_id=self.active_transactionId, + meter_value=[ + { + "timestamp": "2021-06-21T16:15:09Z", + "sampledValue": [ + { + "unit": "kWh", + "value": "11", + "context": "Sample.Periodic", + "format": "Raw", + "measurand": "Energy.Active.Import.Register", + }, + ], + } + ], + ) + resp = await self.call(request) + assert resp is not None + async def send_main_meter_clock_data(self): """Send periodic main meter value. Main meter values dont have transaction_id.""" while self.active_transactionId == 0: