Skip to content

Commit 83f4407

Browse files
committed
Matter Sensor: Add modular profile supports for AQS
1 parent 0d97bf5 commit 83f4407

File tree

2 files changed

+160
-5
lines changed

2 files changed

+160
-5
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
name: aqs-modular
2+
components:
3+
- id: main
4+
capabilities:
5+
- id: airQualityHealthConcern
6+
- id: temperatureMeasurement
7+
optional: true
8+
- id: relativeHumidityMeasurement
9+
optional: true
10+
- id: carbonMonoxideMeasurement
11+
optional: true
12+
- id: carbonMonoxideHealthConcern
13+
optional: true
14+
- id: carbonDioxideMeasurement
15+
optional: true
16+
- id: carbonDioxideHealthConcern
17+
optional: true
18+
- id: nitrogenDioxideMeasurement
19+
optional: true
20+
- id: ozoneMeasurement
21+
optional: true
22+
- id: formaldehydeMeasurement
23+
optional: true
24+
- id: formaldehydeHealthConcern
25+
optional: true
26+
- id: veryFineDustSensor
27+
optional: true
28+
- id: veryFineDustHealthConcern
29+
optional: true
30+
- id: fineDustHealthConcern
31+
optional: true
32+
- id: dustSensor
33+
optional: true
34+
- id: dustHealthConcern
35+
optional: true
36+
- id: radonMeasurement
37+
optional: true
38+
- id: tvocMeasurement
39+
optional: true
40+
- id: firmwareUpdate
41+
- id: refresh
42+
- id: nitrogenDioxideHealthConcern
43+
optional: true
44+
- id: ozoneHealthConcern
45+
optional: true
46+
- id: radonHealthConcern
47+
optional: true
48+
- id: tvocHealthConcern
49+
optional: true
50+
- id: fineDustSensor
51+
optional: true
52+
categories:
53+
- name: AirQualityDetector
54+
preferences:
55+
- preferenceId: tempOffset
56+
explicit: true
57+
- preferenceId: humidityOffset
58+
explicit: true

drivers/SmartThings/matter-sensor/src/air-quality-sensor/init.lua

+102-5
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ local embedded_cluster_utils = require "embedded-cluster-utils"
2020
local log = require "log"
2121
local AIR_QUALITY_SENSOR_DEVICE_TYPE_ID = 0x002C
2222

23+
local SUPPORTED_COMPONENT_CAPABILITIES = "__supported_component_capabilities"
24+
25+
2326
-- Include driver-side definitions when lua libs api version is < 10
2427
local version = require "version"
2528
if version.api < 10 then
@@ -141,10 +144,6 @@ local units_required = {
141144
clusters.TotalVolatileOrganicCompoundsConcentrationMeasurement
142145
}
143146

144-
local function device_init(driver, device)
145-
device:subscribe()
146-
end
147-
148147
local tbl_contains = function(t, val)
149148
for _, v in pairs(t) do
150149
if v == val then
@@ -210,7 +209,30 @@ local function create_level_measurement_profile(device)
210209
return meas_name, level_name
211210
end
212211

213-
local function do_configure(driver, device)
212+
local function supported_level_measurements(device)
213+
local measurement_caps, level_caps = {}, {}
214+
for _, details in ipairs(AIR_QUALITY_MAP) do
215+
local cap_id = details[1]
216+
local cluster = details[3]
217+
-- capability describes either a HealthConcern or Measurement/Sensor
218+
if (cap_id:match("HealthConcern$")) then
219+
local attr_eps = embedded_cluster_utils.get_endpoints(device, cluster.ID, { feature_bitmap = cluster.types.Feature.LEVEL_INDICATION })
220+
if #attr_eps > 0 then
221+
device.log.info(string.format("Adding %s cap to table", cap_id))
222+
table.insert(level_caps, cap_id)
223+
end
224+
elseif (cap_id:match("Measurement$") or cap_id:match("Sensor$")) then
225+
local attr_eps = embedded_cluster_utils.get_endpoints(device, cluster.ID, { feature_bitmap = cluster.types.Feature.NUMERIC_MEASUREMENT })
226+
if #attr_eps > 0 then
227+
device.log.info(string.format("Adding %s cap to table", cap_id))
228+
table.insert(measurement_caps, cap_id)
229+
end
230+
end
231+
end
232+
return measurement_caps, level_caps
233+
end
234+
235+
local function match_profile_switch(driver, device)
214236
local temp_eps = embedded_cluster_utils.get_endpoints(device, clusters.TemperatureMeasurement.ID)
215237
local humidity_eps = embedded_cluster_utils.get_endpoints(device, clusters.RelativeHumidityMeasurement.ID)
216238

@@ -271,6 +293,81 @@ local function do_configure(driver, device)
271293
device:try_update_metadata({profile = profile_name})
272294
end
273295

296+
local function supports_capability_by_id_modular(device, capability, component)
297+
for _, component_capabilities in ipairs(device:get_field(SUPPORTED_COMPONENT_CAPABILITIES)) do
298+
local comp_id = component_capabilities[1]
299+
local capability_ids = component_capabilities[2]
300+
if (component == nil) or (component == comp_id) then
301+
for _, cap in ipairs(capability_ids) do
302+
if cap == capability then
303+
return true
304+
end
305+
end
306+
end
307+
end
308+
return false
309+
end
310+
311+
local function match_modular_profile(driver, device)
312+
local temp_eps = embedded_cluster_utils.get_endpoints(device, clusters.TemperatureMeasurement.ID)
313+
local humidity_eps = embedded_cluster_utils.get_endpoints(device, clusters.RelativeHumidityMeasurement.ID)
314+
315+
-- we have to read the unit before reports of values will do anything
316+
for _, cluster in ipairs(units_required) do
317+
device:send(cluster.attributes.MeasurementUnit:read(device))
318+
end
319+
320+
local optional_supported_component_capabilities = {}
321+
local main_component_capabilities = {}
322+
323+
if #temp_eps > 0 then
324+
table.insert(main_component_capabilities, capabilities.temperatureMeasurement.ID)
325+
end
326+
if #humidity_eps > 0 then
327+
table.insert(main_component_capabilities, capabilities.relativeHumidityMeasurement.ID)
328+
end
329+
330+
local measurement_caps, level_caps = supported_level_measurements(device)
331+
332+
for _, cap_id in ipairs(measurement_caps) do
333+
table.insert(main_component_capabilities, cap_id)
334+
end
335+
336+
for _, cap_id in ipairs(level_caps) do
337+
table.insert(main_component_capabilities, cap_id)
338+
end
339+
340+
table.insert(optional_supported_component_capabilities, {"main", main_component_capabilities})
341+
342+
device:set_field(SUPPORTED_COMPONENT_CAPABILITIES, optional_supported_component_capabilities)
343+
344+
device:try_update_metadata({profile = "aqs-modular", optional_component_capabilities = optional_supported_component_capabilities})
345+
346+
-- add mandatory capabilities for subscription
347+
local total_supported_capabilities = optional_supported_component_capabilities
348+
table.insert(total_supported_capabilities[1][2], capabilities.airQualityHealthConcern.ID)
349+
350+
device:set_field(SUPPORTED_COMPONENT_CAPABILITIES, total_supported_capabilities, { persist = true })
351+
352+
--re-up subscription with new capabiltiies using the moudlar supports_capability override
353+
device:extend_device("supports_capability_by_id", supports_capability_by_id_modular)
354+
device:subscribe()
355+
end
356+
357+
local function do_configure(driver, device)
358+
-- must use profile switching on older hubs
359+
if version.api < 14 and version.rpc < 7 then
360+
match_profile_switch(driver, device)
361+
else
362+
match_modular_profile(driver, device)
363+
end
364+
end
365+
366+
local function device_init(driver, device)
367+
device:extend_device("supports_capability_by_id", supports_capability_by_id_modular)
368+
device:subscribe()
369+
end
370+
274371
local function store_unit_factory(capability_name)
275372
return function(driver, device, ib, response)
276373
device:set_field(capability_name.."_unit", ib.data.value, {persist = true})

0 commit comments

Comments
 (0)