8
8
from homeassistant .config_entries import ConfigEntry
9
9
from homeassistant .const import TIME_MINUTES
10
10
from homeassistant .core import HomeAssistant
11
- from homeassistant .helpers import device_registry
11
+ from homeassistant .helpers import device_registry , entity_registry
12
12
import voluptuous as vol
13
13
import websockets
14
14
59
59
DOMAIN ,
60
60
HA_ENERGY_UNIT ,
61
61
HA_POWER_UNIT ,
62
+ SENSOR ,
62
63
)
63
64
from .enums import (
64
65
ConfigurationKey as ckey ,
@@ -128,8 +129,26 @@ async def create(hass: HomeAssistant, entry: ConfigEntry):
128
129
129
130
async def on_connect (self , websocket , path : str ):
130
131
"""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 ()
131
150
132
- _LOGGER .info (f"path={ path } " )
151
+ _LOGGER .info (f"Charger websocket path={ path } " )
133
152
cp_id = path .strip ("/" )
134
153
try :
135
154
if self .cpid not in self .charge_points :
@@ -177,6 +196,16 @@ async def set_charger_state(
177
196
resp = False
178
197
return resp
179
198
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
+
180
209
def device_info (self ):
181
210
"""Return device information."""
182
211
return {
@@ -246,20 +275,14 @@ async def handle_configure(call):
246
275
"""Handle the configure service call."""
247
276
key = call .data .get ("ocpp_key" )
248
277
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
254
280
255
281
async def handle_get_configuration (call ):
256
282
"""Handle the get configuration service call."""
257
283
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
263
286
264
287
try :
265
288
await self .get_supported_features ()
@@ -281,7 +304,9 @@ async def handle_get_configuration(call):
281
304
# "StopTxnSampledData", ",".join(self.entry.data[CONF_MONITORED_VARIABLES])
282
305
# )
283
306
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
+ ]
285
310
# await self.start_transaction()
286
311
287
312
# Register custom services with home assistant
@@ -322,7 +347,7 @@ async def get_supported_features(self):
322
347
req = call .GetConfigurationPayload (key = [ckey .supported_feature_profiles .value ])
323
348
resp = await self .call (req )
324
349
for key_value in resp .configuration_key :
325
- self ._features_supported = key_value [" value" ]
350
+ self ._features_supported = key_value [om . value . value ]
326
351
self ._metrics [cdet .features .value ] = self ._features_supported
327
352
_LOGGER .debug ("Supported feature profiles: %s" , self ._features_supported )
328
353
@@ -373,10 +398,10 @@ async def set_charge_rate(self, limit_amps: int = 32, limit_watts: int = 22000):
373
398
)
374
399
_LOGGER .debug (
375
400
"Charger supports setting the following units: %s" ,
376
- resp .configuration_key [0 ][" value" ],
401
+ resp .configuration_key [0 ][om . value . value ],
377
402
)
378
403
_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 ]:
380
405
lim = limit_amps
381
406
units = ChargingRateUnitType .amps .value
382
407
else :
@@ -385,7 +410,7 @@ async def set_charge_rate(self, limit_amps: int = 32, limit_watts: int = 22000):
385
410
resp = await self .get_configuration (
386
411
ckey .charge_profile_max_stack_level .value
387
412
)
388
- stack_level = int (resp .configuration_key [0 ][" value" ])
413
+ stack_level = int (resp .configuration_key [0 ][om . value . value ])
389
414
390
415
req = call .SetChargingProfilePayload (
391
416
connector_id = 0 ,
@@ -435,7 +460,7 @@ async def start_transaction(self, limit_amps: int = 32, limit_watts: int = 22000
435
460
"""Start a Transaction."""
436
461
"""Check if authorisation enabled, if it is disable it before remote start"""
437
462
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" :
439
464
await self .configure (ckey .authorize_remote_tx_requests .value , "false" )
440
465
if om .feature_profile_smart .value in self ._features_supported :
441
466
resp = await self .get_configuration (
@@ -446,7 +471,7 @@ async def start_transaction(self, limit_amps: int = 32, limit_watts: int = 22000
446
471
resp .configuration_key [0 ]["value" ],
447
472
)
448
473
_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 ]:
450
475
lim = limit_amps
451
476
units = ChargingRateUnitType .amps .value
452
477
else :
@@ -455,7 +480,7 @@ async def start_transaction(self, limit_amps: int = 32, limit_watts: int = 22000
455
480
resp = await self .get_configuration (
456
481
ckey .charge_profile_max_stack_level .value
457
482
)
458
- stack_level = int (resp .configuration_key [0 ][" value" ])
483
+ stack_level = int (resp .configuration_key [0 ][om . value . value ])
459
484
req = call .RemoteStartTransactionPayload (
460
485
connector_id = 1 ,
461
486
id_tag = self ._metrics [cdet .identifier .value ],
@@ -544,7 +569,9 @@ async def get_configuration(self, key: str = ""):
544
569
req = call .GetConfigurationPayload (key = [key ])
545
570
resp = await self .call (req )
546
571
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
+ )
548
575
return resp
549
576
550
577
async def configure (self , key : str , value : str ):
@@ -564,7 +591,7 @@ async def configure(self, key: str, value: str):
564
591
for key_value in resp .configuration_key :
565
592
# If the key already has the targeted value we don't need to set
566
593
# 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 :
568
595
return
569
596
570
597
if key_value .get (om .readonly .name , False ):
@@ -640,43 +667,27 @@ async def async_update_device_info(self, boot_info: dict):
640
667
def on_meter_values (self , connector_id : int , meter_value : Dict , ** kwargs ):
641
668
"""Request handler for MeterValues Calls."""
642
669
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
650
675
)
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
662
681
)
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
673
686
)
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
680
691
if csess .meter_start .value not in self ._metrics :
681
692
self ._metrics [csess .meter_start .value ] = self ._metrics [DEFAULT_MEASURAND ]
682
693
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):
692
703
- float (self ._metrics [csess .meter_start .value ]),
693
704
1 ,
694
705
)
706
+ self .hass .async_create_task (self .central .update (self .central .cpid ))
695
707
return call_result .MeterValuesPayload ()
696
708
697
709
@on (Action .BootNotification )
@@ -733,6 +745,7 @@ def on_status_notification(self, connector_id, error_code, status, **kwargs):
733
745
if Measurand .power_reactive_import .value in self ._metrics :
734
746
self ._metrics [Measurand .power_reactive_import .value ] = 0
735
747
self ._metrics [cstat .error_code .value ] = error_code
748
+ self .hass .async_create_task (self .central .update (self .central .cpid ))
736
749
return call_result .StatusNotificationPayload ()
737
750
738
751
@on (Action .FirmwareStatusNotification )
0 commit comments