Repository navigation
Add Schlage Sense Pro UWB settings on modular lock profiles #3303
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
Open
Kwang-Hui
wants to merge
7
commits into
SmartThingsCommunity:main
Choose a base branch
from
Kwang-Hui:feature/schlage-sense-pro-uwb
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
c387d3f
Add Schlage Sense Pro UWB subdriver and lock-modular-uwb-settings pro…
Kwang-Hui 7f76405
Support OTA driver update migration for existing Schlage locks
Kwang-Hui f981d40
Remove circular supports_capability_by_id check from match_profile_mo…
Kwang-Hui ea77f73
Decouple main driver and make Schlage UWB subdriver fully self-contained
Kwang-Hui 34b47b7
Enable Schlage UWB settings on the existing modular lock profiles
Kwang-Hui c91147f
Use published UWB capabilities and call the shared profile matcher di…
Kwang-Hui 9718d88
Drop the embedded UWB capability schemas from the Schlage tests.
Kwang-Hui File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
164 changes: 164 additions & 0 deletions
164
drivers/SmartThings/matter-lock/src/device_configuration.lua
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,164 @@ | ||
| -- Copyright 2026 SmartThings, Inc. | ||
| -- Licensed under the Apache License, Version 2.0 | ||
|
|
||
| local capabilities = require "st.capabilities" | ||
| local clusters = require "st.matter.clusters" | ||
| local version = require "version" | ||
| local lock_utils = require "lock_utils" | ||
|
|
||
| if version.api < 10 then | ||
| clusters.DoorLock = require "DoorLock" | ||
| end | ||
|
|
||
| local DoorLock = clusters.DoorLock | ||
|
|
||
| local device_configuration = {} | ||
|
|
||
| device_configuration.battery_support = { | ||
| NO_BATTERY = "NO_BATTERY", | ||
| BATTERY_LEVEL = "BATTERY_LEVEL", | ||
| BATTERY_PERCENTAGE = "BATTERY_PERCENTAGE" | ||
| } | ||
|
|
||
| device_configuration.profiling_data = { | ||
| BATTERY_SUPPORT = "__BATTERY_SUPPORT", | ||
| } | ||
|
|
||
| local optional_capability_providers = {} | ||
|
|
||
| --- Register extra optional capabilities contributed by a subdriver. | ||
| --- The provider is called with the device and returns a list of capability ids, or nil. | ||
| --- @param provider fun(device: Device): string[]|nil | ||
| function device_configuration.register_optional_capabilities(provider) | ||
| table.insert(optional_capability_providers, provider) | ||
| end | ||
|
|
||
| local function extra_optional_capabilities(device) | ||
| local extras = {} | ||
| local seen = {} | ||
| for _, provider in ipairs(optional_capability_providers) do | ||
| local caps = provider(device) | ||
| for _, cap_id in ipairs(caps or {}) do | ||
| if not seen[cap_id] then | ||
| seen[cap_id] = true | ||
| table.insert(extras, cap_id) | ||
| end | ||
| end | ||
| end | ||
| return extras | ||
| end | ||
|
|
||
| local function profiling_data_still_required(device) | ||
| for _, field in pairs(device_configuration.profiling_data) do | ||
| if device:get_field(field) == nil then | ||
| return true | ||
| end | ||
| end | ||
| return false | ||
| end | ||
|
|
||
| function device_configuration.match_profile_modular(driver, device) | ||
| local enabled_optional_component_capability_pairs = {} | ||
| local main_component_capabilities = {} | ||
| local modular_profile_name = "lock-modular" | ||
| for _, device_ep in pairs(device.endpoints) do | ||
| for _, ep_cluster in pairs(device_ep.clusters) do | ||
| if ep_cluster.cluster_id == DoorLock.ID then | ||
| local clus_has_feature = function(feature_bitmap) | ||
| return DoorLock.are_features_supported( | ||
| feature_bitmap, | ||
| lock_utils.get_field_for_endpoint(device, lock_utils.LATEST_DOOR_LOCK_FEATURE_MAP, device_ep.endpoint_id) or | ||
| ep_cluster.feature_map | ||
| ) | ||
| end | ||
| if clus_has_feature(DoorLock.types.Feature.USER) then | ||
| table.insert(main_component_capabilities, capabilities.lockUsers.ID) | ||
| end | ||
| if clus_has_feature(DoorLock.types.Feature.PIN_CREDENTIAL) then | ||
| table.insert(main_component_capabilities, capabilities.lockCredentials.ID) | ||
| end | ||
| if clus_has_feature(DoorLock.types.Feature.WEEK_DAY_ACCESS_SCHEDULES) or | ||
| clus_has_feature(DoorLock.types.Feature.YEAR_DAY_ACCESS_SCHEDULES) then | ||
| table.insert(main_component_capabilities, capabilities.lockSchedules.ID) | ||
| end | ||
| if clus_has_feature(DoorLock.types.Feature.UNBOLT) then | ||
| device:emit_event(capabilities.lock.supportedLockValues({"locked", "unlocked", "unlatched", "not fully locked"}, {visibility = {displayed = false}})) | ||
| device:emit_event(capabilities.lock.supportedLockCommands({"lock", "unlock", "unlatch"}, {visibility = {displayed = false}})) | ||
| modular_profile_name = "lock-modular-embedded-unlatch" | ||
| else | ||
| device:emit_event(capabilities.lock.supportedLockValues({"locked", "unlocked", "not fully locked"}, {visibility = {displayed = false}})) | ||
| device:emit_event(capabilities.lock.supportedLockCommands({"lock", "unlock"}, {visibility = {displayed = false}})) | ||
| end | ||
| if clus_has_feature(DoorLock.types.Feature.ALIRO_PROVISIONING) then | ||
| table.insert(main_component_capabilities, capabilities.lockAliro.ID) | ||
| end | ||
| break | ||
| end | ||
| end | ||
| end | ||
|
|
||
| -- Subdriver registrations, including Schlage UWB settings, are applied on every profile match. | ||
| for _, cap_id in ipairs(extra_optional_capabilities(device)) do | ||
| table.insert(main_component_capabilities, cap_id) | ||
| end | ||
|
|
||
| local supported_battery_type = device:get_field(device_configuration.profiling_data.BATTERY_SUPPORT) | ||
| if supported_battery_type == device_configuration.battery_support.BATTERY_LEVEL then | ||
| table.insert(main_component_capabilities, capabilities.batteryLevel.ID) | ||
| elseif supported_battery_type == device_configuration.battery_support.BATTERY_PERCENTAGE then | ||
| table.insert(main_component_capabilities, capabilities.battery.ID) | ||
| end | ||
|
|
||
| table.insert(enabled_optional_component_capability_pairs, {"main", main_component_capabilities}) | ||
| if modular_profile_name == "lock-modular-embedded-unlatch" | ||
| or device:supports_capability(capabilities.lockCodes) | ||
| or lock_utils.optional_capabilities_list_changed(enabled_optional_component_capability_pairs, device.profile.components) then | ||
| device:try_update_metadata({profile = modular_profile_name, optional_component_capabilities = enabled_optional_component_capability_pairs}) | ||
| end | ||
| end | ||
|
|
||
| local function match_profile_switch(driver, device) | ||
| local user_eps = device:get_endpoints(DoorLock.ID, {feature_bitmap = DoorLock.types.Feature.USER}) | ||
| local pin_eps = device:get_endpoints(DoorLock.ID, {feature_bitmap = DoorLock.types.Feature.PIN_CREDENTIAL}) | ||
| local week_schedule_eps = device:get_endpoints(DoorLock.ID, {feature_bitmap = DoorLock.types.Feature.WEEK_DAY_ACCESS_SCHEDULES}) | ||
| local year_schedule_eps = device:get_endpoints(DoorLock.ID, {feature_bitmap = DoorLock.types.Feature.YEAR_DAY_ACCESS_SCHEDULES}) | ||
| local unbolt_eps = device:get_endpoints(DoorLock.ID, {feature_bitmap = DoorLock.types.Feature.UNBOLT}) | ||
|
|
||
| local profile_name = "lock" | ||
| if #user_eps > 0 then | ||
| profile_name = profile_name .. "-user" | ||
| if #pin_eps > 0 then | ||
| profile_name = profile_name .. "-pin" | ||
| end | ||
| if #week_schedule_eps + #year_schedule_eps > 0 then | ||
| profile_name = profile_name .. "-schedule" | ||
| end | ||
| end | ||
| if #unbolt_eps > 0 then | ||
| profile_name = profile_name .. "-unlatch" | ||
| device:emit_event(capabilities.lock.supportedLockCommands({"lock", "unlock", "unlatch"}, {visibility = {displayed = false}})) | ||
| else | ||
| device:emit_event(capabilities.lock.supportedLockCommands({"lock", "unlock"}, {visibility = {displayed = false}})) | ||
| end | ||
|
|
||
| local supported_battery_type = device:get_field(device_configuration.profiling_data.BATTERY_SUPPORT) | ||
| if supported_battery_type == device_configuration.battery_support.BATTERY_LEVEL then | ||
| profile_name = profile_name .. "-batteryLevel" | ||
| elseif supported_battery_type == device_configuration.battery_support.BATTERY_PERCENTAGE then | ||
| profile_name = profile_name .. "-battery" | ||
| end | ||
|
|
||
| device.log.info_with({hub_logs=true}, string.format("Updating device profile to %s.", profile_name)) | ||
| device:try_update_metadata({profile = profile_name}) | ||
| end | ||
|
|
||
| function device_configuration.match_profile(driver, device, ignore_static_profiling) | ||
| if profiling_data_still_required(device) then return end | ||
| if version.api >= 15 and version.rpc >= 9 then | ||
| device_configuration.match_profile_modular(driver, device) | ||
| elseif ignore_static_profiling ~= true then | ||
| match_profile_switch(driver, device) | ||
| end | ||
| end | ||
|
|
||
| return device_configuration | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
17 changes: 17 additions & 0 deletions
17
drivers/SmartThings/matter-lock/src/schlage-sense-pro/can_handle.lua
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| -- Copyright 2026 SmartThings, Inc. | ||
| -- Licensed under the Apache License, Version 2.0 | ||
|
|
||
| local constants = require "schlage-sense-pro.constants" | ||
|
|
||
| --- Matches Allegion vendor ID 0x1236 with product ID 0x3800 or the custom UWB cluster. | ||
| local function is_schlage_sense_pro(opts, driver, device) | ||
| if device.manufacturer_info and device.manufacturer_info.vendor_id == constants.SCHLAGE_VENDOR_ID then | ||
| if device.manufacturer_info.product_id == constants.SCHLAGE_SENSE_PRO_PRODUCT_ID or | ||
| #device:get_endpoints(constants.SCHLAGE_UWB_CUSTOM_CLUSTER_ID) > 0 then | ||
| return true, require("schlage-sense-pro") | ||
| end | ||
| end | ||
| return false | ||
| end | ||
|
|
||
| return is_schlage_sense_pro |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.