skodaconnect / homeassistant-myskoda

Homeassistant integration for MySkoda.
79 stars 12 forks source link

Consider limitting outstanding requests to 1 #5

Closed dvx76 closed 3 weeks ago

dvx76 commented 2 months ago

Currently requests, e.g. to change the charge limit, will just queue and end up all executing. The 'problem' is that these requests can take a lot of time (10's of seconds, used to be more like minutes in the past).

E.g. if you change the charge limit to 90 and then to 80, you'll get something like this:

2024-09-15 13:44:33.669 INFO (MainThread) [myskoda.rest_api] Setting charge limit to 90.0
2024-09-15 13:44:48.539 INFO (MainThread) [myskoda.rest_api] Setting charge limit to 80.0
2024-09-15 13:44:50.141 DEBUG (MainThread) [custom_components.myskoda] Finished fetching myskoda data in 1.082 seconds (success: True)
2024-09-15 13:45:07.408 DEBUG (MainThread) [custom_components.myskoda] Finished fetching myskoda data in 2.264 seconds (success: True)
2024-09-15 13:45:08.801 DEBUG (MainThread) [custom_components.myskoda] Finished fetching myskoda data in 5.156 seconds (success: True)
2024-09-15 13:45:25.169 DEBUG (MainThread) [custom_components.myskoda] Finished fetching myskoda data in 2.758 seconds (success: True)
2024-09-15 13:45:26.552 DEBUG (MainThread) [custom_components.myskoda] Finished fetching myskoda data in 2.748 seconds (success: True)
2024-09-15 13:45:43.151 DEBUG (MainThread) [custom_components.myskoda] Finished fetching myskoda data in 2.979 seconds (success: True)
2024-09-15 13:45:44.485 DEBUG (MainThread) [custom_components.myskoda] Finished fetching myskoda data in 2.931 seconds (success: True)
2024-09-15 13:45:58.155 DEBUG (MainThread) [custom_components.myskoda.number] Changed charge limit to 90.0.
2024-09-15 13:46:02.314 DEBUG (MainThread) [custom_components.myskoda] Finished fetching myskoda data in 2.827 seconds (success: True)
2024-09-15 13:46:18.186 DEBUG (MainThread) [custom_components.myskoda] Finished fetching myskoda data in 0.868 seconds (success: True)
2024-09-15 13:46:35.144 DEBUG (MainThread) [custom_components.myskoda] Finished fetching myskoda data in 1.955 seconds (success: True)
2024-09-15 13:46:51.299 DEBUG (MainThread) [custom_components.myskoda] Finished fetching myskoda data in 1.153 seconds (success: True)
2024-09-15 13:47:08.281 DEBUG (MainThread) [custom_components.myskoda] Finished fetching myskoda data in 1.979 seconds (success: True)
2024-09-15 13:47:27.105 DEBUG (MainThread) [custom_components.myskoda] Finished fetching myskoda data in 3.819 seconds (success: True)
2024-09-15 13:47:47.610 DEBUG (MainThread) [custom_components.myskoda] Finished fetching myskoda data in 5.503 seconds (success: True)
2024-09-15 13:47:47.611 DEBUG (MainThread) [custom_components.myskoda.number] Changed charge limit to 80.0.

The old integration would cap the number of parallel requests to 1 I believe. Will try to find a reference.

We may also want to relax the poll frequency in the set_ methods (currently 15s) to avoid getting rate limited. Also wondering if we could update just the entity being (instead of everything, which I understand is what coordinator.async_refresh() will do.

I'm also curious what the best-practice is for HA when updating number entities where the execution of the update request is high asynchronous like here.

dvx76 commented 2 months ago

https://github.com/skodaconnect/skodaconnect/blob/75088af7795aa049dafb8442bf9dc0b5d817cdc7/skodaconnect/vehicle.py#L443C23-L443C54

        if self._requests['batterycharge'].get('id', False):
            timestamp = self._requests.get('batterycharge', {}).get('timestamp', datetime.now())
            expired = datetime.now() - timedelta(minutes=3)
            if expired > timestamp:
                self._requests.get('batterycharge', {}).pop('id')
            else:
                raise SkodaRequestInProgressException('Charging action already in progress')

If I'm not mistaken the Vehicle class keeps a map of outstanding requests by 'type' (e.g. batterycharge here) and if a previous requests is outstanding, the new request is rejected.

Prior99 commented 2 months ago

I will integrate the MQTT server that Skoda is using to push updates to the app in the next days, and then we don't need to poll anymore at all :)

On Sun, Sep 15, 2024, 14:45 Fabrice Devaux @.***> wrote:

https://github.com/skodaconnect/skodaconnect/blob/75088af7795aa049dafb8442bf9dc0b5d817cdc7/skodaconnect/vehicle.py#L443C23-L443C54

    if self._requests['batterycharge'].get('id', False):
        timestamp = self._requests.get('batterycharge', {}).get('timestamp', datetime.now())
        expired = datetime.now() - timedelta(minutes=3)
        if expired > timestamp:
            self._requests.get('batterycharge', {}).pop('id')
        else:
            raise SkodaRequestInProgressException('Charging action already in progress')

If I'm not mistaken the Vehicle class keeps a map of outstanding requests by 'type' (e.g. batterycharge here) and if a previous requests is outstanding, the new request is rejected.

— Reply to this email directly, view it on GitHub https://github.com/skodaconnect/homeassistant-myskoda/issues/5#issuecomment-2351577373, or unsubscribe https://github.com/notifications/unsubscribe-auth/AALME5RYZTPVPC6KSC3P5ATZWV6M5AVCNFSM6AAAAABOHZB6BOVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDGNJRGU3TOMZXGM . You are receiving this because you are subscribed to this thread.Message ID: @.***>

Prior99 commented 1 month ago

@dvx76 do we need this, even if we implement debouncing?

dvx76 commented 1 month ago

I guess it depends how the debouncing handles multiple successive requests. In the charge limit example, say it's currently at 100 and the entity is changed to 90, initiating a first request, then before it completes, it's changed to 80 and then 70. How does it work with the debouncer and what is the end result.

I haven't looked at your PR yet though, so maybe I find the answer myself.

Prior99 commented 1 month ago

Tbh I have no clue :shrug:

WebSpider commented 1 month ago

Tbh I have no clue :shrug:

I dont either, but with a bit of debug logging we should be able to determine if the debouncer actually debounces up to a minute of calls into one.

WebSpider commented 1 month ago

While I was looking for solutions for some climate sensor related issues, I found the Throttle decorator in HA-core: https://github.com/home-assistant/core/blob/9921a67a05bdb1b1a17b08b1bebbde7f90b250c3/homeassistant/util/__init__.py#L100

WebSpider commented 1 month ago

Throttle effectively ignores all successive calls except the first one, for a configurable timeperiod.

Basic implementation is in https://github.com/skodaconnect/homeassistant-myskoda/pull/55/

WebSpider commented 3 weeks ago

@dvx76 do you feel this is still relevant?

dvx76 commented 3 weeks ago

Kinda, yes. But with the changes from #55 I'd rather close this and see if there are any actual issues experienced by users. Then new issues can be opened. No need to further pre-optimize IMO.