Skip to content

Commit d62a4b6

Browse files
authored
handle erroneous voltage reporting (#636)
* handle erroneous voltage reporting * fix logic errors * fix testing * extra test cov * tidy ups
1 parent ea5cba0 commit d62a4b6

File tree

2 files changed

+75
-15
lines changed

2 files changed

+75
-15
lines changed

custom_components/ocpp/api.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -988,13 +988,13 @@ def average_of_nonzero(values):
988988

989989
measurand_data = {}
990990
for item in data:
991-
# create ordered Dict for each measurand, eg {"voltage":{"unit":"V","L1":"230"...}}
991+
# create ordered Dict for each measurand, eg {"voltage":{"unit":"V","L1-N":"230"...}}
992992
measurand = item.get(om.measurand.value, None)
993993
phase = item.get(om.phase.value, None)
994994
value = item.get(om.value.value, None)
995995
unit = item.get(om.unit.value, None)
996996
context = item.get(om.context.value, None)
997-
if measurand is not None and phase is not None:
997+
if measurand is not None and phase is not None and unit is not None:
998998
if measurand not in measurand_data:
999999
measurand_data[measurand] = {}
10001000
measurand_data[measurand][om.unit.value] = unit
@@ -1011,27 +1011,33 @@ def average_of_nonzero(values):
10111011
for metric, phase_info in measurand_data.items():
10121012
metric_value = None
10131013
if metric in [Measurand.voltage.value]:
1014-
if (phase_info.keys() & line_to_neutral_phases) is not None:
1014+
if not phase_info.keys().isdisjoint(line_to_neutral_phases):
10151015
# Line to neutral voltages are averaged
10161016
metric_value = average_of_nonzero(
10171017
[phase_info.get(phase, 0) for phase in line_to_neutral_phases]
10181018
)
1019-
elif (phase_info.keys() & line_to_line_phases) is not None:
1019+
elif not phase_info.keys().isdisjoint(line_to_line_phases):
10201020
# Line to line voltages are averaged and converted to line to neutral
10211021
metric_value = average_of_nonzero(
10221022
[phase_info.get(phase, 0) for phase in line_to_line_phases]
10231023
) / sqrt(3)
1024+
elif not phase_info.keys().isdisjoint(line_phases):
1025+
# Workaround for chargers that don't follow engineering convention
1026+
# Assumes voltages are line to neutral
1027+
metric_value = average_of_nonzero(
1028+
[phase_info.get(phase, 0) for phase in line_phases]
1029+
)
10241030
elif metric in [
10251031
Measurand.current_import.value,
10261032
Measurand.current_export.value,
10271033
Measurand.power_active_import.value,
10281034
Measurand.power_active_export.value,
10291035
]:
1030-
if (phase_info.keys() & line_phases) is not None:
1036+
if not phase_info.keys().isdisjoint(line_phases):
10311037
metric_value = sum(
10321038
phase_info.get(phase, 0) for phase in line_phases
10331039
)
1034-
elif (phase_info.keys() & line_to_neutral_phases) is not None:
1040+
elif not phase_info.keys().isdisjoint(line_to_neutral_phases):
10351041
# Workaround for some chargers that erroneously use line to neutral for current
10361042
metric_value = sum(
10371043
phase_info.get(phase, 0) for phase in line_to_neutral_phases

tests/test_charge_point.py

Lines changed: 63 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,8 @@ async def test_services(hass, socket_enabled):
246246
cp.send_firmware_status(),
247247
cp.send_data_transfer(),
248248
cp.send_start_transaction(),
249+
cp.send_meter_err_phases(),
250+
cp.send_meter_line_voltage(),
249251
cp.send_meter_periodic_data(),
250252
# add delay to allow meter data to be processed
251253
cp.send_stop_transaction(2),
@@ -772,6 +774,38 @@ async def send_meter_periodic_data(self):
772774
"unit": "V",
773775
"phase": "L3-N",
774776
},
777+
{
778+
"value": "89.00",
779+
"context": "Sample.Periodic",
780+
"measurand": "Power.Reactive.Import",
781+
"unit": "W",
782+
},
783+
{
784+
"value": "0.010",
785+
"context": "Transaction.Begin",
786+
"unit": "kWh",
787+
},
788+
{
789+
"value": "1305570.000",
790+
},
791+
],
792+
}
793+
],
794+
)
795+
resp = await self.call(request)
796+
assert resp is not None
797+
798+
async def send_meter_line_voltage(self):
799+
"""Send line voltages."""
800+
while self.active_transactionId == 0:
801+
await asyncio.sleep(1)
802+
request = call.MeterValuesPayload(
803+
connector_id=1,
804+
transaction_id=self.active_transactionId,
805+
meter_value=[
806+
{
807+
"timestamp": "2021-06-21T16:15:09Z",
808+
"sampledValue": [
775809
{
776810
"value": "395.900",
777811
"context": "Sample.Periodic",
@@ -796,19 +830,39 @@ async def send_meter_periodic_data(self):
796830
"unit": "V",
797831
"phase": "L3-L1",
798832
},
833+
],
834+
}
835+
],
836+
)
837+
resp = await self.call(request)
838+
assert resp is not None
839+
840+
async def send_meter_err_phases(self):
841+
"""Send erroneous voltage phase."""
842+
while self.active_transactionId == 0:
843+
await asyncio.sleep(1)
844+
request = call.MeterValuesPayload(
845+
connector_id=1,
846+
transaction_id=self.active_transactionId,
847+
meter_value=[
848+
{
849+
"timestamp": "2021-06-21T16:15:09Z",
850+
"sampledValue": [
799851
{
800-
"value": "89.00",
852+
"value": "230",
801853
"context": "Sample.Periodic",
802-
"measurand": "Power.Reactive.Import",
803-
"unit": "W",
804-
},
805-
{
806-
"value": "0.010",
807-
"context": "Transaction.Begin",
808-
"unit": "kWh",
854+
"measurand": "Voltage",
855+
"location": "Outlet",
856+
"unit": "V",
857+
"phase": "L1",
809858
},
810859
{
811-
"value": "1305570.000",
860+
"value": "23",
861+
"context": "Sample.Periodic",
862+
"measurand": "Current.Import",
863+
"location": "Outlet",
864+
"unit": "A",
865+
"phase": "L1-N",
812866
},
813867
],
814868
}

0 commit comments

Comments
 (0)