Smart Alarm Needs a leader

the moment when you realize you gave your address to the ST community :slight_smile:

1 Like

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.

2 Likes

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

  • ([RELEASE] Aeon Labs Multifunction Siren)

  • 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.

1 Like

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.

1 Like

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.

When it comes to writing SmartApps, you’re the man, so if you have the time and want to take this on, go for it.

2 Likes

Hi Kevin,
anything new going on code or feature wise relative to this thread.

Thx

I have some users testing a pre-alpha release of the new SmartApp I’ve been working on. If you’re interested in playing around with it, let me know and I’ll send you the information.

1 Like

Kevin,
I would love to take a look, thanks in advance.

Bob

I don’t understand what smart alarm is not able to do? Just use smart alarm.

I have used it for 2 years, works flawlessly.

You are one lucky man. It’s better than SHM but it’s out of sync with mode quite often before and you can’t easily disable the alarm once it’s triggered with the wrong mode. 2 years is a long time with no problem.

What version of smart alarm do you have? I never have a problem with mode. Honestly the wife would have had a regular alarm system long ago if this didn’t work so well. I even have a tablet with smart things and sharp tools that shows mode changes and even that works everytime. I also have a task that fires a “please disarm the alarm” when the garage door is opened in away mode.

FYI, I started writing a zone based SmartApp for myself before this Topic was created so I’m not trying to replace SmartAlarm.

Mine’s something completely different so a lot of users will probably prefer the way SmartAlarm works.

Just realized I have version 2.2.6. Maybe some new bugs were introduced in 2.4.6

Here is the 2.2.6 code give this a shot.

Did updates to SmartAlarm ever get completed?

I created a new SmartApp called Simple Zone Monitor, but most beta testers found it overly complex and then SmartAlarm was going to be updated so I stopped development on it and never posted it to the forum.

I’ve been using it for months and I think some of the beta testers are still using it too, but I’d still consider it a beta version. Now that SmartAlarm is dead again, I’ll probably continue developing it once I have some free time.

5 Likes

Have been playing with it for a couple days, it is a little complicated, but not over the top. I like it and think if you have the free time complete it!

1 Like