diff --git a/drivers/SmartThings/matter-lock/profiles/lock-user-pin-battery.yml b/drivers/SmartThings/matter-lock/profiles/lock-user-pin-battery.yml new file mode 100644 index 0000000000..2401c4119e --- /dev/null +++ b/drivers/SmartThings/matter-lock/profiles/lock-user-pin-battery.yml @@ -0,0 +1,29 @@ +name: lock-user-pin-battery +components: +- id: main + capabilities: + - id: lock + version: 1 + config: + values: + - key: "lock.value" + enabledValues: + - locked + - unlocked + - not fully locked + - id: lockAlarm + version: 1 + - id: remoteControlStatus + version: 1 + - id: lockUsers + version: 1 + - id: lockCredentials + version: 1 + - id: battery + version: 1 + - id: firmwareUpdate + version: 1 + - id: refresh + version: 1 + categories: + - name: SmartLock diff --git a/drivers/SmartThings/matter-lock/profiles/lock-user-pin-batteryLevel.yml b/drivers/SmartThings/matter-lock/profiles/lock-user-pin-batteryLevel.yml new file mode 100644 index 0000000000..582fcd3975 --- /dev/null +++ b/drivers/SmartThings/matter-lock/profiles/lock-user-pin-batteryLevel.yml @@ -0,0 +1,29 @@ +name: lock-user-pin-batteryLevel +components: +- id: main + capabilities: + - id: lock + version: 1 + config: + values: + - key: "lock.value" + enabledValues: + - locked + - unlocked + - not fully locked + - id: lockAlarm + version: 1 + - id: remoteControlStatus + version: 1 + - id: lockUsers + version: 1 + - id: lockCredentials + version: 1 + - id: batteryLevel + version: 1 + - id: firmwareUpdate + version: 1 + - id: refresh + version: 1 + categories: + - name: SmartLock diff --git a/drivers/SmartThings/matter-lock/profiles/lock-user-pin-schedule-battery.yml b/drivers/SmartThings/matter-lock/profiles/lock-user-pin-schedule-battery.yml new file mode 100644 index 0000000000..ac52c839fa --- /dev/null +++ b/drivers/SmartThings/matter-lock/profiles/lock-user-pin-schedule-battery.yml @@ -0,0 +1,31 @@ +name: lock-user-pin-schedule-battery +components: +- id: main + capabilities: + - id: lock + version: 1 + config: + values: + - key: "lock.value" + enabledValues: + - locked + - unlocked + - not fully locked + - id: lockAlarm + version: 1 + - id: remoteControlStatus + version: 1 + - id: lockUsers + version: 1 + - id: lockCredentials + version: 1 + - id: lockSchedules + version: 1 + - id: battery + version: 1 + - id: firmwareUpdate + version: 1 + - id: refresh + version: 1 + categories: + - name: SmartLock diff --git a/drivers/SmartThings/matter-lock/src/new-matter-lock/init.lua b/drivers/SmartThings/matter-lock/src/new-matter-lock/init.lua index c4cefed6cf..f9df8acb8b 100644 --- a/drivers/SmartThings/matter-lock/src/new-matter-lock/init.lua +++ b/drivers/SmartThings/matter-lock/src/new-matter-lock/init.lua @@ -25,15 +25,19 @@ if version.api < 10 then end local DoorLock = clusters.DoorLock +local PowerSource = clusters.PowerSource local INITIAL_COTA_INDEX = 1 local ALL_INDEX = 0xFFFE local NEW_MATTER_LOCK_PRODUCTS = { {0x115f, 0x2802}, -- AQARA, U200 {0x115f, 0x2801}, -- AQARA, U300 + {0x147F, 0x0001}, -- U-tec {0x10E1, 0x2002} -- VDA } +local PROFILE_BASE_NAME = "__profile_base_name" + local subscribed_attributes = { [capabilities.lock.ID] = { DoorLock.attributes.LockState @@ -53,6 +57,12 @@ local subscribed_attributes = { [capabilities.lockSchedules.ID] = { DoorLock.attributes.NumberOfWeekDaySchedulesSupportedPerUser, DoorLock.attributes.NumberOfYearDaySchedulesSupportedPerUser + }, + [capabilities.battery.ID] = { + PowerSource.attributes.BatPercentRemaining + }, + [capabilities.batteryLevel.ID] = { + PowerSource.attributes.BatChargeLevel } } @@ -126,6 +136,7 @@ local function do_configure(driver, device) 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 battery_eps = device:get_endpoints(PowerSource.ID, {feature_bitmap = PowerSource.types.PowerSourceFeature.BATTERY}) local profile_name = "lock" if #user_eps > 0 then @@ -137,8 +148,16 @@ local function do_configure(driver, device) profile_name = profile_name .. "-schedule" end end - device.log.info(string.format("Updating device profile to %s.", profile_name)) - device:try_update_metadata({profile = profile_name}) + + if #battery_eps > 0 then + device:set_field(PROFILE_BASE_NAME, profile_name, {persist = true}) + local req = im.InteractionRequest(im.InteractionRequest.RequestType.READ, {}) + req:merge(clusters.PowerSource.attributes.AttributeList:read()) + device:send(req) + else + device.log.info(string.format("Updating device profile to %s.", profile_name)) + device:try_update_metadata({profile = profile_name}) + end end local function info_changed(driver, device, event, args) @@ -340,6 +359,49 @@ local function max_year_schedule_of_user_handler(driver, device, ib, response) device:emit_event(capabilities.lockSchedules.yearDaySchedulesPerUser(ib.data.value, {visibility = {displayed = false}})) end +--------------------------------- +-- Power Source Attribute List -- +--------------------------------- +local function handle_power_source_attribute_list(driver, device, ib, response) + local support_battery_percentage = false + for _, attr in ipairs(ib.data.elements) do + -- Re-profile the device if BatPercentRemaining (Attribute ID 0x0C) is present. + if attr.value == 0x0C then + support_battery_percentage = true + end + end + local profile_name = device:get_field(PROFILE_BASE_NAME) + if support_battery_percentage then + profile_name = profile_name .. "-battery" + else + profile_name = profile_name .. "-batteryLevel" + end + device.log.info(string.format("Updating device profile to %s.", profile_name)) + device:try_update_metadata({profile = profile_name}) +end + +------------------------------- +-- Battery Percent Remaining -- +------------------------------- +local function handle_battery_percent_remaining(driver, device, ib, response) + if ib.data.value ~= nil then + device:emit_event(capabilities.battery.battery(math.floor(ib.data.value / 2.0 + 0.5))) + end +end + +-------------------------- +-- Battery Charge Level -- +-------------------------- +local function handle_battery_charge_level(driver, device, ib, response) + if ib.data.value == PowerSource.types.BatChargeLevelEnum.OK then + device:emit_event(capabilities.batteryLevel.battery.normal()) + elseif ib.data.value == PowerSource.types.BatChargeLevelEnum.WARNING then + device:emit_event(capabilities.batteryLevel.battery.warning()) + elseif ib.data.value == PowerSource.types.BatChargeLevelEnum.CRITICAL then + device:emit_event(capabilities.batteryLevel.battery.critical()) + end +end + -- Capability Handler ----------------- -- Lock/Unlock -- @@ -1634,6 +1696,11 @@ local new_matter_lock_handler = { [DoorLock.attributes.RequirePINforRemoteOperation.ID] = require_remote_pin_handler, [DoorLock.attributes.NumberOfWeekDaySchedulesSupportedPerUser.ID] = max_week_schedule_of_user_handler, [DoorLock.attributes.NumberOfYearDaySchedulesSupportedPerUser.ID] = max_year_schedule_of_user_handler, + }, + [PowerSource.ID] = { + [PowerSource.attributes.AttributeList.ID] = handle_power_source_attribute_list, + [PowerSource.attributes.BatPercentRemaining.ID] = handle_battery_percent_remaining, + [PowerSource.attributes.BatChargeLevel.ID] = handle_battery_charge_level, } }, event = { @@ -1685,7 +1752,10 @@ local new_matter_lock_handler = { capabilities.lock, capabilities.lockUsers, capabilities.lockCredentials, - capabilities.lockSchedules + capabilities.lockSchedules, + capabilities.remoteControlStatus, + capabilities.battery, + capabilities.batteryLevel }, can_handle = is_new_matter_lock_products }