Since Geko has stopped development of Smart Alarm as Smart Home Monitor was supposed to be a replacement, but SHM has not been updated for ever and still has no delays or keyboard support. So if someone is willing and able please step up and update SA so it can finally push SHM off the boards as it is a waste of time. ST wants us to buy their premium services and alarm systems so these updates will never happen. If someone from ST wants to chime in with a time when SHM will be a real alarm feel free.
Agreed 100%. My alarm goes off EVERY DAY when we get home because the presence sensors donât trigger quickly enough⌠So the second my wife opens the garage door, WEEEEEEE WOOOOOO BZZZZZZZZZZ and our phones start exploding with notifications. My wife is telling me to get rid of ST because of this⌠So ST - please PLEASE add a delay feature. Otherwise my wife may just start throwing my setup away.
Iâm not looking to be a leader or attempt to create a replacement for Smart Alarm, but if you post the features youâre looking for, I might be able to work them into the security related SmartApp Iâm writing for myself.
Second or triple that - need someone to update and add some features would be great! This is like the foundation and why most would invest in ST.
I can help too, if you guys need my help.
Thanks for the offer to help.
The things I think are essential are.
- Exit and entry delays
- Use of a keypad such as the IRIS Centralite
- Possible of siren beeping as I have now when entering and exiting to warn that alarm is armed or will be armed aftwer the delay.
- We also need a graphical display of alarm status that is in sync with modes⌠Such as Away, Armed Stay and Alarm off.
Those are the first things we need.
and a way to manually arm and disarm from the phone when it gets out of sync with ST Modes
I thought Smart Alarm already had entry/exit delays?
this is already built in to smart alarm today
I think some folks are doing this now but it makes sense to integrate it
would be great to have this integrated as well since we currently need to use CoRE to achieve this.
you might want to check out the smart app in this discussion, it lets you sync Smart Alarm status with SHM status. I use this and it works great, only issue is the multiple notifications but its no big deal when you got entry/exit delays working.
I am already using that version but when I arm with IRIS keypad the state isnât correct all the time so I use another graphical app to show status. called âlgkapps : Smart Alarm Notification Deviceâ
If youâre looking to simulate a beep by turning on and off the siren quickly, that wonât be possible because the commands canât be sent quick enough from a SmartApp. You can do it from a DTH, but it works better with some devices than others.
Already have it working using rule Machine.
the moment when you realize you gave your address to the ST community
Which siren and what DTH are you using?
Thanks, I just answered from my email. That is something I usually donât do. I am not thinking right.
I am using the Aeon v5 siren and and âsmartthings : Z-Wave Switch with Flashâ device type for flash of switch and âkrlaframboise : Aeon Labs Multifunction Sirenâ that gives me the number of beeps.
This is the switch DTH code:
indent preformatted text by 4 spaces
/**
- Copyright 2015 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.
*/
metadata {
definition (name: âZ-Wave Switch with Flashâ, namespace: âsmartthingsâ, author: âSmartThingsâ) {
capability "Actuator"
capability "Indicator"
capability "Switch"
capability "Polling"
capability "Refresh"
capability âSensorâ
command "flash"
fingerprint inClusters: "0x25"
}
// simulator metadata
simulator {
status "on": "command: 2003, payload: FF"
status "off": "command: 2003, payload: 00"
// reply messages
reply "2001FF,delay 100,2502": "command: 2503, payload: FF"
reply "200100,delay 100,2502": "command: 2503, payload: 00"
}
// tile definitions
tiles(scale: 2) {
multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
attributeState "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821"
attributeState "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
}
}
standardTile("indicator", "device.indicatorStatus", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
state "when off", action:"indicator.indicatorWhenOn", icon:"st.indicators.lit-when-off"
state "when on", action:"indicator.indicatorNever", icon:"st.indicators.lit-when-on"
state "never", action:"indicator.indicatorWhenOff", icon:"st.indicators.never-lit"
}
standardTile("refresh", "device.switch", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
}
main "switch"
details(["switch","refresh","indicator"])
}
}
def parse(String description) {
def result = null
def cmd = zwave.parse(description, [0x20: 1, 0x70: 1])
if (cmd) {
result = createEvent(zwaveEvent(cmd))
}
if (result?.name == âhailâ && hubFirmwareLessThan(â000.011.00602â)) {
result = [result, response(zwave.basicV1.basicGet())]
log.debug âWas hailed: requesting state updateâ
} else {
log.debug âParse returned ${result?.descriptionText}â
}
return result
}
def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) {
[name: âswitchâ, value: cmd.value ? âonâ : âoffâ, type: âphysicalâ]
}
def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd) {
[name: âswitchâ, value: cmd.value ? âonâ : âoffâ, type: âphysicalâ]
}
def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd) {
[name: âswitchâ, value: cmd.value ? âonâ : âoffâ, type: âdigitalâ]
}
def zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport cmd) {
def value = "when off"
if (cmd.configurationValue[0] == 1) {value = âwhen onâ}
if (cmd.configurationValue[0] == 2) {value = âneverâ}
[name: âindicatorStatusâ, value: value, display: false]
}
def zwaveEvent(physicalgraph.zwave.commands.hailv1.Hail cmd) {
[name: âhailâ, value: âhailâ, descriptionText: âSwitch button was pressedâ, displayed: false]
}
def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) {
if (state.manufacturer != cmd.manufacturerName) {
updateDataValue(âmanufacturerâ, cmd.manufacturerName)
}
}
def zwaveEvent(physicalgraph.zwave.Command cmd) {
// Handles all Z-Wave commands we arenât interested in
[:]
}
def on() {
delayBetween([
zwave.basicV1.basicSet(value: 0xFF).format(),
zwave.switchBinaryV1.switchBinaryGet().format()
])
}
def off() {
delayBetween([
zwave.basicV1.basicSet(value: 0x00).format(),
zwave.switchBinaryV1.switchBinaryGet().format()
])
}
def flash() {
delayBetween ([
delayBetween ([ zwave.basicV1.basicSet(value: 0xFF).format(),
zwave.basicV1.basicSet(value: 0x00).format()], 1000),
delayBetween ([ zwave.basicV1.basicSet(value: 0xFF).format(),
zwave.basicV1.basicSet(value: 0x00).format()], 1000),
delayBetween ([ zwave.basicV1.basicSet(value: 0xFF).format(),
zwave.basicV1.basicSet(value: 0x00).format()], 1000),
delayBetween ([ zwave.basicV1.basicSet(value: 0xFF).format(),
zwave.basicV1.basicSet(value: 0x00).format()], 1000),
zwave.switchBinaryV1.switchBinaryGet().format()], 2000)
}
def poll() {
delayBetween([
zwave.switchBinaryV1.switchBinaryGet().format(),
zwave.manufacturerSpecificV1.manufacturerSpecificGet().format()
])
}
def refresh() {
delayBetween([
zwave.switchBinaryV1.switchBinaryGet().format(),
zwave.manufacturerSpecificV1.manufacturerSpecificGet().format()
])
}
def indicatorWhenOn() {
sendEvent(name: âindicatorStatusâ, value: âwhen onâ, display: false)
zwave.configurationV1.configurationSet(configurationValue: [1], parameterNumber: 3, size: 1).format()
}
def indicatorWhenOff() {
sendEvent(name: âindicatorStatusâ, value: âwhen offâ, display: false)
zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 3, size: 1).format()
}
def indicatorNever() {
sendEvent(name: âindicatorStatusâ, value: âneverâ, display: false)
zwave.configurationV1.configurationSet(configurationValue: [2], parameterNumber: 3, size: 1).format()
}
def invertSwitch(invert=true) {
if (invert) {
zwave.configurationV1.configurationSet(configurationValue: [1], parameterNumber: 4, size: 1).format()
}
else {
zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 4, size: 1).format()
}
}
This is the Siren code:
/**
-
Aeoc Labs Multifunction Siren v 1.4
-
Capabilities:
-
Switch, Alarm, Tone, Music Player
-
Author:
-
Kevin LaFramboise (krlaframboise)
-
Changelog:
-
1.4 (03/05/2016)
-
- Enhanced Logging
-
- Fixed bug with beep schedule cancellation.
-
1.3 (03/03/2016)
-
- Added startBeepDelayedAlarm command.
-
- Fixed validation logging bug.
-
1.2 (02/29/2016)
-
- Fixed IOS UI issue with beep buttons.
-
- Added Music Player capability.
-
- Added TTS command support so that it can be used
-
with SHM, Notify with Sound, and Rule Machine
-
- Added alarm delay feature.
-
1.1 (02/28/2016)
-
- Logging Enhancements.
-
1.0 (02/28/2016)
-
- Initial Release
-
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.
*/
metadata {
definition (name: âAeon Labs Multifunction Sirenâ, namespace: âkrlaframboiseâ, author: âKevin LaFramboiseâ) {
capability "Actuator"
capability "Switch"
capability "Alarm"
capability "Tone"
capability "Configuration"
capability âMusic Playerâattribute âstatusâ, âenumâ, [âoffâ, âalarmâ, âcustomAlarmâ, âdelayedAlarmâ, âbeepDelayedAlarmâ, âbeepâ, âbeepScheduleâ, âcustomBeepâ, âcustomBeepScheduleâ]
command âcustomAlarmâ, [ânumberâ, ânumberâ, ânumberâ]
command âdelayedAlarmâ, [ânumberâ, ânumberâ, ânumberâ, ânumberâ]
command âcustomBeepâ, [ânumberâ, ânumberâ, ânumberâ, ânumberâ, ânumberâ]
command "startBeep"
command "startBeepDelayedAlarm"
command âstartCustomBeepâ, [ânumberâ, ânumberâ, ânumberâ, ânumberâ, ânumberâ, ânumberâ, ânumberâ]command "customBeep1"
command "customBeep2"
command "customBeep3"
command "customBeep4"
command "customBeep5"
command âcustomBeep6â// Music and Sonos Related Commands
command "playSoundAndTrack"
command "playTrackAndRestore"
command "playTrackAndResume"
command "playTextAndResume"
command âplayTextAndRestoreâfingerprint deviceId: â0x1005â, inClusters: â0x5E,0x98,0x25,0x70,0x85,0x59,0x72,0x2B,0x2C,0x86,0x7A,0x73â, outClusters: â0x5A,0x82â
}
simulator {
reply â9881002001FF,9881002002â: "command: 9881, payload: 002003FF"
reply â988100200100,9881002002â: âcommand: 9881, payload: 00200300â
}
preferences {
input "sirenSound", "number",
title: "Siren Sound (1-5)",
defaultValue: 1,
range: "1..5",
displayDuringSetup: true,
required: false
input "sirenVolume", "number",
title: "Siren Volume (1-3)",
defaultValue: 1,
range: "1..3",
displayDuringSetup: true,
required: false
input "alarmDuration", "number",
title: "Turn siren off after: (seconds)",
defaultValue: 0,
displayDuringSetup: true,
required: false
input "beepSound", "number",
title: "Beep Sound (1-5)",
defaultValue: 3,
range: "1..5",
displayDuringSetup: false,
required: false
input "beepVolume", "number",
title: "Beep Volume (1-3)",
defaultValue: 1,
range: "1..3",
displayDuringSetup: false,
required: false
input "beepRepeat", "number",
title: "Beep Repeat (1-100)",
defaultValue: 1,
range: "1..100",
displayDuringSetup: false,
required: false
input "beepRepeatDelay", "number",
title: "Time Between Beeps in Milliseconds",
defaultValue: 1000,
displayDuringSetup: false,
required: false
input "beepLength", "number",
title: "Length of Beep in Milliseconds",
defaultValue: 100,
displayDuringSetup: false,
required: false
input "beepEvery", "number",
title: "Scheduled Beep Every (seconds)",
defaultValue: 10,
displayDuringSetup: false,
required: false
input "beepStopAfter", "number",
title: "Stop Scheduled Beep After (seconds)",
defaultValue: 60,
displayDuringSetup: false,
required: false
input "useBeepDelayedAlarm", "bool",
title: "Play Beep Schedule Before Sounding Alarm?",
defaultValue: false,
displayDuringSetup: false,
required: false
input "debugOutput", "bool",
title: "Enable debug logging?",
defaultValue: true,
displayDuringSetup: false,
required: false
}
tiles(scale: 2) {
multiAttributeTile(name:"status", type: "generic", width: 6, height: 3, canChangeIcon: true){
tileAttribute ("status", key: "PRIMARY_CONTROL") {
attributeState "off", label:'off', action: "off", icon:"st.alarm.alarm.alarm", backgroundColor:"#ffffff"
attributeState "alarm", label:'Alarm Sounding!', action: "off", icon:"st.alarm.alarm.alarm", backgroundColor:"#ff9999"
attributeState "customAlarm", label:'Custom Alarm Sounding!', action: "off", icon:"", backgroundColor:"#ff9999"
attributeState "delayedAlarm", label:'Delayed Alarm Active!', action: "off", icon:"", backgroundColor:"#ff9999"
attributeState "beepDelayedAlarm", label:'Beep Delayed Alarm Active!', action: "off", icon:"", backgroundColor:"#ff9999"
attributeState "beep", label:'Beeping!', action: "off", icon:"st.Entertainment.entertainment2", backgroundColor:"#99FF99"
attributeState "beepSchedule", label:'Scheduled\nBeeping!', action: "off", icon:"", backgroundColor:"#99FF99"
attributeState "customBeep", label:'Custom Beeping!', action: "off", icon:"", backgroundColor:"#694489"
attributeState "customBeepSchedule", label:'Scheduled Custom Beeping!', action: "off", icon:"", backgroundColor:"#694489"
}
}
standardTile("playAlarm", "device.alarm", label: 'Alarm', width: 2, height: 2) {
state "default", label:'Alarm', action: "both", icon:"st.alarm.alarm.alarm", backgroundColor: "#ff9999"
state "both", label:'Stop', action: "off", icon:"st.alarm.alarm.alarm", backgroundColor: "#ffffff"
}
standardTile("playBeep", "device.status", label: 'Beep', width: 2, height: 2) {
state "default", label:'Beep', action:"beep", icon:"st.Entertainment.entertainment2", backgroundColor: "#99FF99"
}
valueTile("playBeepSchedule", "device.status", label: 'Scheduled Beep', width: 2, height: 2) {
state "default", label:'Start\nBeep', action:"startBeep",backgroundColor: "#99FF99"
state "beepSchedule", label:'Stop\nBeep', action:"off", icon: "", backgroundColor: "#ffffff"
}
valueTile("playCustomBeep1", "device.status", label: 'Custom Beep 1', width: 2, height: 2, wordWrap: true) {
state "default", label:'Beep\n1', action:"customBeep1",backgroundColor: "#694489"
}
valueTile("playCustomBeep2", "device.status", label: 'Custom Beep 2', width: 2, height: 2, wordWrap: true) {
state "default", label:'Beep\n2', action:"customBeep2",backgroundColor: "#694489"
}
valueTile("playCustomBeep3", "device.status", label: 'Custom Beep 3', width: 2, height: 2, wordWrap: true) {
state "default", label:'Beep\n3', action:"customBeep3",backgroundColor: "#694489"
}
valueTile("playCustomBeep4", "device.status", label: 'Custom Beep 4', width: 2, height: 2, wordWrap: true) {
state "default", label:'Beep\n4', action:"customBeep4",backgroundColor: "#694489"
}
valueTile("playCustomBeep5", "device.status", label: 'Custom Beep 5', width: 2, height: 2, wordWrap: true) {
state "default", label:'Beep\n5', action:"customBeep5",backgroundColor: "#694489"
}
valueTile("playCustomBeep6", "device.status", label: 'Custom Beep 6', width: 2, height: 2, wordWrap: true) {
state "default", label:'Beep\n6', action:"customBeep6",backgroundColor: "#694489"
}
main "status"
details(["status", "playAlarm", "playBeep", "playBeepSchedule", "playCustomBeep1", "playCustomBeep2", "playCustomBeep3", "playCustomBeep4", "playCustomBeep5", "playCustomBeep6"])
}
}
// Unsuported Music Player commands
def unmute() { handleUnsupportedCommand(âunmuteâ) }
def resumeTrack(map) { handleUnsupportedCommand(âresumeTrackâ) }
def restoreTrack(map) { handleUnsupportedCommand(ârestoreTrackâ) }
def nextTrack() { handleUnsupportedCommand(ânextTrackâ) }
def setLevel(number) { handleUnsupportedCommand(âsetLevelâ) }
def previousTrack() { handleUnsupportedCommand(âpreviousTrackâ) }
def setTrack(string) { handleUnsupportedCommand(âsetTrackâ) }
// Turns siren off
def pause() { off() }
def stop() { off() }
def mute() { off() }
// Turns siren on
def play() { both() }
// Commands necessary for SHM, Notify w/ Sound, and Rule Machine TTS functionality.
def playSoundAndTrack(text, other, other2, other3) {
playTrackAndResume(text, other, other2)
}
def playTrackAndRestore(text, other, other2) {
playTrackAndResume(text, other, other2)
}
def playTrackAndResume(text, other, other2) {
playText(getTextFromTTSUrl(text))
}
def getTextFromTTSUrl(ttsUrl) {
def urlPrefix = âhttps://s3.amazonaws.com/smartapp-media/tts/â
if (ttsUrl?.toString()?.toLowerCase()?.contains(urlPrefix)) {
return ttsUrl.replace(urlPrefix,ââ).replace(".mp3","")
}
return ttsUrl
}
def playTextAndResume(text, other) { playText(text) }
def playTextAndRestore(text, other) { playText(text) }
def playTrack(text) { playText(text) }
def playText(text) {
text = cleanTextCmd(text)
def cmds
switch (text) {
case [âsirenâ, âstrobeâ, âbothâ, âonâ, âplayâ]:
cmds = both()
break
case [âstopâ, âoffâ, âpauseâ, âmuteâ]:
cmds = off()
break
case âbeepâ:
cmds = beep()
break
case âstartbeepâ:
cmds = startBeep()
break
case âstartbeepdelayedalarmâ:
cmds = startBeepDelayedAlarm()
break
case âcustombeep1â:
cmds = customBeep1()
break
case âcustombeep2â:
cmds = customBeep2()
break
case âcustombeep3â:
cmds = customBeep3()
break
case âcustombeep4â:
cmds = customBeep4()
break
case âcustombeep5â:
cmds = customBeep5()
break
case âcustombeep6â:
cmds = customBeep6()
break
default:
if (text) {
cmds = parseComplexCommand(text)
}
}
if (!cmds) {
logDebug ââ$textâ is not a valid command.â
}
else {
return cmds
}
}
def cleanTextCmd(text) {
return text?.
toLowerCase()?.
replace(",", â_â)?.
replace(" â, ââ)?.
replace(â(", ââ)?.
replace(")", ââ)
}
def parseComplexCommand(text) {
def cmds = []
def args = getComplexCmdArgs(text)
switch (args?.size()) {
case 3:
cmds += customAlarm(
args[0],
args[1],
args[2])
break
case 4:
cmds += delayedAlarm(
args[0],
args[1],
args[2],
args[3])
break
case 5:
cmds += customBeep(
args[0],
args[1],
args[2],
args[3],
args[4])
break
case 7:
cmds += startCustomBeep(
args[0],
args[1],
args[2],
args[3],
args[4],
args[5],
args[6])
break
}
return cmds
}
private getComplexCmdArgs(text) {
for (prefix in [âcustomalarmâ, âdelayedalarmâ,âstartcustombeepâ,âcustombeepâ]) {
text = removeCmdPrefix(text, prefix)
}
def args = text.tokenize("_")
if (args.every { node -> isNumeric(node) }) {
return args
}
else {
return null
}
}
private removeCmdPrefix(text, prefix) {
if (text.startsWith(prefix)) {
return text.replace("$prefix_", ââ).replace(prefix, ââ)
}
else {
return text
}
}
// Turns on siren and strobe
def on() {
both()
}
// Turns off siren and strobe
def off() {
logDebug "Executing off() command"
changeStatus(âoffâ)
turnOff()
}
private turnOff() {
secureDelayBetween([
switchOffSetCmd(),
switchGetCmd()
])
}
// Turns on siren and strobe
def strobe() {
both()
}
// Turns on siren and strobe
def siren() {
both()
}
// Turns on siren and strobe
def both() {
logDebug âExecuting both() commandâ
if (settings.useBeepDelayedAlarm) {
startBeepDelayedAlarm()
}
else {
changeStatus("alarm")
playDefaultAlarm()
}
}
// Repeatedly plays the default beep based on the beepEvery and beepStopAfter settings and then turns on alarm.
def startBeepDelayedAlarm() {
changeStatus(âbeepDelayedAlarmâ)
startDefaultBeepSchedule()
}
private playPendingAlarm() {
state.alarmPending = false
if (state.scheduledAlarm) {
def sound = state.scheduledAlarm?.sound
def volume = state.scheduledAlarm?.volume
def duration = state.scheduledAlarm?.duration
state.scheduledAlarm = null
customAlarm(sound, volume, duration)
}
else {
playDefaultAlarm()
}
}
private playDefaultAlarm() {
playAlarm(settings.sirenSound, settings.sirenVolume, settings.alarmDuration)
}
// Plays sound at volume for duration.
def customAlarm(sound, volume, duration) {
changeStatus(âcustomAlarmâ)
playAlarm(sound, volume, duration)
}
private playAlarm(sound, volume, duration) {
def durationMsg = (duration && duration > 0) ? â, duration: $durationâ : ""
logDebug âSounding Alarm: [sound: $sound, volume: $volume$durationMsg]â
sound = validateSound(sound)
volume = validateVolume(volume)
duration = validateRange(duration, 0, 0, Integer.MAX_VALUE, "Alarm Duration")
if (currentStatus() in ["alarm", "delayedAlarm", "beepDelayedAlarm"]) {
sendEvent(name:"alarm", value: "both", isStateChange: true)
sendEvent(name:"switch", value: "on", isStateChange: true, displayed: false)
}
def cmds = []
cmds << secureCmd(sirenSoundVolumeSetCmd(sound, volume))
if (duration > 0) {
cmds << "delay ${duration * 1000}"
cmds += turnOff()
}
return cmds
}
def delayedAlarm(sound, volume, duration, delay) {
changeStatus(âdelayedAlarmâ)
startDelayedAlarm(sound, volume, duration, delay)
}
private startDelayedAlarm(sound, volume, duration, delay) {
state.scheduledAlarm = [
âsoundâ: sound,
âvolumeâ: volume,
âdurationâ: duration
]
delay = validateRange(delay, 3, 1, Integer.MAX_VALUE, "delay")
logDebug "Starting ${currentStatus()} [sound: $sound, volume: $volume, duration: $duration, delay: $delay]"
[
"delay ${delay * 1000}",
secureCmd(zwave.basicV1.basicGet())
]
}
// Plays the default beep.
def beep() {
changeStatus(âbeepâ)
playDefaultBeep()
}
private playDefaultBeep() {
playBeep(
settings.beepSound,
settings.beepVolume,
settings.beepRepeat,
settings.beepRepeatDelay,
settings.beepLength
)
}
// Plays short beep.
def customBeep1() {
customBeep(3, 1, 1, 0, 50)
}
// Plays medium beep
def customBeep2() {
customBeep(3, 1, 1, 0, 100)
}
// Plays long beep
def customBeep3() {
customBeep(3, 1, 1, 0, 250)
}
// Plays 3 short beeps
def customBeep4() {
customBeep(3, 1, 3, 0, 50)
}
// Plays 3 medium beeps
def customBeep5() {
customBeep(3, 1, 3, 100 , 100)
}
// Plays 3 long beeps
def customBeep6() {
customBeep(3, 1, 3, 150, 200)
}
// Repeatedly plays the default beep based on the beepEvery and beepStopAfter settings.
def startBeep() {
changeStatus(âbeepScheduleâ)
startDefaultBeepSchedule()
}
private startDefaultBeepSchedule() {
startBeepSchedule(
settings.beepEvery,
settings.beepStopAfter,
settings.beepSound,
settings.beepVolume,
settings.beepRepeat,
settings.beepRepeatDelay,
settings.beepLength
)
}
// Repeatedly plays specified beep at specified in specified intervals.
def startCustomBeep(beepEverySeconds, stopAfterSeconds, sound, volume, repeat=1, repeatDelayMS=1000, beepLengthMS=100) {
changeStatus(âcustomBeepScheduleâ)
startBeepSchedule(beepEverySeconds, stopAfterSeconds, sound, volume, repeat, repeatDelayMS, beepLengthMS)
}
private startBeepSchedule(beepEverySeconds, stopAfterSeconds, sound, volume, repeat, repeatDelayMS, beepLengthMS) {
logDebug âStarting ${currentStatus()} [beepEverySeconds: $beepEverySeconds, stopAfterSeconds: $stopAfterSeconds]â
state.beepSchedule = [
"startTime": (new Date().time),
"beepEvery": validateBeepEvery(beepEverySeconds),
"stopAfter": validateBeepStopAfter(stopAfterSeconds),
"sound": sound,
"volume": volume,
"repeat": repeat,
"repeatDelay": repeatDelayMS,
"beepLength": beepLengthMS
]
return playScheduledBeep()
}
private playScheduledBeep() {
def beepSchedule = state.beepSchedule
def cmds = []
if (beepScheduleStillActive(beepSchedule?.startTime, beepSchedule?.stopAfter)) {
cmds += playBeep(
beepSchedule.sound,
beepSchedule.volume,
beepSchedule.repeat,
beepSchedule.repeatDelay,
beepSchedule.beepLength
)
}
if (nextScheduledBeepStillActive()) {
if (beepSchedule.beepEvery > 0) {
cmds << "delay ${beepSchedule.beepEvery * 1000}"
}
cmds << secureCmd(zwave.basicV1.basicGet())
}
else {
state.beepSchedule = null
state.beepScheduleRunning = false
if (state.alarmPending) {
cmds += playPendingAlarm()
}
else {
cmds += turnOff()
}
}
return cmds
}
private nextScheduledBeepStillActive() {
def sched = state.beepSchedule
if (sched?.beepEvery != null) {
def adjustedStartTime = (sched.startTime - (sched.beepEvery * 1000))
return beepScheduleStillActive(adjustedStartTime, sched.stopAfter)
}
else {
return false
}
}
private beepScheduleStillActive(startTime, stopAfter) {
if (startTime && stopAfter) {
def endTimeMS = startTime + (stopAfter * 1000)
return (new Date().time < endTimeMS) && state.beepScheduleRunning
}
else {
return false
}
}
// Plays specified beep.
def customBeep(sound, volume, repeat=1, repeatDelayMS=1000, beepLengthMS=100) {
changeStatus(âcustomBeepâ)
playBeep(sound, volume, repeat, repeatDelayMS, beepLengthMS)
}
private playBeep(sound, volume, repeat, repeatDelayMS, beepLengthMS) {
logDebug â${currentStatus()} [sound: $sound, volume: $volume, repeat: $repeat, repeatDelayMS: $repeatDelayMS, beepLengthMS: $beepLengthMS]â
int maxMS = 18000
sound = validateSound(sound, 3)
volume = validateVolume(volume)
beepLengthMS = validateRange(beepLengthMS, 100, 0, maxMS, "Beep Length")
repeatDelayMS = validateRepeatDelay(repeatDelayMS, beepLengthMS, maxMS)
repeat = validateRepeat(repeat, beepLengthMS, repeatDelayMS, maxMS)
def cmds = []
for (int repeatIndex = 1; repeatIndex <= repeat; repeatIndex++) {
cmds << secureCmd(sirenSoundVolumeSetCmd(sound, volume))
if (beepLengthMS > 0) {
cmds << "delay $beepLengthMS"
}
cmds << secureCmd(switchOffSetCmd())
if (repeat > 1 && repeatDelayMS > 0) {
cmds << "delay $repeatDelayMS"
}
}
if (!state.beepScheduleRunning && !state.alarmPending && currentStatus() != "off") {
cmds << turnOff()
}
return cmds
}
private changeStatus(newStatus) {
def oldStatus = currentStatus()
finalizeOldStatus(oldStatus, newStatus)
if (newStatus in ["delayedAlarm", "beepDelayedAlarm"]) {
state.alarmPending = true
}
if (newStatus in ["beepDelayedAlarm", "beepSchedule", "customBeepSchedule"]) {
state.beepScheduleRunning = true
}
def displayStatus = (
oldStatus != newStatus &&
newStatus != "alarm" &&
!(oldStatus in ["alarm", "delayedAlarm", "beepDelayedAlarm"]))
sendEvent(name:"status", value: newStatus, isStateChange: true, displayed: displayStatus)
}
private finalizeOldStatus(oldStatus, newStatus) {
if (state.alarmPending &&
oldStatus in [âdelayedAlarmâ, âbeepDelayedAlarmâ] &&
!(newStatus in [âalarmâ, âcustomAlarmâ])) {
logDebug âDelayed Alarm Cancelledâ
}
else if (state.beepScheduleRunning) {
if (nextScheduledBeepStillActive()) {
logDebug âBeep Schedule Cancelledâ
}
else {
logDebug âBeep Schedule Completedâ
}
}
state.alarmPending = false
state.beepScheduleRunning = false
state.scheduledAlarm = null
state.beepSchedule = null
}
// Checks if the device supports security commands.
def configure() {
logDebug "Checking for secure inclusion"
state.useSecureCommands = null
def cmds = secureDelayBetween([
supportedSecurityGetCmd(),
sendNotificationsSetCmd()
])
state.useSecureCommands = false
response(cmds)
}
// Stores preferences and displays device settings.
def updated() {
if (!isDuplicateCommand(state.lastUpdated, 1000)) {
state.lastUpdated = new Date().time
state.debugOutput = validateBool(debugOutput, true)
logDebug âUpdatingâ
def cmds = []
if (!state.useSecureCommands) {
logDebug "Checking for Secure Command Support"
state.useSecureCommands = null
cmds << secureCmd(supportedSecurityGetCmd())
}
cmds << secureCmd(zwave.firmwareUpdateMdV2.firmwareMdGet())
cmds += turnOff()
response(cmds)
}
}
private isDuplicateCommand(lastExecuted, allowedMil) {
!lastExecuted ? false : (lastExecuted + allowedMil > new Date().time)
}
private sirenSoundVolumeSetCmd(int sound, int volume) {
zwave.configurationV1.configurationSet(parameterNumber: 37, size: 2, configurationValue: [validateSound(sound), validateVolume(volume)])
}
private sendNotificationsSetCmd() {
zwave.configurationV1.configurationSet(parameterNumber: 80, size: 1, scaledConfigurationValue: 0)
}
private switchOffSetCmd() {
zwave.switchBinaryV1.switchBinarySet(switchValue: 0)
}
private switchGetCmd() {
zwave.switchBinaryV1.switchBinaryGet()
}
private supportedSecurityGetCmd() {
zwave.securityV1.securityCommandsSupportedGet()
}
// Parses incoming message warns if not paired securely
def parse(String description) {
def result = null
if (description.startsWith(âErr 106â)) {
state.useSecureCommands = false
def msg = "Secure Inclusion Failed. You may need to remove and add the device again while pushing the action button repeatedly during the Inclusion process."
log.warn "$msg"
result = createEvent( name: âsecureInclusionâ, value: âfailedâ, isStateChange: true, descriptionText: â$msgâ)
}
else if (description != null && description != âupdatedâ) {
def cmd = zwave.parse(description, [0x98: 1, 0x20: 1, 0x70: 1, 0x7A: 2, 0x25: 1])
if (cmd != null) {
result = zwaveEvent(cmd)
}
}
result
}
// Unencapsulates the secure command.
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
if (cmd != null) {
def encapCmd = cmd.encapsulatedCommand([0x20: 1, 0x85: 2, 0x70: 1, 0x7A: 2, 0x25: 1])
if (encapCmd) {
zwaveEvent(encapCmd)
}
}
}
// Enables secure command setting.
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityCommandsSupportedReport cmd) {
state.useSecureCommands = true
logInfo âSecure Commands Supportedâ
}
// Writes firmware to the Info Log.
def zwaveEvent(physicalgraph.zwave.commands.firmwareupdatemdv2.FirmwareMdReport cmd) {
logInfo âFirmware: $cmdâ
}
// Handles device reporting off and alarm turning on.
def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd) {
if (cmd.value == 0) {
changeStatus("off")
def alarmDisplayed = (device.currentValue("alarm") == "both")
if (alarmDisplayed) {
logDebug "Alarm is off"
}
[
createEvent(name:"alarm", value: "off", isStateChange: true, displayed: alarmDisplayed),
createEvent(name:"switch", value: "off", isStateChange: true, displayed: false)
]
}
}
// Handles the scheduling of beeps.
def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) {
[
response(playScheduledBeep())
]
}
// Writes unexpected commands to debug log
def zwaveEvent(physicalgraph.zwave.Command cmd) {
logDebug("$cmd")
createEvent(descriptionText: cmd.toString(), isStateChange: false)
}
private secureDelayBetween(cmds, delay=100) {
delayBetween(cmds.collect{ secureCmd(it) }, delay)
}
private secureCmd(physicalgraph.zwave.Command cmd) {
if (state.useSecureCommands == null || state.useSecureCommands) {
zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format()
}
else {
cmd.format()
}
}
private int validateSound(sound, int defaultSound=1) {
return validateRange(sound, defaultSound, 1, 5, âSoundâ)
}
private int validateVolume(volume, int defaultVolume=1) {
return validateRange(volume, defaultVolume, 1, 3, âVolumeâ)
}
private int validateRepeatDelay(repeatDelayMS, int beepLengthMS, int maxMS) {
int repeatDelayMaxMS = (beepLengthMS == maxMS) ? 0 : (maxMS - beepLengthMS)
return validateRange(repeatDelayMS, 1000, 0, repeatDelayMaxMS, âRepeat Delayâ)
}
private int validateRepeat(repeat, int beepLengthMS, int repeatDelayMS, int maxMS) {
int combinedMS = (beepLengthMS + repeatDelayMS)
int maxRepeat = (combinedMS >= maxMS) ? 0 : (maxMS / combinedMS).toInteger()
return validateRange(repeat, 1, 0, maxRepeat, âRepeatâ)
}
private int validateBeepEvery(seconds) {
validateRange(seconds, 10, 0, Integer.MAX_VALUE, âBeep Everyâ)
}
private int validateBeepStopAfter(seconds) {
validateRange(seconds, 60, 2, Integer.MAX_VALUE, âBeep Stop Afterâ)
}
private int validateRange(val, defaultVal, minVal, maxVal, desc) {
def result
def errorType = null
if (isNumeric(val)) {
result = val.toInteger()
}
else {
errorType = "invalid"
result = defaultVal
}
if (result > maxVal) {
errorType = "too high"
result = maxVal
} else if (result < minVal) {
errorType = "too low"
result = minVal
}
if (errorType) {
logDebug("$desc: $val is $errorType, using $result instead.")
}
result
}
private isNumeric(val) {
return val?.toString()?.isNumber()
}
private validateBool(val, defaulVal) {
if (val == null) {
defaultVal
}
else {
(val == true || val == âtrueâ)
}
}
private currentStatus() {
return device.currentValue(âstatusâ) ? device.currentValue(âstatusâ) : ââ
}
private handleUnsupportedCmd(cmd) {
logDebug âCommand $cmd not supportedâ
}
private logDebug(msg) {
if (state.debugOutput || state.debugOutput == null) {
log.debug â$msgâ
}
}
private logInfo(msg) {
log.info â${device.displayName} - $msgâ
}
That was my point. Itâs possible to build the functionally into the DTH like I did with that one, but if itâs not built into the DTH, you canât replicate the behavior from a SmartApp.
Itâs always better to post a link to the page you got the code from, but if youâre going to paste code, make sure you select it afterwards and use the </> toolbar button to format it.
FYI: You posted a really old version of the Aeon Labs Multifunction Siren DTH so some of the features wonât work with the latest version of CoRE.
Yes of course but my point is that if it was all in SA it would make it a less complicated setup for those that want the alarm and donât have the ability to install loads of device types and DTHâs. My system works as is but it requires three DTH and two or three Smart Apps, which would work much better in SA as one app. SA would just call to the proper DTH like for the flashing Z-Wave switch or the beeping Siren. But I wouldnât need a separate front end and a DTH for the lock.
The Aeon Siren DTH already allows you to use the full beeping functionality including delayed alarm from SHM and Smart Alarm, but only because you can pass the custom commands as plain text.
There is no capability that supports the command âflashâ so the only way to call it is to use a SmartApp like CoRE or Rule Machine because they support custom commands.
A Tone input could be added to the Alarm SmartApp and it could provide a variety of beeping options, but it would only work with DTHs that support the Tone capability because thatâs the only capability with the âbeepâ command.
The 5 DTHs Iâve created support it, but most of the other DTHs Iâve looked at donât so you wouldnât be able to use that feature with them.