Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V6 Support #34

Open
nsnyder opened this issue Jan 9, 2021 · 33 comments
Open

V6 Support #34

nsnyder opened this issue Jan 9, 2021 · 33 comments
Assignees

Comments

@nsnyder
Copy link
Contributor

nsnyder commented Jan 9, 2021

Noticed errors being thrown from my implementation of this package. After a bit of looking, it appears that Chamberlain has deprecated V5 of their API, and has moved to V6. (Discussion here.)

It looks like someone on that side has been able to reverse engineer it, but I'm not sure how to grab the logs myself to get an implementation going. Might be possible to get into the Discord server and ask them for the info.

Not sure how big a change this is. Ideally, the public facing API for this package remains as-is.

Happy to help if I have time.

@thomasmunduchira
Copy link
Owner

Acknowledged, thanks for reporting! This does align with the cron tests that started failing as of yesterday.

I might try to see if I can MITM to see what the new requests look like. I will join the Discord as well to keep up with progress on the Homebridge end.

@RedondoBoony
Copy link

Thanks a lot.
Stupid that they just can change the api without compatibility to the last one...

@cjustinhall
Copy link

I'll add my thanks - yes, would love to see this work again! Missing the functionality here. Shout if I can help in any way.

@thomasmunduchira
Copy link
Owner

A short-term fix with the V5 API has landed (see #35). I did some MITM/research with the V6 API that I didn't get a chance to publish yet, but hopefully I can get around to it relatively soon.

@avie
Copy link

avie commented Mar 21, 2021

Does the app work for you using mitmproxy? I was trying to capture some traces (and yes, noticed the app is now using V6 per the hombridge implementation) but when using mitmproxy the app says it can't find the server. I can see the initial OAuth traffic but that's it.

BTW, thanks for doing this!

@heisler3030
Copy link

I'm getting consistent 400 errors today, chatter on other projects seems to indicate something changed with the API to break it. The Homebridge plugin ported to v6 Oauth and seems to be still working. Any current plans to update to v6 or otherwise fix?

@syntag
Copy link

syntag commented Aug 25, 2021

Receiving the same 400 issue today as well. Coincidently when I got a new garage motor, thought it was because of that 😅

@simmonyau
Copy link

Yes, I'm getting the same error:

Error received:
{ MyQError: Unidentified error returned from service.
    at Function.handleServiceError (/srv/dev/cfg/openhab2/scripts/node_modules/myq-api/src/ErrorHandler.js:49:13)
    at MyQ._executeServiceRequest (/srv/dev/cfg/openhab2/scripts/node_modules/myq-api/src/MyQ.js:732:27)
    at process._tickCallback (internal/process/next_tick.js:68:7)
  name: 'MyQError',
  code: 'ERR_MYQ_INVALID_SERVICE_RESPONSE',
  _serviceError:
   { Error: Request failed with status code 400
       at createError (/srv/dev/cfg/openhab2/scripts/node_modules/axios/lib/core/createError.js:16:15)
       at settle (/srv/dev/cfg/openhab2/scripts/node_modules/axios/lib/core/settle.js:17:12)
       at IncomingMessage.handleStreamEnd (/srv/dev/cfg/openhab2/scripts/node_modules/axios/lib/adapters/http.js:244:11)
       at IncomingMessage.emit (events.js:203:15)
       at endReadableNT (_stream_readable.js:1145:12)
       at process._tickCallback (internal/process/next_tick.js:63:19)

@thomasmunduchira
Copy link
Owner

We need to prioritize this now that the v5 API is turned down. I'll actively start working on this, but it won't be an overnight fix by any means.

@nsnyder
Copy link
Contributor Author

nsnyder commented Aug 30, 2021

Let me know if I can be of assistance :)

@avie
Copy link

avie commented Aug 31, 2021

I haven't looked at what @koush did but have been updating some home automation code I have that was based on what I learned from this project. I now have it mostly working. I figured most things out by looking at the HomeBridge code along with some detective work watching network traffic. The MyQ server is very finicky and wants headers and such a certain way. Anyway, if it's helpful, the general gist of V6 is the switch to the Oauth2/PKCE flow and minor changes to the regular api. Here's some more details:

For auth:

Instead of the old security token you use an Access Code generated by the OAuth flow. It's passed along in the api with an Authorization header. [Yes, it gives you an "access_token" in a JSON reply but you put it in an Authorization header].

To do this, start at https://partner-identity.myq-cloud.com/connect/authorize with a bunch of query params (see Homebridge code) and the PKCE challenge. This will return with several redirects. Keep following them until the server returns with a 200 returns code. With this return you need to parse the html and find a __RequestVerificationToken. With that token, redo the last query as a POST and in the body of the POST put url encoded email, password and token.

This will return a page with one more redirect... don't follow the redirect! Instead, pull out the "code" and "scope" it returns and turn around and use them in a POST to https://partner-identity.myq-cloud.com/connect/token (you will also send along the PKCE verifier now), which will return an actual access code.

The reply will have an "access_token" and "token_type" which are then used in all the api commands. Basically, create a header as "Authorization: <token_type> <access_token>" which replaces prior use of the Security Token. You can also save away the refresh_token to get new access tokens when you need them without logging back in. [I have not tried that].

Now that you've got your access token everything is very similar to what it was before. First, you need to get the account ID. This is a little different. Query https://accounts.myq-cloud.com/api/v6.0/accounts and a list of accounts will be returned. Not sure why there can be more than one. Anyway, this will give you and Account ID which you use in the api as before.

To get the device list, use https://devices.myq-cloud.com/api/v5.2/Accounts//Devices. This list will be the same as before.

To then control a device use https://account-devices-gdo.myq-cloud.com/api/v5.2/Accounts//door_openers//

where command is "Open" or "Close" (used to be open or close). This is a PUT command as before with an empty body (be sure to set Content-Length: 0).

Oh, make sure to have cookie processing on as it is required.

You can find details of some of the search parameters in the HomeBridge code.

Hope it helps!

@cjustinhall
Copy link

Here's what koush did... https://github.com/koush/myq/blob/main/src/myq-api.ts

Some useful bits from his commenting:

  • Starting with v6 of the myQ API, myQ now uses OAuth 2.0 + PKCE to authenticate users and
  • provide access tokens for future API calls. In order to successfully use the API, we need
  • to first authenticate to the myQ API using OAuth, get the access token, and use that for
  • future API calls.
  • On the plus side, the myQ application identifier and HTTP user agent - previously pain
  • points for the community when they get seemingly randomly changed or blacklisted - are
  • no longer required.
  • For those familiar with prior versions of the API, v6 does not represent a substantial
  • change outside of the shift in authentication type and slightly different endpoint
  • semantics. The largest non-authentication-related change relate to how commands are
  • sent to the myQ API to execute actions such as opening and closing a garage door, and
  • even those changes are relatively minor.

@koush
Copy link

koush commented Aug 31, 2021

Here's what koush did... https://github.com/koush/myq/blob/main/src/myq-api.ts

The above is not my code, it's from the myq homebridge plugin. I stripped out the homebridge stuff for just the MyQ API bindings, which were updated to work with the v6 API. Hopefully it helps the authors of this project. Or anyone else that has a pressing need to control their MyQ devices, and want a relatively quick way to access them, albeit by targeting a different node library.

@skykep
Copy link

skykep commented Sep 3, 2021

What's the best way to test this? Node won't run it, nor npm.

@parnic
Copy link
Contributor

parnic commented Sep 3, 2021

What's the best way to test this? Node won't run it, nor npm.

This is a library, not an application, so you would need to build an application to use this library's API. Examples are listed in the readme.

@simmonyau
Copy link

In case it helps anyone, I ended up switching my home automation to use the following API that works very well:

https://github.com/arraylabs/pymyq

@skykep
Copy link

skykep commented Sep 3, 2021

What's the best way to test this? Node won't run it, nor npm.

This is a library, not an application, so you would need to build an application to use this library's API. Examples are listed in the readme.

Perfect. Just what I needed. Thanks!

@skykep
Copy link

skykep commented Sep 3, 2021

What's the best way to test this? Node won't run it, nor npm.

This is a library, not an application, so you would need to build an application to use this library's API. Examples are listed in the readme.

It appears I need a little more handholding. How do you reference the v6 library since it's written in TypeScript?
The older myq-api is JS. I tried running tsc on it to convert it, but get errors about it being an ES module...

@koush
Copy link

koush commented Sep 3, 2021

What's the best way to test this? Node won't run it, nor npm.

This is a library, not an application, so you would need to build an application to use this library's API. Examples are listed in the readme.

It appears I need a little more handholding. How do you reference the v6 library since it's written in TypeScript?
The older myq-api is JS. I tried running tsc on it to convert it, but get errors about it being an ES module...

Sorry, it's been fixed. I had the incorrect package entry point. Should work from a normal js project.

@skykep
Copy link

skykep commented Sep 3, 2021

Sorry, it's been fixed. I had the incorrect package entry point. Should work from a normal js project.

Thanks, so what is the proper way to call this?
Using const MyQ = require('myq-api'); still calls the v5 api and pointing the require directly to your myq-api.js doesn't work either.

@koush
Copy link

koush commented Sep 3, 2021

Sorry, it's been fixed. I had the incorrect package entry point. Should work from a normal js project.

Thanks, so what is the proper way to call this?
Using const MyQ = require('myq-api'); still calls the v5 api and pointing the require directly to your myq-api.js doesn't work either.

Make sure you're on at least v1.0.9

npm install @koush/myq@latest

require('@koush/myq')

@skykep
Copy link

skykep commented Sep 3, 2021

Make sure you're on at least v1.0.9

npm install @koush/myq@latest

require('@koush/myq')

No joy.

const MyQ = require('@koush/myq');
const account = new MyQ();
phuz@wazebot:~/MyQ$ node myq.js
/home/phuz/MyQ/myq.js:6
const account = new MyQ();
                ^
TypeError: MyQ is not a constructor

(that was me that DM'd you on discord earlier)

@koush
Copy link

koush commented Sep 4, 2021

> require('@koush/myq')
{ myQApi: [Getter] }

that example is out of date I think.

@skykep
Copy link

skykep commented Sep 4, 2021

that example is out of date I think.

yeah that old example won't work with this new API, but I can't even figure out my starting point on this. It also looks like multiple things need to happen during the authentication process. I wonder if we look at the homebridge code to see how they're using it specifically.

@koush
Copy link

koush commented Sep 4, 2021

yeah, that's how I did it. it's not complicated.

    this.account = new myQApi(console.log.bind(console), console, email, password);
    await this.account.refreshDevices();
    
    const devices: Device[] = [];
    for (const device of this.account.devices) {
      // do stuff
    }

@skykep
Copy link

skykep commented Sep 4, 2021

yeah, that's how I did it. it's not complicated.

I've tried very similar but I still get the myQApi is not a constructor

@koush
Copy link

koush commented Sep 4, 2021

const {myQApi} = require('@koush/myq')

you're probably importing it wrong

@skykep
Copy link

skykep commented Sep 4, 2021

you're probably importing it wrong

hah, yup. completely missed the deconstruction. but that opened up more errors. I hope this isn't all related to being on node v12 (which I need on this test box)

@skykep
Copy link

skykep commented Sep 4, 2021

For the heck of it, I just wiped my code and pasted what you have above (with credentials) and it still blows up.
Did that actually work for you?
TypeError: this.devices is not iterable

@koush
Copy link

koush commented Sep 4, 2021

For the heck of it, I just wiped my code and pasted what you have above (with credentials) and it still blows up.
Did that actually work for you?
TypeError: this.devices is not iterable

yes.

const { myQApi } = require('@koush/myq')


async function start() {
    const account = new myQApi(console.log.bind(console), console, 'username', 'password');
    await account.refreshDevices();
    
    for (const device of account.devices) {
        console.log(device);
    }
}

start();

@skykep
Copy link

skykep commented Sep 4, 2021

yes.

Then I've got something else going on because that's more or less what I had with the exception of a different function name.
Thanks for what you've done and provided so far.

@cjustinhall
Copy link

cjustinhall commented Sep 5, 2021

I can confirm that this does indeed work.

(I'm running on redhat ent latest, npm 7.21.1)

Create a directory for the myq-v6, cd that directory
npm install @koush/myq@latest
Created a test.js with the above code... edit with your username/pwd...
node test.js

Works! Returns all the gateways/hubs and door openers that I have physically installed.

To Open all doors, (for testing purposes...)
Inside of the account.devices iteration, add these lines
if(device.device_family=='garagedoor') {
account.execute(device,"Open");
}

@nsnyder
Copy link
Contributor Author

nsnyder commented Sep 6, 2021

Could we please limit discussion to this issue, in this library? Thanks to @koush for his work porting a V6 implementation, but issues with that just muddy up this ticket and make noise for the rest of us.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests