yenoiwesa / homebridge-connexoon

A homebridge plugin to integrate Somfy blinds with the Connexoon RTS hub
Apache License 2.0
27 stars 2 forks source link

Login issue - invalid grant #5

Closed Cardo1 closed 4 years ago

Cardo1 commented 4 years ago

Hi there,

I'm trying to set up this plugin, however I receive a "error.invalid.grant" error when it tries to log in. The user/pass appear to be correct, as if I change these I get a wrong credentials error, instead.

I've tried using all the different service types, but I get the same error with all of them. I can log into the Somfy website using the username/password without issues.

I can post the extensive error log, if necessary.

Thoughts?

yenoiwesa commented 4 years ago

Hi @Cardo1,

Could you please let me know what your geographic region is?

When I swapped the login mechanism to Somfy's OAuth for reliability during maintenance windows, I reverse engineered the Australian/Singaporian Somfy app for its access token, and it's possible that a different one be used in other markets.

I might have made this plugin Connexoon RTS specific for the APAC market. It's difficult to test for because I don't have an account or devices in other regions.

Let me know, and I will look further.

Cardo1 commented 4 years ago

Thanks for responding.

I’m in the UK, so probably Europe?

yenoiwesa commented 4 years ago

Thanks.

Could you point me to the Somfy app that you are using to control the Somfy devices on your phone?

Cardo1 commented 4 years ago

https://apps.apple.com/gb/app/connexoon-window-rts/id1168009218

This one.

yenoiwesa commented 4 years ago

Alright so the good news is that the app has the same ID as the one on the Australian App Store so the codebase should be the same.

Could you send the logs surrounding the failure, making sure that your credentials are redacted?

Cardo1 commented 4 years ago

Here we go. Hopefully I haven't redacted too much...

[10/28/2019, 2:07:12 PM] [My Connexoon Hub] Failed to login { message: 'error.invalid.grant', data: [], uid: 'hex stuff' }
[10/28/2019, 2:07:12 PM] [My Connexoon Hub] Failed to get device list { message: 'error.invalid.grant', data: [], uid: 'hex stuff' }
[10/28/2019, 2:07:12 PM] [My Connexoon Hub] StatusCodeError: 400 - {"message":"error.invalid.grant","data":[],"uid":"hex stuff"}
    at new StatusCodeError (/usr/local/lib/node_modules/homebridge-connexoon/node_modules/request-promise-core/lib/errors.js:32:15)
    at Request.plumbing.callback (/usr/local/lib/node_modules/homebridge-connexoon/node_modules/request-promise-core/lib/plumbing.js:104:33)
    at Request.RP$callback [as _callback] (/usr/local/lib/node_modules/homebridge-connexoon/node_modules/request-promise-core/lib/plumbing.js:46:31)
    at Request.self.callback (/usr/local/lib/node_modules/homebridge-connexoon/node_modules/request/request.js:185:22)
    at Request.emit (events.js:210:5)
    at Request.<anonymous> (/usr/local/lib/node_modules/homebridge-connexoon/node_modules/request/request.js:1161:10)
    at Request.emit (events.js:210:5)
    at IncomingMessage.<anonymous> (/usr/local/lib/node_modules/homebridge-connexoon/node_modules/request/request.js:1083:12)
    at Object.onceWrapper (events.js:299:28)
    at IncomingMessage.emit (events.js:215:7)
    at endReadableNT (_stream_readable.js:1198:12)
    at processTicksAndRejections (internal/process/task_queues.js:80:21) {
  name: 'StatusCodeError',
  statusCode: 400,
  message: '400 - {"message":"error.invalid.grant","data":[],"uid":"hex stuff"}',
  error: { message: 'error.invalid.grant', data: [], uid: 'hex stuff' },
  options: {
    url: 'https://accounts.somfy.com/oauth/oauth/v2/token',
    form: {
      grant_type: 'password',
      username: 'user',
      password: 'pass',
      client_id: 'long numbers',
      client_secret: 'long numbers'
    },
    json: true,
    method: 'POST',
    callback: [Function: RP$callback],
    transform: undefined,
    simple: true,
    resolveWithFullResponse: false,
    transform2xxOnly: false
  },
  response: <ref *1> IncomingMessage {
    _readableState: ReadableState {   
      objectMode: false,
      highWaterMark: 16384,
      buffer: BufferList { head: null, tail: null, length: 0 },
      length: 0,
      pipes: [],
      flowing: true,
      ended: true,
      endEmitted: true,
      reading: false,
      sync: true,
      needReadable: false,
      emittedReadable: false,
      readableListening: false,
      resumeScheduled: false,
      paused: false,
      errorEmitted: false,
      emitClose: true,
      autoDestroy: false,
      destroyed: false,
      defaultEncoding: 'utf8',
      awaitDrainWriters: null,
      multiAwaitDrain: false,
      readingMore: true,
      decoder: null,
      encoding: null
    },
    readable: false,
    _events: [Object: null prototype] {
      end: [Array],
      close: [Array],
      data: [Function (anonymous)],
      error: [Function (anonymous)]
    },
    _eventsCount: 4,
    _maxListeners: undefined,
    socket: TLSSocket {
      _tlsOptions: [Object],
      _secureEstablished: true,
      _securePending: false,
      _newSessionPending: false,
      _controlReleased: true,
      _SNICallback: null,
      servername: 'accounts.somfy.com',
      alpnProtocol: false, 
      authorized: true,
      authorizationError: null,
      encrypted: true,
      _events: [Object: null prototype],
      _eventsCount: 9,
      connecting: false,
      _hadError: false,
      _parent: null,
      _host: 'accounts.somfy.com',
      _readableState: [ReadableState],
      readable: true,
      _maxListeners: undefined,
      _writableState: [WritableState],
      writable: false,
      allowHalfOpen: false,
      _sockname: null,
      _pendingData: null,
      _pendingEncoding: '',
      server: undefined,
      _server: null,
      ssl: [TLSWrap],
      _requestCert: true,
      _rejectUnauthorized: true,
      parser: null,
      _httpMessage: [ClientRequest],
      [Symbol(res)]: [TLSWrap],
      [Symbol(asyncId)]: 193,
      [Symbol(kHandle)]: [TLSWrap],
      [Symbol(lastWriteQueueSize)]: 0,
      [Symbol(timeout)]: null,
      [Symbol(kBuffer)]: null,
      [Symbol(kBufferCb)]: null,
      [Symbol(kBufferGen)]: null,
      [Symbol(kBytesRead)]: 0,
      [Symbol(kBytesWritten)]: 0,  
      [Symbol(connect-options)]: [Object]
    },
    httpVersionMajor: 1,
    httpVersionMinor: 1,
    httpVersion: '1.1',
    complete: true,
    headers: {
      'cache-control': 'no-cache, private',
      'content-type': 'application/json',
      date: 'Mon, 28 Oct 2019 14:07:11 GMT',
      server: 'openresty',
      'content-length': '65',
      connection: 'Close'
    },
    rawHeaders: [
      'Cache-Control',
      'no-cache, private',
      'Content-Type',
      'application/json',
      'Date',
      'Mon, 28 Oct 2019 14:07:11 GMT',
      'Server',
      'openresty',
      'Content-Length',
      '65',
      'Connection',
      'Close'
    ],
    trailers: {},
    rawTrailers: [],
    aborted: false,
    upgrade: false,
    url: '',
    method: null,
    statusCode: 400,
    statusMessage: 'Bad Request',
    client: TLSSocket {
      _tlsOptions: [Object],
      _secureEstablished: true,
      _securePending: false,
      _newSessionPending: false,
      _controlReleased: true,
      _SNICallback: null,
      servername: 'accounts.somfy.com',
      alpnProtocol: false,
      authorized: true,
      authorizationError: null,
      encrypted: true,
      _events: [Object: null prototype],
      _eventsCount: 9,
      connecting: false,
      _hadError: false,
      _parent: null,
      _host: 'accounts.somfy.com',
      _readableState: [ReadableState],
      readable: true,
      _maxListeners: undefined,
      _writableState: [WritableState],
      writable: false,
      allowHalfOpen: false,
      _sockname: null,
      _pendingData: null,
      _pendingEncoding: '',
      server: undefined,
      _server: null,
      ssl: [TLSWrap],
      _requestCert: true,
      _rejectUnauthorized: true,
      parser: null,
      _httpMessage: [ClientRequest],
      [Symbol(res)]: [TLSWrap],
      [Symbol(asyncId)]: 193,
      [Symbol(kHandle)]: [TLSWrap],
      [Symbol(lastWriteQueueSize)]: 0,
      [Symbol(timeout)]: null,
      [Symbol(kBuffer)]: null,
      [Symbol(kBufferCb)]: null,
      [Symbol(kBufferGen)]: null,
      [Symbol(kBytesRead)]: 0,
      [Symbol(kBytesWritten)]: 0,
      [Symbol(connect-options)]: [Object]
    },
    _consuming: false,
    _dumped: false,
    req: ClientRequest {
      _events: [Object: null prototype],
      _eventsCount: 5,
      _maxListeners: undefined,
      outputData: [],
      outputSize: 0,
      writable: true,
      _last: true,
      chunkedEncoding: false,
      shouldKeepAlive: false,
      useChunkedEncodingByDefault: true,
      sendDate: false,
      _removedConnection: false,
      _removedContLen: false,
      _removedTE: false,
      _contentLength: null,
      _hasBody: true,
      _trailer: '',
      finished: true,
      _headerSent: true,
      socket: [TLSSocket],
      _header: 'POST /oauth/oauth/v2/token HTTP/1.1\r\n' +
        'host: accounts.somfy.com\r\n' +
        'content-type: application/x-www-form-urlencoded\r\n' +
        'accept: application/json\r\n' +
        'content-length: 221\r\n' +
        'Connection: close\r\n' +
        '\r\n',
      _onPendingData: [Function: noopPendingOutput],
      agent: [Agent],
      socketPath: undefined,
      method: 'POST',
      path: '/oauth/oauth/v2/token',
      _ended: true,
      res: [Circular *1],
      aborted: false,
      timeoutCb: null,
      upgradeOrConnect: false,
      parser: null,
      maxHeadersCount: null,
      reusedSocket: false,
      [Symbol(kNeedDrain)]: false,
      [Symbol(isCorked)]: false,
      [Symbol(kOutHeaders)]: [Object: null prototype]
    },
    request: Request {
      _events: [Object: null prototype],
      _eventsCount: 5,
      _maxListeners: undefined,
      method: 'POST',
      readable: true,
      writable: true,
      explicitMethod: true,
      _qs: [Querystring],
      _auth: [Auth],
      _oauth: [OAuth],
      _multipart: [Multipart],
      _redirect: [Redirect],
      _tunnel: [Tunnel],
      _rp_resolve: [Function (anonymous)],
      _rp_reject: [Function (anonymous)],
      _rp_promise: [Promise],
      _rp_callbackOrig: undefined,
      callback: [Function (anonymous)],
      _rp_options: [Object],
      headers: [Object],
      setHeader: [Function (anonymous)],
      hasHeader: [Function (anonymous)],
      getHeader: [Function (anonymous)],
      removeHeader: [Function (anonymous)],
      localAddress: undefined,
      pool: {},
      dests: [],
      __isRequestRequest: true,  
      _callback: [Function: RP$callback],
      uri: [Url],
      proxy: null,
      tunnel: true,
      setHost: true,
      originalCookieHeader: undefined,
      _disableCookies: true,
      _jar: undefined,
      port: 443,
      host: 'accounts.somfy.com',
      body: 'grant_type=password&username=user&password=pass&client_id=long numbers&client_secret=long numbers$
      path: '/oauth/oauth/v2/token',
      _json: true,
      httpModule: [Object],
      agentClass: [Function: Agent],
      agent: [Agent],
      _started: true,
      href: 'https://accounts.somfy.com/oauth/oauth/v2/token',
      req: [ClientRequest],
      ntick: true,
      response: [Circular *1],
      originalHost: 'accounts.somfy.com',
      originalHostHeaderName: 'host',
      responseContent: [Circular *1],
      _destdata: true,
      _ended: true,
      _callbackCalled: true
    },
    toJSON: [Function: responseToJSON],
    caseless: Caseless { dict: [Object] },
    body: { message: 'error.invalid.grant', data: [], uid: 'hex stuff' }
  }
yenoiwesa commented 4 years ago

I'll be honest I have no idea what is causing the authentication process to fail.

I'd normally use an Android phone and Android Studio in debug mode to log all network requests sent from the device to find out what the Somfy app (the original one) is sending to the server to login for your account. It's not possible to do the same from iOS unfortunately (not easily at least).

What I would like to know is if the previous login mechanism was working for you. Could you please go to your homebridge setup's package.json file and change the version of Homebridge Connexoon to 1.0.0? Then do an npm install and restart the platform.

If that works, I will just put both login mechanisms in place and fallback to the non OAuth one when the first one fails.

Cardo1 commented 4 years ago

I've given 1.0.0 a try, unfortunately also doesn't work... I get:

[30/10/2019, 12:53:04] [My Connexoon Hub] Failed to login { errorCode: 'AUTHENTICATION_ERROR', error: 'Bad credentials' }
[30/10/2019, 12:53:04] [My Connexoon Hub] Failed to get device list { errorCode: 'AUTHENTICATION_ERROR', error: 'Bad credentials' }
[30/10/2019, 12:53:04] [My Connexoon Hub] StatusCodeError: 401 - {"errorCode":"AUTHENTICATION_ERROR","error":"Bad credentials"}

etc. etc.

yenoiwesa commented 4 years ago

Have you tried the other services too?

Cardo1 commented 4 years ago

I’m afraid so.

If it makes any difference, the Homebridge-Tahoma plugin works. (Though I’m having other issues with that one!)

yenoiwesa commented 4 years ago

Mmmmmh that's interesting. v1.0.0's login method is entirely based on Homebridge-Tahoma's implementation.

I'll need to go back it and see if there is any difference in the processing that could explain a Bad Credentials error.

Cardo1 commented 4 years ago

Apologies, had a brain blip.

Using version 1.0.0, I managed to get it to work using either "TaHoma" or "Connexoon". "ConnexoonRTS" (the default, I believe?) gives me a bad credentials error.

Cardo1 commented 4 years ago

So now I've got login working, I'm not getting any devices!

I've configured an awning as a "Roller Shutter" in the Connexoon Window app, however nothing's loading in the app. Simply get this:

[10/30/2019, 10:20:21 PM] [My Connexoon Hub] Initializing Connexoon platform...
[10/30/2019, 10:20:21 PM] [My Connexoon Hub] Connexoon Init

This is using a Connexoon RTS box.

Cardo1 commented 4 years ago

If it helps, my config is...

   {
        "platform": "Connexoon",
        "name": "My Connexoon Hub",
        "username": "email",
        "password": "pass",
        "service": "TaHoma",
        "devices": {
            "Awning": {
                "commands": [
                    { "command": "open", "position": 0 },
                    { "command": "close", "position": 100 }
                            ]
                      }
                    }
    },
yenoiwesa commented 4 years ago

Alright, it's good to know that the previous authentication mechanism is working, I will have to put up a fix for it when possible.

Regarding the next issue, could you please go to your local node_modules directory and find the following file: node_modules/homebridge-connexoon/src/api/overkiz-api.js

If you could add some logs at this line: https://github.com/yenoiwesa/homebridge-connexoon/blob/v1.0.0/src/api/overkiz-api.js#L102

Insert:

this.log.info('Device List Response:', jsonDevices);

Restart the platform, and then paste here the result that you get for the added log line (you can redact the device IDs, but I need the device class and type).

Cardo1 commented 4 years ago

The log is as follows:

  {
    creationTime: 1572191556000,
    lastUpdateTime: 1572191556000,
    label: 'Box',
    deviceURL: 'internal://device pin/pod/0',
    shortcut: false,
    controllableName: 'internal:PodMiniComponent',
    definition: {
      commands: [Array],
      states: [Array],
      dataProperties: [],
      widgetName: 'Pod',
      uiClass: 'Pod',
      qualifiedName: 'internal:PodMiniComponent',
      type: 'ACTUATOR'
    },
    states: [ [Object], [Object] ],
    attributes: [],
    available: true,
    enabled: true,
    placeOID: 'long number',
    widget: 'Pod',
    type: 1,
    oid: 'long number',
    uiClass: 'Pod'
  },
  {
    creationTime: 1572473791000,
    lastUpdateTime: 1572473791000,
    label: 'Awning',
    deviceURL: 'rts://pin/number',                                                 
    shortcut: false,
    controllableName: 'rts:RollerShutterRTSComponent',
    definition: {
      commands: [Array],
      states: [],
      dataProperties: [Array],
      widgetName: 'UpDownRollerShutter',
      uiClass: 'RollerShutter',
      qualifiedName: 'rts:RollerShutterRTSComponent',
      type: 'ACTUATOR'
    },
    attributes: [ [Object] ],
    available: true,
    enabled: true,
    placeOID: 'number',
    widget: 'UpDownRollerShutter',
    type: 1,
    oid: 'long number',
    uiClass: 'RollerShutter'
  }

Thanks!

yenoiwesa commented 4 years ago

Alright we're getting closer! The uiClass of that device is RollerShutter, which isn't mapped in my plugin (I only have roller blinds at home hehe).

Could you please go again to your local node_modules and find the following file: homebridge-connexoon/src/services-mapping.js

And in the file, add a new entry for RollerShutter as such:

module.exports = {
    // maps API device UI Class to a list of services
    Screen: [WindowCovering],
    RollerShutter: [WindowCovering],
};

Now restart the platform and let me know if the device is properly discovered.

Cardo1 commented 4 years ago

Fantastic, that's done the trick. My awning now shows up and works as it should with the reversed open/close positions.

Thanks for all your help!

yenoiwesa commented 4 years ago

That's great!

Alright so there are two actions for me now:

  1. Implement the two login strategies and have a mechanism to fallback when the first one fails.
  2. Add Roller Shutter mapping.

I will be working on that when time allows and then release a new version of the plugin. I'll let you know. In the meantime, you can keep using the modified version of the plugin.

Cheers! Matt

yenoiwesa commented 4 years ago

Hey @Cardo1,

I have just released v1.2.0 of the plugin with the fixes discussed above. Please update to the latest version in your package.json file and do npm install, then restart the homebridge platform.

Let me know if everything works fine! :-)

Cardo1 commented 4 years ago

Just tried it out. Looks like it's working fine.

Thanks!

yenoiwesa commented 4 years ago

Awesome! I'll close the issue then.

Don't hesitate to open a new one if you have more feedback/issues.

tejakolla commented 4 years ago

Can anyone explain what is the issue for the below errors??

[2/25/2020, 12:47:03 PM] Registering platform 'homebridge-config-ui-x.config' [2/25/2020, 12:47:03 PM] --- [2/25/2020, 12:47:03 PM] Loading 1 platforms... [2/25/2020, 12:47:03 PM] [My Connexoon Hub] Initializing Connexoon platform... [2/25/2020, 12:47:03 PM] [My Connexoon Hub] Connexoon Init [2/25/2020, 12:47:03 PM] Loading 0 accessories... [2/25/2020, 12:47:04 PM] [My Connexoon Hub] Could not login with any of the authentication methods. [2/25/2020, 12:47:04 PM] [My Connexoon Hub] Failed to get device list undefined [2/25/2020, 12:47:04 PM] [My Connexoon Hub] Could not login with any of the authentication methods. Setup Payload: X-HM://0024YV17WIHO4

yenoiwesa commented 4 years ago

@tejakolla Hi 👋 The error indicates that the authentication failed with your credentials on the service you selected. Make sure that you are using the right service in your homebridge configuration file for the Connexoon platform according to your region and Somfy bridge.

tejakolla commented 4 years ago

@yenoiwesa Hi.. please go through my config file. Am i doing any wrong or should i enter more details on it ??

yenoiwesa commented 4 years ago

@tejakolla nothing is wrong per se with your configuration file. You just happen to not have a Somfy account for that service. Try the different service enums if you don't know which one works for your region.

Also please make sure to entirely redact your credentials next time.