Skip to content

Re-Open add maileke air quality detector #2103

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions drivers/SmartThings/zigbee-air-quality-detector/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
name: 'Zigbee Air Quality Detector'
packageKey: 'zigbee-air-quality-detector'
permissions:
zigbee: {}
description: "SmartThings driver for Zigbee air quality detector"
vendorSupportInformation: "https://support.smartthings.com"
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
zigbeeManufacturer:
- id: "MultiIR/PMT1006S-SGM-ZTN"
deviceLabel: MultiIR Air Quality Detector
manufacturer: MultiIR
model: PMT1006S-SGM-ZTN
deviceProfileName: air-quality-detector-MultiIR
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: air-quality-detector-MultiIR
components:
- id: main
capabilities:
- id: airQualityHealthConcern
version: 1
- id: temperatureMeasurement
version: 1
- id: relativeHumidityMeasurement
version: 1
- id: carbonDioxideMeasurement
version: 1
- id: carbonDioxideHealthConcern
version: 1
- id: fineDustSensor
version: 1
- id: fineDustHealthConcern
version: 1
- id: veryFineDustSensor
version: 1
- id: veryFineDustHealthConcern
version: 1
- id: dustSensor
version: 1
- id: dustHealthConcern
version: 1
- id: formaldehydeMeasurement
version: 1
- id: tvocMeasurement
version: 1
- id: tvocHealthConcern
version: 1
- id: firmwareUpdate
version: 1
- id: refresh
version: 1
categories:
- name: AirQualityDetector
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
-- Copyright 2025 SmartThings
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.

local data_types = require "st.zigbee.data_types"

local custom_clusters = {
carbonDioxide = {
id = 0xFCC3,
mfg_specific_code = 0x1235,
attributes = {
measured_value = {
id = 0x0000,
value_type = data_types.Uint16,
}
}
},
particulate_matter = {
id = 0xFCC1,
mfg_specific_code = 0x1235,
attributes = {
pm2_5_MeasuredValue = {
id = 0x0000,
value_type = data_types.Uint16,
},
pm1_0_MeasuredValue = {
id = 0x0001,
value_type = data_types.Uint16,
},
pm10_MeasuredValue = {
id = 0x0002,
value_type = data_types.Uint16,
}
}
},
unhealthy_gas = {
id = 0xFCC2,
mfg_specific_code = 0x1235,
attributes = {
CH2O_MeasuredValue = {
id = 0x0000,
value_type = data_types.SinglePrecisionFloat,
},
tvoc_MeasuredValue = {
id = 0x0001,
value_type = data_types.SinglePrecisionFloat,
}
}
},
AQI = {
id = 0xFCC5,
mfg_specific_code = 0x1235,
attributes = {
AQI_value = {
id = 0x0000,
value_type = data_types.Uint16,
}
}
}
}

return custom_clusters
152 changes: 152 additions & 0 deletions drivers/SmartThings/zigbee-air-quality-detector/src/MultiIR/init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
-- Copyright 2025 SmartThings
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.

local clusters = require "st.zigbee.zcl.clusters"
local capabilities = require "st.capabilities"
local custom_clusters = require "MultiIR/custom_clusters"
local cluster_base = require "st.zigbee.cluster_base"

local RelativeHumidity = clusters.RelativeHumidity
local TemperatureMeasurement = clusters.TemperatureMeasurement

local MultiIR_SENSOR_FINGERPRINTS = {
{ mfr = "MultiIR", model = "PMT1006S-SGM-ZTN" }--This is not a sleep end device
}

local function can_handle_MultiIR_sensor(opts, driver, device)
for _, fingerprint in ipairs(MultiIR_SENSOR_FINGERPRINTS) do
if device:get_manufacturer() == fingerprint.mfr and device:get_model() == fingerprint.model then
return true
end
end
return false
end

local function send_read_attr_request(device, cluster, attr)
device:send(
cluster_base.read_manufacturer_specific_attribute(
device,
cluster.id,
attr.id,
cluster.mfg_specific_code
)
)
end

local function do_refresh(driver, device)
device:send(RelativeHumidity.attributes.MeasuredValue:read(device):to_endpoint(0x01))
device:send(TemperatureMeasurement.attributes.MeasuredValue:read(device):to_endpoint(0x01))
send_read_attr_request(device, custom_clusters.particulate_matter, custom_clusters.particulate_matter.attributes.pm2_5_MeasuredValue)
send_read_attr_request(device, custom_clusters.particulate_matter, custom_clusters.particulate_matter.attributes.pm1_0_MeasuredValue)
send_read_attr_request(device, custom_clusters.particulate_matter, custom_clusters.particulate_matter.attributes.pm10_MeasuredValue)
send_read_attr_request(device, custom_clusters.unhealthy_gas, custom_clusters.unhealthy_gas.attributes.CH2O_MeasuredValue)
send_read_attr_request(device, custom_clusters.unhealthy_gas, custom_clusters.unhealthy_gas.attributes.tvoc_MeasuredValue)
send_read_attr_request(device, custom_clusters.carbonDioxide, custom_clusters.carbonDioxide.attributes.measured_value)
send_read_attr_request(device, custom_clusters.AQI, custom_clusters.AQI.attributes.AQI_value)
end

local function airQualityHealthConcern_attr_handler(driver, device, value, zb_rx)
local airQuality_level = "good"
if value.value >= 51 then
airQuality_level = "moderate"
end
if value.value >= 101 then
airQuality_level = "slightlyUnhealthy"
end
if value.value >= 151 then
airQuality_level = "unhealthy"
end
if value.value >= 201 then
airQuality_level = "veryUnhealthy"
end
if value.value >= 301 then
airQuality_level = "hazardous"
end
device:emit_event_for_endpoint(zb_rx.address_header.src_endpoint.value, capabilities.airQualityHealthConcern.airQualityHealthConcern({value = airQuality_level}))
end

local function carbonDioxide_attr_handler(driver, device, value, zb_rx)
local level = "unhealthy"
device:emit_event_for_endpoint(zb_rx.address_header.src_endpoint.value, capabilities.carbonDioxideMeasurement.carbonDioxide({value = value.value, unit = "ppm"}))
if value.value <= 1500 then
level = "good"
elseif value.value >= 1501 and value.value <= 2500 then
level = "moderate"
end
device:emit_event_for_endpoint(zb_rx.address_header.src_endpoint.value, capabilities.carbonDioxideHealthConcern.carbonDioxideHealthConcern({value = level}))
end

local function particulate_matter_attr_handler(cap,Concern,good,bad)
return function(driver, device, value, zb_rx)
local level = "unhealthy"
device:emit_event_for_endpoint(zb_rx.address_header.src_endpoint.value, cap({value = value.value}))
if value.value <= good then
level = "good"
elseif bad > 0 and value.value > good and value.value < bad then
level = "moderate"
end
device:emit_event_for_endpoint(zb_rx.address_header.src_endpoint.value, Concern({value = level}))
end
end

local function CH2O_attr_handler(driver, device, value, zb_rx)
device:emit_event_for_endpoint(zb_rx.address_header.src_endpoint.value, capabilities.formaldehydeMeasurement.formaldehydeLevel({value = value.value, unit = "mg/m^3"}))
end

local function tvoc_attr_handler(driver, device, value, zb_rx)
device:emit_event_for_endpoint(zb_rx.address_header.src_endpoint.value, capabilities.tvocMeasurement.tvocLevel({value = value.value, unit = "ug/m3"}))
local level = "unhealthy"
if value.value < 600.0 then
level = "good"
end
device:emit_event_for_endpoint(zb_rx.address_header.src_endpoint.value, capabilities.tvocHealthConcern.tvocHealthConcern({value = level}))
end

local function added_handler(self, device)
do_refresh()
end

local MultiIR_sensor = {
NAME = "MultiIR air quality detector",
lifecycle_handlers = {
added = added_handler
},
zigbee_handlers = {
attr = {
[custom_clusters.carbonDioxide.id] = {
[custom_clusters.carbonDioxide.attributes.measured_value.id] = carbonDioxide_attr_handler
},
[custom_clusters.particulate_matter.id] = {
[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
[custom_clusters.particulate_matter.attributes.pm1_0_MeasuredValue.id] = particulate_matter_attr_handler(capabilities.veryFineDustSensor.veryFineDustLevel,capabilities.veryFineDustHealthConcern.veryFineDustHealthConcern,100,0),
[custom_clusters.particulate_matter.attributes.pm10_MeasuredValue.id] = particulate_matter_attr_handler(capabilities.dustSensor.dustLevel,capabilities.dustHealthConcern.dustHealthConcern,150,0)
},
[custom_clusters.unhealthy_gas.id] = {
[custom_clusters.unhealthy_gas.attributes.CH2O_MeasuredValue.id] = CH2O_attr_handler,
[custom_clusters.unhealthy_gas.attributes.tvoc_MeasuredValue.id] = tvoc_attr_handler
},
[custom_clusters.AQI.id] = {
[custom_clusters.AQI.attributes.AQI_value.id] = airQualityHealthConcern_attr_handler
}
}
},
capability_handlers = {
[capabilities.refresh.ID] = {
[capabilities.refresh.commands.refresh.NAME] = do_refresh,
}
},
can_handle = can_handle_MultiIR_sensor
}

return MultiIR_sensor
41 changes: 41 additions & 0 deletions drivers/SmartThings/zigbee-air-quality-detector/src/init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
-- Copyright 2025 SmartThings
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.

local ZigbeeDriver = require "st.zigbee"
local capabilities = require "st.capabilities"
local defaults = require "st.zigbee.defaults"

local zigbee_air_quality_detector_template = {
supported_capabilities = {
capabilities.airQualityHealthConcern,
capabilities.temperatureMeasurement,
capabilities.relativeHumidityMeasurement,
capabilities.carbonDioxideMeasurement,
capabilities.carbonDioxideHealthConcern,
capabilities.fineDustSensor,
capabilities.fineDustHealthConcern,
capabilities.veryFineDustSensor,
capabilities.veryFineDustHealthConcern,
capabilities.dustSensor,
capabilities.dustHealthConcern,
capabilities.formaldehydeMeasurement,
capabilities.tvocMeasurement,
capabilities.tvocHealthConcern
},
sub_drivers = { require("MultiIR") }
}

defaults.register_for_default_handlers(zigbee_air_quality_detector_template, zigbee_air_quality_detector_template.supported_capabilities)
local zigbee_air_quality_detector_driver = ZigbeeDriver("zigbee-air-quality-detector", zigbee_air_quality_detector_template)
zigbee_air_quality_detector_driver:run()
Loading
Loading