Skip to content

Commit bcaa370

Browse files
lelandbluethinkaNamegreens
authored
add maileke air quality detector (#1850) (#2103)
Co-authored-by: thinkaName <[email protected]> Co-authored-by: Steven Green <[email protected]>
1 parent 754f2a7 commit bcaa370

File tree

8 files changed

+547
-1
lines changed

8 files changed

+547
-1
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
name: 'Zigbee Air Quality Detector'
2+
packageKey: 'zigbee-air-quality-detector'
3+
permissions:
4+
zigbee: {}
5+
description: "SmartThings driver for Zigbee air quality detector"
6+
vendorSupportInformation: "https://support.smartthings.com"
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
zigbeeManufacturer:
2+
- id: "MultiIR/PMT1006S-SGM-ZTN"
3+
deviceLabel: MultiIR Air Quality Detector
4+
manufacturer: MultiIR
5+
model: PMT1006S-SGM-ZTN
6+
deviceProfileName: air-quality-detector-MultiIR
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
name: air-quality-detector-MultiIR
2+
components:
3+
- id: main
4+
capabilities:
5+
- id: airQualityHealthConcern
6+
version: 1
7+
- id: temperatureMeasurement
8+
version: 1
9+
- id: relativeHumidityMeasurement
10+
version: 1
11+
- id: carbonDioxideMeasurement
12+
version: 1
13+
- id: carbonDioxideHealthConcern
14+
version: 1
15+
- id: fineDustSensor
16+
version: 1
17+
- id: fineDustHealthConcern
18+
version: 1
19+
- id: veryFineDustSensor
20+
version: 1
21+
- id: veryFineDustHealthConcern
22+
version: 1
23+
- id: dustSensor
24+
version: 1
25+
- id: dustHealthConcern
26+
version: 1
27+
- id: formaldehydeMeasurement
28+
version: 1
29+
- id: tvocMeasurement
30+
version: 1
31+
- id: tvocHealthConcern
32+
version: 1
33+
- id: firmwareUpdate
34+
version: 1
35+
- id: refresh
36+
version: 1
37+
categories:
38+
- name: AirQualityDetector
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
-- Copyright 2025 SmartThings
2+
--
3+
-- Licensed under the Apache License, Version 2.0 (the "License");
4+
-- you may not use this file except in compliance with the License.
5+
-- You may obtain a copy of the License at
6+
--
7+
-- http://www.apache.org/licenses/LICENSE-2.0
8+
--
9+
-- Unless required by applicable law or agreed to in writing, software
10+
-- distributed under the License is distributed on an "AS IS" BASIS,
11+
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
-- See the License for the specific language governing permissions and
13+
-- limitations under the License.
14+
15+
local data_types = require "st.zigbee.data_types"
16+
17+
local custom_clusters = {
18+
carbonDioxide = {
19+
id = 0xFCC3,
20+
mfg_specific_code = 0x1235,
21+
attributes = {
22+
measured_value = {
23+
id = 0x0000,
24+
value_type = data_types.Uint16,
25+
}
26+
}
27+
},
28+
particulate_matter = {
29+
id = 0xFCC1,
30+
mfg_specific_code = 0x1235,
31+
attributes = {
32+
pm2_5_MeasuredValue = {
33+
id = 0x0000,
34+
value_type = data_types.Uint16,
35+
},
36+
pm1_0_MeasuredValue = {
37+
id = 0x0001,
38+
value_type = data_types.Uint16,
39+
},
40+
pm10_MeasuredValue = {
41+
id = 0x0002,
42+
value_type = data_types.Uint16,
43+
}
44+
}
45+
},
46+
unhealthy_gas = {
47+
id = 0xFCC2,
48+
mfg_specific_code = 0x1235,
49+
attributes = {
50+
CH2O_MeasuredValue = {
51+
id = 0x0000,
52+
value_type = data_types.SinglePrecisionFloat,
53+
},
54+
tvoc_MeasuredValue = {
55+
id = 0x0001,
56+
value_type = data_types.SinglePrecisionFloat,
57+
}
58+
}
59+
},
60+
AQI = {
61+
id = 0xFCC5,
62+
mfg_specific_code = 0x1235,
63+
attributes = {
64+
AQI_value = {
65+
id = 0x0000,
66+
value_type = data_types.Uint16,
67+
}
68+
}
69+
}
70+
}
71+
72+
return custom_clusters
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
-- Copyright 2025 SmartThings
2+
--
3+
-- Licensed under the Apache License, Version 2.0 (the "License");
4+
-- you may not use this file except in compliance with the License.
5+
-- You may obtain a copy of the License at
6+
--
7+
-- http://www.apache.org/licenses/LICENSE-2.0
8+
--
9+
-- Unless required by applicable law or agreed to in writing, software
10+
-- distributed under the License is distributed on an "AS IS" BASIS,
11+
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
-- See the License for the specific language governing permissions and
13+
-- limitations under the License.
14+
15+
local clusters = require "st.zigbee.zcl.clusters"
16+
local capabilities = require "st.capabilities"
17+
local custom_clusters = require "MultiIR/custom_clusters"
18+
local cluster_base = require "st.zigbee.cluster_base"
19+
20+
local RelativeHumidity = clusters.RelativeHumidity
21+
local TemperatureMeasurement = clusters.TemperatureMeasurement
22+
23+
local MultiIR_SENSOR_FINGERPRINTS = {
24+
{ mfr = "MultiIR", model = "PMT1006S-SGM-ZTN" }--This is not a sleep end device
25+
}
26+
27+
local function can_handle_MultiIR_sensor(opts, driver, device)
28+
for _, fingerprint in ipairs(MultiIR_SENSOR_FINGERPRINTS) do
29+
if device:get_manufacturer() == fingerprint.mfr and device:get_model() == fingerprint.model then
30+
return true
31+
end
32+
end
33+
return false
34+
end
35+
36+
local function send_read_attr_request(device, cluster, attr)
37+
device:send(
38+
cluster_base.read_manufacturer_specific_attribute(
39+
device,
40+
cluster.id,
41+
attr.id,
42+
cluster.mfg_specific_code
43+
)
44+
)
45+
end
46+
47+
local function do_refresh(driver, device)
48+
device:send(RelativeHumidity.attributes.MeasuredValue:read(device):to_endpoint(0x01))
49+
device:send(TemperatureMeasurement.attributes.MeasuredValue:read(device):to_endpoint(0x01))
50+
send_read_attr_request(device, custom_clusters.particulate_matter, custom_clusters.particulate_matter.attributes.pm2_5_MeasuredValue)
51+
send_read_attr_request(device, custom_clusters.particulate_matter, custom_clusters.particulate_matter.attributes.pm1_0_MeasuredValue)
52+
send_read_attr_request(device, custom_clusters.particulate_matter, custom_clusters.particulate_matter.attributes.pm10_MeasuredValue)
53+
send_read_attr_request(device, custom_clusters.unhealthy_gas, custom_clusters.unhealthy_gas.attributes.CH2O_MeasuredValue)
54+
send_read_attr_request(device, custom_clusters.unhealthy_gas, custom_clusters.unhealthy_gas.attributes.tvoc_MeasuredValue)
55+
send_read_attr_request(device, custom_clusters.carbonDioxide, custom_clusters.carbonDioxide.attributes.measured_value)
56+
send_read_attr_request(device, custom_clusters.AQI, custom_clusters.AQI.attributes.AQI_value)
57+
end
58+
59+
local function airQualityHealthConcern_attr_handler(driver, device, value, zb_rx)
60+
local airQuality_level = "good"
61+
if value.value >= 51 then
62+
airQuality_level = "moderate"
63+
end
64+
if value.value >= 101 then
65+
airQuality_level = "slightlyUnhealthy"
66+
end
67+
if value.value >= 151 then
68+
airQuality_level = "unhealthy"
69+
end
70+
if value.value >= 201 then
71+
airQuality_level = "veryUnhealthy"
72+
end
73+
if value.value >= 301 then
74+
airQuality_level = "hazardous"
75+
end
76+
device:emit_event_for_endpoint(zb_rx.address_header.src_endpoint.value, capabilities.airQualityHealthConcern.airQualityHealthConcern({value = airQuality_level}))
77+
end
78+
79+
local function carbonDioxide_attr_handler(driver, device, value, zb_rx)
80+
local level = "unhealthy"
81+
device:emit_event_for_endpoint(zb_rx.address_header.src_endpoint.value, capabilities.carbonDioxideMeasurement.carbonDioxide({value = value.value, unit = "ppm"}))
82+
if value.value <= 1500 then
83+
level = "good"
84+
elseif value.value >= 1501 and value.value <= 2500 then
85+
level = "moderate"
86+
end
87+
device:emit_event_for_endpoint(zb_rx.address_header.src_endpoint.value, capabilities.carbonDioxideHealthConcern.carbonDioxideHealthConcern({value = level}))
88+
end
89+
90+
local function particulate_matter_attr_handler(cap,Concern,good,bad)
91+
return function(driver, device, value, zb_rx)
92+
local level = "unhealthy"
93+
device:emit_event_for_endpoint(zb_rx.address_header.src_endpoint.value, cap({value = value.value}))
94+
if value.value <= good then
95+
level = "good"
96+
elseif bad > 0 and value.value > good and value.value < bad then
97+
level = "moderate"
98+
end
99+
device:emit_event_for_endpoint(zb_rx.address_header.src_endpoint.value, Concern({value = level}))
100+
end
101+
end
102+
103+
local function CH2O_attr_handler(driver, device, value, zb_rx)
104+
device:emit_event_for_endpoint(zb_rx.address_header.src_endpoint.value, capabilities.formaldehydeMeasurement.formaldehydeLevel({value = value.value, unit = "mg/m^3"}))
105+
end
106+
107+
local function tvoc_attr_handler(driver, device, value, zb_rx)
108+
device:emit_event_for_endpoint(zb_rx.address_header.src_endpoint.value, capabilities.tvocMeasurement.tvocLevel({value = value.value, unit = "ug/m3"}))
109+
local level = "unhealthy"
110+
if value.value < 600.0 then
111+
level = "good"
112+
end
113+
device:emit_event_for_endpoint(zb_rx.address_header.src_endpoint.value, capabilities.tvocHealthConcern.tvocHealthConcern({value = level}))
114+
end
115+
116+
local function added_handler(self, device)
117+
do_refresh()
118+
end
119+
120+
local MultiIR_sensor = {
121+
NAME = "MultiIR air quality detector",
122+
lifecycle_handlers = {
123+
added = added_handler
124+
},
125+
zigbee_handlers = {
126+
attr = {
127+
[custom_clusters.carbonDioxide.id] = {
128+
[custom_clusters.carbonDioxide.attributes.measured_value.id] = carbonDioxide_attr_handler
129+
},
130+
[custom_clusters.particulate_matter.id] = {
131+
[custom_clusters.particulate_matter.attributes.pm2_5_MeasuredValue.id] = particulate_matter_attr_handler(capabilities.fineDustSensor.fineDustLevel,capabilities.fineDustHealthConcern.fineDustHealthConcern,75,115),--75 115 is a comparative value of good moderate unhealthy, and 0 is no comparison
132+
[custom_clusters.particulate_matter.attributes.pm1_0_MeasuredValue.id] = particulate_matter_attr_handler(capabilities.veryFineDustSensor.veryFineDustLevel,capabilities.veryFineDustHealthConcern.veryFineDustHealthConcern,100,0),
133+
[custom_clusters.particulate_matter.attributes.pm10_MeasuredValue.id] = particulate_matter_attr_handler(capabilities.dustSensor.dustLevel,capabilities.dustHealthConcern.dustHealthConcern,150,0)
134+
},
135+
[custom_clusters.unhealthy_gas.id] = {
136+
[custom_clusters.unhealthy_gas.attributes.CH2O_MeasuredValue.id] = CH2O_attr_handler,
137+
[custom_clusters.unhealthy_gas.attributes.tvoc_MeasuredValue.id] = tvoc_attr_handler
138+
},
139+
[custom_clusters.AQI.id] = {
140+
[custom_clusters.AQI.attributes.AQI_value.id] = airQualityHealthConcern_attr_handler
141+
}
142+
}
143+
},
144+
capability_handlers = {
145+
[capabilities.refresh.ID] = {
146+
[capabilities.refresh.commands.refresh.NAME] = do_refresh,
147+
}
148+
},
149+
can_handle = can_handle_MultiIR_sensor
150+
}
151+
152+
return MultiIR_sensor
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
-- Copyright 2025 SmartThings
2+
--
3+
-- Licensed under the Apache License, Version 2.0 (the "License");
4+
-- you may not use this file except in compliance with the License.
5+
-- You may obtain a copy of the License at
6+
--
7+
-- http://www.apache.org/licenses/LICENSE-2.0
8+
--
9+
-- Unless required by applicable law or agreed to in writing, software
10+
-- distributed under the License is distributed on an "AS IS" BASIS,
11+
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
-- See the License for the specific language governing permissions and
13+
-- limitations under the License.
14+
15+
local ZigbeeDriver = require "st.zigbee"
16+
local capabilities = require "st.capabilities"
17+
local defaults = require "st.zigbee.defaults"
18+
19+
local zigbee_air_quality_detector_template = {
20+
supported_capabilities = {
21+
capabilities.airQualityHealthConcern,
22+
capabilities.temperatureMeasurement,
23+
capabilities.relativeHumidityMeasurement,
24+
capabilities.carbonDioxideMeasurement,
25+
capabilities.carbonDioxideHealthConcern,
26+
capabilities.fineDustSensor,
27+
capabilities.fineDustHealthConcern,
28+
capabilities.veryFineDustSensor,
29+
capabilities.veryFineDustHealthConcern,
30+
capabilities.dustSensor,
31+
capabilities.dustHealthConcern,
32+
capabilities.formaldehydeMeasurement,
33+
capabilities.tvocMeasurement,
34+
capabilities.tvocHealthConcern
35+
},
36+
sub_drivers = { require("MultiIR") }
37+
}
38+
39+
defaults.register_for_default_handlers(zigbee_air_quality_detector_template, zigbee_air_quality_detector_template.supported_capabilities)
40+
local zigbee_air_quality_detector_driver = ZigbeeDriver("zigbee-air-quality-detector", zigbee_air_quality_detector_template)
41+
zigbee_air_quality_detector_driver:run()

0 commit comments

Comments
 (0)