Skip to content

Commit c399ab7

Browse files
authored
Merge pull request #10 from drc38/enums
Enums
2 parents 31febe4 + 310b346 commit c399ab7

File tree

4 files changed

+101
-113
lines changed

4 files changed

+101
-113
lines changed

custom_components/ocpp/api.py

Lines changed: 69 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from homeassistant.config_entries import ConfigEntry
99
from homeassistant.const import TIME_MINUTES
1010
from homeassistant.core import HomeAssistant
11-
from homeassistant.helpers import device_registry
11+
from homeassistant.helpers import device_registry, entity_registry
1212
import voluptuous as vol
1313
import websockets
1414

@@ -59,6 +59,7 @@
5959
DOMAIN,
6060
HA_ENERGY_UNIT,
6161
HA_POWER_UNIT,
62+
SENSOR,
6263
)
6364
from .enums import (
6465
ConfigurationKey as ckey,
@@ -128,8 +129,26 @@ async def create(hass: HomeAssistant, entry: ConfigEntry):
128129

129130
async def on_connect(self, websocket, path: str):
130131
"""Request handler executed for every new OCPP connection."""
132+
try:
133+
requested_protocols = websocket.request_headers["Sec-WebSocket-Protocol"]
134+
except KeyError:
135+
_LOGGER.error("Client hasn't requested any Subprotocol. Closing connection")
136+
return await websocket.close()
137+
if requested_protocols in websocket.available_subprotocols:
138+
_LOGGER.info("Websocket Subprotocol matched: %s", requested_protocols)
139+
else:
140+
# In the websockets lib if no subprotocols are supported by the
141+
# client and the server, it proceeds without a subprotocol,
142+
# so we have to manually close the connection.
143+
_LOGGER.warning(
144+
"Protocols mismatched | expected Subprotocols: %s,"
145+
" but client supports %s | Closing connection",
146+
websocket.available_subprotocols,
147+
requested_protocols,
148+
)
149+
return await websocket.close()
131150

132-
_LOGGER.info(f"path={path}")
151+
_LOGGER.info(f"Charger websocket path={path}")
133152
cp_id = path.strip("/")
134153
try:
135154
if self.cpid not in self.charge_points:
@@ -177,6 +196,16 @@ async def set_charger_state(
177196
resp = False
178197
return resp
179198

199+
async def update(self, cp_id: str):
200+
"""Update sensors values in HA."""
201+
er = entity_registry.async_get(self.hass)
202+
dr = device_registry.async_get(self.hass)
203+
identifiers = {(DOMAIN, cp_id)}
204+
dev = dr.async_get_device(identifiers)
205+
for ent in entity_registry.async_entries_for_device(er, dev):
206+
if ent.platform == SENSOR:
207+
self.hass.async_create_task(ent.update())
208+
180209
def device_info(self):
181210
"""Return device information."""
182211
return {
@@ -246,20 +275,14 @@ async def handle_configure(call):
246275
"""Handle the configure service call."""
247276
key = call.data.get("ocpp_key")
248277
value = call.data.get("value")
249-
for line in ckey:
250-
if key == line.value:
251-
await self.configure(key, value)
252-
return
253-
_LOGGER.error("Ocpp key not supported: %s ", key)
278+
await self.configure(key, value)
279+
return
254280

255281
async def handle_get_configuration(call):
256282
"""Handle the get configuration service call."""
257283
key = call.data.get("ocpp_key")
258-
for line in ckey:
259-
if key == line.value:
260-
await self.get_configuration(key)
261-
return
262-
_LOGGER.error("Ocpp key not supported: %s ", key)
284+
await self.get_configuration(key)
285+
return
263286

264287
try:
265288
await self.get_supported_features()
@@ -281,7 +304,9 @@ async def handle_get_configuration(call):
281304
# "StopTxnSampledData", ",".join(self.entry.data[CONF_MONITORED_VARIABLES])
282305
# )
283306
resp = await self.get_configuration(ckey.number_of_connectors.value)
284-
self._metrics[cdet.connectors.value] = resp.configuration_key[0]["value"]
307+
self._metrics[cdet.connectors.value] = resp.configuration_key[0][
308+
om.value.value
309+
]
285310
# await self.start_transaction()
286311

287312
# Register custom services with home assistant
@@ -322,7 +347,7 @@ async def get_supported_features(self):
322347
req = call.GetConfigurationPayload(key=[ckey.supported_feature_profiles.value])
323348
resp = await self.call(req)
324349
for key_value in resp.configuration_key:
325-
self._features_supported = key_value["value"]
350+
self._features_supported = key_value[om.value.value]
326351
self._metrics[cdet.features.value] = self._features_supported
327352
_LOGGER.debug("Supported feature profiles: %s", self._features_supported)
328353

@@ -373,10 +398,10 @@ async def set_charge_rate(self, limit_amps: int = 32, limit_watts: int = 22000):
373398
)
374399
_LOGGER.debug(
375400
"Charger supports setting the following units: %s",
376-
resp.configuration_key[0]["value"],
401+
resp.configuration_key[0][om.value.value],
377402
)
378403
_LOGGER.debug("If more than one unit supported default unit is Amps")
379-
if om.current.value in resp.configuration_key[0]["value"]:
404+
if om.current.value in resp.configuration_key[0][om.value.value]:
380405
lim = limit_amps
381406
units = ChargingRateUnitType.amps.value
382407
else:
@@ -385,7 +410,7 @@ async def set_charge_rate(self, limit_amps: int = 32, limit_watts: int = 22000):
385410
resp = await self.get_configuration(
386411
ckey.charge_profile_max_stack_level.value
387412
)
388-
stack_level = int(resp.configuration_key[0]["value"])
413+
stack_level = int(resp.configuration_key[0][om.value.value])
389414

390415
req = call.SetChargingProfilePayload(
391416
connector_id=0,
@@ -435,7 +460,7 @@ async def start_transaction(self, limit_amps: int = 32, limit_watts: int = 22000
435460
"""Start a Transaction."""
436461
"""Check if authorisation enabled, if it is disable it before remote start"""
437462
resp = await self.get_configuration(ckey.authorize_remote_tx_requests.value)
438-
if resp.configuration_key[0]["value"].lower() == "true":
463+
if resp.configuration_key[0][om.value.value].lower() == "true":
439464
await self.configure(ckey.authorize_remote_tx_requests.value, "false")
440465
if om.feature_profile_smart.value in self._features_supported:
441466
resp = await self.get_configuration(
@@ -446,7 +471,7 @@ async def start_transaction(self, limit_amps: int = 32, limit_watts: int = 22000
446471
resp.configuration_key[0]["value"],
447472
)
448473
_LOGGER.debug("If more than one unit supported default unit is Amps")
449-
if om.current.value in resp.configuration_key[0]["value"]:
474+
if om.current.value in resp.configuration_key[0][om.value.value]:
450475
lim = limit_amps
451476
units = ChargingRateUnitType.amps.value
452477
else:
@@ -455,7 +480,7 @@ async def start_transaction(self, limit_amps: int = 32, limit_watts: int = 22000
455480
resp = await self.get_configuration(
456481
ckey.charge_profile_max_stack_level.value
457482
)
458-
stack_level = int(resp.configuration_key[0]["value"])
483+
stack_level = int(resp.configuration_key[0][om.value.value])
459484
req = call.RemoteStartTransactionPayload(
460485
connector_id=1,
461486
id_tag=self._metrics[cdet.identifier.value],
@@ -544,7 +569,9 @@ async def get_configuration(self, key: str = ""):
544569
req = call.GetConfigurationPayload(key=[key])
545570
resp = await self.call(req)
546571
for key_value in resp.configuration_key:
547-
_LOGGER.debug("Get Configuration for %s: %s", key, key_value["value"])
572+
_LOGGER.debug(
573+
"Get Configuration for %s: %s", key, key_value[om.value.value]
574+
)
548575
return resp
549576

550577
async def configure(self, key: str, value: str):
@@ -564,7 +591,7 @@ async def configure(self, key: str, value: str):
564591
for key_value in resp.configuration_key:
565592
# If the key already has the targeted value we don't need to set
566593
# it.
567-
if key_value["key"] == key and key_value["value"] == value:
594+
if key_value[om.key.value] == key and key_value[om.value.value] == value:
568595
return
569596

570597
if key_value.get(om.readonly.name, False):
@@ -640,43 +667,27 @@ async def async_update_device_info(self, boot_info: dict):
640667
def on_meter_values(self, connector_id: int, meter_value: Dict, **kwargs):
641668
"""Request handler for MeterValues Calls."""
642669
for bucket in meter_value:
643-
for sampled_value in bucket["sampled_value"]:
644-
if om.measurand.value in sampled_value:
645-
self._metrics[sampled_value[om.measurand.value]] = sampled_value[
646-
"value"
647-
]
648-
self._metrics[sampled_value[om.measurand.value]] = round(
649-
float(self._metrics[sampled_value[om.measurand.value]]), 1
670+
for sv in bucket[om.sampled_value.name]:
671+
if om.measurand.value in sv:
672+
self._metrics[sv[om.measurand.value]] = sv[om.value.value]
673+
self._metrics[sv[om.measurand.value]] = round(
674+
float(self._metrics[sv[om.measurand.value]]), 1
650675
)
651-
if "unit" in sampled_value:
652-
self._units[sampled_value[om.measurand.value]] = sampled_value[
653-
"unit"
654-
]
655-
if (
656-
self._units[sampled_value[om.measurand.value]]
657-
== DEFAULT_POWER_UNIT
658-
):
659-
self._metrics[sampled_value[om.measurand.value]] = (
660-
float(self._metrics[sampled_value[om.measurand.value]])
661-
/ 1000
676+
if om.unit.value in sv:
677+
self._units[sv[om.measurand.value]] = sv[om.unit.value]
678+
if self._units[sv[om.measurand.value]] == DEFAULT_POWER_UNIT:
679+
self._metrics[sv[om.measurand.value]] = (
680+
float(self._metrics[sv[om.measurand.value]]) / 1000
662681
)
663-
self._units[
664-
sampled_value[om.measurand.value]
665-
] = HA_POWER_UNIT
666-
if (
667-
self._units[sampled_value[om.measurand.value]]
668-
== DEFAULT_ENERGY_UNIT
669-
):
670-
self._metrics[sampled_value[om.measurand.value]] = (
671-
float(self._metrics[sampled_value[om.measurand.value]])
672-
/ 1000
682+
self._units[sv[om.measurand.value]] = HA_POWER_UNIT
683+
if self._units[sv[om.measurand.value]] == DEFAULT_ENERGY_UNIT:
684+
self._metrics[sv[om.measurand.value]] = (
685+
float(self._metrics[sv[om.measurand.value]]) / 1000
673686
)
674-
self._units[
675-
sampled_value[om.measurand.value]
676-
] = HA_ENERGY_UNIT
677-
if len(sampled_value.keys()) == 1: # for backwards compatibility
678-
self._metrics[DEFAULT_MEASURAND] = sampled_value["value"]
679-
self._units[DEFAULT_MEASURAND] = DEFAULT_ENERGY_UNIT
687+
self._units[sv[om.measurand.value]] = HA_ENERGY_UNIT
688+
if len(sv.keys()) == 1: # for backwards compatibility
689+
self._metrics[DEFAULT_MEASURAND] = float(sv[om.value.value]) / 1000
690+
self._units[DEFAULT_MEASURAND] = HA_ENERGY_UNIT
680691
if csess.meter_start.value not in self._metrics:
681692
self._metrics[csess.meter_start.value] = self._metrics[DEFAULT_MEASURAND]
682693
if csess.transaction_id.value not in self._metrics:
@@ -692,6 +703,7 @@ def on_meter_values(self, connector_id: int, meter_value: Dict, **kwargs):
692703
- float(self._metrics[csess.meter_start.value]),
693704
1,
694705
)
706+
self.hass.async_create_task(self.central.update(self.central.cpid))
695707
return call_result.MeterValuesPayload()
696708

697709
@on(Action.BootNotification)
@@ -733,6 +745,7 @@ def on_status_notification(self, connector_id, error_code, status, **kwargs):
733745
if Measurand.power_reactive_import.value in self._metrics:
734746
self._metrics[Measurand.power_reactive_import.value] = 0
735747
self._metrics[cstat.error_code.value] = error_code
748+
self.hass.async_create_task(self.central.update(self.central.cpid))
736749
return call_result.StatusNotificationPayload()
737750

738751
@on(Action.FirmwareStatusNotification)

custom_components/ocpp/const.py

Lines changed: 13 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -31,46 +31,30 @@
3131
SWITCH = "switch"
3232
PLATFORMS = [SENSOR, SWITCH]
3333

34-
# Ocpp SupportedFeatureProfiles
35-
FEATURE_PROFILE_CORE = "Core"
36-
FEATURE_PROFILE_FW = "FirmwareManagement"
37-
FEATURE_PROFILE_SMART = "SmartCharging"
38-
FEATURE_PROFILE_RESERV = "Reservation"
39-
FEATURE_PROFILE_REMOTE = "RemoteTrigger"
40-
FEATURE_PROFILE_AUTH = "LocalAuthListManagement"
41-
42-
# Services to register for use in HA
43-
SERVICE_CHARGE_START = "start_transaction"
44-
SERVICE_CHARGE_STOP = "stop_transaction"
45-
SERVICE_AVAILABILITY = "availability"
46-
SERVICE_SET_CHARGE_RATE = "set_charge_rate"
47-
SERVICE_RESET = "reset"
48-
SERVICE_UNLOCK = "unlock"
49-
5034
# Ocpp supported measurands
5135
MEASURANDS = [
52-
Measurand.current_export.value,
53-
Measurand.current_import.value,
54-
Measurand.current_offered.value,
55-
Measurand.energy_active_export_register.value,
5636
Measurand.energy_active_import_register.value,
57-
Measurand.energy_reactive_export_register.value,
5837
Measurand.energy_reactive_import_register.value,
59-
Measurand.energy_active_export_interval.value,
6038
Measurand.energy_active_import_interval.value,
61-
Measurand.energy_reactive_export_interval.value,
6239
Measurand.energy_reactive_import_interval.value,
63-
Measurand.frequency.value,
64-
Measurand.power_active_export.value,
6540
Measurand.power_active_import.value,
66-
Measurand.power_factor.value,
67-
Measurand.power_offered.value,
68-
Measurand.power_reactive_export.value,
6941
Measurand.power_reactive_import.value,
42+
Measurand.power_offered.value,
43+
Measurand.power_factor.value,
44+
Measurand.current_import.value,
45+
Measurand.current_offered.value,
46+
Measurand.voltage.value,
47+
Measurand.frequency.value,
7048
Measurand.rpm.value,
7149
Measurand.soc.value,
7250
Measurand.temperature.value,
73-
Measurand.voltage.value,
51+
Measurand.current_export.value,
52+
Measurand.energy_active_export_register.value,
53+
Measurand.energy_reactive_export_register.value,
54+
Measurand.energy_active_export_interval.value,
55+
Measurand.energy_reactive_export_interval.value,
56+
Measurand.power_active_export.value,
57+
Measurand.power_reactive_export.value,
7458
]
7559
DEFAULT_MEASURAND = Measurand.energy_active_import_register.value
7660
DEFAULT_MONITORED_VARIABLES = ",".join(MEASURANDS)

custom_components/ocpp/enums.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,18 @@ class OcppMisc(str, Enum):
5555

5656
"""For pythonic version use .name (eg with kwargs) for ocpp json use .value"""
5757

58+
context = "context"
59+
key = "key"
5860
limit = "limit"
61+
location = "location"
5962
measurand = "measurand"
63+
phase = "phase"
6064
reason = "reason"
6165
readonly = "readonly"
6266
status = "status"
67+
unit = "unit"
68+
value = "value"
69+
sampled_value = "sampledValue"
6370
transaction_id = "transactionId"
6471
charge_point_serial_number = "chargePointSerialNumber"
6572
charge_point_vendor = "chargePointVendor"

0 commit comments

Comments
 (0)