Schlage Encode Wifi

I’ve finally got mitm-proxy installed to test this out on my Sense/BR400 bridge. The unlock/lock commands seem to be a POST instead of a PUT to https://api.allegion.yonomi.cloud/v1/devices/{deviceID}/commands
and the JSON data has a lot more.

{
   "data": {
      "CAT": "{CAT attribute from GET command}",
      "deviceId": "{deviceID}",
      "state": 1,
      "userId": "{userid}"
      },
   "name": "changelockstate"
}

Not sure how hard this is to add, or maybe it will work with the Encode locks too.
When I hit replay in the mitm-proxy console, the lock instantly responds.
I also have it working as a set of curl commands, so I’ll test it in Home Assistant to see if a delay starts to happen.

1 Like

You can create virtual keys and then register them in separate user accounts with Schlage. I did this because I wanted the history log for my lock to differentiate between me using the mobile app and HA using the API to change the lock state. It may also help keep HA and your mobile device from interfering with each other if there is any sort of per user identity rate limiting on the API.

This suggestion doesn’t solve for multiple locks on one account (I’m guessing the mobile app has some way of dealing with this) but that does seem like it would allow WS push for state changes to HA without fighting against your mobile app.

Yes, bridges use a different endpoint. I’m surprised it’s sending a PUT, though–that’s not in the Retrofit spec from the APK. I guess the server is lenient about what HTTP verb is used.

I think the /commands endpoint works for standalone locks too. I’ll test it out on mine and if it works I’ll make pyschlage use that instead.

For reference, here’s the majority of API that the Android app uses (I translated this by hand, so may have missed some things): Details of the Schlage WiFi API · GitHub

Do virtual keys generate new login credentials? If not, I don’t think this helps.

My hypothesis for how this works:

  1. There is only one MQTT clientId generated per account.
  2. When you call /wss, the server makes a call back to the Amazon IoT service and reconfigures it to only publish the requested device to your clientId. (The clientId never seems to change no matter how many times you call /wss)

This sort of makes sense given how the mobile app works. Since it only shows you one lock at a time, it will actually make a call back to /wss every time you change what lock you’re viewing. I suspect that maybe when you’re not actively watching a lock via MQTT the physical device goes back into a sleep mode to preserve battery, but if you have a MQTT subscriber it will stay active.

Note that “notifications” use a different transport: it seems to use Firebase to send notifications across platforms (e.g. Android/iOS). I haven’t had time to poke at this yet, so I don’t know if the notifications have enough metadata attached to be useful for push-based Home Assistant integration.

1 Like

The changelockstate command doesn’t seem to work with my Encode lock. It returns a 504/Gateway Timeout after 30 seconds.

@rwoelk would you mind sending me a dump of the output from /devices?devicetypeId=br400 and /devices?archetype=lock? (feel free to redact any sensitive info). I’d like to understand how to differentiate between Encode locks and those attached to a bridge.

Thank you all for the hard work.
So why is it a small company like wink had sclage all wrapped up in a great bow. I bought wink as my first hub and sclage was great push notifications for every code coustom automations lights for who ever entered depending on time of day loved everything.
Why can’t home assistant get things like this working 100% if a new lock company shows up with some crap no name off the wall can only buy in 1 store in the US in person made from 3d printed plastic. Home assistant has the integration hacs works and every feature under the son works including the read out of the surface temp of the son. Sorry ranting. Why can’t home assistant flex a little just by the growing use of home assistant people want no cloud based systems. Thanks again for your work on this lock hope to get it running on mine.

David,
here is the output from /devices?devicetypeId=br400
I triggered this by going to the WiFi adapters section of my app.

[
    {
        "attributes": {
            "bleFirmwareVersion": "1.4.0",
            "hardwareVersion": "1.0.0",
            "lastActionStatus": "null",
            "lastTalkedTime": [
                {
                    "deviceId": "<redacted>",
                    "timestamp": "2022-12-23T16:22:38Z"
                }
            ],
            "mainFirmwareVersion": "1.1.12",
            "manufacturerName": "Schlage",
            "modelName": "BR400",
            "serialNumber": "1089293",
            "wifiFirmwareVersion": "SD878x-14.76.36.p121-702.1.0-WM"
        },
        "connected": true,
        "connectivityUpdated": "2022-12-23T16:20:13.000Z",
        "created": "2022-08-13T03:21:47.000Z",
        "deviceId": "b6<redacted>",
        "devicetypeId": "br400",
        "lastUpdated": "2022-12-23T16:20:13.000Z",
        "modelName": "BR400",
        "name": "My Home",
        "physicalId": "1089293",
        "relatedDevices": [
            {
                "deviceId": "b6<redacted>"
            }
        ],
        "role": "owner",
        "serialNumber": "<redacted>",
        "users": [
            {
                "email": "r<redacted>",
                "friendlyName": "R<redacted>",
                "identityId": "6e2<redacted>",
                "role": "owner"
            }
        ]
    }
]

and from /devices?archetype=lock


[
    {
        "CAT": "5820834319200146081a27d2abc1420c0050cd939c01db1293d1b6ed2234a3fb5905",                
        "SAT": "582182431930014a0048ba67a2f65c2b742750f54e5626c546dd745091b25e44249f3c",
        "attributes": {
            "CAT": "5820834319200146081a27d2abc1420c0050cd939c01db1293d1b6ed2234a3fb5905",
            "SAT": "582182431930014a0048ba67a2f65c2b742750f54e5626c546dd745091b25e44249f3c",
            "accessCodeLength": 4,
            "adminOnlyEnabled": 0,
            "alarmSelection": 0,
            "alarmSensitivity": 0,
            "alarmState": 0,
            "autoLockTime": 240,
            "batteryLevel": 66,
            "batteryLowState": 0,
            "beeperEnabled": 1,
            "hardwareVersion": "1.3.0",
            "lastTalkedTime": "2022-12-20T22:46:11Z",
            "lockAndLeaveEnabled": 1,
            "lockState": 1,
            "macAddress": "EA:10:CA:87:19:F6",
            "mainFirmwareVersion": "004.031.000",
            "manufacturerName": "Schlage ",
            "modelName": "BE479CEN619",
            "name": "Front Door",
            "profileVersion": "1.1",
            "serialNumber": "<redacted>6",
            "timezone": -60
        },
        "connected": false,
        "connectivityUpdated": "2022-12-20T23:02:35.000Z",
        "created": "2021-03-03T20:19:18.000Z",
        "deviceId": "<redacted>",
        "devicetypeId": "be479",
        "lastUpdated": "2022-12-20T23:02:35.000Z",
        "macAddress": "EA:10:CA:87:19:F6",
        "modelName": "BE479CEN619",
        "name": "Front Door",
        "physicalId": "ea:10:ca:87:19:f6",
        "relatedDevices": [
            {
                "deviceId": "<redacted>"
            }
        ],
        "role": "owner",
        "serialNumber": "0<redacted>6",
        "timezone": -60,
        "users": [
            {
                "email": "<redacted>",
                "friendlyName": "<redacted>",
                "identityId": "<redacted>",
                "role": "admin"
            },
            {
                "email": "<redacted>",
                "friendlyName": "Home Assistant",
                "identityId": "<redacted>",
                "role": "guest"
            },
            {
                "email": "m<redacted>",
                "friendlyName": "M<redacted>",
                "identityId": "<redacted>",
                "role": "guest"
            },
            {
                "email": "r<redacted>",
                "friendlyName": "R<redacted>",
                "identityId": "6e2<redacted>",
                "role": "owner"
            }
        ]
    }
]

I’ve redacted Identity IDs and device IDs just in case, and also names and email addresses.
I would hope there is filtering between accounts that would block someone from adding my device ID with their credentials to open my lock.
I did notice that I could transplant the identityID of another user from my account and it would operate the lock, and show that name in the history. That might be because I was logged in as the owner when I captured the Authorization Bearer. I’ll have to test with the guest account.

1 Like

If you mean login credentials as in the username and password used to sign in to the mobile app, yes. The virtual key is a URL and a verification code you send to somebody else (or yourself in this case). In the normal flow, the other human you send the URL and verification code too clicks on the URL, it opens their Schlage app (to which they are signed in with their own username and password) and then they punch in the verification code and your lock gets added to their app. The owner of the lock can revoke this, but the other person can control the lock until that time. While I wouldn’t put it past the implementors to not have dealt with this, this whole setup seems designed to support one to many between a lock and instances of the mobile app used by different people with their own sign-ins.

Thanks @rwoelk! I just pushed Add support for locking & unlocking non-WiFi locks. by dknowles2 · Pull Request #17 · dknowles2/pyschlage · GitHub and cut a new release (which should get uploaded to pypi automatically) which should hopefully make lock()/unlock() work for you.

I didn’t add support for Hubs themselves yet, but it seems like a useful thing to have. (Home Assistant understands hubs and can show a nice link on a device page such as “Connected via Foo Hub”)

1 Like

Do you have some sample flows for the /wss endpoint interactions (payloads, HTTP methods, URL params, etc.). I want to play around with the queue subscriptions to see if the MQTT connections being active makes any difference in the reliability of the PUT requests to change the lock state.

@dknowles2 is your branch newer / better than mcnutter1?

I installed the mcnutter1 via HACS a while ago, but notice you have cleaned up code that is newer. Any advantage in moving over to that?

I’m referring to this: GitHub - dknowles2/ha-schlage at pyformat

Experimental support for MQTT subscriptions. by dknowles2 · Pull Request #22 · dknowles2/pyschlage · GitHub has MQTT support if you want to play around with it.

2 Likes

No, I don’t think there’s any advantage to using my fork (yet). The only changes in the fork right now are just code cleanups. I was trying to get the code to a state where dropping in my pyschlage library won’t make a huge diff and then add features from there.

1 Like

Because wink more than likely paid schlage and had a paid dev team to work this out. HA is all open source and relies on the community to build/ show what is in demand for the limited team of devs to work on.

Be grateful that there are talented people willing to work on thier own time to help make a product free to use. I sure am.

2 Likes

Thanks for the MQTT code. The branch tries to update the Lock state from the message payload from MQTT but the payload I get back is not a Lock payload. I commented this line out and instead had the lock refresh itself synchronously for the moment.

I wrote a short program that requests to toggle the lock state from whatever it is, then waits for events over MQTT. When the lock state finally toggles to the requested state, it then toggles it again and waits. What I observe is that the lock takes 20 minutes to lock / unlock when working through the python client where as it responds to the mobile app in under ~15 seconds. I’m really curious if the mobile app is subscribing to a specific topic, pushing something over MQTT, or calling another API endpoint after setting the lock state to allow the mobile app to interact with the lock in a more timely manner.

I have not yet taken a look at the /wss response payload to see why your branch doesn’t get the lock as the payload as the code in the branch expected.

What model lock? I wonder if the mobile app is talking Bluetooth to your lock. It’s strange that it takes so long to trigger through the cloud API.

Ok I subscribed to all the topics. Here’s what I see after I try to lock / unlock. There might be some variations but either way, there’s a long delay. When the lock finally reacts, I get a Reported event with the device state JSON in it.

Delta Event Received:
{'delta': {'attributes': {'lockState': 1, 'CAT': '...'}, 'CAT': '...', 'queuedCommands': True}}

Desired Event Received: {'desired': {'deviceId': '...', 'devicetypeId': 'be489wifi2', 'physicalId': '...', 'timezone': ..., 'name': 'My Door', 'attributes': {'timezone': ..., 'mode': 2, 'lockState': 1, 'periodicDeepQueryTimeSetting': 60, 'batteryLowState': 0, 'alarmState': 0, 'alarmSelection': 3, 'alarmSensitivity': 3, 'actAlarmBuzzerEnabled': 0, 'actAlarmState': 0, 'lockAndLeaveEnabled': 1, 'autoLockTime': 0, 'beeperEnabled': 1, 'mainFirmwareVersion': '01.00.00423847', 'keypadFirmwareVersion': '03.00.00250052', 'bleFirmwareVersion': '0118.000103.016', 'wifiFirmwareVersion': '03.15.00.01', 'batteryLevel': 41, 'batteryChangeDate': 0, 'accessCodeLength': ..., 'actuationCurrentMax': 115, 'homePosCurrentMax': 92, 'psPollEnabled': 1, 'batterySaverState': 0, ... 'queuedCommands': True}}

Delta Event Received: {'delta': {'attributes': {'lockState': 1, 'CAT': '...'}, 'CAT': '...', 'queuedCommands': True}}

Reported Event Received: {'reported': {'queuedCommands': False}}

Desired Event Received: {'desired': {'queuedCommands': True}}

Delta Event Received: {'delta': {'attributes': {'lockState': 1, 'CAT': '...'}, 'CAT': '...', 'queuedCommands': True}}

Then we sit and we wait. Eventually the lock responds / Schlage pushes the state to the lock

Notes / Context:

  • I see the same delays and event sequence using a username and password associated with a Virtual Key for this lock as I do with the username and password for the primary owner for this lock.
  • Also appearing in a section of the JSON payload I omitted above is “‘modelName’: 'BE489WB2”
  • If I use this library to “queue” up a lock / unlock as the virtual key user and then use the mobile app to trigger the same action as the primary owner, the lock does what it is asked and the history entry shows that the virtual key user was the one to perform the action.

This all makes me suspect there’s some “commit” or “wake up” command that we’re missing to make Schlage push the state change to the lock or to make the lock pick up the desired state and take action.

Son of a b…

I turned off BT and Wifi on my mobile and the app will not trigger any action on the lock now and complains about not being connected to the lock. So I’ve never tried the app outside my home. Does this mean that the app is useless outside BT range or does it mean that the mobile code wants BT on and to not see the lock in range and only then switches over to the API?

I wonder if the WiFi signal to your lock is poor. That could explain both the large delay from triggering via the python client, and why the app doesn’t work when disconnected from BT & WiFi.

An update: I published a version of the custom integration at GitHub - dknowles2/ha-schlage: Schlage Encode implementation for Home Assistant that uses my pyschlage library. It has basic support for lock & unlock operations and can monitor the battery level as a sensor. You can add it as a custom HACS repository and it should get update notifications automatically when new releases are pushed.

I’ve started sending @mcnutter1 pull requests for all the changes I’ve made, but I think we could probably also send a PR to HA-core to get this added as a native integration. I’ll look and see what’s involved with that–I assume the main thing missing is better documentation.

Cheers!

4 Likes