Schlage Encode Wifi

Dubious. My AP sees the lock at -51dBm and the lock API reports “‘wifiRssi’: -52” in the response to getting the lock. The lock is on a 2.4GHz network so that’s a pretty solid signal strength.

There may be some power save stuff going on with the wifi or the lock has an issue in its firmware. Seems I’m not the only one seeing this weirdness in responsiveness: Schlage Encode Wifi - #133 by DavidValeri

Wait a minute… these locks do have BT/BLE going on… does this mean that there is some possibility of local BT control???

I’ve installed this from HACS and it works great on my Sense BT lock with BR400 bridge.
I can now lock it 10 minutes after I close it instead of it auto-locking while open. I also have alerts when its jammed. Thank you for your work on this.

Yes, there’s some chance of it. I’ve tried to sniff the Bluetooth protocol, but something isn’t working right in my sniffer.

1 Like

I’ve examined the APK. For the deviceId that my “Schlage Encode™ Smart WiFi Deadbolt” reports, the app considers it a WiFi device in WiFi mode. But what this appears to do is to blend a combination of the REST client and a BLE client together. I have not yet dug through the abstraction, decorators, and obfuscation to figure out how it blends the two together or falls back to the REST API.

I did try to send the lock / unlock command to the …/commands path and I get a 504. Not sure if I butchered the payload or if the app ultimately ends up using a different mechanism if out of BT range.

I have not done much APK reverse engineering before. I would kill for the ability to use an interactive debugger on the app as that would make it much easier to move around and understand the flow / effective composition of obfuscated stuff. Is this possible using the mess of decompiled code from things like APKTool or is that a lost cause and the easier route is getting setup with the proxies so I can sniff traffic?

This works much better for me now - not sure what changes exactly made a difference, but the lock and unlock commands are noticeably faster. Status reflecting in HA when manually locked/unlocked is about the same 5-10 sec delay (I assume this is due to polling) vs the Schlage app push notifications being instant.
Overall great stuff! This should be HA native integration at this point

I’ve been using GitHub - skylot/jadx: Dex to Java decompiler to step through the decompiled APK. I spent a lot of time renaming the obfuscated code based on string hints and what I’ve seen from other decompiled APKs that were less obfuscated. The parts that switch between BT and WiFi are indeed confusing and horribly obfuscated.

It still seems strange that your model has this large delay over the wifi connection. I wonder if it’s some side-effect of using a virtual key instead of the native account? I haven’t had a chance to finish setting up a virtual key yet, and I’m also travelling so can’t observe the latency on my locks at the moment.

Its not the virtual key, I use the admin account on mine and over time it begins to react slower. When I first power up the lock it will react instantly, over time (a week or two) it will get to the point its 20-30 minutes late reacting. If I pull the lock’s battery and power cycle it, it’ll go right back to reacting instantly.
I’ve been trying to figure out what in the world causes it to do that, but so far I’ve not found any indications. I can see the lock get the packet from the internet instantly. The lock just doesn’t process it. And since I can’t decrypt the lock’s end of things currently I can’t see why that might be.

Well this is not encouraging :frowning:

This AM, I played around some with the async command endpoint and I was able to create a changelockstate command (API returns a 201 w/ a UUID in the payload) and then query the command state endpoint for it. I assume the long delay in lock response is why sending the command to my lock is generating the 504 response.

Right now my command is sitting in the pending status. I’ve got other things to do today but I will look more at the APK and the other things one can do with the lock to see if any of them get it to execute the pending command(s) despite its seeming reluctance to respond to the API calls.

On the lock becoming less responsive over time:
The PUT with the partial update generates an entry in the commands queue. The entry gets logged as a “_update” command. It has a PENDING, FAILED, and SUCCESSFUL state just like the commands sent to /commands.

The “changelockstate” command and the PUT with a partial payload both get stuck in there when the lock becomes less responsive. Power cycling the lock does not seem to get the lock to pick up older PENDING commands.

This leads me to believe that commands won’t be the magic bullet to deal with the problem of the lock becoming less responsive over time. FWIW, the lock took less than 24 hours this time to become unresponsive to the REST API.

Also would seem that the client being subscribed to the MQTT topics doesn’t have any impact on the the lock being responsive to commands.

On the BLE interface:
I found the code that handles lock and unlock over BLE. Unlike the REST client interfaces, it is obfuscated. I’ll eventually figure out the dance it does with the BLE services and the bit fiddling for the request / response if nobody beats me to it.

2 Likes

Man, you’re not kidding. I took a poke at it a bit this evening. Not nearly as easy to decode as the API parts. It seems to jump through a LOT of strange hoops in there.

Is there a way to get user codes. So if I input on touch pad 1111 home assistant could recognize who so on entering I could get my speakers to say welcome home to whomevers code was entered.

1 Like

The data is all there, but we can’t get instantaneous updates about the lock being unlocked. So you’d probably have a 30sec+ delay getting your announcement. Annotating lock events in HA with the user that made the change is also a bit tricky—we’d need to read the Schlage event logs when updating the HA state to get the annotation. Reading lock state and reading the logs are two different API calls, so it’s not totally straightforward to get working.

So I did some more digging on Bluetooth access to the lock. It appears from hints in the code that it may operate only in Wifi mode OR BLE mode, not both at the same time. That makes this a bit less useful, as we’d be limiting the lock to a bluetooth only lock. But its still got some uses and I’m still digging. I’ve yet to nail down exactly where it’s actually talking to the lock over BLE, but such is the joys of reverse compiled code…

2 Likes

ik and jk look like to be involved in reading and writing lock state. There’s some fun layers of abstraction, presumably from the obfuscation so also check up the class hierarchy. All the BLE command related classes I went through seem to have a basic state machine flow of something like discovering, connecting, authenticating, sending the command, waiting for the response, etc…

I was getting updates within 2 sec of button pushed on phone the app for sclage and home assistant had the same trigger times. However I do have a ubiquiti range extender wifi 6 within 10 ft of lock. I am learning and trying to figure stuff out. I installed mcnutters program for the sclage encode. This morning I got some errors

2023-01-09 08:36:10.439 ERROR (MainThread) [homeassistant.helpers.integration_platform] Unexpected error importing schlage/logbook.py
Traceback (most recent call last):
File “/usr/src/homeassistant/homeassistant/helpers/integration_platform.py”, line 40, in _async_process_single_integration_platform_component
platform = integration.get_platform(platform_name)
File “/usr/src/homeassistant/homeassistant/loader.py”, line 744, in get_platform
cache[full_name] = self._import_platform(platform_name)
File “/usr/src/homeassistant/homeassistant/loader.py”, line 761, in _import_platform
return importlib.import_module(f"{self.pkg_path}.{platform_name}")
File “/usr/local/lib/python3.10/importlib/init.py”, line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File “”, line 1050, in _gcd_import
File “”, line 1027, in _find_and_load
File “”, line 1004, in _find_and_load_unlocked
ModuleNotFoundError: No module named ‘custom_components.sc.logbook’
2023-01-09 08:36:10.444 ERROR (MainThread) [homeassistant.helpers.integration_platform] Unexpected error importing schlage/media_source.py
Traceback (most recent call last):
File “/usr/src/homeassistant/homeassistant/helpers/integration_platform.py”, line 40, in _async_process_single_integration_platform_component
platform = integration.get_platform(platform_name)
File “/usr/src/homeassistant/homeassistant/loader.py”, line 744, in get_platform
cache[full_name] = self._import_platform(platform_name)
File “/usr/src/homeassistant/homeassistant/loader.py”, line 761, in _import_platform
return importlib.import_module(f"{self.pkg_path}.{platform_name}")
File “/usr/local/lib/python3.10/importlib/init.py”, line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File “”, line 1050, in _gcd_import
File “”, line 1027, in _find_and_load
File “”, line 1004, in _find_and_load_unlocked
ModuleNotFoundError: No module named ‘custom_components.sc.media_source’
2023-01-09 08:36:10.448 ERROR (MainThread) [homeassistant.helpers.integration_platform] Unexpected error importing schlage/system_health.py
Traceback (most recent call last):
File “/usr/src/homeassistant/homeassistant/helpers/integration_platform.py”, line 40, in _async_process_single_integration_platform_component
platform = integration.get_platform(platform_name)
File “/usr/src/homeassistant/homeassistant/loader.py”, line 744, in get_platform
cache[full_name] = self._import_platform(platform_name)
File “/usr/src/homeassistant/homeassistant/loader.py”, line 761, in _import_platform
return importlib.import_module(f"{self.pkg_path}.{platform_name}")
File “/usr/local/lib/python3.10/importlib/init.py”, line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File “”, line 1050, in _gcd_import
File “”, line 1027, in _find_and_load
File “”, line 1004, in _find_and_load_unlocked
ModuleNotFoundError: No module named ‘custom_components.sc.system_health’
2023-01-09 08:36:14.456 ERROR (MainThread) [homeassistant.helpers.integration_platform] Unexpected error importing schlage/diagnostics.py
Traceback (most recent call last):
File “/usr/src/homeassistant/homeassistant/helpers/integration_platform.py”, line 40, in _async_process_single_integration_platform_component
platform = integration.get_platform(platform_name)
File “/usr/src/homeassistant/homeassistant/loader.py”, line 744, in get_platform
cache[full_name] = self._import_platform(platform_name)
File “/usr/src/homeassistant/homeassistant/loader.py”, line 761, in _import_platform
return importlib.import_module(f"{self.pkg_path}.{platform_name}")
File “/usr/local/lib/python3.10/importlib/init.py”, line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File “”, line 1050, in _gcd_import
File “”, line 1027, in _find_and_load
File “”, line 1004, in _find_and_load_unlocked
ModuleNotFoundError: No module named ‘custom_components.sc.diagnostics’
2023-01-09 08:36:14.458 ERROR (MainThread) [homeassistant.helpers.integration_platform] Unexpected error importing schlage/recorder.py
Traceback (most recent call last):
File “/usr/src/homeassistant/homeassistant/helpers/integration_platform.py”, line 40, in _async_process_single_integration_platform_component
platform = integration.get_platform(platform_name)
File “/usr/src/homeassistant/homeassistant/loader.py”, line 744, in get_platform
cache[full_name] = self._import_platform(platform_name)
File “/usr/src/homeassistant/homeassistant/loader.py”, line 761, in _import_platform
return importlib.import_module(f"{self.pkg_path}.{platform_name}")
File “/usr/local/lib/python3.10/importlib/init.py”, line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File “”, line 1050, in _gcd_import
File “”, line 1027, in _find_and_load
File “”, line 1004, in _find_and_load_unlocked
ModuleNotFoundError: No module named ‘custom_components.sc.recorder’
2023-01-09 08:43:46.712 ERROR (MainThread) [homeassistant.helpers.integration_platform] Unexpected error importing schlage/hardware.py
Traceback (most recent call last):
File “/usr/src/homeassistant/homeassistant/helpers/integration_platform.py”, line 40, in _async_process_single_integration_platform_component
platform = integration.get_platform(platform_name)
File “/usr/src/homeassistant/homeassistant/loader.py”, line 744, in get_platform
cache[full_name] = self._import_platform(platform_name)
File “/usr/src/homeassistant/homeassistant/loader.py”, line 761, in _import_platform
return importlib.import_module(f"{self.pkg_path}.{platform_name}")
File “/usr/local/lib/python3.10/importlib/init.py”, line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File “”, line 1050, in _gcd_import
File “”, line 1027, in _find_and_load
File “”, line 1004, in _find_and_load_unlocked
ModuleNotFoundError: No module named ‘custom_components.sc.hardware’

@mcnutter1 I see in HACS a new update is being advertised from you GitHub but this item will not update. Here’s the error I’m seeing in the HA core log:

Logger: homeassistant.components.websocket_api.http.connection
Source: custom_components/hacs/repositories/integration.py:182
Integration: Home Assistant WebSocket API (documentation, issues)
First occurred: 9:53:50 AM (1 occurrences)
Last logged: 9:53:50 AM

[139626694222048] Error handling message: Unknown error (unknown_error) from 10.1.2.45 (Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 Edg/108.0.1462.76)
Traceback (most recent call last):
File “/usr/src/homeassistant/homeassistant/components/websocket_api/decorators.py”, line 27, in _handle_async_response
await func(hass, connection, msg)
File “/config/custom_components/hacs/websocket/repository.py”, line 40, in hacs_repository_info
await repository.update_repository(ignore_issues=True, force=True)
File “/config/custom_components/hacs/utils/decorator.py”, line 28, in wrapper
result = await function(*args, **kwargs)
File “/config/custom_components/hacs/repositories/integration.py”, line 126, in update_repository
if manifest := await self.async_get_integration_manifest():
File “/config/custom_components/hacs/repositories/integration.py”, line 182, in async_get_integration_manifest
return json_loads(decode_content(response.data.content))
orjson.JSONDecodeError: trailing comma is not allowed: line 11 column 2 (char 359)

UPDATE: I see this report and in a new pull request for anyone else having this issue.

Remove trailing comma to fix HAOS error by glarue · Pull Request #13 · mcnutter1/homeasssitant-schlage · GitHub

On the subject of bluetooth. Maybe this helps someone, I’m just getting into the game both working with HA and trying to get my expensive Schlage Encode to work with HA. I would rather go the bluetooth approach as I’m just not interested in being reliant on cloud access.

┌──────────────┬────────────────────────────────────────────────────────────────┬─────────────┬───────────────────────────────────────────────────────────────┐
│   Handles    │                   Service > Characteristics                    │ Properties  │                             Data                              │
├──────────────┼────────────────────────────────────────────────────────────────┼─────────────┼───────────────────────────────────────────────────────────────┤
│ 0001 -> 0007 │ Generic Access (1800)                                          │             │                                                               │
│ 0003         │     Device Name (2a00)                                         │ WRITE       │                                                               │
│ 0005         │     Appearance (2a01)                                          │ WRITE       │                                                               │
│ 0007         │     Peripheral Preferred Connection Parameters (2a04)          │ READ        │ Connection Interval: 12 -> 12                                 │
│              │                                                                │             │ Slave Latency: 0                                              │
│              │                                                                │             │ Connection Supervision Timeout Multiplier: 500                │
│              │                                                                │             │                                                               │
│ 0008 -> 0008 │ Generic Attribute (1801)                                       │             │                                                               │
│              │                                                                │             │                                                               │
│ 0009 -> 001b │ Device Information (180a)                                      │             │                                                               │
│ 000b         │     System ID (2a23)                                           │ READ        │ 0000000000000000                                              │
│ 000d         │     Model Number String (2a24)                                 │ READ        │ BE489WB                                                       │
│ 000f         │     Serial Number String (2a25)                                │ READ        │ 3100xxxxxxxxxxx                                              │
│ 0011         │     Firmware Revision String (2a26)                            │ READ        │ 10.00.00264232                                                │
│ 0013         │     Hardware Revision String (2a27)                            │ READ        │                                                               │
│ 0015         │     Software Revision String (2a28)                            │ READ        │ Software Revision                                             │
│ 0017         │     Manufacturer Name String (2a29)                            │ READ        │ Schlage                                                       │
│ 0019         │     IEEE 11073-20601 Regulatory Certification Data List (2a2a) │ READ        │ þ00experimental                                               │
│ 001b         │     PnP ID (2a50)                                              │ READ        │ Vendor ID: 0x000d (Bluetooth SIG assigned Company Identifier) │
│              │                                                                │             │ Product ID: 0x0000                                            │
│              │                                                                │             │ Product Version: 0x0110                                       │
│              │                                                                │             │                                                               │
│ 001c -> 0023 │ 883f45ec14cb46aa98649a4e782b33d0                               │             │                                                               │
│ 001e         │     e604e95da759481787d3aa005083a0d1                           │ READ        │ reserved error code                                           │
│ 0020         │     ff530c78cd504bb9bbd40712f32b3796                           │ WRITE       │                                                               │
│ 0022         │     26002998e00148128c085cd2afda0830                           │ INDICATE    │                                                               │
│              │                                                                │             │                                                               │
│ 0024 -> 002a │ 7f0dee734a3f410398e6a46cd301bdfb                               │             │                                                               │
│ 0026         │     e604e95da759481787d3aa005083a0d1                           │ READ        │ read not permitted                                            │
│ 0028         │     44ff685358db4956b2985f6650dd61f6                           │ WRITE       │                                                               │
│ 002a         │     bcde3b9e39634123b24d42eccbb3a9c4                           │ READ        │ reserved error code                                           │
│              │                                                                │             │                                                               │
│ 002b -> ffff │ 1f6b43aa94de4ba9981cda38823117bd                               │             │                                                               │
│ 002d         │     e604e95da759481787d3aa005083a0d1                           │ READ        │ reserved error code                                           │
│ 002f         │     048d8799695b4a7fa7f7a4a1301587fe                           │ READ, WRITE │ reserved error code                                           │
│ 0031         │     66b7c7fd95a74f89b0ad38073a67c46c                           │ WRITE       │                                                               │
│ 0033         │     507efc3f9231438c976afa04427f1f8f                           │ WRITE       │                                                               │
│ 0035         │     1dc1571908824badab0f9aeab0600c90                           │ READ        │ reserved error code                                           │
│              │                                                                │             │                                                               │
└──────────────┴────────────────────────────────────────────────────────────────┴─────────────┴───────────────────────────────────────────────────────────────┘

2 Likes

I am successfully using this integration and it is working mostly great. I haven’t seen the status delays up to this point.

On the topic of bluetooth, my biggest issue with the lock is that it randomly drops the wifi connection and I have to reconfirm the settings in the Schlage app to get it to reconnect. This is seemingly initiated by bluetooth since the app tells me I need to be within bluetooth range. If there was some way to automate this within HA, I would be thrilled.

Anyway, great work on the integration and thanks for all the hard work so far. Very happy to have it.

Is this still working for everyone? I have it installed, encode plus shows up in home assistant, but it doesn’t lock/unlock/update status. I can still control it from HomeKit but I lose the ability to control/update status in the Schlage app. Thank you in advance!