Skip to content

Commit c6d9f82

Browse files
author
Jan Thunqvist
committed
Remove separate connector device breaking change. Fix test race condition. CodeRabbit fixes.
1 parent 853f80a commit c6d9f82

17 files changed

+432
-265
lines changed

.gitignore

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,5 +129,5 @@ dmypy.json
129129
.pyre/
130130

131131
# HA Development
132-
config/
133-
**/.DS_Store
132+
/config/
133+
.DS_Store

custom_components/ocpp/api.py

Lines changed: 33 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -293,19 +293,19 @@ def _get_metrics(self, id: str):
293293
"""Return metrics."""
294294
cp_id = self.cpids.get(id, id)
295295
cp = self.charge_points.get(cp_id)
296-
return (cp_id, cp._metrics) if cp is not None else (None, None)
296+
n_connectors = getattr(cp, "num_connectors", 1) or 1
297+
return (
298+
(cp_id, cp._metrics, cp, n_connectors)
299+
if cp is not None
300+
else (None, None, None, None)
301+
)
297302

298303
def get_metric(self, id: str, measurand: str, connector_id: int | None = None):
299304
"""Return last known value for given measurand."""
300-
# allow id to be either cpid or cp_id
301-
cp_id = self.cpids.get(id, id)
302-
if cp_id not in self.charge_points:
305+
cp_id, m, cp, n_connectors = self._get_metrics(id)
306+
if cp is None:
303307
return None
304308

305-
cp = self.charge_points[cp_id]
306-
m = cp._metrics
307-
n_connectors = getattr(cp, "num_connectors", 1) or 1
308-
309309
def _try_val(key):
310310
with contextlib.suppress(Exception):
311311
val = m[key].value
@@ -314,7 +314,7 @@ def _try_val(key):
314314

315315
# 1) Explicit connector_id (including 0): just get it
316316
if connector_id is not None:
317-
conn = 0 if connector_id == 0 else connector_id
317+
conn = self._norm_conn(connector_id)
318318
return _try_val((conn, measurand))
319319

320320
# 2) No connector_id: try CHARGER level (conn=0)
@@ -344,8 +344,8 @@ def _try_val(key):
344344

345345
def del_metric(self, id: str, measurand: str, connector_id: int | None = None):
346346
"""Set given measurand to None."""
347-
# allow id to be either cpid or cp_id
348-
cp_id, m = self._get_metrics(id)
347+
cp_id, m, cp, n_connectors = self._get_metrics(id)
348+
349349
if m is None:
350350
return None
351351

@@ -360,22 +360,18 @@ def del_metric(self, id: str, measurand: str, connector_id: int | None = None):
360360

361361
def get_unit(self, id: str, measurand: str, connector_id: int | None = None):
362362
"""Return unit of given measurand."""
363-
# allow id to be either cpid or cp_id
364-
cp_id = self.cpids.get(id, id)
365-
if cp_id not in self.charge_points:
366-
return None
363+
cp_id, m, cp, n_connectors = self._get_metrics(id)
367364

368-
cp = self.charge_points[cp_id]
369-
m = cp._metrics
370-
n_connectors = getattr(cp, "num_connectors", 1) or 1
365+
if cp is None:
366+
return None
371367

372368
def _try_unit(key):
373369
with contextlib.suppress(Exception):
374370
return m[key].unit
375371
return None
376372

377373
if connector_id is not None:
378-
conn = 0 if connector_id == 0 else connector_id
374+
conn = self._norm_conn(connector_id)
379375
return _try_unit((conn, measurand))
380376

381377
val = _try_unit((0, measurand))
@@ -401,21 +397,18 @@ def _try_unit(key):
401397

402398
def get_ha_unit(self, id: str, measurand: str, connector_id: int | None = None):
403399
"""Return home assistant unit of given measurand."""
404-
cp_id = self.cpids.get(id, id)
405-
if cp_id not in self.charge_points:
406-
return None
400+
cp_id, m, cp, n_connectors = self._get_metrics(id)
407401

408-
cp = self.charge_points[cp_id]
409-
m = cp._metrics
410-
n_connectors = getattr(cp, "num_connectors", 1) or 1
402+
if cp is None:
403+
return None
411404

412405
def _try_ha_unit(key):
413406
with contextlib.suppress(Exception):
414407
return m[key].ha_unit
415408
return None
416409

417410
if connector_id is not None:
418-
conn = 0 if connector_id == 0 else connector_id
411+
conn = self._norm_conn(connector_id)
419412
return _try_ha_unit((conn, measurand))
420413

421414
val = _try_ha_unit((0, measurand))
@@ -441,22 +434,18 @@ def _try_ha_unit(key):
441434

442435
def get_extra_attr(self, id: str, measurand: str, connector_id: int | None = None):
443436
"""Return extra attributes for given measurand."""
444-
# allow id to be either cpid or cp_id
445-
cp_id = self.cpids.get(id, id)
446-
if cp_id not in self.charge_points:
447-
return None
437+
cp_id, m, cp, n_connectors = self._get_metrics(id)
448438

449-
cp = self.charge_points[cp_id]
450-
m = cp._metrics
451-
n_connectors = getattr(cp, "num_connectors", 1) or 1
439+
if cp is None:
440+
return None
452441

453442
def _try_extra(key):
454443
with contextlib.suppress(Exception):
455444
return m[key].extra_attr
456445
return None
457446

458447
if connector_id is not None:
459-
conn = 0 if connector_id == 0 else connector_id
448+
conn = self._norm_conn(connector_id)
460449
return _try_extra((conn, measurand))
461450

462451
val = _try_extra((0, measurand))
@@ -482,28 +471,27 @@ def _try_extra(key):
482471

483472
def get_available(self, id: str, connector_id: int | None = None):
484473
"""Return whether the charger (or a specific connector) is available."""
485-
# allow id to be either cpid or cp_id
486-
cp_id = self.cpids.get(id, id)
487-
if cp_id not in self.charge_points:
488-
return False
474+
cp_id, m, cp, n_connectors = self._get_metrics(id)
489475

490-
cp = self.charge_points[cp_id]
476+
if cp is None:
477+
return None
491478

492-
if connector_id is None or connector_id == 0:
479+
if self._norm_conn(connector_id) == 0:
493480
return cp.status == STATE_OK
494481

495-
m = cp._metrics
496482
status_val = None
497483
with contextlib.suppress(Exception):
498-
status_val = m[(connector_id, cstat.status_connector.value)].value
484+
status_val = m[
485+
(self._norm_conn(connector_id), cstat.status_connector.value)
486+
].value
499487

500488
if not status_val:
501489
try:
502490
flat = m[cstat.status_connector.value]
503491
if hasattr(flat, "extra_attr"):
504-
status_val = flat.extra_attr.get(connector_id) or getattr(
505-
flat, "value", None
506-
)
492+
status_val = flat.extra_attr.get(
493+
self._norm_conn(connector_id)
494+
) or getattr(flat, "value", None)
507495
except Exception:
508496
pass
509497

custom_components/ocpp/button.py

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,17 +55,29 @@ async def async_setup_entry(hass, entry, async_add_devices):
5555
cp_id_settings = list(charger.values())[0]
5656
cpid = cp_id_settings[CONF_CPID]
5757

58-
num_connectors = int(cp_id_settings.get(CONF_NUM_CONNECTORS, 1))
58+
num_connectors = int(cp_id_settings.get(CONF_NUM_CONNECTORS, 1) or 1)
5959

6060
for desc in BUTTONS:
6161
if desc.per_connector:
62-
for connector_id in range(1, num_connectors + 1):
62+
if num_connectors > 1:
63+
for connector_id in range(1, num_connectors + 1):
64+
entities.append(
65+
ChargePointButton(
66+
central_system=central_system,
67+
cpid=cpid,
68+
description=desc,
69+
connector_id=connector_id,
70+
op_connector_id=connector_id,
71+
)
72+
)
73+
else:
6374
entities.append(
6475
ChargePointButton(
6576
central_system=central_system,
6677
cpid=cpid,
6778
description=desc,
68-
connector_id=connector_id,
79+
connector_id=None,
80+
op_connector_id=1,
6981
)
7082
)
7183
else:
@@ -75,6 +87,7 @@ async def async_setup_entry(hass, entry, async_add_devices):
7587
cpid=cpid,
7688
description=desc,
7789
connector_id=None,
90+
op_connector_id=None,
7891
)
7992
)
8093

@@ -93,12 +106,14 @@ def __init__(
93106
cpid: str,
94107
description: OcppButtonDescription,
95108
connector_id: int | None = None,
109+
op_connector_id: int | None = None,
96110
):
97111
"""Instantiate instance of a ChargePointButton."""
98112
self.cpid = cpid
99113
self.central_system = central_system
100114
self.entity_description = description
101115
self.connector_id = connector_id
116+
self._op_connector_id = op_connector_id
102117
parts = [BUTTON_DOMAIN, DOMAIN, cpid, description.key]
103118
if self.connector_id:
104119
parts.insert(3, f"conn{self.connector_id}")
@@ -119,12 +134,12 @@ def __init__(
119134
@property
120135
def available(self) -> bool:
121136
"""Return charger availability."""
122-
return self.central_system.get_available(self.cpid, self.connector_id)
137+
return self.central_system.get_available(self.cpid, self._op_connector_id)
123138

124139
async def async_press(self) -> None:
125140
"""Triggers the charger press action service."""
126141
await self.central_system.set_charger_state(
127142
self.cpid,
128143
self.entity_description.press_action,
129-
connector_id=self.connector_id,
144+
connector_id=self._op_connector_id,
130145
)

custom_components/ocpp/chargepoint.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -118,11 +118,11 @@ def extra_attr(self, extra_attr: dict):
118118
class _ConnectorAwareMetrics(MutableMapping):
119119
"""Backwards compatible mapping for metrics.
120120
121-
- m["Power.Active.Import"] -> Metric for connector 0 (flat access)
122-
- m[(2, "Power.Active.Import")] -> Metric for connector 2 (per connector)
123-
- m[2] -> dict[str -> Metric] for connector 2
121+
- m["Power.Active.Import"] -> Metric for connector 0 (flat access)
122+
- m[(2, "Power.Active.Import")] -> Metric for connector 2 (per connector)
123+
- m[2] -> dict[str -> Metric] for connector 2
124124
125-
Iteration, len, keys(), items() etc act like flat dict (connector 0).
125+
Iteration, len, keys(), values(), items() operate on connector 0 (flat view).
126126
"""
127127

128128
def __init__(self):
@@ -798,7 +798,7 @@ def process_measurands(
798798
else:
799799
# Derive: EAIR_kWh - meter_start_kWh
800800
ms_val = self._metrics[
801-
(connector_id, csess.meter_start)
801+
(connector_id, csess.meter_start.value)
802802
].value
803803
if ms_val is not None:
804804
self._metrics[

custom_components/ocpp/config_flow.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,9 @@
9393
vol.Required(
9494
CONF_FORCE_SMART_CHARGING, default=DEFAULT_FORCE_SMART_CHARGING
9595
): bool,
96-
vol.Required(CONF_NUM_CONNECTORS, default=DEFAULT_NUM_CONNECTORS): int,
96+
vol.Required(CONF_NUM_CONNECTORS, default=DEFAULT_NUM_CONNECTORS): vol.All(
97+
vol.Coerce(int), vol.Range(min=1)
98+
),
9799
}
98100
)
99101

custom_components/ocpp/const.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ class ChargerSystemSettings:
153153
skip_schema_validation: bool
154154
force_smart_charging: bool
155155
connection: int | None = None # number of this connection in central server
156-
num_connectors: int = 1
156+
num_connectors: int = DEFAULT_NUM_CONNECTORS
157157

158158

159159
@dataclass

custom_components/ocpp/enums.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Additional enumerated values to use in home assistant."""
22

3-
from enum import Enum, Flag, auto
3+
from enum import Enum, IntFlag, auto
44

55

66
class HAChargerServices(str, Enum):
@@ -62,7 +62,7 @@ class HAChargerSession(str, Enum):
6262
meter_start = "Energy.Meter.Start" # in kWh
6363

6464

65-
class Profiles(Flag):
65+
class Profiles(IntFlag):
6666
"""Flags to indicate supported feature profiles."""
6767

6868
NONE = 0

0 commit comments

Comments
 (0)