diff --git a/drivers/SmartThings/matter-switch/src/init.lua b/drivers/SmartThings/matter-switch/src/init.lua index 5a834c0385..6655f81e56 100644 --- a/drivers/SmartThings/matter-switch/src/init.lua +++ b/drivers/SmartThings/matter-switch/src/init.lua @@ -49,6 +49,10 @@ function SwitchLifecycleHandlers.do_configure(driver, device) if device.network_type == device_lib.NETWORK_TYPE_MATTER and not switch_utils.detect_bridge(device) then switch_cfg.set_device_control_options(device) device_cfg.match_profile(driver, device) + elseif device.network_type == device_lib.NETWORK_TYPE_CHILD then + -- because get_parent_device() may cause race conditions if used in init, an initial child subscribe is handled in doConfigure. + -- all future calls to subscribe will be handled by the parent device in init + device:subscribe() end end @@ -68,7 +72,7 @@ function SwitchLifecycleHandlers.info_changed(driver, device, event, args) device:get_endpoints(clusters.Switch.ID, {feature_bitmap=clusters.Switch.types.SwitchFeature.MOMENTARY_SWITCH}) ) elseif device.network_type == device_lib.NETWORK_TYPE_CHILD then - switch_utils.update_subscriptions(device:get_parent_device()) -- parent device required to scan through EPs and update subscriptions + device:get_parent_device():subscribe() -- parent device required to send subscription requests end end @@ -87,7 +91,8 @@ function SwitchLifecycleHandlers.device_init(driver, device) if device:get_field(fields.IS_PARENT_CHILD_DEVICE) then device:set_find_child(switch_utils.find_child) end - switch_utils.update_subscriptions(device) + device:extend_device("subscribe", switch_utils.subscribe) + device:subscribe() -- device energy reporting must be handled cumulatively, periodically, or by both simulatanously. -- To ensure a single source of truth, we only handle a device's periodic reporting if cumulative reporting is not supported. @@ -290,7 +295,40 @@ local matter_driver_template = { [capabilities.valve.commands.open.NAME] = capability_handlers.handle_valve_open, }, }, - supported_capabilities = fields.supported_capabilities, + supported_capabilities = { + capabilities.audioMute, + capabilities.audioRecording, + capabilities.audioVolume, + capabilities.battery, + capabilities.batteryLevel, + capabilities.button, + capabilities.cameraPrivacyMode, + capabilities.cameraViewportSettings, + capabilities.colorControl, + capabilities.colorTemperature, + capabilities.energyMeter, + capabilities.fanMode, + capabilities.fanSpeedPercent, + capabilities.hdr, + capabilities.illuminanceMeasurement, + capabilities.imageControl, + capabilities.level, + capabilities.localMediaStorage, + capabilities.mechanicalPanTiltZoom, + capabilities.motionSensor, + capabilities.nightVision, + capabilities.powerMeter, + capabilities.powerConsumptionReport, + capabilities.relativeHumidityMeasurement, + capabilities.sounds, + capabilities.switch, + capabilities.switchLevel, + capabilities.temperatureMeasurement, + capabilities.valve, + capabilities.videoStreamSettings, + capabilities.webrtc, + capabilities.zoneManagement + }, sub_drivers = { switch_utils.lazy_load_if_possible("sub_drivers.aqara_cube"), switch_utils.lazy_load("sub_drivers.camera"), diff --git a/drivers/SmartThings/matter-switch/src/sub_drivers/camera/camera_utils/utils.lua b/drivers/SmartThings/matter-switch/src/sub_drivers/camera/camera_utils/utils.lua index dcdd936950..f08e757553 100644 --- a/drivers/SmartThings/matter-switch/src/sub_drivers/camera/camera_utils/utils.lua +++ b/drivers/SmartThings/matter-switch/src/sub_drivers/camera/camera_utils/utils.lua @@ -193,7 +193,8 @@ function CameraUtils.subscribe(device) clusters.CameraAvStreamManagement.attributes.StatusLightBrightness }, [capabilities.switch.ID] = { - clusters.CameraAvStreamManagement.attributes.StatusLightEnabled + clusters.CameraAvStreamManagement.attributes.StatusLightEnabled, + clusters.OnOff.attributes.OnOff }, [capabilities.videoStreamSettings.ID] = { clusters.CameraAvStreamManagement.attributes.RateDistortionTradeOffPoints, @@ -223,8 +224,26 @@ function CameraUtils.subscribe(device) }, [capabilities.motionSensor.ID] = { clusters.OccupancySensing.attributes.Occupancy - } + }, + [capabilities.switchLevel.ID] = { + clusters.LevelControl.attributes.CurrentLevel, + clusters.LevelControl.attributes.MaxLevel, + clusters.LevelControl.attributes.MinLevel, + }, + [capabilities.colorControl.ID] = { + clusters.ColorControl.attributes.ColorMode, + clusters.ColorControl.attributes.CurrentHue, + clusters.ColorControl.attributes.CurrentSaturation, + clusters.ColorControl.attributes.CurrentX, + clusters.ColorControl.attributes.CurrentY, + }, + [capabilities.colorTemperature.ID] = { + clusters.ColorControl.attributes.ColorTemperatureMireds, + clusters.ColorControl.attributes.ColorTempPhysicalMaxMireds, + clusters.ColorControl.attributes.ColorTempPhysicalMinMireds, + }, } + local camera_subscribed_events = { [capabilities.zoneManagement.ID] = { clusters.ZoneManagement.events.ZoneTriggered, @@ -238,56 +257,26 @@ function CameraUtils.subscribe(device) } } - for capability, attr_list in pairs(camera_subscribed_attributes) do - if device:supports_capability_by_id(capability) then - for _, attr in pairs(attr_list) do - device:add_subscribed_attribute(attr) - end - end - end - for capability, event_list in pairs(camera_subscribed_events) do - if device:supports_capability_by_id(capability) then - for _, event in pairs(event_list) do - device:add_subscribed_event(event) - end - end - end + local im = require "st.matter.interaction_model" + + local subscribe_request = im.InteractionRequest(im.InteractionRequest.RequestType.SUBSCRIBE, {}) + local devices_seen, capabilities_seen, attributes_seen, events_seen = {}, {}, {}, {} - -- match_profile is called from the CameraAvStreamManagement AttributeList handler, - -- so the subscription needs to be added here first if #device:get_endpoints(clusters.CameraAvStreamManagement.ID) > 0 then - device:add_subscribed_attribute(clusters.CameraAvStreamManagement.attributes.AttributeList) + local ib = im.InteractionInfoBlock(nil, clusters.CameraAvStreamManagement.ID, clusters.CameraAvStreamManagement.attributes.AttributeList.ID) + subscribe_request:with_info_block(ib) end - -- Add subscription for attributes specific to child devices - if device:get_field(fields.IS_PARENT_CHILD_DEVICE) then - for _, ep in ipairs(device.endpoints or {}) do - local id = 0 - for _, dt in ipairs(ep.device_types or {}) do - if dt.device_type_id ~= fields.DEVICE_TYPE_ID.GENERIC_SWITCH then - id = math.max(id, dt.device_type_id) - end - end - for _, attr in pairs(fields.device_type_attribute_map[id] or {}) do - device:add_subscribed_attribute(attr) - end + for _, endpoint_info in ipairs(device.endpoints) do + local checked_device = switch_utils.find_child(device, endpoint_info.endpoint_id) or device + if not devices_seen[checked_device.id] then + switch_utils.populate_subscribe_request_for_device(checked_device, subscribe_request, capabilities_seen, attributes_seen, events_seen, + camera_subscribed_attributes, camera_subscribed_events + ) + devices_seen[checked_device.id] = true -- only loop through any device once end end - local im = require "st.matter.interaction_model" - local subscribed_attributes = device:get_field("__subscribed_attributes") or {} - local subscribed_events = device:get_field("__subscribed_events") or {} - local subscribe_request = im.InteractionRequest(im.InteractionRequest.RequestType.SUBSCRIBE, {}) - for _, attributes in pairs(subscribed_attributes) do - for _, ib in pairs(attributes) do - subscribe_request:with_info_block(ib) - end - end - for _, events in pairs(subscribed_events) do - for _, ib in pairs(events) do - subscribe_request:with_info_block(ib) - end - end if #subscribe_request.info_blocks > 0 then device:send(subscribe_request) end diff --git a/drivers/SmartThings/matter-switch/src/sub_drivers/camera/init.lua b/drivers/SmartThings/matter-switch/src/sub_drivers/camera/init.lua index e8d5aad8fa..f13589ff41 100644 --- a/drivers/SmartThings/matter-switch/src/sub_drivers/camera/init.lua +++ b/drivers/SmartThings/matter-switch/src/sub_drivers/camera/init.lua @@ -49,10 +49,10 @@ end function CameraLifecycleHandlers.info_changed(driver, device, event, args) if camera_utils.profile_changed(device.profile.components, args.old_st_store.profile.components) then camera_cfg.initialize_camera_capabilities(device) + device:subscribe() if #switch_utils.get_endpoints_by_device_type(device, fields.DEVICE_TYPE_ID.DOORBELL) > 0 then button_cfg.configure_buttons(device, device:get_endpoints(clusters.Switch.ID, {feature_bitmap=clusters.Switch.types.SwitchFeature.MOMENTARY_SWITCH})) end - device:subscribe() end end diff --git a/drivers/SmartThings/matter-switch/src/switch_handlers/attribute_handlers.lua b/drivers/SmartThings/matter-switch/src/switch_handlers/attribute_handlers.lua index 1a36001d6c..e8dfe3f120 100644 --- a/drivers/SmartThings/matter-switch/src/switch_handlers/attribute_handlers.lua +++ b/drivers/SmartThings/matter-switch/src/switch_handlers/attribute_handlers.lua @@ -139,7 +139,7 @@ function AttributeHandlers.color_temperature_mireds_handler(driver, device, ib, end function AttributeHandlers.current_x_handler(driver, device, ib, response) - if device:get_field(fields.COLOR_MODE) == fields.HUE_SAT_COLOR_MODE then + if device:get_field(fields.COLOR_MODE) == clusters.ColorControl.types.ColorMode.CURRENT_HUE_AND_CURRENT_SATURATION then return end local y = device:get_field(fields.RECEIVED_Y) @@ -157,7 +157,7 @@ function AttributeHandlers.current_x_handler(driver, device, ib, response) end function AttributeHandlers.current_y_handler(driver, device, ib, response) - if device:get_field(fields.COLOR_MODE) == fields.HUE_SAT_COLOR_MODE then + if device:get_field(fields.COLOR_MODE) == clusters.ColorControl.types.ColorMode.CURRENT_HUE_AND_CURRENT_SATURATION then return end local x = device:get_field(fields.RECEIVED_X) @@ -173,15 +173,17 @@ function AttributeHandlers.current_y_handler(driver, device, ib, response) end function AttributeHandlers.color_mode_handler(driver, device, ib, response) - if ib.data.value == device:get_field(fields.COLOR_MODE) or (ib.data.value ~= fields.HUE_SAT_COLOR_MODE and ib.data.value ~= fields.X_Y_COLOR_MODE) then - return + if ib.data.value == device:get_field(fields.COLOR_MODE) + or (ib.data.value ~= clusters.ColorControl.types.ColorMode.CURRENT_HUE_AND_CURRENT_SATURATION + and ib.data.value ~= clusters.ColorControl.types.ColorMode.CURRENTX_AND_CURRENTY) then + return end device:set_field(fields.COLOR_MODE, ib.data.value) local req = im.InteractionRequest(im.InteractionRequest.RequestType.READ, {}) - if ib.data.value == fields.HUE_SAT_COLOR_MODE then + if ib.data.value == clusters.ColorControl.types.ColorMode.CURRENT_HUE_AND_CURRENT_SATURATION then req:merge(clusters.ColorControl.attributes.CurrentHue:read()) req:merge(clusters.ColorControl.attributes.CurrentSaturation:read()) - elseif ib.data.value == fields.X_Y_COLOR_MODE then + elseif ib.data.value == clusters.ColorControl.types.ColorMode.CURRENTX_AND_CURRENTY then req:merge(clusters.ColorControl.attributes.CurrentX:read()) req:merge(clusters.ColorControl.attributes.CurrentY:read()) end diff --git a/drivers/SmartThings/matter-switch/src/switch_utils/fields.lua b/drivers/SmartThings/matter-switch/src/switch_utils/fields.lua index f5b85e1009..f670b154c8 100644 --- a/drivers/SmartThings/matter-switch/src/switch_utils/fields.lua +++ b/drivers/SmartThings/matter-switch/src/switch_utils/fields.lua @@ -1,27 +1,13 @@ -- Copyright © 2025 SmartThings, Inc. -- Licensed under the Apache License, Version 2.0 -local clusters = require "st.matter.clusters" -local capabilities = require "st.capabilities" -local version = require "version" - --- Include driver-side definitions when lua libs api version is < 11 -if version.api < 11 then - clusters.ElectricalEnergyMeasurement = require "embedded_clusters.ElectricalEnergyMeasurement" - clusters.ElectricalPowerMeasurement = require "embedded_clusters.ElectricalPowerMeasurement" -end - local SwitchFields = {} -SwitchFields.HUE_SAT_COLOR_MODE = clusters.ColorControl.types.ColorMode.CURRENT_HUE_AND_CURRENT_SATURATION -SwitchFields.X_Y_COLOR_MODE = clusters.ColorControl.types.ColorMode.CURRENTX_AND_CURRENTY - SwitchFields.MOST_RECENT_TEMP = "mostRecentTemp" SwitchFields.RECEIVED_X = "receivedX" SwitchFields.RECEIVED_Y = "receivedY" SwitchFields.HUESAT_SUPPORT = "huesatSupport" - SwitchFields.MIRED_KELVIN_CONVERSION_CONSTANT = 1000000 -- These values are a "sanity check" to check that values we are getting are reasonable @@ -192,119 +178,4 @@ SwitchFields.TRANSITION_TIME = 0 --1/10ths of a second SwitchFields.OPTIONS_MASK = 0x01 SwitchFields.OPTIONS_OVERRIDE = 0x01 - -SwitchFields.supported_capabilities = { - capabilities.audioMute, - capabilities.audioRecording, - capabilities.audioVolume, - capabilities.battery, - capabilities.batteryLevel, - capabilities.button, - capabilities.cameraPrivacyMode, - capabilities.cameraViewportSettings, - capabilities.colorControl, - capabilities.colorTemperature, - capabilities.energyMeter, - capabilities.fanMode, - capabilities.fanSpeedPercent, - capabilities.hdr, - capabilities.illuminanceMeasurement, - capabilities.imageControl, - capabilities.level, - capabilities.localMediaStorage, - capabilities.mechanicalPanTiltZoom, - capabilities.motionSensor, - capabilities.nightVision, - capabilities.powerMeter, - capabilities.powerConsumptionReport, - capabilities.relativeHumidityMeasurement, - capabilities.sounds, - capabilities.switch, - capabilities.switchLevel, - capabilities.temperatureMeasurement, - capabilities.valve, - capabilities.videoStreamSettings, - capabilities.webrtc, - capabilities.zoneManagement -} - -SwitchFields.device_type_attribute_map = { - [SwitchFields.DEVICE_TYPE_ID.LIGHT.ON_OFF] = { - clusters.OnOff.attributes.OnOff - }, - [SwitchFields.DEVICE_TYPE_ID.LIGHT.DIMMABLE] = { - clusters.OnOff.attributes.OnOff, - clusters.LevelControl.attributes.CurrentLevel, - clusters.LevelControl.attributes.MaxLevel, - clusters.LevelControl.attributes.MinLevel - }, - [SwitchFields.DEVICE_TYPE_ID.LIGHT.COLOR_TEMPERATURE] = { - clusters.OnOff.attributes.OnOff, - clusters.LevelControl.attributes.CurrentLevel, - clusters.LevelControl.attributes.MaxLevel, - clusters.LevelControl.attributes.MinLevel, - clusters.ColorControl.attributes.ColorTemperatureMireds, - clusters.ColorControl.attributes.ColorTempPhysicalMaxMireds, - clusters.ColorControl.attributes.ColorTempPhysicalMinMireds - }, - [SwitchFields.DEVICE_TYPE_ID.LIGHT.EXTENDED_COLOR] = { - clusters.OnOff.attributes.OnOff, - clusters.LevelControl.attributes.CurrentLevel, - clusters.LevelControl.attributes.MaxLevel, - clusters.LevelControl.attributes.MinLevel, - clusters.ColorControl.attributes.ColorTemperatureMireds, - clusters.ColorControl.attributes.ColorTempPhysicalMaxMireds, - clusters.ColorControl.attributes.ColorTempPhysicalMinMireds, - clusters.ColorControl.attributes.CurrentHue, - clusters.ColorControl.attributes.CurrentSaturation, - clusters.ColorControl.attributes.CurrentX, - clusters.ColorControl.attributes.CurrentY, - clusters.ColorControl.attributes.ColorMode - }, - [SwitchFields.DEVICE_TYPE_ID.ON_OFF_PLUG_IN_UNIT] = { - clusters.OnOff.attributes.OnOff - }, - [SwitchFields.DEVICE_TYPE_ID.DIMMABLE_PLUG_IN_UNIT] = { - clusters.OnOff.attributes.OnOff, - clusters.LevelControl.attributes.CurrentLevel, - clusters.LevelControl.attributes.MaxLevel, - clusters.LevelControl.attributes.MinLevel - }, - [SwitchFields.DEVICE_TYPE_ID.SWITCH.ON_OFF_LIGHT] = { - clusters.OnOff.attributes.OnOff - }, - [SwitchFields.DEVICE_TYPE_ID.SWITCH.DIMMER] = { - clusters.OnOff.attributes.OnOff, - clusters.LevelControl.attributes.CurrentLevel, - clusters.LevelControl.attributes.MaxLevel, - clusters.LevelControl.attributes.MinLevel - }, - [SwitchFields.DEVICE_TYPE_ID.SWITCH.COLOR_DIMMER] = { - clusters.OnOff.attributes.OnOff, - clusters.LevelControl.attributes.CurrentLevel, - clusters.LevelControl.attributes.MaxLevel, - clusters.LevelControl.attributes.MinLevel, - clusters.ColorControl.attributes.ColorTemperatureMireds, - clusters.ColorControl.attributes.ColorTempPhysicalMaxMireds, - clusters.ColorControl.attributes.ColorTempPhysicalMinMireds, - clusters.ColorControl.attributes.CurrentHue, - clusters.ColorControl.attributes.CurrentSaturation, - clusters.ColorControl.attributes.CurrentX, - clusters.ColorControl.attributes.CurrentY, - clusters.ColorControl.attributes.ColorMode - }, - [SwitchFields.DEVICE_TYPE_ID.GENERIC_SWITCH] = { - clusters.PowerSource.attributes.BatPercentRemaining, - clusters.Switch.events.InitialPress, - clusters.Switch.events.LongPress, - clusters.Switch.events.ShortRelease, - clusters.Switch.events.MultiPressComplete - }, - [SwitchFields.DEVICE_TYPE_ID.ELECTRICAL_SENSOR] = { - clusters.ElectricalPowerMeasurement.attributes.ActivePower, - clusters.ElectricalEnergyMeasurement.attributes.CumulativeEnergyImported, - clusters.ElectricalEnergyMeasurement.attributes.PeriodicEnergyImported - } -} - -return SwitchFields \ No newline at end of file +return SwitchFields diff --git a/drivers/SmartThings/matter-switch/src/switch_utils/utils.lua b/drivers/SmartThings/matter-switch/src/switch_utils/utils.lua index 88c846501a..c6299fba95 100644 --- a/drivers/SmartThings/matter-switch/src/switch_utils/utils.lua +++ b/drivers/SmartThings/matter-switch/src/switch_utils/utils.lua @@ -433,27 +433,63 @@ function utils.lazy_load_if_possible(sub_driver_name) end end -function utils.update_subscriptions(device) - local default_endpoint_id = utils.find_default_endpoint(device) - -- ensure subscription to all endpoint attributes- including those mapped to child devices - for idx, ep in ipairs(device.endpoints) do - if ep.endpoint_id ~= default_endpoint_id then - local id = 0 - for _, dt in ipairs(ep.device_types) do - id = math.max(id, dt.device_type_id) - end - for _, attr in pairs(fields.device_type_attribute_map[id] or {}) do - if id == fields.DEVICE_TYPE_ID.GENERIC_SWITCH and - attr ~= clusters.PowerSource.attributes.BatPercentRemaining and - attr ~= clusters.PowerSource.attributes.BatChargeLevel then - device:add_subscribed_event(attr) - else - device:add_subscribed_attribute(attr) +--- helper for the switch subscribe override, which adds to a subscribed request for a checked device +--- +--- @param checked_device any a Matter device object, either a parent or child device, so not necessarily the same as device +--- @param subscribe_request table a subscribe request that will be appended to as needed for the device +--- @param capabilities_seen table a list of capabilities that have already been checked by previously handled devices +--- @param attributes_seen table a list of attributes that have already been checked +--- @param events_seen table a list of events that have already been checked +--- @param subscribed_attributes table key-value pairs mapping capability ids to subscribed attributes +--- @param subscribed_events table key-value pairs mapping capability ids to subscribed events +function utils.populate_subscribe_request_for_device(checked_device, subscribe_request, capabilities_seen, attributes_seen, events_seen, subscribed_attributes, subscribed_events) + for _, component in pairs(checked_device.st_store.profile.components) do + for _, capability in pairs(component.capabilities) do + if not capabilities_seen[capability.id] then + for _, attr in ipairs(subscribed_attributes[capability.id] or {}) do + local cluster_id = attr.cluster or attr._cluster.ID + local attr_id = attr.ID or attr.attribute + if not attributes_seen[cluster_id..attr_id] then + local ib = im.InteractionInfoBlock(nil, cluster_id, attr_id) + subscribe_request:with_info_block(ib) + attributes_seen[cluster_id..attr_id] = true + end end + for _, event in ipairs(subscribed_events[capability.id] or {}) do + local cluster_id = event.cluster or event._cluster.ID + local event_id = event.ID or event.event + if not events_seen[cluster_id..event_id] then + local ib = im.InteractionInfoBlock(nil, cluster_id, nil, event_id) + subscribe_request:with_info_block(ib) + events_seen[cluster_id..event_id] = true + end + end + capabilities_seen[capability.id] = true -- only loop through any capability once end end end - device:subscribe() +end + +--- create and send a subscription request by checking all devices, accounting for both parent and child devices +--- +--- @param device any a Matter device object +function utils.subscribe(device) + local subscribe_request = im.InteractionRequest(im.InteractionRequest.RequestType.SUBSCRIBE, {}) + local devices_seen, capabilities_seen, attributes_seen, events_seen = {}, {}, {}, {} + + for _, endpoint_info in ipairs(device.endpoints) do + local checked_device = utils.find_child(device, endpoint_info.endpoint_id) or device + if not devices_seen[checked_device.id] then + utils.populate_subscribe_request_for_device(checked_device, subscribe_request, capabilities_seen, attributes_seen, events_seen, + device.driver.subscribed_attributes, device.driver.subscribed_events + ) + devices_seen[checked_device.id] = true -- only loop through any device once + end + end + + if #subscribe_request.info_blocks > 0 then + device:send(subscribe_request) + end end return utils diff --git a/drivers/SmartThings/matter-switch/src/test/test_eve_energy.lua b/drivers/SmartThings/matter-switch/src/test/test_eve_energy.lua index 1598f953a4..a189b1e5fc 100644 --- a/drivers/SmartThings/matter-switch/src/test/test_eve_energy.lua +++ b/drivers/SmartThings/matter-switch/src/test/test_eve_energy.lua @@ -55,7 +55,7 @@ local mock_device = test.mock_device.build_test_matter_device({ }) local mock_device_electrical_sensor = test.mock_device.build_test_matter_device({ - profile = t_utils.get_profile_definition("power-energy-powerConsumption.yml"), + profile = t_utils.get_profile_definition("plug-energy-powerConsumption.yml"), manufacturer_info = { vendor_id = 0x130A, product_id = 0x0050, diff --git a/drivers/SmartThings/matter-switch/src/test/test_matter_bridge.lua b/drivers/SmartThings/matter-switch/src/test/test_matter_bridge.lua index 1aaef32d37..c696815a3e 100644 --- a/drivers/SmartThings/matter-switch/src/test/test_matter_bridge.lua +++ b/drivers/SmartThings/matter-switch/src/test/test_matter_bridge.lua @@ -59,34 +59,15 @@ local mock_bridge = test.mock_device.build_test_matter_device({ } }) -local cluster_subscribe_list = { - clusters.OnOff.attributes.OnOff -} - local function test_init_mock_bridge() - local subscribe_request = cluster_subscribe_list[1]:subscribe(mock_bridge) - for i, cluster in ipairs(cluster_subscribe_list) do - if i > 1 then - subscribe_request:merge(cluster:subscribe(mock_bridge)) - end - end - test.socket.matter:__expect_send({mock_bridge.id, subscribe_request}) test.mock_device.add_test_device(mock_bridge) end test.register_coroutine_test( "Profile should not change for devices with aggregator device type (bridges)", function() - local subscribe_request = cluster_subscribe_list[1]:subscribe(mock_bridge) - for i, cluster in ipairs(cluster_subscribe_list) do - if i > 1 then - subscribe_request:merge(cluster:subscribe(mock_bridge)) - end - end test.socket.device_lifecycle:__queue_receive({ mock_bridge.id, "added" }) - test.socket.matter:__expect_send({mock_bridge.id, subscribe_request}) test.socket.device_lifecycle:__queue_receive({ mock_bridge.id, "init" }) - test.socket.matter:__expect_send({mock_bridge.id, subscribe_request}) test.socket.device_lifecycle:__queue_receive({ mock_bridge.id, "doConfigure" }) mock_bridge:expect_metadata_update({ provisioning_state = "PROVISIONED" }) end, diff --git a/drivers/SmartThings/matter-switch/src/test/test_matter_camera.lua b/drivers/SmartThings/matter-switch/src/test/test_matter_camera.lua index 95bef23644..a327c39c31 100644 --- a/drivers/SmartThings/matter-switch/src/test/test_matter_camera.lua +++ b/drivers/SmartThings/matter-switch/src/test/test_matter_camera.lua @@ -122,6 +122,19 @@ local mock_device = test.mock_device.build_test_matter_device({ local subscribe_request local subscribed_attributes = { clusters.CameraAvStreamManagement.attributes.AttributeList, + clusters.CameraAvStreamManagement.attributes.StatusLightEnabled, + clusters.OnOff.attributes.OnOff, + clusters.LevelControl.attributes.CurrentLevel, + clusters.LevelControl.attributes.MaxLevel, + clusters.LevelControl.attributes.MinLevel, + clusters.ColorControl.attributes.ColorTemperatureMireds, + clusters.ColorControl.attributes.ColorTempPhysicalMaxMireds, + clusters.ColorControl.attributes.ColorTempPhysicalMinMireds, + clusters.ColorControl.attributes.CurrentHue, + clusters.ColorControl.attributes.CurrentSaturation, + clusters.ColorControl.attributes.CurrentX, + clusters.ColorControl.attributes.CurrentY, + clusters.ColorControl.attributes.ColorMode, } local function test_init() @@ -153,60 +166,125 @@ end test.set_test_init_function(test_init) -local function update_device_profile() - test.socket.matter:__set_channel_ordering("relaxed") - local uint32 = require "st.matter.data_types.Uint32" - local expected_metadata = { - optional_component_capabilities = { - { - "main", - { - "videoCapture2", - "cameraViewportSettings", - "localMediaStorage", - "audioRecording", - "cameraPrivacyMode", - "imageControl", - "hdr", - "nightVision", - "mechanicalPanTiltZoom", - "videoStreamSettings", - "zoneManagement", - "webrtc", - "motionSensor", - "sounds", - } - }, +local additional_subscribed_attributes = { + clusters.CameraAvStreamManagement.attributes.HDRModeEnabled, + clusters.CameraAvStreamManagement.attributes.ImageRotation, + clusters.CameraAvStreamManagement.attributes.NightVision, + clusters.CameraAvStreamManagement.attributes.NightVisionIllum, + clusters.CameraAvStreamManagement.attributes.ImageFlipHorizontal, + clusters.CameraAvStreamManagement.attributes.ImageFlipVertical, + clusters.CameraAvStreamManagement.attributes.SoftRecordingPrivacyModeEnabled, + clusters.CameraAvStreamManagement.attributes.SoftLivestreamPrivacyModeEnabled, + clusters.CameraAvStreamManagement.attributes.HardPrivacyModeOn, + clusters.CameraAvStreamManagement.attributes.TwoWayTalkSupport, + clusters.CameraAvStreamManagement.attributes.SpeakerMuted, + clusters.CameraAvStreamManagement.attributes.MicrophoneMuted, + clusters.CameraAvStreamManagement.attributes.SpeakerVolumeLevel, + clusters.CameraAvStreamManagement.attributes.SpeakerMaxLevel, + clusters.CameraAvStreamManagement.attributes.SpeakerMinLevel, + clusters.CameraAvStreamManagement.attributes.MicrophoneVolumeLevel, + clusters.CameraAvStreamManagement.attributes.MicrophoneMaxLevel, + clusters.CameraAvStreamManagement.attributes.MicrophoneMinLevel, + clusters.CameraAvStreamManagement.attributes.StatusLightBrightness, + clusters.CameraAvStreamManagement.attributes.StatusLightEnabled, + clusters.CameraAvStreamManagement.attributes.RateDistortionTradeOffPoints, + clusters.CameraAvStreamManagement.attributes.LocalSnapshotRecordingEnabled, + clusters.CameraAvStreamManagement.attributes.LocalVideoRecordingEnabled, + clusters.CameraAvStreamManagement.attributes.MaxEncodedPixelRate, + clusters.CameraAvStreamManagement.attributes.VideoSensorParams, + clusters.CameraAvStreamManagement.attributes.AllocatedVideoStreams, + clusters.CameraAvStreamManagement.attributes.Viewport, + clusters.CameraAvStreamManagement.attributes.MinViewportResolution, + clusters.CameraAvStreamManagement.attributes.AttributeList, + clusters.CameraAvSettingsUserLevelManagement.attributes.MPTZPosition, + clusters.CameraAvSettingsUserLevelManagement.attributes.MPTZPresets, + clusters.CameraAvSettingsUserLevelManagement.attributes.MaxPresets, + clusters.CameraAvSettingsUserLevelManagement.attributes.ZoomMax, + clusters.CameraAvSettingsUserLevelManagement.attributes.PanMax, + clusters.CameraAvSettingsUserLevelManagement.attributes.PanMin, + clusters.CameraAvSettingsUserLevelManagement.attributes.TiltMax, + clusters.CameraAvSettingsUserLevelManagement.attributes.TiltMin, + clusters.Chime.attributes.InstalledChimeSounds, + clusters.Chime.attributes.SelectedChime, + clusters.ZoneManagement.attributes.MaxZones, + clusters.ZoneManagement.attributes.Zones, + clusters.ZoneManagement.attributes.Triggers, + clusters.ZoneManagement.attributes.SensitivityMax, + clusters.ZoneManagement.attributes.Sensitivity, + clusters.ZoneManagement.events.ZoneTriggered, + clusters.ZoneManagement.events.ZoneStopped, + clusters.OnOff.attributes.OnOff, + clusters.LevelControl.attributes.CurrentLevel, + clusters.LevelControl.attributes.MaxLevel, + clusters.LevelControl.attributes.MinLevel, + clusters.ColorControl.attributes.ColorTemperatureMireds, + clusters.ColorControl.attributes.ColorTempPhysicalMaxMireds, + clusters.ColorControl.attributes.ColorTempPhysicalMinMireds, + clusters.ColorControl.attributes.CurrentHue, + clusters.ColorControl.attributes.CurrentSaturation, + clusters.ColorControl.attributes.CurrentX, + clusters.ColorControl.attributes.CurrentY, + clusters.OccupancySensing.attributes.Occupancy, + clusters.Switch.server.events.InitialPress, + clusters.Switch.server.events.LongPress, + clusters.Switch.server.events.ShortRelease, + clusters.Switch.server.events.MultiPressComplete +} + +local expected_metadata = { + optional_component_capabilities = { + { + "main", { - "statusLed", - { - "switch", - "mode" - } - }, + "videoCapture2", + "cameraViewportSettings", + "localMediaStorage", + "audioRecording", + "cameraPrivacyMode", + "imageControl", + "hdr", + "nightVision", + "mechanicalPanTiltZoom", + "videoStreamSettings", + "zoneManagement", + "webrtc", + "motionSensor", + "sounds", + } + }, + { + "statusLed", { - "speaker", - { - "audioMute", - "audioVolume" - } - }, + "switch", + "mode" + } + }, + { + "speaker", { - "microphone", - { - "audioMute", - "audioVolume" - } - }, + "audioMute", + "audioVolume" + } + }, + { + "microphone", { - "doorbell", - { - "button" - } + "audioMute", + "audioVolume" } }, - profile = "camera" - } + { + "doorbell", + { + "button" + } + } + }, + profile = "camera" +} + +local function update_device_profile() + local uint32 = require "st.matter.data_types.Uint32" test.socket.matter:__queue_receive({ mock_device.id, clusters.CameraAvStreamManagement.attributes.AttributeList:build_test_report_data(mock_device, CAMERA_EP, { @@ -219,6 +297,7 @@ local function update_device_profile() local updated_device_profile = t_utils.get_profile_definition( "camera.yml", {enabled_optional_capabilities = expected_metadata.optional_component_capabilities} ) + test.wait_for_events() test.socket.device_lifecycle:__queue_receive(mock_device:generate_info_changed({ profile = updated_device_profile })) test.socket.capability:__expect_send( mock_device:generate_test_message("main", capabilities.webrtc.supportedFeatures( @@ -258,77 +337,12 @@ local function update_device_profile() {"setSoftRecordingPrivacyMode", "setSoftLivestreamPrivacyMode"} )) ) - local additional_subscribed_attributes = { - clusters.CameraAvStreamManagement.attributes.HDRModeEnabled, - clusters.CameraAvStreamManagement.attributes.ImageRotation, - clusters.CameraAvStreamManagement.attributes.NightVision, - clusters.CameraAvStreamManagement.attributes.NightVisionIllum, - clusters.CameraAvStreamManagement.attributes.ImageFlipHorizontal, - clusters.CameraAvStreamManagement.attributes.ImageFlipVertical, - clusters.CameraAvStreamManagement.attributes.SoftRecordingPrivacyModeEnabled, - clusters.CameraAvStreamManagement.attributes.SoftLivestreamPrivacyModeEnabled, - clusters.CameraAvStreamManagement.attributes.HardPrivacyModeOn, - clusters.CameraAvStreamManagement.attributes.TwoWayTalkSupport, - clusters.CameraAvStreamManagement.attributes.SpeakerMuted, - clusters.CameraAvStreamManagement.attributes.MicrophoneMuted, - clusters.CameraAvStreamManagement.attributes.SpeakerVolumeLevel, - clusters.CameraAvStreamManagement.attributes.SpeakerMaxLevel, - clusters.CameraAvStreamManagement.attributes.SpeakerMinLevel, - clusters.CameraAvStreamManagement.attributes.MicrophoneVolumeLevel, - clusters.CameraAvStreamManagement.attributes.MicrophoneMaxLevel, - clusters.CameraAvStreamManagement.attributes.MicrophoneMinLevel, - clusters.CameraAvStreamManagement.attributes.StatusLightBrightness, - clusters.CameraAvStreamManagement.attributes.StatusLightEnabled, - clusters.CameraAvStreamManagement.attributes.RateDistortionTradeOffPoints, - clusters.CameraAvStreamManagement.attributes.LocalSnapshotRecordingEnabled, - clusters.CameraAvStreamManagement.attributes.LocalVideoRecordingEnabled, - clusters.CameraAvStreamManagement.attributes.MaxEncodedPixelRate, - clusters.CameraAvStreamManagement.attributes.VideoSensorParams, - clusters.CameraAvStreamManagement.attributes.AllocatedVideoStreams, - clusters.CameraAvStreamManagement.attributes.Viewport, - clusters.CameraAvStreamManagement.attributes.MinViewportResolution, - clusters.CameraAvStreamManagement.attributes.AttributeList, - clusters.CameraAvSettingsUserLevelManagement.attributes.MPTZPosition, - clusters.CameraAvSettingsUserLevelManagement.attributes.MPTZPresets, - clusters.CameraAvSettingsUserLevelManagement.attributes.MaxPresets, - clusters.CameraAvSettingsUserLevelManagement.attributes.ZoomMax, - clusters.CameraAvSettingsUserLevelManagement.attributes.PanMax, - clusters.CameraAvSettingsUserLevelManagement.attributes.PanMin, - clusters.CameraAvSettingsUserLevelManagement.attributes.TiltMax, - clusters.CameraAvSettingsUserLevelManagement.attributes.TiltMin, - clusters.Chime.attributes.InstalledChimeSounds, - clusters.Chime.attributes.SelectedChime, - clusters.ZoneManagement.attributes.MaxZones, - clusters.ZoneManagement.attributes.Zones, - clusters.ZoneManagement.attributes.Triggers, - clusters.ZoneManagement.attributes.SensitivityMax, - clusters.ZoneManagement.attributes.Sensitivity, - clusters.ZoneManagement.events.ZoneTriggered, - clusters.ZoneManagement.events.ZoneStopped, - clusters.OnOff.attributes.OnOff, - clusters.LevelControl.attributes.CurrentLevel, - clusters.LevelControl.attributes.MaxLevel, - clusters.LevelControl.attributes.MinLevel, - clusters.ColorControl.attributes.ColorTemperatureMireds, - clusters.ColorControl.attributes.ColorTempPhysicalMaxMireds, - clusters.ColorControl.attributes.ColorTempPhysicalMinMireds, - clusters.ColorControl.attributes.CurrentHue, - clusters.ColorControl.attributes.CurrentSaturation, - clusters.ColorControl.attributes.CurrentX, - clusters.ColorControl.attributes.CurrentY, - clusters.ColorControl.attributes.ColorMode, - clusters.OccupancySensing.attributes.Occupancy, - clusters.Switch.server.events.InitialPress, - clusters.Switch.server.events.LongPress, - clusters.Switch.server.events.ShortRelease, - clusters.Switch.server.events.MultiPressComplete - } for _, attr in ipairs(additional_subscribed_attributes) do subscribe_request:merge(attr:subscribe(mock_device)) end + test.socket.matter:__expect_send({mock_device.id, subscribe_request}) test.socket.matter:__expect_send({mock_device.id, clusters.Switch.attributes.MultiPressMax:read(mock_device, DOORBELL_EP)}) test.socket.capability:__expect_send(mock_device:generate_test_message("doorbell", capabilities.button.button.pushed({state_change = false}))) - test.socket.matter:__expect_send({mock_device.id, subscribe_request}) end -- Matter Handler UTs diff --git a/drivers/SmartThings/matter-switch/src/test/test_matter_multi_button_switch_mcd.lua b/drivers/SmartThings/matter-switch/src/test/test_matter_multi_button_switch_mcd.lua index 49fd69d0ec..d412c2b35e 100644 --- a/drivers/SmartThings/matter-switch/src/test/test_matter_multi_button_switch_mcd.lua +++ b/drivers/SmartThings/matter-switch/src/test/test_matter_multi_button_switch_mcd.lua @@ -102,7 +102,7 @@ local mock_device = test.mock_device.build_test_matter_device({ local mock_device_mcd_unsupported_switch_device_type = test.mock_device.build_test_matter_device({ label = "Matter Switch", - profile = t_utils.get_profile_definition("matter-thing.yml"), + profile = t_utils.get_profile_definition("button.yml"), manufacturer_info = { vendor_id = 0x0000, product_id = 0x0000, @@ -166,7 +166,18 @@ local child_data = { local mock_child = test.mock_device.build_test_child_device(child_data) -- add device for each mock device -local CLUSTER_SUBSCRIBE_LIST ={ +local CLUSTER_SUBSCRIBE_LIST_NO_CHILD ={ + clusters.OnOff.attributes.OnOff, + clusters.LevelControl.attributes.CurrentLevel, + clusters.LevelControl.attributes.MaxLevel, + clusters.LevelControl.attributes.MinLevel, + clusters.Switch.server.events.InitialPress, + clusters.Switch.server.events.LongPress, + clusters.Switch.server.events.ShortRelease, + clusters.Switch.server.events.MultiPressComplete, +} + +local CLUSTER_SUBSCRIBE_LIST_WITH_CHILD ={ clusters.OnOff.attributes.OnOff, clusters.LevelControl.attributes.CurrentLevel, clusters.LevelControl.attributes.MaxLevel, @@ -199,10 +210,10 @@ end local function test_init() test.disable_startup_messages() test.mock_device.add_test_device(mock_device) -- make sure the cache is populated - test.mock_device.add_test_device(mock_child) - local subscribe_request = CLUSTER_SUBSCRIBE_LIST[1]:subscribe(mock_device) - for i, clus in ipairs(CLUSTER_SUBSCRIBE_LIST) do + -- added sets a bunch of fields on the device, and calls init + local subscribe_request = CLUSTER_SUBSCRIBE_LIST_NO_CHILD[1]:subscribe(mock_device) + for i, clus in ipairs(CLUSTER_SUBSCRIBE_LIST_NO_CHILD) do if i > 1 then subscribe_request:merge(clus:subscribe(mock_device)) end end test.socket.matter:__expect_send({mock_device.id, subscribe_request}) @@ -225,12 +236,6 @@ local function test_init() }) expect_configure_buttons() test.socket.device_lifecycle:__queue_receive({ mock_device.id, "doConfigure" }) - - test.socket.matter:__expect_send({mock_device.id, clusters.OnOff.attributes.OnOff:read(mock_device)}) - test.socket.device_lifecycle:__queue_receive({ mock_child.id, "added" }) - test.socket.device_lifecycle:__queue_receive({ mock_child.id, "init" }) - mock_child:expect_metadata_update({ provisioning_state = "PROVISIONED" }) - test.socket.device_lifecycle:__queue_receive({ mock_child.id, "doConfigure" }) end -- All messages queued and expectations set are done before the driver is actually run @@ -350,63 +355,45 @@ test.register_coroutine_test( end ) -test.register_message_test( +test.register_coroutine_test( "Switch child device: Set color temperature should send the appropriate commands", - { - { - channel = "capability", - direction = "receive", - message = { - mock_child.id, - { capability = "colorTemperature", component = "main", command = "setColorTemperature", args = {1800} } - } - }, - { - channel = "matter", - direction = "send", - message = { - mock_device.id, - clusters.ColorControl.server.commands.MoveToColorTemperature(mock_device, mock_device_ep5, 556, TRANSITION_TIME, OPTIONS_MASK, OPTIONS_OVERRIDE) - } - }, - { - channel = "matter", - direction = "receive", - message = { - mock_device.id, - clusters.ColorControl.server.commands.MoveToColorTemperature:build_test_command_response(mock_device, mock_device_ep5) - } - }, - { - channel = "matter", - direction = "receive", - message = { - mock_device.id, - clusters.ColorControl.attributes.ColorTemperatureMireds:build_test_report_data(mock_device, mock_device_ep5, 556) - } - }, - { - channel = "capability", - direction = "send", - message = mock_child:generate_test_message("main", capabilities.colorTemperature.colorTemperature(1800)) - }, - } + function() + test.mock_device.add_test_device(mock_child) + test.wait_for_events() + test.socket.capability:__queue_receive({ + mock_child.id, + { capability = "colorTemperature", component = "main", command = "setColorTemperature", args = {1800} } + }) + test.socket.matter:__expect_send({ + mock_device.id, + clusters.ColorControl.server.commands.MoveToColorTemperature(mock_device, mock_device_ep5, 556, TRANSITION_TIME, OPTIONS_MASK, OPTIONS_OVERRIDE) + }) + test.socket.matter:__queue_receive({ + mock_device.id, + clusters.ColorControl.server.commands.MoveToColorTemperature:build_test_command_response(mock_device, mock_device_ep5) + }) + test.wait_for_events() + test.socket.matter:__queue_receive({ + mock_device.id, + clusters.ColorControl.attributes.ColorTemperatureMireds:build_test_report_data(mock_device, mock_device_ep5, 556) + }) + test.socket.capability:__expect_send(mock_child:generate_test_message("main", capabilities.colorTemperature.colorTemperature(1800))) + end ) test.register_coroutine_test( "Test MCD configuration not including switch for unsupported switch device type, create child device instead", function() + local unsup_mock_device = mock_device_mcd_unsupported_switch_device_type -- added sets a bunch of fields on the device, and calls init - local cluster_subscribe_list = { - clusters.OnOff.attributes.OnOff, + local CLUSTER_SUBSCRIBE_LIST = { clusters.Switch.server.events.InitialPress, clusters.Switch.server.events.LongPress, clusters.Switch.server.events.ShortRelease, clusters.Switch.server.events.MultiPressComplete, } - local unsup_mock_device = mock_device_mcd_unsupported_switch_device_type - local subscribe_request = cluster_subscribe_list[1]:subscribe(unsup_mock_device) - for _, cluster in ipairs(cluster_subscribe_list) do + local subscribe_request = CLUSTER_SUBSCRIBE_LIST[1]:subscribe(unsup_mock_device) + for _, cluster in ipairs(CLUSTER_SUBSCRIBE_LIST) do subscribe_request:merge(cluster:subscribe(unsup_mock_device)) end test.socket.device_lifecycle:__queue_receive({ unsup_mock_device.id, "added" }) @@ -423,14 +410,24 @@ test.register_coroutine_test( parent_device_id = unsup_mock_device.id, parent_assigned_child_key = string.format("%d", 7) }) + test.socket.capability:__expect_send(unsup_mock_device:generate_test_message("main", capabilities.button.supportedButtonValues({"pushed", "held"}, {visibility = {displayed = false}}))) + test.socket.capability:__expect_send(unsup_mock_device:generate_test_message("main", button_attr.pushed({state_change = false}))) unsup_mock_device:expect_metadata_update({ profile = "2-button" }) unsup_mock_device:expect_metadata_update({ provisioning_state = "PROVISIONED" }) + test.wait_for_events() local updated_device_profile = t_utils.get_profile_definition("2-button.yml") test.socket.device_lifecycle:__queue_receive(unsup_mock_device:generate_info_changed({ profile = updated_device_profile })) - local subscribe_request = cluster_subscribe_list[1]:subscribe(unsup_mock_device) - for i, cluster in ipairs(cluster_subscribe_list) do + + local CLUSTER_SUBSCRIBE_LIST = { + clusters.Switch.server.events.InitialPress, + clusters.Switch.server.events.LongPress, + clusters.Switch.server.events.ShortRelease, + clusters.Switch.server.events.MultiPressComplete, + } + local subscribe_request = CLUSTER_SUBSCRIBE_LIST[1]:subscribe(unsup_mock_device) + for i, cluster in ipairs(CLUSTER_SUBSCRIBE_LIST) do if i > 1 then subscribe_request:merge(cluster:subscribe(unsup_mock_device)) end @@ -449,9 +446,10 @@ test.register_coroutine_test( test.register_coroutine_test( "Test driver switched event", function() + test.mock_device.add_test_device(mock_child) test.socket.device_lifecycle:__queue_receive({ mock_device.id, "init" }) - local subscribe_request = CLUSTER_SUBSCRIBE_LIST[1]:subscribe(mock_device) - for i, clus in ipairs(CLUSTER_SUBSCRIBE_LIST) do + local subscribe_request = CLUSTER_SUBSCRIBE_LIST_WITH_CHILD[1]:subscribe(mock_device) + for i, clus in ipairs(CLUSTER_SUBSCRIBE_LIST_WITH_CHILD) do if i > 1 then subscribe_request:merge(clus:subscribe(mock_device)) end end test.socket.matter:__expect_send({mock_device.id, subscribe_request}) @@ -465,8 +463,8 @@ test.register_coroutine_test( test.register_coroutine_test( "Test info changed event with parent device profile update", function() - local subscribe_request = CLUSTER_SUBSCRIBE_LIST[1]:subscribe(mock_device) - for i, clus in ipairs(CLUSTER_SUBSCRIBE_LIST) do + local subscribe_request = CLUSTER_SUBSCRIBE_LIST_NO_CHILD[1]:subscribe(mock_device) + for i, clus in ipairs(CLUSTER_SUBSCRIBE_LIST_NO_CHILD) do if i > 1 then subscribe_request:merge(clus:subscribe(mock_device)) end end local updated_device_profile = t_utils.get_profile_definition("light-level-3-button.yml") @@ -480,6 +478,7 @@ test.register_coroutine_test( test.register_coroutine_test( "Test info changed event with matter_version update", function() + test.mock_device.add_test_device(mock_child) test.socket.device_lifecycle:__queue_receive(mock_device:generate_info_changed({ matter_version = { hardware = 1, software = 2 } })) -- bump sw to 2 mock_child:expect_metadata_update({ profile = "light-color-level" }) mock_device:expect_metadata_update({ profile = "light-level-3-button" }) @@ -487,6 +486,22 @@ test.register_coroutine_test( end ) +test.register_coroutine_test( + "Test child device initialization, and that subscriptions are initialized correctly", + function () + test.mock_device.add_test_device(mock_child) + test.socket.matter:__expect_send({mock_device.id, clusters.OnOff.attributes.OnOff:read(mock_device)}) + test.socket.device_lifecycle:__queue_receive({ mock_child.id, "added" }) + test.socket.device_lifecycle:__queue_receive({ mock_child.id, "init" }) + local subscribe_request = CLUSTER_SUBSCRIBE_LIST_WITH_CHILD[1]:subscribe(mock_device) + for i, clus in ipairs(CLUSTER_SUBSCRIBE_LIST_WITH_CHILD) do + if i > 1 then subscribe_request:merge(clus:subscribe(mock_device)) end + end + test.socket.matter:__expect_send({mock_device.id, subscribe_request}) + mock_child:expect_metadata_update({ provisioning_state = "PROVISIONED" }) + test.socket.device_lifecycle:__queue_receive({ mock_child.id, "doConfigure" }) + end +) -- run the tests test.run_registered_tests() diff --git a/drivers/SmartThings/matter-switch/src/test/test_matter_switch.lua b/drivers/SmartThings/matter-switch/src/test/test_matter_switch.lua index 25bc0fdfaa..6a5ecd2cc4 100644 --- a/drivers/SmartThings/matter-switch/src/test/test_matter_switch.lua +++ b/drivers/SmartThings/matter-switch/src/test/test_matter_switch.lua @@ -126,6 +126,7 @@ local cluster_subscribe_list = { clusters.ColorControl.attributes.ColorTemperatureMireds, clusters.ColorControl.attributes.ColorTempPhysicalMaxMireds, clusters.ColorControl.attributes.ColorTempPhysicalMinMireds, + clusters.ColorControl.attributes.ColorMode, } local function set_color_mode(device, endpoint, color_mode) diff --git a/drivers/SmartThings/matter-switch/src/test/test_matter_switch_device_types.lua b/drivers/SmartThings/matter-switch/src/test/test_matter_switch_device_types.lua index f90855a308..5513c915b7 100644 --- a/drivers/SmartThings/matter-switch/src/test/test_matter_switch_device_types.lua +++ b/drivers/SmartThings/matter-switch/src/test/test_matter_switch_device_types.lua @@ -439,14 +439,8 @@ local mock_device_light_level_motion = test.mock_device.build_test_matter_device local function test_init_parent_child_switch_types() test.mock_device.add_test_device(mock_device_parent_child_switch_types) - local subscribe_request = clusters.OnOff.attributes.OnOff:subscribe(mock_device_parent_child_switch_types) - test.socket.device_lifecycle:__queue_receive({ mock_device_parent_child_switch_types.id, "added" }) - test.socket.matter:__expect_send({mock_device_parent_child_switch_types.id, subscribe_request}) - test.socket.device_lifecycle:__queue_receive({ mock_device_parent_child_switch_types.id, "init" }) - test.socket.matter:__expect_send({mock_device_parent_child_switch_types.id, subscribe_request}) - test.socket.device_lifecycle:__queue_receive({ mock_device_parent_child_switch_types.id, "doConfigure" }) test.socket.matter:__expect_send({ mock_device_parent_child_switch_types.id, @@ -483,13 +477,10 @@ end local function test_init_parent_client_child_server() test.mock_device.add_test_device(mock_device_parent_client_child_server) - local subscribe_request = clusters.OnOff.attributes.OnOff:subscribe(mock_device_parent_client_child_server) test.socket.device_lifecycle:__queue_receive({ mock_device_parent_client_child_server.id, "added" }) - test.socket.matter:__expect_send({mock_device_parent_client_child_server.id, subscribe_request}) test.socket.device_lifecycle:__queue_receive({ mock_device_parent_client_child_server.id, "init" }) - test.socket.matter:__expect_send({mock_device_parent_client_child_server.id, subscribe_request}) test.socket.device_lifecycle:__queue_receive({ mock_device_parent_client_child_server.id, "doConfigure" }) mock_device_parent_client_child_server:expect_metadata_update({ profile = "switch-binary" }) @@ -597,25 +588,9 @@ end local function test_init_parent_child_different_types() test.mock_device.add_test_device(mock_device_parent_child_different_types) local cluster_subscribe_list = { - clusters.OnOff.attributes.OnOff, - clusters.LevelControl.attributes.CurrentLevel, - clusters.LevelControl.attributes.MaxLevel, - clusters.LevelControl.attributes.MinLevel, - clusters.ColorControl.attributes.ColorTemperatureMireds, - clusters.ColorControl.attributes.ColorTempPhysicalMaxMireds, - clusters.ColorControl.attributes.ColorTempPhysicalMinMireds, - clusters.ColorControl.attributes.CurrentHue, - clusters.ColorControl.attributes.CurrentSaturation, - clusters.ColorControl.attributes.CurrentX, - clusters.ColorControl.attributes.CurrentY, - clusters.ColorControl.attributes.ColorMode, + clusters.OnOff.attributes.OnOff } local subscribe_request = cluster_subscribe_list[1]:subscribe(mock_device_parent_child_different_types) - for i, cluster in ipairs(cluster_subscribe_list) do - if i > 1 then - subscribe_request:merge(cluster:subscribe(mock_device_parent_child_different_types)) - end - end test.socket.device_lifecycle:__queue_receive({ mock_device_parent_child_different_types.id, "added" }) test.socket.matter:__expect_send({mock_device_parent_child_different_types.id, subscribe_request}) diff --git a/drivers/SmartThings/matter-switch/src/test/test_multi_switch_parent_child_lights.lua b/drivers/SmartThings/matter-switch/src/test/test_multi_switch_parent_child_lights.lua index e218371df4..c0ed244d51 100644 --- a/drivers/SmartThings/matter-switch/src/test/test_multi_switch_parent_child_lights.lua +++ b/drivers/SmartThings/matter-switch/src/test/test_multi_switch_parent_child_lights.lua @@ -245,7 +245,7 @@ local function test_init_parent_child_endpoints_non_sequential() clusters.ColorControl.attributes.CurrentSaturation, clusters.ColorControl.attributes.CurrentX, clusters.ColorControl.attributes.CurrentY, - clusters.ColorControl.attributes.ColorMode + clusters.ColorControl.attributes.ColorMode, } local subscribe_request = cluster_subscribe_list[1]:subscribe(unsup_mock_device) for i, cluster in ipairs(cluster_subscribe_list) do diff --git a/drivers/SmartThings/matter-switch/src/test/test_multi_switch_parent_child_plugs.lua b/drivers/SmartThings/matter-switch/src/test/test_multi_switch_parent_child_plugs.lua index 9ac3642a63..09fa39c4f6 100644 --- a/drivers/SmartThings/matter-switch/src/test/test_multi_switch_parent_child_plugs.lua +++ b/drivers/SmartThings/matter-switch/src/test/test_multi_switch_parent_child_plugs.lua @@ -239,7 +239,7 @@ local function test_init_parent_child_endpoints_non_sequential() clusters.ColorControl.attributes.CurrentSaturation, clusters.ColorControl.attributes.CurrentX, clusters.ColorControl.attributes.CurrentY, - clusters.ColorControl.attributes.ColorMode, + clusters.ColorControl.attributes.ColorMode, } local subscribe_request = cluster_subscribe_list[1]:subscribe(mock_device_parent_child_endpoints_non_sequential) for i, cluster in ipairs(cluster_subscribe_list) do