@@ -377,36 +377,6 @@ async def clear_profile(
377
377
_LOGGER .debug ("ClearChargingProfile raised %s (ignored)" , ex )
378
378
return False
379
379
380
- def _profile_ids_for (
381
- self , conn_id : int , purpose : str , tx_id : int | None = None
382
- ) -> tuple [int , int ]:
383
- """Return (chargingProfileId, stackLevel) unique per (purpose, connector).
384
-
385
- - Keeps IDs small and stable across restarts.
386
- - For TxProfile you may include tx_id to avoid clashes if multiple are alive.
387
- """
388
- PURPOSE_CODE = {
389
- "ChargePointMaxProfile" : 1 ,
390
- "TxDefaultProfile" : 2 ,
391
- "TxProfile" : 3 ,
392
- }
393
- if purpose == "ChargePointMaxProfile" :
394
- conn_seg = 0
395
- else :
396
- try :
397
- conn_seg = max (1 , int (conn_id or 1 ))
398
- except Exception :
399
- conn_seg = 1
400
-
401
- base = 1000
402
- pid = base + PURPOSE_CODE [purpose ] + conn_seg * 10
403
-
404
- if purpose == "TxProfile" and tx_id is not None :
405
- pid = pid * 1000 + (int (tx_id ) % 1000 )
406
-
407
- stack_level = 1
408
- return pid , stack_level
409
-
410
380
async def set_charge_rate (
411
381
self ,
412
382
limit_amps : int = 32 ,
@@ -417,118 +387,108 @@ async def set_charge_rate(
417
387
"""Set charge rate."""
418
388
if profile is not None :
419
389
try :
420
- resp = await self .call (
421
- call .SetChargingProfile (
422
- connector_id = int (conn_id ), cs_charging_profiles = profile
423
- )
390
+ req = call .SetChargingProfile (
391
+ connector_id = int (conn_id ), cs_charging_profiles = profile
424
392
)
393
+ resp = await self .call (req )
425
394
if resp .status == ChargingProfileStatus .accepted :
426
395
return True
427
396
_LOGGER .warning ("Custom SetChargingProfile rejected: %s" , resp .status )
428
397
except Exception as ex :
429
398
_LOGGER .warning ("Custom SetChargingProfile failed: %s" , ex )
399
+ await self .notify_ha (
400
+ "Warning: Set charging profile failed with response Exception"
401
+ )
402
+ return False
403
+
404
+ if prof .SMART not in self ._attr_supported_features :
405
+ _LOGGER .info ("Smart charging is not supported by this charger" )
406
+ return False
430
407
431
- resp_units = await self .get_configuration (
408
+ # Determine allowed unit (default to Amps if not reported)
409
+ units_resp = await self .get_configuration (
432
410
ckey .charging_schedule_allowed_charging_rate_unit .value
433
411
)
434
- if resp_units is None :
435
- _LOGGER .warning ( "Failed to query charging rate unit, assuming Amps" )
436
- resp_units = om .current .value
412
+ if not units_resp :
413
+ _LOGGER .debug ( "Charging rate unit not reported; assuming Amps" )
414
+ units_resp = om .current .value
437
415
438
- use_amps = om .current .value in resp_units
439
- limit_val = float (limit_amps if use_amps else limit_watts )
440
- unit_val = (
416
+ use_amps = om .current .value in units_resp
417
+ limit_value = float (limit_amps if use_amps else limit_watts )
418
+ units_value = (
441
419
ChargingRateUnitType .amps .value
442
420
if use_amps
443
421
else ChargingRateUnitType .watts .value
444
422
)
445
423
446
- # Build attempt order (CPMax -> TxDefault -> TxProfile if active)
447
- attempts : list [tuple [int , str ]] = []
448
- attempts .append ((0 , "ChargePointMaxProfile" ))
449
- if conn_id and conn_id > 0 :
450
- attempts .append ((conn_id , "TxDefaultProfile" ))
451
-
452
- has_active = bool (getattr (self , "active_transaction_id" , 0 ))
453
- if has_active :
454
- tx_conn = next (
455
- (c for c , tx in getattr (self , "_active_tx" , {}).items () if tx ),
456
- conn_id or 1 ,
457
- )
458
- attempts .append ((tx_conn , "TxProfile" ))
459
-
460
- await self .clear_profile (
461
- None , ChargingProfilePurposeType .charge_point_max_profile
462
- )
463
- if conn_id and conn_id > 0 :
464
- await self .clear_profile (
465
- conn_id , ChargingProfilePurposeType .tx_default_profile
424
+ # Read max stack level (default to 1 on parse errors)
425
+ try :
426
+ stack_level_resp = await self .get_configuration (
427
+ ckey .charge_profile_max_stack_level .value
466
428
)
429
+ stack_level = int (stack_level_resp )
430
+ except Exception :
431
+ stack_level = 1
467
432
468
- def _mk_profile (purpose : str , cid : int ) -> dict :
469
- tx_id = (
470
- self .active_transaction_id
471
- if (purpose == "TxProfile" and has_active )
472
- else None
473
- )
474
- pid , stack = self ._profile_ids_for (cid , purpose , tx_id = tx_id )
433
+ # Helper to build a simple relative schedule with one period
434
+ def _mk_schedule (_units : str , _limit : float ) -> dict :
475
435
return {
476
- om .charging_profile_id .value : pid ,
477
- om .stack_level .value : stack ,
478
- om .charging_profile_kind .value : ChargingProfileKindType .relative .value ,
479
- om .charging_profile_purpose .value : purpose ,
480
- om .charging_schedule .value : {
481
- om .charging_rate_unit .value : unit_val ,
482
- om .charging_schedule_period .value : [
483
- {om .start_period .value : 0 , om .limit .value : limit_val }
484
- ],
485
- },
436
+ om .charging_rate_unit .value : _units ,
437
+ om .charging_schedule_period .value : [
438
+ {om .start_period .value : 0 , om .limit .value : _limit }
439
+ ],
486
440
}
487
441
488
- # Try each purpose/connector in order; optionally clear-by-id before setting
489
- last_status = None
490
- for cid , purpose in attempts :
491
- try :
492
- try :
493
- tx_id = (
494
- self .active_transaction_id
495
- if (purpose == "TxProfile" and has_active )
496
- else None
497
- )
498
- pid , _ = self ._profile_ids_for (cid , purpose , tx_id = tx_id )
499
- await self .call (call .ClearChargingProfile (id = pid ))
500
- except Exception :
501
- pass
502
-
503
- req = call .SetChargingProfile (
504
- connector_id = cid , cs_charging_profiles = _mk_profile (purpose , cid )
505
- )
506
- resp = await self .call (req )
507
- last_status = resp .status
508
- if resp .status == ChargingProfileStatus .accepted :
509
- _LOGGER .debug (
510
- "SetChargingProfile accepted with purpose=%s connectorId=%s" ,
511
- purpose ,
512
- cid ,
513
- )
514
- return True
515
- _LOGGER .debug (
516
- "SetChargingProfile %s on connector %s -> %s" ,
517
- purpose ,
518
- cid ,
519
- resp .status ,
520
- )
521
- except Exception as ex :
522
- _LOGGER .debug (
523
- "SetChargingProfile %s on connector %s raised %s" , purpose , cid , ex
524
- )
442
+ # Try ChargePointMaxProfile (connectorId = 0)
443
+ try :
444
+ req = call .SetChargingProfile (
445
+ connector_id = 0 ,
446
+ cs_charging_profiles = {
447
+ om .charging_profile_id .value : 8 ,
448
+ om .stack_level .value : stack_level ,
449
+ om .charging_profile_kind .value : ChargingProfileKindType .relative .value ,
450
+ om .charging_profile_purpose .value : ChargingProfilePurposeType .charge_point_max_profile .value ,
451
+ om .charging_schedule .value : _mk_schedule (units_value , limit_value ),
452
+ },
453
+ )
454
+ resp = await self .call (req )
455
+ if resp .status == ChargingProfileStatus .accepted :
456
+ return True
457
+ _LOGGER .debug (
458
+ "ChargePointMaxProfile not accepted (%s); will try TxDefaultProfile." ,
459
+ resp .status ,
460
+ )
461
+ except Exception as ex :
462
+ _LOGGER .debug ("ChargePointMaxProfile call raised: %s" , ex )
525
463
526
- if last_status is not None :
527
- _LOGGER .warning ("SetChargingProfile failed (last status=%s)." , last_status )
464
+ # Fallback: TxDefaultProfile on target connector
465
+ # If no connector given, prefer 1 as a reasonable default.
466
+ target_cid = int (conn_id ) if conn_id and int (conn_id ) > 0 else 1
467
+ try :
468
+ # Some chargers are picky: try a slightly lower stack level if possible.
469
+ tx_stack = max (1 , stack_level - 1 )
470
+ req = call .SetChargingProfile (
471
+ connector_id = target_cid ,
472
+ cs_charging_profiles = {
473
+ om .charging_profile_id .value : 8 ,
474
+ om .stack_level .value : tx_stack ,
475
+ om .charging_profile_kind .value : ChargingProfileKindType .relative .value ,
476
+ om .charging_profile_purpose .value : ChargingProfilePurposeType .tx_default_profile .value ,
477
+ om .charging_schedule .value : _mk_schedule (units_value , limit_value ),
478
+ },
479
+ )
480
+ resp = await self .call (req )
481
+ if resp .status == ChargingProfileStatus .accepted :
482
+ return True
483
+ _LOGGER .warning ("Set TxDefaultProfile rejected: %s" , resp .status )
528
484
await self .notify_ha (
529
- f"SetChargingProfile failed (last status= { last_status } ). "
485
+ f"Warning: Set charging profile failed with response { resp . status } "
530
486
)
531
- return False
487
+ return False
488
+ except Exception as ex :
489
+ _LOGGER .warning ("Set TxDefaultProfile failed: %s" , ex )
490
+ await self .notify_ha (f"Warning: Set charging profile failed: { ex } " )
491
+ return False
532
492
533
493
async def set_availability (self , state : bool = True , connector_id : int | None = 0 ):
534
494
"""Change availability."""
0 commit comments