lbbrhzn / ocpp

Home Assistant integration for electric vehicle chargers that support the Open Charge Point Protocol (OCPP).
MIT License
227 stars 71 forks source link

Growatt Thor 22AS - Unable to change maximum current #1293

Closed Plawasan closed 1 month ago

Plawasan commented 2 months ago

Version of the custom_component

v0.5.9

Configuration

image

Describe the bug

I'm trying to use basic controls for the Growatt Thor 22AS charger which should support OCPP 1.6. I have been able to add it to HA with the change implemented in https://github.com/lbbrhzn/ocpp/pull/1283, I can now see the charger is communicating with HA (e.g. the error state changes correctly depending on the status of the emergency switch on the charger) however when I try to change the only control the integration now provides (maximum charging current) I get the following errors in the UI and in logs:

Debug log

GUI: Failed to perform the action number/set_value. list index out of range

Logs:
Logger: homeassistant.components.websocket_api.http.connection
Source: components/websocket_api/commands.py:241
integration: Home Assistant WebSocket API (documentation, issues)
First occurred: 21:56:45 (1 occurrences)
Last logged: 21:56:45

[140093852333200] Unexpected exception
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/components/websocket_api/commands.py", line 241, in handle_call_service
    response = await hass.services.async_call(
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/core.py", line 2763, in async_call
    response_data = await coro
                    ^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/core.py", line 2806, in _execute_service
    return await target(service_call)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/helpers/service.py", line 999, in entity_service_call
    single_response = await _handle_entity_call(
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/helpers/service.py", line 1071, in _handle_entity_call
    result = await task
             ^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/components/number/__init__.py", line 120, in async_set_value
    await entity.async_set_native_value(native_value)
  File "/config/custom_components/ocpp/number.py", line 129, in async_set_native_value
    resp = await self.central_system.set_max_charge_rate_amps(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/config/custom_components/ocpp/api.py", line 298, in set_max_charge_rate_amps
    return await self.charge_points[cp_id].set_charge_rate(limit_amps=value)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/config/custom_components/ocpp/api.py", line 674, in set_charge_rate
    resp = await self.get_configuration(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/config/custom_components/ocpp/api.py", line 895, in get_configuration
    value = resp.configuration_key[0][om.value.value]
            ~~~~~~~~~~~~~~~~~~~~~~^^^
IndexError: list index out of range

Let me know if there's anything else I can do/provide to help troubleshoot it, I'm stuck with this charger as it was part of a solar install so I'm very happy to do anything within my powers to get it supported at least on a basic level.

malaakso commented 2 months ago

Can you get the full logs to see what the charger actually responds with? Could be similar to #1278

Plawasan commented 2 months ago

Happy to, but this is all I get even with ocpp debug logging enabled:

logger:
  default: info
  logs:
    custom_components.ocpp: debug
    websockets.server: debug

Anything else I need to enable to get more detailed logging? I assume everything is stored in the HA Core log.. or should I be looking someplace else?

malaakso commented 2 months ago

I'm looking for lines like

2024-08-20 17:24:56.423 INFO (MainThread) [ocpp] 0312116102322520367: send [2,"2920a1b9-18ae-4650-926d-1a2bf5b7d952","GetConfiguration",{"key":["ChargingScheduleAllowedChargingRateUnit"]}]
2024-08-20 17:24:56.599 INFO (MainThread) [ocpp] 0312116102322520367: receive message [3, "2920a1b9-18ae-4650-926d-1a2bf5b7d952", {"unknownKey":["ChargingScheduleAllowedChargingRateUnit"]}]

maybe they only appear in stdout.

Plawasan commented 2 months ago

ah, thanks for the hint:

This is what happens when I try to change the charging current:

homeassistant:/config# tail -f home-assistant.log | grep ocpp
2024-08-31 09:30:26.591 DEBUG (MainThread) [custom_components.ocpp] Connection latency from 'central' to 'XGJ0000321510194': ping=1.0 ms, pong=172.0 ms
2024-08-31 09:30:28.348 INFO (MainThread) [ocpp] XGJ0000321510194: send [2,"d7b37b6a-0aa8-474e-948a-2b8ddc067426","GetConfiguration",{"key":["ChargingScheduleAllowedChargingRateUnit"]}]
2024-08-31 09:30:28.537 INFO (MainThread) [ocpp] XGJ0000321510194: receive message [3,"d7b37b6a-0aa8-474e-948a-2b8ddc067426",{"configurationKey":[],"unknownKey":["ChargingScheduleAllowedChargingRateUnit"]}]
  File "/config/custom_components/ocpp/number.py", line 129, in async_set_native_value
  File "/config/custom_components/ocpp/api.py", line 298, in set_max_charge_rate_amps
  File "/config/custom_components/ocpp/api.py", line 674, in set_charge_rate
  File "/config/custom_components/ocpp/api.py", line 895, in get_configuration

for reference this is toggling availability (off and then on) which works ok:

2024-08-31 09:33:16.558 INFO (MainThread) [ocpp] XGJ0000321510194: send [2,"96c9fd0b-2519-4174-9614-201f126ef71f","ChangeAvailability",{"connectorId":0,"type":"Inoperative"}]
2024-08-31 09:33:16.679 INFO (MainThread) [ocpp] XGJ0000321510194: receive message [3,"96c9fd0b-2519-4174-9614-201f126ef71f",{"status":"Accepted"}]
2024-08-31 09:33:16.784 INFO (MainThread) [ocpp] XGJ0000321510194: receive message [2,"119","StatusNotification",{"connectorId":1,"errorCode":"OtherError","info":"","status":"Unavailable"}]
2024-08-31 09:33:16.797 INFO (MainThread) [ocpp] XGJ0000321510194: send [3,"119",{}]
2024-08-31 09:33:27.942 DEBUG (MainThread) [custom_components.ocpp] Connection latency from 'central' to 'XGJ0000321510194': ping=1.0 ms, pong=171.0 ms

2024-08-31 09:33:33.488 INFO (MainThread) [ocpp] XGJ0000321510194: send [2,"9673b652-db66-4d0d-8cc7-7cf9ebf48377","ChangeAvailability",{"connectorId":0,"type":"Operative"}]
2024-08-31 09:33:33.677 INFO (MainThread) [ocpp] XGJ0000321510194: receive message [3,"9673b652-db66-4d0d-8cc7-7cf9ebf48377",{"status":"Accepted"}]
2024-08-31 09:33:33.677 INFO (MainThread) [ocpp] XGJ0000321510194: receive message [2,"120","StatusNotification",{"connectorId":1,"errorCode":"NoError","info":"","status":"Available"}]
2024-08-31 09:33:33.686 INFO (MainThread) [ocpp] XGJ0000321510194: send [3,"120",{}]
malaakso commented 2 months ago

Thanks, this is same as my problem, but my charger doesn't report the empty configurationKey array, only the unknownKey. I suppose we should also check for empty array there, after that the workaround from #1278 should work for you as well. I'll create a PR.

Plawasan commented 2 months ago

Also not sure if this is helpful but here's what Steve gives me when I poll the charger for configuration:

Key | Value | Read only?
-- | -- | --
G_ChargerID | XGJ0000321510194 | false
G_ChargerRate | 1.00 | false
G_ChargerLanguage | English | false
G_MaxCurrent | 25.00 | false
G_ChargerMode | 2 | false
G_CardPin | xxxxxxxx | false
G_Authentication | xxxxxxxx | false
G_ChargerNetIP | 192.168.1.5 | false
G_MaxTemperature | 80 | false
G_ExternalLimitPower | 45 | false
G_ExternalLimitPowerEnable | 0 | false
G_ExternalSamplingCurWring | 1 | false
G_SolarMode | 1&2 | false
G_SolarLimitPower | 3.96 | false
G_PeakValleyEnable | 0 | false
G_AutoChargeTime | 00:00-00:00 | false
G_RCDProtection | 6 | false
G_PowerMeterAddr | 4 | false
G_PowerMeterType | Din-Rail DTSU666 MID | false
G_TimeZone | UTC+02:00 | false
G_ServerURL | ws://192.168.1.7:8180/steve/websocket/CentralSystemService/ | false
G_RandDelayChargeTime | 0 | false
G_WebSocketPingInterval | 60 | false
HeartbeatInterval | 14400 | false
MeterValueSampleInterval | 60 | false
ConnectionTimeOut | 90 | false
LocalAuthorizeOffline | false | false
AuthorizationCacheEnabled | false | false
LocalPreAuthorize | false | false
LocalAuthListEnabled | false | false
AuthorizeRemoteTxRequests | false | false
WebSocketPingInterval | 60 | false
AllowOfflineTxForUnknownId | false | false
MeterValuesSampledData | Energy.Active.Import.Register | false

When I then try to change the charger rate using the G_MaxCurrent key, it works as expected:

Getting the value (btw how do I get to the result of this call in HA?)

2024-08-31 10:07:53.933 INFO (MainThread) [ocpp] XGJ0000321510194: send [2,"224b4a1c-c613-4bb5-8cda-acd5172a7c8b","GetConfiguration",{"key":["G_MaxCurrent"]}]
2024-08-31 10:07:54.180 INFO (MainThread) [ocpp] XGJ0000321510194: receive message [3,"224b4a1c-c613-4bb5-8cda-acd5172a7c8b",{"configurationKey":[{"key":"G_MaxCurrent","value":"25.00","readonly":false}],"unknownKey":[]}]
2024-08-31 10:07:54.181 DEBUG (MainThread) [custom_components.ocpp] Get Configuration for G_MaxCurrent: 25.00

Changing it via ocpp.configure:

2024-08-31 10:12:16.953 INFO (MainThread) [ocpp] XGJ0000321510194: send [2,"8b548330-381d-4d99-8d5e-7cd00bbd16f6","GetConfiguration",{"key":["G_MaxCurrent"]}]
2024-08-31 10:12:17.247 INFO (MainThread) [ocpp] XGJ0000321510194: receive message [3,"8b548330-381d-4d99-8d5e-7cd00bbd16f6",{"configurationKey":[{"key":"G_MaxCurrent","value":"10.00","readonly":false}],"unknownKey":[]}]
2024-08-31 10:12:17.248 INFO (MainThread) [ocpp] XGJ0000321510194: send [2,"0d8e7cb5-0c52-44b3-bc95-c4fd26f9977d","ChangeConfiguration",{"key":"G_MaxCurrent","value":"12"}]
2024-08-31 10:12:17.645 INFO (MainThread) [ocpp] XGJ0000321510194: receive message [3,"0d8e7cb5-0c52-44b3-bc95-c4fd26f9977d",{"status":"Accepted"}]

Confirming it was changed:

2024-08-31 10:13:04.356 DEBUG (MainThread) [custom_components.ocpp] Connection latency from 'central' to 'XGJ0000321510194': ping=1.0 ms, pong=233.0 ms
2024-08-31 10:13:18.532 INFO (MainThread) [ocpp] XGJ0000321510194: send [2,"df283e0e-f5b5-4fb8-adbd-a33041c5ded0","GetConfiguration",{"key":["G_MaxCurrent"]}]
2024-08-31 10:13:18.687 INFO (MainThread) [ocpp] XGJ0000321510194: receive message [3,"df283e0e-f5b5-4fb8-adbd-a33041c5ded0",{"configurationKey":[{"key":"G_MaxCurrent","value":"12.00","readonly":false}],"unknownKey":[]}]
2024-08-31 10:13:18.687 DEBUG (MainThread) [custom_components.ocpp] Get Configuration for G_MaxCurrent: 12.00

However when I try to use ocpp.charge_rate I get an error:

action: ocpp.set_charge_rate
data:
  limit_amps: 16

024-08-31 10:15:00.863 INFO (MainThread) [ocpp] XGJ0000321510194: send [2,"8921694b-812f-4f26-ae32-dfb2d29bdf31","GetConfiguration",{"key":["ChargingScheduleAllowedChargingRateUnit"]}]
2024-08-31 10:15:01.088 INFO (MainThread) [ocpp] XGJ0000321510194: receive message [3,"8921694b-812f-4f26-ae32-dfb2d29bdf31",{"configurationKey":[],"unknownKey":["ChargingScheduleAllowedChargingRateUnit"]}]
  File "/config/custom_components/ocpp/api.py", line 454, in handle_set_charge_rate
  File "/config/custom_components/ocpp/api.py", line 674, in set_charge_rate
  File "/config/custom_components/ocpp/api.py", line 895, in get_configuration
  File "/config/custom_components/ocpp/api.py", line 454, in handle_set_charge_rate
  File "/config/custom_components/ocpp/api.py", line 674, in set_charge_rate
  File "/config/custom_components/ocpp/api.py", line 895, in get_configuration

So is this Growatt defining their own custom keys for something that should be standardized?

malaakso commented 2 months ago

Yes, configuration keys starting with G_ are proprietary. In OCPP specification charging rate is controlled with charging profiles, probably your charger supports that mechanism as well. Of course you can also make custom automations and switches by using ocpp.configure with proprietary configuration options.

Plawasan commented 2 months ago

Thanks, I'll play around with it. Still one question though - how do I get to the return value of ocpp.get_configuration outside of the stdout log, i.e. for a template sensor? I tried using return_variable in a script but that doesn't seem to be supported for this action.

I'll keep an eye out for the PR and will test it, thanks again!

Plawasan commented 2 months ago

Quick feedback on the PR: I've replaced my api.py with the updated version (assuming it's the only thing that has changed against 0.5.9), I still can't change max current:

Failed to perform the action number/set_value. int() argument must be a string, a bytes-like object or a real number, not 'NoneType'

2024-08-31 12:13:22.049 INFO (MainThread) [ocpp] XGJ0000321510194: send [2,"34f0a094-87fe-4f2b-863d-682484d0755a","GetConfiguration",{"key":["ChargingScheduleAllowedChargingRateUnit"]}]
2024-08-31 12:13:22.132 INFO (MainThread) [ocpp] XGJ0000321510194: receive message [3,"34f0a094-87fe-4f2b-863d-682484d0755a",{"configurationKey":[],"unknownKey":["ChargingScheduleAllowedChargingRateUnit"]}]
2024-08-31 12:13:22.132 WARNING (MainThread) [custom_components.ocpp] Get Configuration returned unknown key for: ChargingScheduleAllowedChargingRateUnit
2024-08-31 12:13:22.133 INFO (MainThread) [custom_components.ocpp] Charger supports setting the following units: None
2024-08-31 12:13:22.133 INFO (MainThread) [custom_components.ocpp] If more than one unit supported default unit is Amps
2024-08-31 12:13:22.133 WARNING (MainThread) [custom_components.ocpp] Failed to query charging rate unit, assuming Amps
2024-08-31 12:13:22.133 INFO (MainThread) [ocpp] XGJ0000321510194: send [2,"787fb975-d096-47fb-b0f7-adaeaa164619","GetConfiguration",{"key":["ChargeProfileMaxStackLevel"]}]
2024-08-31 12:13:22.209 INFO (MainThread) [ocpp] XGJ0000321510194: receive message [3,"787fb975-d096-47fb-b0f7-adaeaa164619",{"configurationKey":[],"unknownKey":["ChargeProfileMaxStackLevel"]}]
2024-08-31 12:13:22.210 WARNING (MainThread) [custom_components.ocpp] Get Configuration returned unknown key for: ChargeProfileMaxStackLevel
  File "/config/custom_components/ocpp/number.py", line 129, in async_set_native_value
  File "/config/custom_components/ocpp/api.py", line 298, in set_max_charge_rate_amps
  File "/config/custom_components/ocpp/api.py", line 698, in set_charge_rate

I'm starting to suspect OCPP 1.6 compliant is a bit of a stretch on Growatt side...

drc38 commented 2 months ago

Thanks, I'll play around with it. Still one question though - how do I get to the return value of ocpp.get_configuration outside of the stdout log, i.e. for a template sensor? I tried using return_variable in a script but that doesn't seem to be supported for this action.

I'll keep an eye out for the PR and will test it, thanks again!

The response appears in the attributes of this sensor: sensor.charger_timestamp_config_response

malaakso commented 2 months ago

You can also try to call the set maximum charge rate action with payload like

action: ocpp.set_charge_rate
data:
  custom_profile:
    chargingProfileId: 8
    stackLevel: 0
    chargingProfileKind: Relative
    chargingProfilePurpose: ChargePointMaxProfile
    chargingSchedule:
      chargingRateUnit: A
      chargingSchedulePeriod:
        - startPeriod: 0
          limit: 8

and see if that is accepted.

Plawasan commented 2 months ago

That seems to work OK:

2024-09-01 13:45:37.755 INFO (MainThread) [ocpp] XGJ00003215101B9: send [2,"68106fb5-1543-489f-b8df-3dc0ec68c179","SetChargingProfile",{"connectorId":0,"csChargingProfiles":{"chargingProfileId":8,"stackLevel":0,"chargingProfileKind":"Relative","chargingProfilePurpose":"ChargePointMaxProfile","chargingSchedule":{"chargingRateUnit":"A","chargingSchedulePeriod":[{"startPeriod":0,"limit":8}]}}}]
2024-09-01 13:45:38.242 INFO (MainThread) [ocpp] XGJ00003215101B9: receive message [3,"68106fb5-1543-489f-b8df-3dc0ec68c179",{"status":"Accepted"}]

However when I then check G_MaxCurrent, it's still set to 25 although I'm out of my depth here as to whether those two actually control the same thing:

2024-09-01 13:46:45.520 INFO (MainThread) [ocpp] XGJ00003215101B9: send [2,"a8802e05-4367-48ab-b10d-9c52a8b2a461","GetConfiguration",{"key":["G_MaxCurrent"]}]
2024-09-01 13:46:45.720 INFO (MainThread) [ocpp] XGJ00003215101B9: receive message [3,"a8802e05-4367-48ab-b10d-9c52a8b2a461",{"configurationKey":[{"key":"G_MaxCurrent","value":"25.00","readonly":false}],"unknownKey":[]}]

Ultimately I just need to figure out which way actually controls the charging current to the EV and stick with that.