Skip to content
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

[frient] Added smart siren #553

Closed
Closed
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
5 changes: 5 additions & 0 deletions drivers/SmartThings/frient-smoke-siren/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
name: 'frient Water leak_Heat_Smoke_Siren'
description: 'frient EDGE driver for Intelligent smoke alarm, Intelligent heat alarm, Water leak detector and Smart Siren'
packageKey: 'frient-smoke-siren'
permissions:
zigbee: {}
11 changes: 11 additions & 0 deletions drivers/SmartThings/frient-smoke-siren/fingerprints.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
zigbeeManufacturer:
- id: "frient/SMSZB-120"
deviceLabel: Intelligent Smoke Alarm
manufacturer: frient A/S
model: SMSZB-120
deviceProfileName: smoke-siren-temperature-battery
- id: "frient/SIRZB-110"
deviceLabel: Smart Siren
manufacturer: frient A/S
model: SIRZB-110
deviceProfileName: siren-battery-source-tamper
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
name: siren-battery-source-tamper
components:
- id: main
capabilities:
- id: alarm
version: 1
- id: tone
version: 1
- id: battery
version: 1
- id: powerSource
version: 1
- id: tamperAlert
version: 1
- id: firmwareUpdate
version: 1
- id: refresh
version: 1
categories:
- name: Siren
metadata:
vid: 656d48d0-0c37-3573-8ba5-03122a5b4c5c
mnmn: SmartThingsCommunity
preferences:
- title: "Alarm sound"
name: warningSound
description: "Type to sound to play when alarm is triggered"
required: false
preferenceType: enumeration
definition:
options:
BURGLAR: "Burglar"
FIRE: "Fire"
EMERGENCY: "Emergency"
POLICE_PANIC: "Panic"
FIRE_PANIC: "Panic Fire"
EMERGENCY_PANIC: "Panic Emergency"
default: "BURGLAR"
- title: "Alarm duration (s)"
name: warningDuration
description: "After how many seconds should the alarm turn off"
required: false
preferenceType: integer
definition:
minimum: 0
maximum: 65534
default: 240
- title: "Find sensor sound"
name: squawkSound
description: "Squawk sound to play when beep tone is triggered"
required: false
preferenceType: enumeration
definition:
options:
SOUND_FOR_SYSTEM_IS_ARMED: "Armed"
SOUND_FOR_SYSTEM_IS_DISARMED: "Disarmed"
default: "SOUND_FOR_SYSTEM_IS_ARMED"
- title: "Volume"
name: warningLevel
description: "Alarm/Beep volume level"
required: false
preferenceType: enumeration
definition:
options:
LOW_LEVEL: "Low"
MEDIUM_LEVEL: "Medium"
HIGH_LEVEL: "High"
VERY_HIGH_LEVEL: "Very High"
default: "VERY_HIGH_LEVEL"

Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: smoke-siren-temperature-battery
components:
- id: main
capabilities:
- id: smokeDetector
version: 1
- id: alarm
version: 1
- id: temperatureMeasurement
version: 1
- id: battery
version: 1
- id: firmwareUpdate
version: 1
- id: refresh
version: 1
categories:
- name: SmokeDetector
metadata:
vid: 45c81942-30bb-38b9-9e66-3343e2a8c330
mnmn: SmartThingsCommunity
preferences:
- preferenceId: tempOffset
explicit: true
- title: "Temperature Sensitivity (°)"
name: temperatureSensitivity
description: "Minimum change in temperature to report"
required: false
preferenceType: number
definition:
minimum: 0.1
maximum: 2.0
default: 1.0
- title: "Alarm duration (s)"
name: warningDuration
description: "After how many seconds should the alarm turn off"
required: false
preferenceType: integer
definition:
minimum: 0
maximum: 65534
default: 240
101 changes: 101 additions & 0 deletions drivers/SmartThings/frient-smoke-siren/src/device_base_functions.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
local capabilities = require "st.capabilities"
local battery_defaults = require "st.zigbee.defaults.battery_defaults"
local cluster_base = require "st.zigbee.cluster_base"
local zcl_clusters = require "st.zigbee.zcl.clusters"
local log = require "log"

local PowerConfiguration = zcl_clusters.PowerConfiguration
local IASZone = zcl_clusters.IASZone
local IASWD = zcl_clusters.IASWD
local TemperatureMeasurement = zcl_clusters.TemperatureMeasurement
local Basic = zcl_clusters.Basic

local FRIENT_DEVICE_FINGERPRINTS = require "device_config"

local BASE_FUNCTIONS = {}

-- Constants
BASE_FUNCTIONS.DEVELCO_MANUFACTURER_CODE = 0x1015
BASE_FUNCTIONS.DEVELCO_BASIC_PRIMARY_SW_VERSION_ATTR = 0x8000

BASE_FUNCTIONS.SIREN_ENDIAN = "siren_endian"
BASE_FUNCTIONS.PRIMARY_SW_VERSION = "primary_sw_version"

BASE_FUNCTIONS.ALARM_COMMAND = "alarmCommand"
BASE_FUNCTIONS.ALARM_LAST_DURATION = "lastDuration"
BASE_FUNCTIONS.ALARM_MAX_DURATION = "maxDuration"

BASE_FUNCTIONS.ALARM_DEFAULT_MAX_DURATION = 240

--- @param driver ZigbeeDriver The current driver running containing necessary context for execution
--- @param device st.zigbee.Device The device this message was received from containing identifying information
function BASE_FUNCTIONS.added(driver, device)
for _, fingerprint in ipairs(FRIENT_DEVICE_FINGERPRINTS) do
if device:get_manufacturer() == fingerprint.mfr and device:get_model() == fingerprint.model then
if device:supports_capability(capabilities.tamperAlert) then
device:emit_event(capabilities.tamperAlert.tamper.clear())
end
if device:supports_capability(capabilities.smokeDetector) then
device:emit_event(capabilities.smokeDetector.smoke.clear())
end
if device:supports_capability(capabilities.temperatureAlarm) then
device:emit_event(capabilities.temperatureAlarm.temperatureAlarm.cleared())
end
if device:supports_capability(capabilities.waterSensor) then
device:emit_event(capabilities.waterSensor.water.dry())
end
if device:supports_capability(capabilities.switch) then
device:emit_event(capabilities.switch.switch.off())
end
if device:supports_capability(capabilities.alarm) then
device:emit_event(capabilities.alarm.alarm.off())
device:set_field(BASE_FUNCTIONS.ALARM_MAX_DURATION, BASE_FUNCTIONS.ALARM_DEFAULT_MAX_DURATION, { persist = true })
end

device:send(cluster_base.read_manufacturer_specific_attribute(device, Basic.ID, BASE_FUNCTIONS.DEVELCO_BASIC_PRIMARY_SW_VERSION_ATTR, BASE_FUNCTIONS.DEVELCO_MANUFACTURER_CODE)) -- Read the firmware version
end
end
end

function BASE_FUNCTIONS.init(driver, device)
for _, fingerprint in ipairs(FRIENT_DEVICE_FINGERPRINTS) do
if device:get_manufacturer() == fingerprint.mfr and device:get_model() == fingerprint.model then
if device:supports_capability(capabilities.battery) then
battery_defaults.build_linear_voltage_init(2.3, 3.0)(driver, device)
end
end
end
end

--- @param driver ZigbeeDriver The current driver running containing necessary context for execution
--- @param device st.zigbee.Device The device this message was received from containing identifying information
function BASE_FUNCTIONS.do_refresh(driver, device)
device:refresh()

-- Check if we have the software version
local sw_version = device:get_field(BASE_FUNCTIONS.PRIMARY_SW_VERSION)
if ((sw_version == nil) or (sw_version == "")) then
log.warn("Refresh: Firmware version not detected, checking software version")
device:send(cluster_base.read_manufacturer_specific_attribute(device, Basic.ID, BASE_FUNCTIONS.DEVELCO_BASIC_PRIMARY_SW_VERSION_ATTR, BASE_FUNCTIONS.DEVELCO_MANUFACTURER_CODE))
else
log.trace("Refresh: Firmware version: 0x" .. sw_version)
end
end

--- @param driver ZigbeeDriver The current driver running containing necessary context for execution
--- @param device st.zigbee.Device The device this message was received from containing identifying information
--- @param event string The lifecycle event name
--- @param args table Table containing information relevant to the lifecycle event
function BASE_FUNCTIONS.do_configure(driver, device, event, args)
device:configure()
for _, fingerprint in ipairs(FRIENT_DEVICE_FINGERPRINTS) do
if device:get_manufacturer() == fingerprint.mfr and device:get_model() == fingerprint.model then
if fingerprint.ENDPOINT_SIREN then
device:set_field(BASE_FUNCTIONS.ALARM_MAX_DURATION, device.preferences.warningDuration == nil and BASE_FUNCTIONS.ALARM_DEFAULT_MAX_DURATION or device.preferences.warningDuration, { persist = true })
device:send(IASWD.attributes.MaxDuration:write(device, device.preferences.warningDuration == nil and BASE_FUNCTIONS.ALARM_DEFAULT_MAX_DURATION or device.preferences.warningDuration):to_endpoint(fingerprint.ENDPOINT_SIREN))
end
end
end
end

return BASE_FUNCTIONS
7 changes: 7 additions & 0 deletions drivers/SmartThings/frient-smoke-siren/src/device_config.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
local FRIENT_DEVICE_FINGERPRINTS = {
{ mfr = "frient A/S", model = "SMSZB-120", subdriver = "smoke", ENDPOINT_SIREN = 0x23, ENDPOINT_TEMPERATURE = 0x26,
ENDPOINT_TAMPER = 0x23 }, -- Siren, Temperature, Smoke
{ mfr = "frient A/S", model = "SIRZB-110", subdriver = "siren", ENDPOINT_SIREN = 0x2B, ENDPOINT_TAMPER = 0x2B } -- Siren, Tamper
}

return FRIENT_DEVICE_FINGERPRINTS
100 changes: 100 additions & 0 deletions drivers/SmartThings/frient-smoke-siren/src/frient-siren/init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
local battery_defaults = require "st.zigbee.defaults.battery_defaults"
local zcl_clusters = require "st.zigbee.zcl.clusters"
local capabilities = require "st.capabilities"
local log = require "log"

local IASZone = zcl_clusters.IASZone
local Basic = zcl_clusters.Basic

local FRIENT_DEVICE_FINGERPRINTS = require "device_config"
local BASE_FUNCTIONS = require "device_base_functions"

local SIREN_FIXED_ENDIAN_SW_VERSION = "010903"

--- @param opts table A table containing optional arguments that can be used to determine if something is handleable
--- @param driver ZigbeeDriver The current driver running containing necessary context for execution
--- @param device st.zigbee.Device The device this message was received from containing identifying information
local function can_handle_frient(opts, driver, device, ...)
for _, fingerprint in ipairs(FRIENT_DEVICE_FINGERPRINTS) do
if device:get_manufacturer() == fingerprint.mfr and device:get_model() == fingerprint.model and fingerprint.subdriver == "siren" then
return true
end
end
return false
end

--- @param driver ZigbeeDriver The current driver running containing necessary context for execution
--- @param device st.zigbee.Device The device this message was received from containing identifying information
local function device_init(driver, device)
--battery_defaults.build_linear_voltage_init(3.3, 4.0)(driver, device) -- Update the battery threholds for this device (the device never reaches 4.1)
end

--- @param driver ZigbeeDriver The current driver running containing necessary context for execution
--- @param zone_status st.zigbee.zcl.types.IasZoneStatus 2 byte bitmap zoneStatus attribute value of the IAS Zone cluster
--- @param zigbee_message st.zigbee.ZigbeeMessageRx the full message this report came in
local function generate_event_from_zone_status(driver, device, zone_status, zigbee_message)
device:emit_event_for_endpoint(
zigbee_message.address_header.src_endpoint.value,
zone_status:is_tamper_set() and capabilities.tamperAlert.tamper.detected() or capabilities.tamperAlert.tamper.clear()
)
device:emit_event_for_endpoint(
zigbee_message.address_header.src_endpoint.value,
zone_status:is_ac_mains_fault_set() and capabilities.powerSource.powerSource.battery() or capabilities.powerSource.powerSource.mains()
)
end

--- @param driver ZigbeeDriver The current driver running containing necessary context for execution
--- @param device st.zigbee.Device The device this message was received from containing identifying information
--- @param zone_status st.zigbee.zcl.types.IasZoneStatus the value of the attribute
--- @param zb_rx st.zigbee.ZigbeeMessageRx the full message this report came in
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

--- @param driver ZigbeeDriver The current driver running containing necessary context for execution
--- @param device st.zigbee.Device The device this message was received from containing identifying information
--- @param zb_rx st.zigbee.ZigbeeMessageRx the full message this report came in
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

--- @param driver ZigbeeDriver The current driver running containing necessary context for execution
--- @param device st.zigbee.Device The device this message was received from containing identifying information
--- @param value st.zigbee.data_types.StringABC the value of the Attribute
--- @param zb_rx st.zigbee.ZigbeeMessageRx the full message this report came in
local function primary_sw_version_attr_handler(driver, device, value, zb_rx)
--log.warn("Siren Primary Software Version Attribute report: 0x"..string.format("%x", zb_rx.body.zcl_body.attr_records[1].attr_id.value).."=0x"..value.value)
local primary_sw_version = value.value:gsub('.', function(c) return string.format('%02x', string.byte(c)) end)
log.debug("Siren Primary Software Version firmware: 0x" .. primary_sw_version)
device:set_field(BASE_FUNCTIONS.PRIMARY_SW_VERSION, primary_sw_version, { persist = true })
if (primary_sw_version < SIREN_FIXED_ENDIAN_SW_VERSION) then
log.warn("Device has reverse Siren endian firmware")
device:set_field(BASE_FUNCTIONS.SIREN_ENDIAN, "reverse", { persist = true })
end
end

local frient_siren = {
NAME = "frient Siren",
lifecycle_handlers = {
init = device_init,
},
zigbee_handlers = {
cluster = {
[IASZone.ID] = {
[IASZone.client.commands.ZoneStatusChangeNotification.ID] = ias_zone_status_change_handler
}
},
attr = {
[Basic.ID] = {
[BASE_FUNCTIONS.DEVELCO_BASIC_PRIMARY_SW_VERSION_ATTR] = primary_sw_version_attr_handler,
},
[IASZone.ID] = {
[IASZone.attributes.ZoneStatus.ID] = ias_zone_status_attr_handler
}
}
},
can_handle = can_handle_frient
}

return frient_siren
Loading