@dmrichards26 or @bluk did either of you gain access to the developer portal? Schlage is coming out with a similar version this spring that is compatible with Apple Home Key and I am hoping to replace my current lock with that one.
Here’s a link with more info if anyone is interested: CES 2022: Schlage Introduces Encode Plus Deadbolt With Support for Apple’s Home Key Feature - MacRumors
My request status is still at “requested”. Will update when approved or not. Strange as it stated the response/approval would be quick.
I still have not gained access either. Like @bluk, my account sits at the “requested” state. I have reached our to Allegion today and hope I’ll be able to get in touch with someone who can help.
I made some progress discovering API end points and documenting for myself and others.
Get lock status API
bearer="" # authentication from AWS
api_key="" # maybe hardcoded into app?
curl -vv -H "content-type: application/json" -H "x-api-key: $api_key" -H "authorization: Bearer $bearer" -H "accept-encoding: gzip" -H "user-agent: okhttp/3.8.0" "https://api.allegion.yonomi.cloud/v1/devices?archetype=lock"
Returns lots of info! lock state; battery change date, level; alarm settings; firmware version; wifi signal strength, mac address; users associated with lock; and more. I’m too lazy to properly redact it to paste a sample; sorry.
Lock/Unlock API
value=1 # lock
value=0 # unlock
bearer="" # authentication from AWS
api_key="" # maybe hardcoded into app?
lock_device_id="" # uuid from get lock status response
curl -vv -H "content-type: application/json" -H "x-api-key: $api_key" -H "authorization: Bearer $bearer" -H "accept-encoding: gzip" -H "user-agent: okhttp/3.8.0" -d '{"attributes":{"lockState":$value}}' -X PUT "https://api.allegion.yonomi.cloud/v1/devices/$lock_device_id"
Method
- I used a random site to get the Schlage Home APK from the play store onto a computer
- I used apk-mitm to remove the certificate pinning in the APK
- I installed the patched APK onto an android phone
- I used mitmproxy to intercept the requests
a. start mitmproxy on computer
b. configure android phone to use proxy
c. install cert (http://mitm.it) on android phone - I made a few requests
More Notes
- This should be enough to put together a very crude initial proof-of-concept integration
- Bearer auth token
- I’m pretty sure this is available via mitmproxy without having to patch an app to avoid certificate pinning. This would mean hoops to jump through to get it and put it in an HA config file, but very doable (kinda like what’s had to happen with Nest integration in the past)
- Not sure how long this token is good for. I though I saw somewhere that it was 3600 units. Units were defined, and now I can’t find the response that included that expiration information. Who knows.
- Would be nice to figure out how to do the auth for real, using the username and password users use with the app. I think @mcnutter1 post #40 in this thread got the closest. Not quite enough there for me to reproduce, though.
- api key
- I’m thinking this might be hardcoded into the app. I didn’t see it an auth responses. I didn’t want to start trying other values
- The way I currently know how to get this involves the apk-mitm certificate pinning patching. Can surely find it in the packaged apps somewhere, but I don’t know how to do that.
- It might be enough for someone to figure this out everytime a new app version drops (if that’s even necessary)
-
shasum -a512 -
“108826$api_key” is3ea4e400bb5706aff440274d9bc1bff77efa0aa2a32e9541b7ee0d3428c4c681fa30d0fc33675541a5e762ff0375d242373d329bc770c80ed54efac93d42a1c1
. would be nice to know if it matches other people.
- the lock status appears to update in the app with websockets in addition to the conventional HTTP lock status request above. For example, when I have the app open on my phone and I issue a curl (un)lock request, the phone updates quickly. mitmproxy didn’t show me these requests (yet?) so I don’t know how they work.
No worries, I’ll help out here. Here is a schema with all the parameters returned and their possible values:
{
"type": "object",
"properties": {
"lockState": {
"enum": [
"Unlocked",
"Locked",
"Jammed",
"Unknown"
],
"type": "string",
"description": "Constants used to indicate the state of the device."
},
"batteryState": {
"enum": [
"Normal",
"Low",
"CriticallyLow",
"Unknown"
],
"type": "string",
"description": "Potential set of values that can be assigned to battery state."
},
"modelName": {
"type": "string",
"description": "Human readable model name to present to users",
"nullable": true
},
"serialNumber": {
"type": "string",
"description": "Serial number of the device",
"nullable": true
},
"percentageBatteryLevel": {
"type": "integer",
"description": "Battery level in percentage\r\nNote: This value has been known to be unreliable",
"format": "int32",
"nullable": true
},
"connected": {
"type": "boolean",
"description": "Boolean to check if the device's most recent heartbeat indicates it was connected"
},
"lastConnectedToCloud": {
"type": "string",
"description": "Timestamp when the device was last connected to the cloud.",
"format": "date-time",
"nullable": true
},
"lastUpdated": {
"type": "string",
"description": "Timestamp when the device was last updated",
"format": "date-time"
},
"created": {
"type": "string",
"description": "Timestamp when the device was created",
"format": "date-time"
},
"accessCodeLength": {
"type": "integer",
"description": "Access code length of the lock. Ranges from 4-8 digit. It will be null if no accessCodes created in lock.",
"format": "int32",
"nullable": true
},
"timezoneOffset": {
"type": "string",
"description": "Timezone of the lock.",
"nullable": true
},
"id": {
"type": "string",
"description": "Device unique identification.",
"format": "uuid"
},
"name": {
"type": "string",
"description": "Device friendly name.",
"nullable": true
},
"type": {
"type": "string",
"description": "Device factory model type.",
"nullable": true
}
},
"additionalProperties": false,
"description": "Detailed dto to be returned from the api"
}
Hello, All -
I am happy to say that I have some positive conversations open with Yonomi to allow this integration to happen officially, and without any reverse engineering. They provide a platform for integration between a growing number of cloud-connected devices, and they are also the people who actually developed the API and cloud platform for Schlage.
Later, Schlage’s parent company Allegion actually acquired Yonomi, so they are now under the corporate umbrella.
Please checkout my post here - Yonomi Integration (For devices such as the Schalge Encode WiFi) - #6 by dmrichards26
The whole cloud things is such a shame…the idea that my internet should be up to use my door lock is absurd (I know I’m preaching to the choir here).
I certainly empathize with the idea of ongoing costs for cloud but I would argue the reverse engineering of the protocol continue, why? Because they chose the cloud for this lock, and they chose some open ended unstated future agreement to continue to support it until when?
I didn’t sign up to have to pay at some point in the future due to their lack of understanding that cloud costs will continue to eat at them for every unit sold.
As we’ve already seen time and time again a vendor will support a cloud option until they either have to charge or are tired of the bleeding and then you have a brick.
Until there is a way to locally control this lock, I’d rate it as a “Don’t Buy” one executive can decide it’s too much and now I’m renting my lock from them just to unlock my door…
I 100% agree. One of the biggest reasons why I chose to start an HA instance was to keep things off the cloud as much as possible. Hopefully, someone does end up completely reverse engineering the protocol.
I just wanted to chime in to say thanks for this thread! With the info provided, I reverse engineered the API to get this working… It logs in with Amazon Cognito user pools, which I’ve managed to fab up a roughly working app in Python to login with your creds from the Schlage app, get info from the lock, get lock state and toggle the lock. I’m currently cleaning it up to make sure I don’t have anything tied directly to my account before tossing it up on Github for anyone to tinker with.
Awesome work!
To add to this:
I’ve been following along the Thread integration through Homekit controller. And while I don’t have my system up and running yet, while sniffing through the HA logs, I confirmed that my Encode Plus was hitting my system via zeronconf broadcasts.
For those who have the Encode Plus version that works with WiFi and Thread, follow the post below for updates on the Thread front. They are making great progress and should have it working sometime in the September release ( at least that’s what I took from it)
So hopefully if what you are working on works out, there will be thread support for it as well.
@mrdj863 interesting, I was wondering if there were any devices besides Nanoleaf using HAP over Thread currently. What is it broadcasting? _hap._udp
?
Wow surprised to see you on this thread lambda.
Actually in the code block that you were helping me troubleshoot that I posted on your thread, there is a cluster of Nanoleaf bulbs and in between it is Encode Plus, which is this lock.
I didn’t get a chance to work out my IPv6 settings on my ubiquity network but when I’m home next week, I plan on digging deeper.
Looks like hap.udp
2022-08-23 20:27:52.102 DEBUG (MainThread) [homeassistant.components.zeroconf] service_update: type=_hap._udp.local. name=Encode Plus._hap._udp.local. state_change=ServiceStateChange.Updated
2022-08-23 20:27:52.103 DEBUG (MainThread) [homeassistant.components.zeroconf] service_update: type=_hap._udp.local. name=Nanoleaf A19 7BSK._hap._udp.local. state_change=ServiceStateChange.Updated
2022-08-23 20:27:52.103 DEBUG (MainThread) [homeassistant.components.zeroconf] Discovered new device Encode Plus._hap._udp.local. ZeroconfServiceInfo(host='fd54:6e34:1e50:0:ecc5:dea1:f1c9:609d', addresses=['fd54:6e34:1e50:0:ecc5:dea1:f1c9:609d'], port=5683, hostname='Encode-Plus.local.', type='_hap._udp.local.', name='Encode Plus._hap._udp.local.', properties={'_raw': {'c#': b'1', 'ff': b'1', 'id': b'C8:15:DD:9E:F2:31', 'md': b'be499WB', 'pv': b'1.2', 's#': b'7', 'sf': b'0', 'ci': b'6', 'sh': b'VYYK3A=='}, 'c#': '1', 'ff': '1', 'id': 'C8:15:DD:9E:F2:31', 'md': 'be499WB', 'pv': '1.2', 's#': '7', 'sf': '0', 'ci': '6', 'sh': 'VYYK3A=='})
2022-08-23 20:27:52.104 DEBUG (MainThread) [homeassistant.components.zeroconf] Discovered new device Nanoleaf A19 7BSK._hap._udp.local. ZeroconfServiceInfo(host='fd54:6e34:1e50:0:d179:d25a:4f37:5d36', addresses=['fd54:6e34:1e50:0:d179:d25a:4f37:5d36'], port=5683, hostname='Nanoleaf-A19-7BSK.local.', type='_hap._udp.local.', name='Nanoleaf A19 7BSK._hap._udp.local.', properties={'_raw': {'c#': b'2', 'ff': b'2', 'id': b'35:F9:9C:95:D5:B9', 'md': b'NL45', 'pv': b'1.2', 's#': b'91', 'sf': b'0', 'ci': b'5', 'sh': b'vW6VBA=='}, 'c#': '2', 'ff': '2', 'id': '35:F9:9C:95:D5:B9', 'md': 'NL45', 'pv': '1.2', 's#': '91', 'sf': '0', 'ci': '5', 'sh': 'vW6VBA=='})
Yep, that should work with my HAP/Thread code. The tricky part is getting HAP devices on a Thread network without pairing them. If the ZC property sf
is 0, the device is already paired and cannot be added to HA. HAP/BLE was also released in 2022.8 which may be worth a try.
EDIT: Someone should try removing the device from the Apple Home app & see if the lock sticks around on Thread (advertising _hap._udp
but with a new ID). If so, congrats, it is now pairable with HA.
@punk-kaos Great news, I got stuck on the auth and gave up… Happy to test, I have 5 Encodes and would love to have them in HASS.
Yeah, that Cognito auth is kinda rough, but thats good for security. And it doesn’t help that some of the data is hard coded in the APK, luckily it all was there when I de-ODEXed the APK.
I’m leaning towards making it an MQTT bridge, as that’ll work for lots of automation platforms. I’ll toss something out fairly soon if my day is slow today.
Ok, this is really dirty right now but it does work… Most of the time.
Put your app username and pass in at the top, then run.
It will output the current lock state to the console, then attempt to lock the door(You can change it from lock/unlock in the call near the bottom) then it will sleep 10 seconds and query the state again.
It does work, it toggles the lock HOWEVER it doesn’t always seem to do it consistently and I’ve not yet figured out why that is yet…
Once I do I plan to wrap it in an MQTT lib so it’ll talk nicely to anything you like via MQTT but I have to sort out why its being cranky first. I’m open to suggestions!
Well… After powercycling my lock it seems to be working perfectly. I’m gonna test a bit more but that intermittent issue might have been totally with the lock itself.
Just tested, worked to lock my lock without any changes to the code (just creds).
I have multiple locks so will likely have to iterate through the device list and sort each lock into an object.
Awesome! Nice to see confirmation I’m not crazy!
Yeah, it assumes right now that there’s only one lock for simplicity. Once I flesh it out a bit I’ll have it check for multiple locks. Its already pulling the first lock ID so it has the tools needed.