Skip to content

Adding support for frient Intelligent Smoke Alarm (SMSZB-120) #2024

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 10 commits into
base: main
Choose a base branch
from
2 changes: 1 addition & 1 deletion drivers/SmartThings/zigbee-smoke-detector/fingerprints.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ zigbeeManufacturer:
deviceLabel: frient Smoke Detector
manufacturer: frient A/S
model: SMSZB-120
deviceProfileName: smoke-battery
deviceProfileName: smoke-temp-battery-alarm
- id: "Heiman/Orvibo/Gas3"
deviceLabel: Orvibo Gas Detector
manufacturer: Heiman
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: smoke-temp-battery-alarm
components:
- id: main
capabilities:
- id: smokeDetector
version: 1
- id: temperatureMeasurement
version: 1
- id: battery
version: 1
- id: firmwareUpdate
version: 1
- id: refresh
version: 1
- id: alarm
version: 1
config:
values:
- key: "alarm.value"
enabledValues:
- off
- siren
- key: "{{enumCommands}}"
enabledValues:
- off
- siren
categories:
- name: SmokeDetector
preferences:
- preferenceId: tempOffset
explicit: true
- name: "tempSensitivity"
title: "Temperature Sensitivity (18.0°)"
description: "Minimum change in temperature to report"
required: false
preferenceType: number
definition:
minimum: 0.1
maximum: 2.0
default: 1.0
- name: "warningDuration"
title: "Alarm duration (s)"
description: "After how many seconds should the alarm turn off"
required: false
preferenceType: integer
definition:
minimum: 0
maximum: 65534
default: 240

201 changes: 191 additions & 10 deletions drivers/SmartThings/zigbee-smoke-detector/src/frient/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,201 @@
-- limitations under the License.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

leave this in place, thanks

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in the latest commit.

local battery_defaults = require "st.zigbee.defaults.battery_defaults"
local capabilities = require "st.capabilities"
local zcl_clusters = require "st.zigbee.zcl.clusters"
local zcl_global_commands = require "st.zigbee.zcl.global_commands"
local data_types = require "st.zigbee.data_types"
local alarm = capabilities.alarm
local smokeDetector = capabilities.smokeDetector

local is_frient_smoke_detector = function(opts, driver, device)
if device:get_manufacturer() == "frient A/S" then
return true
local IASWD = zcl_clusters.IASWD
local IASZone = zcl_clusters.IASZone
local TemperatureMeasurement = zcl_clusters.TemperatureMeasurement

local ALARM_COMMAND = "alarmCommand"
local ALARM_LAST_DURATION = "Custom_Alarm_Duration"
local ALARM_DEFAULT_MAX_DURATION = 0x00F0
local DEFAULT_WARNING_DURATION = 240
local BATTERY_MIN_VOLTAGE = 2.3
local BATTERY_MAX_VOLTAGE = 3.0

local TEMPERATURE_MEASUREMENT_ENDPOINT = 0x26

local alarm_command = {
OFF = 0,
SIREN = 1
}

local CONFIGURATIONS = {
{
cluster = IASZone.ID,
attribute = IASZone.attributes.ZoneStatus.ID,
minimum_interval = 30,
maximum_interval = 300,
data_type = IASZone.attributes.ZoneStatus.base_type,
reportable_change = 1
},
{
cluster = TemperatureMeasurement.ID,
attribute = TemperatureMeasurement.attributes.MeasuredValue.ID,
minimum_interval = 60,
maximum_interval = 600,
data_type = TemperatureMeasurement.attributes.MeasuredValue.base_type,
reportable_change = 100
}
}

local function device_added(driver, device)
device:emit_event(alarm.alarm.off())
device:emit_event(smokeDetector.smoke.clear())
end

local function device_init(driver, device)
battery_defaults.build_linear_voltage_init(BATTERY_MIN_VOLTAGE, BATTERY_MAX_VOLTAGE)(driver, device)
if CONFIGURATIONS ~= nil then
for _, attribute in ipairs(CONFIGURATIONS) do
device:add_configured_attribute(attribute)
device:add_monitored_attribute(attribute)
end
end
end

local function do_configure(self, device)
device:configure()
device:send(IASWD.attributes.MaxDuration:write(device, ALARM_DEFAULT_MAX_DURATION))
end

local info_changed = function (driver, device, event, args)
for name, info in pairs(device.preferences) do
if (device.preferences[name] ~= nil and args.old_st_store.preferences[name] ~= device.preferences[name]) then
local input = device.preferences[name]
if (name == "tempSensitivity") then
local sensitivity = math.floor((device.preferences.tempSensitivity or 0.1)*100 + 0.5)
device:send(TemperatureMeasurement.attributes.MeasuredValue:configure_reporting(device, 60, 600, sensitivity):to_endpoint(TEMPERATURE_MEASUREMENT_ENDPOINT))
elseif (name == "warningDuration") then
device:set_field(ALARM_LAST_DURATION, input, {persist = true})
device:send(IASWD.attributes.MaxDuration:write(device, tonumber(input)))
end
end
end
return false
end

local frient_smoke_detector = {
NAME = "Freint Smoke Detector",
local function generate_event_from_zone_status(driver, device, zone_status, zigbee_message)

if zone_status:is_test_set() then
device:emit_event(smokeDetector.smoke.tested())
elseif zone_status:is_alarm1_set() then
device:emit_event(smokeDetector.smoke.detected())
else
device.thread:call_with_delay(6, function ()
device:emit_event(smokeDetector.smoke.clear())
end)
end
end

local function ias_zone_status_attr_handler(driver, device, zone_status, zb_rx)
generate_event_from_zone_status(driver, device, zone_status, zb_rx)
end

local function ias_zone_status_change_handler(driver, device, zb_rx)
local zone_status = zb_rx.body.zcl_body.zone_status
generate_event_from_zone_status(driver, device, zone_status, zb_rx)
end

local function send_siren_command(device)
local warning_duration = device:get_field(ALARM_LAST_DURATION) or DEFAULT_WARNING_DURATION
local sirenConfiguration = IASWD.types.SirenConfiguration(0x00)

sirenConfiguration:set_warning_mode(0x01)

device:send(
IASWD.server.commands.StartWarning(
device,
sirenConfiguration,
data_types.Uint16(warning_duration)
)
)

end

local emit_alarm_event = function(device, cmd)
if cmd == alarm_command.OFF then
device:emit_event(alarm.alarm.off())
else
if cmd == alarm_command.SIREN then
device:emit_event(alarm.alarm.siren())
end
end
end

local default_response_handler = function(driver, device, zigbee_message)
local command = zigbee_message.body.zcl_body.cmd.value
local alarm_ev = device:get_field(ALARM_COMMAND)
if command == IASWD.server.commands.StartWarning.ID then
if alarm_ev ~= alarm_command.OFF then
emit_alarm_event(device, alarm_ev)
local lastDuration = device:get_field(ALARM_LAST_DURATION) or ALARM_DEFAULT_MAX_DURATION
device.thread:call_with_delay(lastDuration, function(d)
device:emit_event(alarm.alarm.off())
end)
else
emit_alarm_event(device,alarm_command.OFF)
end
end
end

local siren_alarm_siren_handler = function(driver, device, command)
device:set_field(ALARM_COMMAND, alarm_command.SIREN, {persist = true})
send_siren_command(device)
end


local siren_switch_off_handler = function(driver, device, command)
local sirenConfiguration = IASWD.types.SirenConfiguration(0x00)
sirenConfiguration:set_warning_mode(0x00)
device:set_field(ALARM_COMMAND, alarm_command.OFF, {persist = true})

device:send(
IASWD.server.commands.StartWarning(
device,
sirenConfiguration
)
)
end

local frient_smoke_sensor = {
NAME = "frient smoke sensor",
lifecycle_handlers = {
init = battery_defaults.build_linear_voltage_init(2.3, 3.0)
added = device_added,
doConfigure = do_configure,
init = device_init,
infoChanged = info_changed
},
capability_handlers = {
[alarm.ID] = {
[alarm.commands.off.NAME] = siren_switch_off_handler,
[alarm.commands.siren.NAME] = siren_alarm_siren_handler,
},
},
zigbee_handlers = {
global = {
[IASWD.ID] = {
[zcl_global_commands.DEFAULT_RESPONSE_ID] = default_response_handler
},
},
cluster = {
[IASZone.ID] = {
[IASZone.client.commands.ZoneStatusChangeNotification.ID] = ias_zone_status_change_handler
}
},
attr = {
[IASZone.ID] = {
[IASZone.attributes.ZoneStatus.ID] = ias_zone_status_attr_handler
}
}
},
can_handle = is_frient_smoke_detector
can_handle = function(opts, driver, device, ...)
return device:get_manufacturer() == "frient A/S" and device:get_model() == "SMSZB-120"
end
}

return frient_smoke_detector
return frient_smoke_sensor
9 changes: 5 additions & 4 deletions drivers/SmartThings/zigbee-smoke-detector/src/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,18 @@ local constants = require "st.zigbee.constants"
local zigbee_smoke_driver_template = {
supported_capabilities = {
capabilities.smokeDetector,
capabilities.battery
capabilities.battery,
capabilities.alarm,
capabilities.temperatureMeasurement
},
sub_drivers = {
require("frient"),
require("aqara-gas"),
require("aqara"),
require("frient")
require("aqara")
},
ias_zone_configuration_method = constants.IAS_ZONE_CONFIGURE_TYPE.AUTO_ENROLL_RESPONSE,
}

defaults.register_for_default_handlers(zigbee_smoke_driver_template, zigbee_smoke_driver_template.supported_capabilities)
local zigbee_smoke_driver = ZigbeeDriver("zigbee-smoke-detector", zigbee_smoke_driver_template)
zigbee_smoke_driver:run()
zigbee_smoke_driver:run()
Loading
Loading