diff --git a/drivers/SmartThings/matter-switch/fingerprints.yml b/drivers/SmartThings/matter-switch/fingerprints.yml index 96ee824e7a..323daa4e90 100644 --- a/drivers/SmartThings/matter-switch/fingerprints.yml +++ b/drivers/SmartThings/matter-switch/fingerprints.yml @@ -87,6 +87,33 @@ matterManufacturer: productId: 0x9002 deviceProfileName: light-color-level-2700K-6500K +#AiDot + - id: "Linkind/Smart/Light/Bulb/A19/RGBTW" + deviceLabel: Linkind Smart Light Bulb A19 RGBTW + vendorId: 0x1168 + productId: 0x03e8 + deviceProfileName: light-color-level-1800K-6500K + - id: "OREiN/Smart/Light/Bulb/A19/RGBTW" + deviceLabel: OREiN Smart Light Bulb A19 RGBTW + vendorId: 0x1168 + productId: 0x03ea + deviceProfileName: light-color-level-1800K-6500K + - id: "Linkind/Smart/Light/Bulb/BR30/RGBTW" + deviceLabel: Linkind Smart Light Bulb BR30 RGBTW + vendorId: 0x1168 + productId: 0x03eb + deviceProfileName: light-color-level-1800K-6500K + - id: "OREiN/Smart/Light/Bulb/BR30/RGBTW" + deviceLabel: OREiN Smart Light Bulb BR30 RGBTW + vendorId: 0x1168 + productId: 0x03ec + deviceProfileName: light-color-level-1800K-6500K + - id: "Consciot/Smart/Light/Bulb/A19/RGBT" + deviceLabel: Consciot Smart Light Bulb A19 RGBT + vendorId: 0x1168 + productId: 0x0405 + deviceProfileName: light-color-level-1800K-6500K + #WiZ - id: "WiZ A19" deviceLabel: WiZ A19 @@ -1884,6 +1911,7 @@ matterManufacturer: productId: 0x228F deviceProfileName: light-color-level-2200K-6500K + #Bridge devices need manufacturer specific fingerprints until #bridge support is released to all hubs. This is because of the way generic #fingerprints work for bridges joined prior to hubs being able to support them diff --git a/drivers/SmartThings/matter-switch/profiles/light-color-level-1800K-6500K.yml b/drivers/SmartThings/matter-switch/profiles/light-color-level-1800K-6500K.yml new file mode 100755 index 0000000000..1638962b8f --- /dev/null +++ b/drivers/SmartThings/matter-switch/profiles/light-color-level-1800K-6500K.yml @@ -0,0 +1,26 @@ +name: light-color-level-1800K-6500K +components: +- id: main + capabilities: + - id: switch + version: 1 + - id: switchLevel + version: 1 + config: + values: + - key: "level.value" + range: [1, 100] + - id: colorTemperature + version: 1 + config: + values: + - key: "colorTemperature.value" + range: [ 1800, 6500 ] + - id: colorControl + version: 1 + - id: firmwareUpdate + version: 1 + - id: refresh + version: 1 + categories: + - name: Light diff --git a/drivers/SmartThings/zigbee-lock/src/init.lua b/drivers/SmartThings/zigbee-lock/src/init.lua index 804a65b897..091384e3da 100644 --- a/drivers/SmartThings/zigbee-lock/src/init.lua +++ b/drivers/SmartThings/zigbee-lock/src/init.lua @@ -34,8 +34,12 @@ local UserStatusEnum = LockCluster.types.DrlkUserStatus local UserTypeEnum = LockCluster.types.DrlkUserType local ProgrammingEventCodeEnum = LockCluster.types.ProgramEventCode +local socket = require "cosock.socket" local lock_utils = require "lock_utils" +local DELAY_LOCK_EVENT = "_delay_lock_event" +local MAX_DELAY = 10 + local reload_all_codes = function(driver, device, command) -- starts at first user code index then iterates through all lock codes as they come in device:send(LockCluster.attributes.SendPINOverTheAir:write(device, true)) @@ -300,6 +304,92 @@ local function init(driver, device) lock_utils.populate_state_from_data(device) end +-- The following two functions are from the lock defaults. They are in the base driver temporarily +-- until the fix is widely released in the lua libs +local lock_state_handler = function(driver, device, value, zb_rx) + local attr = capabilities.lock.lock + local LOCK_STATE = { + [value.NOT_FULLY_LOCKED] = attr.unknown(), + [value.LOCKED] = attr.locked(), + [value.UNLOCKED] = attr.unlocked(), + [value.UNDEFINED] = attr.unknown(), + } + + -- this is where we decide whether or not we need to delay our lock event because we've + -- observed it coming before the event (or we're starting to compute the timer) + local delay = device:get_field(DELAY_LOCK_EVENT) or 100 + if (delay < MAX_DELAY) then + device.thread:call_with_delay(delay+.5, function () + device:emit_event_for_endpoint(zb_rx.address_header.src_endpoint.value, LOCK_STATE[value.value]) + end) + else + device:set_field(DELAY_LOCK_EVENT, socket.gettime()) + device:emit_event_for_endpoint(zb_rx.address_header.src_endpoint.value, LOCK_STATE[value.value]) + end +end + +local lock_operation_event_handler = function(driver, device, zb_rx) + local event_code = zb_rx.body.zcl_body.operation_event_code.value + local source = zb_rx.body.zcl_body.operation_event_source.value + local OperationEventCode = require "st.zigbee.generated.zcl_clusters.DoorLock.types.OperationEventCode" + local METHOD = { + [0] = "keypad", + [1] = "command", + [2] = "manual", + [3] = "rfid", + [4] = "fingerprint", + [5] = "bluetooth" + } + local STATUS = { + [OperationEventCode.LOCK] = capabilities.lock.lock.locked(), + [OperationEventCode.UNLOCK] = capabilities.lock.lock.unlocked(), + [OperationEventCode.ONE_TOUCH_LOCK] = capabilities.lock.lock.locked(), + [OperationEventCode.KEY_LOCK] = capabilities.lock.lock.locked(), + [OperationEventCode.KEY_UNLOCK] = capabilities.lock.lock.unlocked(), + [OperationEventCode.AUTO_LOCK] = capabilities.lock.lock.locked(), + [OperationEventCode.MANUAL_LOCK] = capabilities.lock.lock.locked(), + [OperationEventCode.MANUAL_UNLOCK] = capabilities.lock.lock.unlocked(), + [OperationEventCode.SCHEDULE_LOCK] = capabilities.lock.lock.locked(), + [OperationEventCode.SCHEDULE_UNLOCK] = capabilities.lock.lock.unlocked() + } + local event = STATUS[event_code] + if (event ~= nil) then + event["data"] = {} + if (event_code == OperationEventCode.AUTO_LOCK or + event_code == OperationEventCode.SCHEDULE_LOCK or + event_code == OperationEventCode.SCHEDULE_UNLOCK + ) then + event.data.method = "auto" + else + event.data.method = METHOD[source] + end + if (source == 0) then --keypad + local code_id = zb_rx.body.zcl_body.user_id.value + local code_name = "Code "..code_id + local lock_codes = device:get_field("lockCodes") + if (lock_codes ~= nil and + lock_codes[code_id] ~= nil) then + code_name = lock_codes[code_id] + end + event.data = {method = METHOD[0], codeId = code_id .. "", codeName = code_name} + end + + -- if this is an event corresponding to a recently-received attribute report, we + -- want to set our delay timer for future lock attribute report events + if device:get_latest_state( + device:get_component_id_for_endpoint(zb_rx.address_header.src_endpoint.value), + capabilities.lock.ID, + capabilities.lock.lock.ID) == event.value.value then + local preceding_event_time = device:get_field(DELAY_LOCK_EVENT) or 0 + local time_diff = socket.gettime() - preceding_event_time + if time_diff < MAX_DELAY then + device:set_field(DELAY_LOCK_EVENT, time_diff) + end + end + + device:emit_event_for_endpoint(zb_rx.address_header.src_endpoint.value, event) + end +end local zigbee_lock_driver = { supported_capabilities = { @@ -314,11 +404,13 @@ local zigbee_lock_driver = { }, [LockCluster.ID] = { [LockCluster.client.commands.GetPINCodeResponse.ID] = get_pin_response_handler, - [LockCluster.client.commands.ProgrammingEventNotification.ID] = programming_event_handler + [LockCluster.client.commands.ProgrammingEventNotification.ID] = programming_event_handler, + [LockCluster.client.commands.OperatingEventNotification.ID] = lock_operation_event_handler } }, attr = { [LockCluster.ID] = { + [LockCluster.attributes.LockState.ID] = lock_state_handler, [LockCluster.attributes.MaxPINCodeLength.ID] = handle_max_code_length, [LockCluster.attributes.MinPINCodeLength.ID] = handle_min_code_length, [LockCluster.attributes.NumberOfPINUsersSupported.ID] = handle_max_codes diff --git a/drivers/SmartThings/zigbee-lock/src/samsungsds/init.lua b/drivers/SmartThings/zigbee-lock/src/samsungsds/init.lua index 4f69bbf6e7..3b50544949 100644 --- a/drivers/SmartThings/zigbee-lock/src/samsungsds/init.lua +++ b/drivers/SmartThings/zigbee-lock/src/samsungsds/init.lua @@ -51,6 +51,10 @@ local function unlock_cmd_handler(driver, device, command) "\x10\x04\x31\x32\x33\x35")) end +local function lock_cmd_handler(driver, device, command) + --do nothing in lock command handler +end + local refresh = function(driver, device, cmd) -- do nothing in refresh capability handler end @@ -95,7 +99,8 @@ local samsung_sds_driver = { [capabilities.refresh.commands.refresh.NAME] = refresh }, [capabilities.lock.ID] = { - [capabilities.lock.commands.unlock.NAME] = unlock_cmd_handler + [capabilities.lock.commands.unlock.NAME] = unlock_cmd_handler, + [capabilities.lock.commands.lock.NAME] = lock_cmd_handler } }, lifecycle_handlers = { diff --git a/drivers/SmartThings/zigbee-lock/src/test/test_zigbee_lock.lua b/drivers/SmartThings/zigbee-lock/src/test/test_zigbee_lock.lua index 18d4b8b453..b3fff00323 100644 --- a/drivers/SmartThings/zigbee-lock/src/test/test_zigbee_lock.lua +++ b/drivers/SmartThings/zigbee-lock/src/test/test_zigbee_lock.lua @@ -721,4 +721,67 @@ test.register_coroutine_test( end ) +test.register_coroutine_test( + "Lock state attribute reports (after the first) should be delayed if they come before event notifications ", + function() + init_code_slot(1, "Code 1", mock_device) + test.socket.capability:__expect_send(mock_device:generate_test_message("main", + capabilities.lockCodes.lockCodes(json.encode({["1"] = "Code 1"}), { visibility = { displayed = false } }))) + test.socket.zigbee:__queue_receive({mock_device.id, DoorLock.attributes.LockState:build_test_attr_report(mock_device, DoorLockState.UNLOCKED)}) + test.socket.capability:__expect_send( + mock_device:generate_test_message("main", + capabilities.lock.lock.unlocked() + ) + ) + test.mock_time.advance_time(2) + test.socket.zigbee:__queue_receive( + { + mock_device.id, + DoorLock.client.commands.OperatingEventNotification.build_test_rx( + mock_device, + 0x00, -- 0 = keypad + OperationEventCode.UNLOCK, + 0x0001, + "1234", + 0x0000, + "" + ) + } + ) + test.socket.capability:__expect_send( + mock_device:generate_test_message("main", + capabilities.lock.lock.unlocked({ data = { method = "keypad", codeId = "1", codeName = "Code 1" } }) + ) + ) + test.mock_time.advance_time(2) + test.timer.__create_and_queue_test_time_advance_timer(2.5, "oneshot") + test.socket.zigbee:__queue_receive({mock_device.id, DoorLock.attributes.LockState:build_test_attr_report(mock_device, DoorLockState.LOCKED)}) + test.socket.zigbee:__queue_receive( + { + mock_device.id, + DoorLock.client.commands.OperatingEventNotification.build_test_rx( + mock_device, + 0x00, -- 0 = keypad + OperationEventCode.LOCK, + 0x0001, + "1234", + 0x0000, + "" + ) + } + ) + test.socket.capability:__expect_send( + mock_device:generate_test_message("main", + capabilities.lock.lock.locked({ data = { method = "keypad", codeId = "1", codeName = "Code 1" } }) + ) + ) + test.mock_time.advance_time(2.5) + test.socket.capability:__expect_send( + mock_device:generate_test_message("main", + capabilities.lock.lock.locked() + ) + ) + end +) + test.run_registered_tests() diff --git a/drivers/SmartThings/zigbee-lock/src/test/test_zigbee_samsungsds.lua b/drivers/SmartThings/zigbee-lock/src/test/test_zigbee_samsungsds.lua index 85163e3b8c..256f73ea51 100644 --- a/drivers/SmartThings/zigbee-lock/src/test/test_zigbee_samsungsds.lua +++ b/drivers/SmartThings/zigbee-lock/src/test/test_zigbee_samsungsds.lua @@ -1349,6 +1349,19 @@ test.register_coroutine_test( end ) +test.register_coroutine_test( + "Handle Lock cmd", + function() + test.socket.capability:__queue_receive( + { + mock_device.id, + { capability = "lock", component = "main", command = "lock", args = {} } + } + ) + test.wait_for_events() + end +) + test.register_coroutine_test( "Device added function handler", function() diff --git a/tools/localizations/cn.csv b/tools/localizations/cn.csv index 741b79e0b9..5c4b930dd6 100644 --- a/tools/localizations/cn.csv +++ b/tools/localizations/cn.csv @@ -35,18 +35,24 @@ Zigbee Thermostat,Zigbee温控 Zigbee Thing,Zigbee设备 Zigbee Vent,Zigbee新风系统 Zigbee Window Treatment,Zigbee窗帘电机 -Aqara Motion Sensor T1, Aqara 人体传感器 T1 -Aqara High Precision Motion Sensor, Aqara高精度人体传感器 -Aqara Door and Window Sensor T1, Aqara 门窗传感器T1 -Aqara Temperature and Humidity Sensor T1, Aqara温湿度传感器 T1 -Aqara Water Leak Sensor, Aqara 水浸传感器 T1 -Aqara Illuminance Sensor T1, Aqara 光照传感器T1 -Aqara Presence Sensor FP1, Aqara 人体存在传感器FP1 -Aqara Smart Dimmer Controller T1 Pro, Aqara 双色温驱动器 T1 Pro -Aqara Single Switch Module T1 (With Neutral), Aqara 单路控制器T1 (零火版) -Aqara Curtain Controller, Agara智能窗帘电机 (Zigbee开合帘版) -Aqara Roller Shade Controller, Aqara智能管状电机 -Aqara Smart Pet Feeder C1, Aqara智能宠物喂食器 C1 -"Aqara Smart Wall Switch H1 (With Neutral, Single Rocker)", Aqara智能墙壁开关H1 (零火线单键版) -"Aqara Smart Wall Switch H1 (With Neutral, Double Rocker) 1", Aqara智能墙壁开关H1 (零火线双键版) 1 -"Aqara Smart Wall Switch H1 (With Neutral, Triple Rocker) 1", Aqara智能墙壁开关H1 (零火线三键版) 1 \ No newline at end of file +Aqara Motion Sensor T1,Aqara 人体传感器 T1 +Aqara High Precision Motion Sensor,Aqara高精度人体传感器 +Aqara Door and Window Sensor T1,Aqara 门窗传感器T1 +Aqara Temperature and Humidity Sensor T1,Aqara温湿度传感器 T1 +Aqara Water Leak Sensor,Aqara 水浸传感器 T1 +Aqara Illuminance Sensor T1,Aqara 光照传感器T1 +Aqara Presence Sensor FP1,Aqara 人体存在传感器FP1 +Aqara Smart Dimmer Controller T1 Pro,Aqara 双色温驱动器 T1 Pro +Aqara Single Switch Module T1 (With Neutral),Aqara 单路控制器T1 (零火版) +Aqara Curtain Controller,Agara智能窗帘电机 (Zigbee开合帘版) +Aqara Roller Shade Controller,Aqara智能管状电机 +Aqara Smart Pet Feeder C1,Aqara智能宠物喂食器 C1 +"Aqara Smart Wall Switch H1 (With Neutral,Single Rocker)",Aqara智能墙壁开关H1 (零火线单键版) +"Aqara Smart Wall Switch H1 (With Neutral,Double Rocker) 1",Aqara智能墙壁开关H1 (零火线双键版) 1 +"Aqara Smart Wall Switch H1 (With Neutral,Triple Rocker) 1",Aqara智能墙壁开关H1 (零火线三键版) 1 +Aqara Smart Gas Detector,Aqara天然气报警器 +Aqara Smart Smoke Detector,Aqara烟雾报警器 +Aqara Smart Plug T1,Aqara 智能插座 T1 (国标) +Aqara Wireless Mini Switch T1,Aqara 无线开关 T1 +"Aqara Wireless Remote Switch E1 (Single Rocker)",Aqara 无线开关 E1 (贴墙式单键版) +"Aqara LED Light Bulb T1 (Tunable White)",Aqara LED灯泡 T1 (可调色温) \ No newline at end of file diff --git a/tools/run_driver_tests.py b/tools/run_driver_tests.py index a6e2b8d677..c797ef5320 100755 --- a/tools/run_driver_tests.py +++ b/tools/run_driver_tests.py @@ -73,7 +73,14 @@ def run_tests(verbosity_level, filter, junit, coverage_files): if m is not None: test_title = line in_progress_test_name = m.group(1) - test_case = junit_xml.TestCase(in_progress_test_name) + test_name_regex = re.compile(in_progress_test_name) + line_number = None + with open(test_file, 'r') as search_file: + for idx, line in enumerate(search_file, 1): + if test_name_regex.search(line) : + line_number = idx + break + test_case = junit_xml.TestCase(in_progress_test_name, line=line_number) test_count += 1 elif re.search("PASSED", line) is not None: test_done = True @@ -84,7 +91,8 @@ def run_tests(verbosity_level, filter, junit, coverage_files): elif re.search("FAILED", line) is not None: test_done = True test_status = line - failure_files[test_file].append(in_progress_test_name) + failure_string = f"{in_progress_test_name} [line {test_case.line}]" + failure_files[test_file].append(failure_string) if "traceback" in test_case.stdout: test_case.add_error_info(line, test_case.stdout) else: