diff --git a/drivers/SmartThings/matter-sensor/profiles/smoke-co-comeas-colevel-battery.yml b/drivers/SmartThings/matter-sensor/profiles/smoke-co-comeas-colevel-battery.yml new file mode 100644 index 0000000000..f4d42a008a --- /dev/null +++ b/drivers/SmartThings/matter-sensor/profiles/smoke-co-comeas-colevel-battery.yml @@ -0,0 +1,22 @@ +name: smoke-co-comeas-colevel-battery +components: +- id: main + capabilities: + - id: smokeDetector + version: 1 + - id: carbonMonoxideDetector + version: 1 + - id: carbonMonoxideMeasurement + version: 1 + - id: carbonMonoxideHealthConcern + version: 1 + - id: battery + version: 1 + - id: hardwareFault + version: 1 + - id: firmwareUpdate + version: 1 + - id: refresh + version: 1 + categories: + - name: SmokeDetector \ No newline at end of file diff --git a/drivers/SmartThings/matter-sensor/src/smoke-co-alarm/init.lua b/drivers/SmartThings/matter-sensor/src/smoke-co-alarm/init.lua index c5fe00a4be..7c40fd282c 100644 --- a/drivers/SmartThings/matter-sensor/src/smoke-co-alarm/init.lua +++ b/drivers/SmartThings/matter-sensor/src/smoke-co-alarm/init.lua @@ -66,6 +66,7 @@ local supported_profiles = "smoke-battery", "smoke-co-comeas", "smoke-co-comeas-battery", + "smoke-co-comeas-colevel-battery", "smoke-co-temp-humidity-comeas", "smoke-co-temp-humidity-comeas-battery" } @@ -247,6 +248,18 @@ local function handle_battery_percent_remaining(driver, device, ib, response) end end +local level_strings = { + [clusters.CarbonMonoxideConcentrationMeasurement.types.LevelValueEnum.UNKNOWN] = "unknown", + [clusters.CarbonMonoxideConcentrationMeasurement.types.LevelValueEnum.LOW] = "good", + [clusters.CarbonMonoxideConcentrationMeasurement.types.LevelValueEnum.MEDIUM] = "moderate", + [clusters.CarbonMonoxideConcentrationMeasurement.types.LevelValueEnum.HIGH] = "unhealthy", + [clusters.CarbonMonoxideConcentrationMeasurement.types.LevelValueEnum.CRITICAL] = "hazardous", +} + +local function carbon_monoxide_level_handler(driver, device, ib, response) + device:emit_event_for_endpoint(ib.endpoint_id, capabilities.carbonMonoxideHealthConcern.carbonMonoxideHealthConcern(level_strings[ib.data.value])) +end + local function do_configure(driver, device) local battery_feature_eps = device:get_endpoints(clusters.PowerSource.ID, {feature_bitmap = clusters.PowerSource.types.PowerSourceFeature.BATTERY}) if #battery_feature_eps > 0 then @@ -275,6 +288,7 @@ local matter_smoke_co_alarm_handler = { [clusters.CarbonMonoxideConcentrationMeasurement.ID] = { [clusters.CarbonMonoxideConcentrationMeasurement.attributes.MeasuredValue.ID] = carbon_monoxide_attr_handler, [clusters.CarbonMonoxideConcentrationMeasurement.attributes.MeasurementUnit.ID] = carbon_monoxide_unit_attr_handler, + [clusters.CarbonMonoxideConcentrationMeasurement.attributes.LevelValue.ID] = carbon_monoxide_level_handler, }, [clusters.PowerSource.ID] = { [clusters.PowerSource.attributes.AttributeList.ID] = power_source_attribute_list_handler, @@ -307,6 +321,9 @@ local matter_smoke_co_alarm_handler = { clusters.CarbonMonoxideConcentrationMeasurement.attributes.MeasuredValue, clusters.CarbonMonoxideConcentrationMeasurement.attributes.MeasurementUnit, }, + [capabilities.carbonMonoxideHealthConcern.ID] = { + clusters.CarbonMonoxideConcentrationMeasurement.attributes.LevelValue, + }, [capabilities.batteryLevel.ID] = { clusters.PowerSource.attributes.BatChargeLevel, }, diff --git a/drivers/SmartThings/matter-sensor/src/test/test_matter_smoke_co_alarm.lua b/drivers/SmartThings/matter-sensor/src/test/test_matter_smoke_co_alarm.lua index f5029147d7..03ce8068e9 100644 --- a/drivers/SmartThings/matter-sensor/src/test/test_matter_smoke_co_alarm.lua +++ b/drivers/SmartThings/matter-sensor/src/test/test_matter_smoke_co_alarm.lua @@ -57,6 +57,36 @@ local mock_device = test.mock_device.build_test_matter_device({ } }) +local mock_device_colevel = test.mock_device.build_test_matter_device({ + profile = t_utils.get_profile_definition("co-comeas-colevel-battery.yml"), + manufacturer_info = { + vendor_id = 0x0000, + product_id = 0x0000, + }, + endpoints = { + { + endpoint_id = 0, + clusters = { + {cluster_id = clusters.Basic.ID, cluster_type = "SERVER"}, + }, + device_types = { + {device_type_id = 0x0016, device_type_revision = 1} -- RootNode + } + }, + { + endpoint_id = 1, + clusters = { + {cluster_id = clusters.SmokeCoAlarm.ID, cluster_type = "SERVER", feature_map = clusters.SmokeCoAlarm.types.Feature.CO_ALARM}, + {cluster_id = clusters.CarbonMonoxideConcentrationMeasurement.ID, cluster_type = "SERVER", feature_map = clusters.CarbonMonoxideConcentrationMeasurement.types.Feature.NUMERIC_MEASUREMENT | clusters.CarbonMonoxideConcentrationMeasurement.types.Feature.LEVEL_INDICATION}, + {cluster_id = clusters.PowerSource.ID, cluster_type = "SERVER", feature_map = clusters.PowerSource.types.PowerSourceFeature.BATTERY}, + }, + device_types = { + {device_type_id = 0x0076, device_type_revision = 1} -- Smoke CO Alarm + } + } + } +}) + local cluster_subscribe_list = { clusters.SmokeCoAlarm.attributes.SmokeState, clusters.SmokeCoAlarm.attributes.TestInProgress, @@ -87,6 +117,27 @@ local function test_init() test.mock_device.add_test_device(mock_device) end +local cluster_subscribe_list_colevel = { + clusters.SmokeCoAlarm.attributes.TestInProgress, + clusters.SmokeCoAlarm.attributes.COState, + clusters.SmokeCoAlarm.attributes.HardwareFaultAlert, + clusters.CarbonMonoxideConcentrationMeasurement.attributes.MeasurementUnit, + clusters.CarbonMonoxideConcentrationMeasurement.attributes.LevelValue, + clusters.CarbonMonoxideConcentrationMeasurement.attributes.MeasuredValue, + clusters.PowerSource.attributes.BatPercentRemaining, +} + +local function test_init_colevel() + local subscribe_request = cluster_subscribe_list_colevel[1]:subscribe(mock_device_colevel) + for i, cluster in ipairs(cluster_subscribe_list_colevel) do + if i > 1 then + subscribe_request:merge(cluster:subscribe(mock_device_colevel)) + end + end + test.socket.matter:__expect_send({mock_device_colevel.id, subscribe_request}) + test.mock_device.add_test_device(mock_device_colevel) +end + test.set_test_init_function(test_init) test.register_message_test( @@ -134,6 +185,58 @@ test.register_message_test( } ) +test.register_coroutine_test( + "Level value reports should generate events", + function() + test.socket.matter:__queue_receive({ + mock_device_colevel.id, + clusters.CarbonMonoxideConcentrationMeasurement.attributes.LevelValue:build_test_report_data( + mock_device_colevel, 1, clusters.CarbonMonoxideConcentrationMeasurement.types.LevelValueEnum.UNKNOWN + ) + }) + test.socket.capability:__expect_send( + mock_device_colevel:generate_test_message("main", capabilities.carbonMonoxideHealthConcern.carbonMonoxideHealthConcern.unknown()) + ) + test.socket.matter:__queue_receive({ + mock_device_colevel.id, + clusters.CarbonMonoxideConcentrationMeasurement.attributes.LevelValue:build_test_report_data( + mock_device_colevel, 1, clusters.CarbonMonoxideConcentrationMeasurement.types.LevelValueEnum.LOW + ) + }) + test.socket.capability:__expect_send( + mock_device_colevel:generate_test_message("main", capabilities.carbonMonoxideHealthConcern.carbonMonoxideHealthConcern.good()) + ) + test.socket.matter:__queue_receive({ + mock_device_colevel.id, + clusters.CarbonMonoxideConcentrationMeasurement.attributes.LevelValue:build_test_report_data( + mock_device_colevel, 1, clusters.CarbonMonoxideConcentrationMeasurement.types.LevelValueEnum.MEDIUM + ) + }) + test.socket.capability:__expect_send( + mock_device_colevel:generate_test_message("main", capabilities.carbonMonoxideHealthConcern.carbonMonoxideHealthConcern.moderate()) + ) + test.socket.matter:__queue_receive({ + mock_device_colevel.id, + clusters.CarbonMonoxideConcentrationMeasurement.attributes.LevelValue:build_test_report_data( + mock_device_colevel, 1, clusters.CarbonMonoxideConcentrationMeasurement.types.LevelValueEnum.HIGH + ) + }) + test.socket.capability:__expect_send( + mock_device_colevel:generate_test_message("main", capabilities.carbonMonoxideHealthConcern.carbonMonoxideHealthConcern.unhealthy()) + ) + test.socket.matter:__queue_receive({ + mock_device_colevel.id, + clusters.CarbonMonoxideConcentrationMeasurement.attributes.LevelValue:build_test_report_data( + mock_device_colevel, 1, clusters.CarbonMonoxideConcentrationMeasurement.types.LevelValueEnum.CRITICAL + ) + }) + test.socket.capability:__expect_send( + mock_device_colevel:generate_test_message("main", capabilities.carbonMonoxideHealthConcern.carbonMonoxideHealthConcern.hazardous()) + ) + end, + { test_init = test_init_colevel } +) + test.register_message_test( "Test CO state handler", {