tomekkleszcz / homebridge-electrolux-devices

Homebridge plugin for Electrolux devices
Apache License 2.0
21 stars 4 forks source link

Access Token not being refreshed when expired (after 12h) #9

Closed dezeroku closed 6 months ago

dezeroku commented 1 year ago

First of all, thanks a lot for this project, it's great!

Describe The Bug:

After the token expires (12h since being issued), it's not refreshed automatically. This results in a "flat line" in my graphs, showing that the plugin reports last known value as current. Had this happen 3 times in a row (in 12h intervals), restarting the plugin via Homebridge UI helps. Also no "Refreshing access token" is seen in logs, as expected by this line.

A dummy fix also seems to show that the logic itself is fine, it's just not being called (probably this can be solved in a cleaner way with some setIntervals, but I don't really know JS), I haven't seen the issue anymore with it applied.

My use-case might be a bit special, as I basically only use it for polling. It wouldn't really be visible without having graphs, just stuck on the last known value. But I think it breaks the "issuing commands" functionality.

To Reproduce:

Start the plugin and let it poll for more than 12 hours without restarts.

Expected behavior:

Access token is refreshed correctly and no downtime is seen.

Logs: Collected with debug mode enabled:

(Click the arrow to get the full trace) The gist is that we get a 403 with: data: { Message: 'User is not authorized to access this resource with an explicit deny' } [8/7/2023, 3:08:29 PM] [homebridge-electrolux-devices] Polling error: AxiosError: Request failed with status code 403 at settle (/homebridge/node_modules/homebridge-electrolux-devices/node_modules/axios/lib/core/settle.js:19:12) at IncomingMessage.handleStreamEnd (/homebridge/node_modules/homebridge-electrolux-devices/node_modules/axios/lib/adapters/http.js:570:11) at IncomingMessage.emit (node:events:525:35) at endReadableNT (node:internal/streams/readable:1359:12) at processTicksAndRejections (node:internal/process/task_queues:82:21) { code: 'ERR_BAD_REQUEST', config: { transitional: { silentJSONParsing: true, forcedJSONParsing: true, clarifyTimeoutError: false }, adapter: [ 'xhr', 'http' ], transformRequest: [ [Function: transformRequest] ], transformResponse: [ [Function: transformResponse] ], timeout: 0, xsrfCookieName: 'XSRF-TOKEN', xsrfHeaderName: 'X-XSRF-TOKEN', maxContentLength: -1, maxBodyLength: -1, env: { FormData: [Function], Blob: [class Blob] }, validateStatus: [Function: validateStatus], headers: AxiosHeaders { Accept: 'application/json', 'Accept-Encoding': 'gzip, deflate, br', 'Accept-Charset': 'utf-8', 'x-api-key': 'REDACTED', 'User-Agent': 'Ktor client', Authorization: 'Bearer REDACTED' }, baseURL: 'https://api.eu.ocp.electrolux.one/appliance/api/v2', method: 'get', url: '/appliances', data: undefined }, request: ClientRequest { _events: [Object: null prototype] { abort: [Function (anonymous)], aborted: [Function (anonymous)], connect: [Function (anonymous)], error: [Function (anonymous)], socket: [Function (anonymous)], timeout: [Function (anonymous)], finish: [Function: requestOnFinish] }, _eventsCount: 7, _maxListeners: undefined, outputData: [], outputSize: 0, writable: true, destroyed: false, _last: true, chunkedEncoding: false, shouldKeepAlive: false, maxRequestsOnConnectionReached: false, _defaultKeepAlive: true, useChunkedEncodingByDefault: false, sendDate: false, _removedConnection: false, _removedContLen: false, _removedTE: false, strictContentLength: false, _contentLength: 0, _hasBody: true, _trailer: '', finished: true, _headerSent: true, _closed: false, socket: TLSSocket { _tlsOptions: [Object], _secureEstablished: true, _securePending: false, _newSessionPending: false, _controlReleased: true, secureConnecting: false, _SNICallback: null, servername: 'api.eu.ocp.electrolux.one', alpnProtocol: false, authorized: true, authorizationError: null, encrypted: true, _events: [Object: null prototype], _eventsCount: 10, connecting: false, _hadError: false, _parent: null, _host: 'api.eu.ocp.electrolux.one', _closeAfterHandlingError: false, _readableState: [ReadableState], _maxListeners: undefined, _writableState: [WritableState], allowHalfOpen: false, _sockname: null, _pendingData: null, _pendingEncoding: '', server: undefined, _server: null, ssl: [TLSWrap], _requestCert: true, _rejectUnauthorized: true, parser: null, _httpMessage: [Circular *1], [Symbol(res)]: [TLSWrap], [Symbol(verified)]: true, [Symbol(pendingSession)]: null, [Symbol(async_id_symbol)]: 202536, [Symbol(kHandle)]: [TLSWrap], [Symbol(lastWriteQueueSize)]: 0, [Symbol(timeout)]: null, [Symbol(kBuffer)]: null, [Symbol(kBufferCb)]: null, [Symbol(kBufferGen)]: null, [Symbol(kCapture)]: false, [Symbol(kSetNoDelay)]: false, [Symbol(kSetKeepAlive)]: true, [Symbol(kSetKeepAliveInitialDelay)]: 60, [Symbol(kBytesRead)]: 0, [Symbol(kBytesWritten)]: 0, [Symbol(connect-options)]: [Object] }, _header: 'GET /appliance/api/v2/appliances HTTP/1.1\r\n' + 'Accept: application/json\r\n' + 'Accept-Encoding: gzip, deflate, br\r\n' + 'Accept-Charset: utf-8\r\n' + 'x-api-key: REDACTED\r\n' + 'User-Agent: Ktor client\r\n' + 'Authorization: Bearer REDACTED\r\n' + 'Host: api.eu.ocp.electrolux.one\r\n' + 'Connection: close\r\n' + '\r\n', _keepAliveTimeout: 0, _onPendingData: [Function: nop], agent: Agent { _events: [Object: null prototype], _eventsCount: 2, _maxListeners: undefined, defaultPort: 443, protocol: 'https:', options: [Object: null prototype], requests: [Object: null prototype] {}, sockets: [Object: null prototype], freeSockets: [Object: null prototype] {}, keepAliveMsecs: 1000, keepAlive: false, maxSockets: Infinity, maxFreeSockets: 256, scheduling: 'lifo', maxTotalSockets: Infinity, totalSocketCount: 1, maxCachedSessions: 100, _sessionCache: [Object], [Symbol(kCapture)]: false }, socketPath: undefined, method: 'GET', maxHeaderSize: undefined, insecureHTTPParser: undefined, joinDuplicateHeaders: undefined, path: '/appliance/api/v2/appliances', _ended: true, res: IncomingMessage { _readableState: [ReadableState], _events: [Object: null prototype], _eventsCount: 4, _maxListeners: undefined, socket: [TLSSocket], httpVersionMajor: 1, httpVersionMinor: 1, httpVersion: '1.1', complete: true, rawHeaders: [Array], rawTrailers: [], joinDuplicateHeaders: undefined, aborted: false, upgrade: false, url: '', method: null, statusCode: 403, statusMessage: 'Forbidden', client: [TLSSocket], _consuming: false, _dumped: false, req: [Circular *1], responseUrl: 'https://api.eu.ocp.electrolux.one/appliance/api/v2/appliances', redirects: [], [Symbol(kCapture)]: false, [Symbol(kHeaders)]: [Object], [Symbol(kHeadersCount)]: 16, [Symbol(kTrailers)]: null, [Symbol(kTrailersCount)]: 0 }, aborted: false, timeoutCb: null, upgradeOrConnect: false, parser: null, maxHeadersCount: null, reusedSocket: false, host: 'api.eu.ocp.electrolux.one', protocol: 'https:', _redirectable: Writable { _writableState: [WritableState], _events: [Object: null prototype], _eventsCount: 3, _maxListeners: undefined, _options: [Object], _ended: true, _ending: true, _redirectCount: 0, _redirects: [], _requestBodyLength: 0, _requestBodyBuffers: [], _onNativeResponse: [Function (anonymous)], _currentRequest: [Circular *1], _currentUrl: 'https://api.eu.ocp.electrolux.one/appliance/api/v2/appliances', [Symbol(kCapture)]: false }, [Symbol(kCapture)]: false, [Symbol(kBytesWritten)]: 0, [Symbol(kNeedDrain)]: false, [Symbol(corked)]: 0, [Symbol(kOutHeaders)]: [Object: null prototype] { accept: [Array], 'accept-encoding': [Array], 'accept-charset': [Array], 'x-api-key': [Array], 'user-agent': [Array], authorization: [Array], host: [Array] }, [Symbol(errored)]: null, [Symbol(kUniqueHeaders)]: null }, response: { status: 403, statusText: 'Forbidden', headers: AxiosHeaders { date: 'Mon, 07 Aug 2023 13:08:29 GMT', 'content-type': 'application/json', 'content-length': '83', connection: 'close', 'x-amzn-requestid': 'REDACTED', 'x-amzn-errortype': 'AccessDeniedException', 'x-amz-apigw-id': 'REDACTED', 'x-amzn-trace-id': 'REDACTED' }, config: { transitional: [Object], adapter: [Array], transformRequest: [Array], transformResponse: [Array], timeout: 0, xsrfCookieName: 'XSRF-TOKEN', xsrfHeaderName: 'X-XSRF-TOKEN', maxContentLength: -1, maxBodyLength: -1, env: [Object], validateStatus: [Function: validateStatus], headers: [AxiosHeaders], baseURL: 'https://api.eu.ocp.electrolux.one/appliance/api/v2', method: 'get', url: '/appliances', data: undefined }, request: ClientRequest { _events: [Object: null prototype], _eventsCount: 7, _maxListeners: undefined, outputData: [], outputSize: 0, writable: true, destroyed: false, _last: true, chunkedEncoding: false, shouldKeepAlive: false, maxRequestsOnConnectionReached: false, _defaultKeepAlive: true, useChunkedEncodingByDefault: false, sendDate: false, _removedConnection: false, _removedContLen: false, _removedTE: false, strictContentLength: false, _contentLength: 0, _hasBody: true, _trailer: '', finished: true, _headerSent: true, _closed: false, socket: [TLSSocket], _header: 'GET /appliance/api/v2/appliances HTTP/1.1\r\n' + 'Accept: application/json\r\n' + 'Accept-Encoding: gzip, deflate, br\r\n' + 'Accept-Charset: utf-8\r\n' + 'x-api-key: REDACTED\r\n' + 'User-Agent: Ktor client\r\n' + 'Authorization: Bearer REDACTED\r\n' + 'Host: api.eu.ocp.electrolux.one\r\n' + 'Connection: close\r\n' + '\r\n', _keepAliveTimeout: 0, _onPendingData: [Function: nop], agent: [Agent], socketPath: undefined, method: 'GET', maxHeaderSize: undefined, insecureHTTPParser: undefined, joinDuplicateHeaders: undefined, path: '/appliance/api/v2/appliances', _ended: true, res: [IncomingMessage], aborted: false, timeoutCb: null, upgradeOrConnect: false, parser: null, maxHeadersCount: null, reusedSocket: false, host: 'api.eu.ocp.electrolux.one', protocol: 'https:', _redirectable: [Writable], [Symbol(kCapture)]: false, [Symbol(kBytesWritten)]: 0, [Symbol(kNeedDrain)]: false, [Symbol(corked)]: 0, [Symbol(kOutHeaders)]: [Object: null prototype], [Symbol(errored)]: null, [Symbol(kUniqueHeaders)]: null }, data: { Message: 'User is not authorized to access this resource with an explicit deny' } } }

Plugin Config:

{
    "bridge": {
        "name": "Homebridge 5921",
        "username": "REDACTED",
        "port": 51931,
        "pin": "REDACTED",
        "advertiser": "bonjour-hap"
    },
    "accessories": [
        {
            "name": "report-sensors",
            "serial": "REDACTED",
            "cron": "* * * * *",
            "_bridge": {
                "username": "REDACTED",
                "port": 47981
            },
            "accessory": "Schedule"
        }
    ],
    "platforms": [
        {
            "name": "Config",
            "port": 8581,
            "platform": "config"
        },
        {
            "platform": "WizSmarthome",
            "name": "WizSmarthome",
            "_bridge": {
                "username": "REDACTED",
                "port": 35551
            },
            "devices": [
                {
                    "host": "REDACTED"
                }
            ]
        },
        {
            "email": "REDACTED",
            "password": "REDACTED",
            "region": "eu",
            "pollingInterval": 55,
            "carbonDioxideSensorAlarmValue": 1000,
            "_bridge": {
                "username": "REDACTED",
                "port": 45909
            },
            "platform": "HomebridgeElectroluxDevices"
        }
    ],
    "disabledPlugins": [
        "homebridge-linak"
    ]
}

Screenshots:

Flatline between 1PM and 4PM is the time "after 12h, but before the restart". Carbon Dioxide level is a good indicator for this issue as it changes basically with every reading, when working as expected. obraz

Environment:

tomekkleszcz commented 6 months ago

Hi @dezeroku I know you've created an issue long time ago, but this should be resolved in the newest version. I'm closing this issue, but if you encounter this issue I'll reopen the issue. Thank you!