Monoprice Z-Wave Smoke Detector (Device Handler)

communitydevices

(Jim W) #1

##Monoprice Z-Wave Smoke Detector##
This is a device type for the Monoprice Z-wave Smoke detector - No Logo.

This device supports four types of events: Smoke detected, smoke cleared, tamper alarm, and low battery alarm. When the TEST button is pressed the smoke detector will report smoke, there is no special code for the test button.

##Capabilities and Attributes##
Capabilities:
capability.smokeDetector
capability.sensor
capability.battery

Attributes:
battery - numeric value with the percent of battery life
smoke - “detected” or "clear"
deviceAlert - “clear”, “tamper”, or “lowbat”

##Download##
The latest code can always be found here: Monoprice Z-Wave Smoke Detector

But in the interest of being easier to find through Google, here is the full source code:

/**
 *  Monoprice Z-Wave Smoke Detector
 *
 *  Copyright 2015 Jim Worley
 *      Updated by Cuboy29: 8/24 - Add ability to change wakeup time.
 *
 *  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.
 *
 *  - Original code by @NoName4444
 *  - Wakeup times and battery reporting fix by @Cuboy29
 *
 */

preferences {
    // manufacturer default wake up is every hour; optionally increase for better battery life
    input "userWakeUpInterval", "number", title: "Wake Up Interval (seconds)", description: "Default 3600 sec (10 minutes - 7 days)", defaultValue: '3600', required: false, displayDuringSetup: true
}

metadata {
    definition (name: "Monoprice Z-Wave Smoke Detector", namespace: "Smartthings", author: "Jim Wolley") {
        capability "Smoke Detector"
        capability "Sensor"
        capability "Battery"

        attribute "alarmState", "string"
        attribute "deviceAlert", "string"
        
        //Support Command Class
        //0x30 Sensor Binary
        //0x71 Alarm/Notification
        //0x72 Manufacture Specific
        //0x86 Version
        //0x85 Association V2
        //0x80 Battery
        //0x84 Wake Up V2

        fingerprint deviceId: "0xA100", inClusters: "0x30,0x71,0x72,0x86,0x85,0x80,0x84"
    }

    simulator { 
        status "smoke/test": "command: 7105, payload: 01 FF 00 FF 01 02 00 00"
        status "clear": "command: 7105, payload: 01 00 00 FF 01 02 00 00"
        status "tamper" : "command: 7105, payload: 01 FF 00 FF 01 FE 00 00"
        status "wakeUp Device" : "command: 8407, payload:"
        status "battery 100%": "command: 8003, payload: 64"
        status "battery 5%": "command: 8003, payload: 05"
    }

    tiles {
        standardTile("smoke", "device.alarmState", width: 2, height: 2) {
            state("clear", label:"clear", icon:"st.alarm.smoke.clear", backgroundColor:"#ffffff")
            state("smoke", label:"SMOKE", icon:"st.alarm.smoke.smoke", backgroundColor:"#e86d13")
            state("tamper", label:"tamper", icon:"st.alarm.smoke.smoke", backgroundColor:"#e86d13")
            state("lowbat", label:"lowbat", icon:"st.alarm.smoke.smoke", backgroundColor:"#e86d13")
        }
        valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat") {
            state "battery", label:'${currentValue}% battery', unit:""
        }

        
        main "smoke"
        details(["smoke", "battery"])
    }
}

// parse events into attributes
def parse(String description) {
    log.debug "Smoke Detector description: $description"
    def results = []
    if (description.startsWith("Err")) {
        results << createEvent(descriptionText:description, displayed:true)
    } else {
        def cmd = zwave.parse(description, [0x71: 2, 0x84: 2, 0x80: 1])
        if (cmd) {
            //log.debug "Smoke Detector CMD: $cmd"
            //log.debug "Smoke Detector CMD properties:  ${cmd.getProperties()}"
            zwaveEvent(cmd, results)
        }
    }
    log.debug "\"$description\" parsed to ${results.inspect()}"
    return results
}

def createSmokeEvents(name, results) {
    if (name == "smoke") {
        // these are displayed:false because the composite event is the one we want to see in the app
        results << createEvent(name: "smoke", value: "detected", descriptionText: "$device.displayName smoke was detected!", displayed: false)
        // This composite event is used for updating the tile
        results << createEvent(name: "alarmState", value: name, descriptionText: text)
    } else if (name == "clear") {
        results << createEvent(name: "smoke", value: "clear", descriptionText: "$device.displayName smoke is clear!", displayed: false)
        results << createEvent(name: "alarmState", value: name, descriptionText: text)
    } else if (name == "tamper"){
        results << createEvent(name: "smoke", value: "tamper", descriptionText: "$device.displayName has been tampered with!", displayed: false)
        results << createEvent(name: "alarmState", value: name, descriptionText: text)
        results << createEvent(name: "deviceAlert",value: name, descriptionText:"$device.displayName has been tampered with!")
    } else if (name == "lowbat"){
        results << createEvent(name: "smoke", value: "lowbat", descriptionText: "$device.displayName has been tampered with!", displayed: false)
        results << createEvent(name: "alarmState", value: name, descriptionText: text)
        results << createEvent(name: "deviceAlert",value: name,descriptionText:"$device.displayName has a low battery!")
    }
}

def zwaveEvent(physicalgraph.zwave.commands.alarmv2.AlarmReport cmd, results) {
    if (cmd.zwaveAlarmEvent == 2){
        createSmokeEvents(cmd.alarmLevel ? "smoke" : "clear", results)
    } else if (cmd.zwaveAlarmEvent == 254){
        createSmokeEvents("tamper", results)
    } else {
        createSmokeEvents("lowbat", results)
    }
}

def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpNotification cmd, results)
{
        createEvent(descriptionText: "${device.displayName} woke up", isStateChange: false)
        def userWake = getUserWakeUp(userWakeUpInterval)
        
        // Only ask for battery if we haven't had a BatteryReport in a while
        if (!state.lastbatt || (new Date().time) - state.lastbatt > 50*60*1000) {
                results << response(zwave.batteryV1.batteryGet())
                results << response("delay 1500")  // leave time for device to respond to batteryGet
        }
        // If user has changed userWakeUpInterval, send the new interval to the device 
        if (state.wakeUpInterval != userWake){
            state.wakeUpInterval = userWake
            log.debug "Setting New WakeUp Interval to: " + state.wakeUpInterval
            results << response(zwave.wakeUpV2.wakeUpIntervalSet(seconds:state.wakeUpInterval, nodeid:zwaveHubNodeId))
            results << response("delay 200")
            results << response(zwave.wakeUpV2.wakeUpIntervalGet())
            results << response("delay 1500")
        }  
        results << new physicalgraph.device.HubAction(zwave.wakeUpV2.wakeUpNoMoreInformation().format())
        results << createEvent(descriptionText: "$device.displayName woke up", isStateChange: false)
        results << createEvent(name: "deviceAlert",value:"clear",descriptionText:"No more alerts are found!")
}

def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd, results) {
    def map = [ name: "battery", unit: "%" ]
    if (cmd.batteryLevel == 0xFF) {
        map.value = 1
        map.descriptionText = "$device.displayName battery is low!"
    } else {
        map.value = cmd.batteryLevel
        results << createEvent(name: "deviceAlert",value:"clear",descriptionText:"Reported Battery Level: $cmd.batteryLevel")
    }
    results << createEvent(map)
}

def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpIntervalCapabilitiesReport cmd, results) {

    def map = [ name: "defaultWakeUpInterval", unit: "seconds" ]
    map.value = cmd.defaultWakeUpIntervalSeconds
    map.displayed = false
    state.defaultWakeUpInterval = cmd.defaultWakeUpIntervalSeconds
    results << createEvent(map)
}

def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpIntervalReport cmd, results) {

    def map = [ name: "reportedWakeUpInterval", unit: "seconds" ]
    map.value = cmd.seconds
    map.displayed = false
    results << createEvent(map)
}

def zwaveEvent(physicalgraph.zwave.Command cmd, results) {
    def event = [ displayed: false ]
    event.linkText = device.label ?: device.name
    event.descriptionText = "$event.linkText: $cmd"
    results << createEvent(event)
}


private getUserWakeUp(userWake) {

    if (!userWake)  { userWake =     '3600' }  // set default 1 hr if no user preference 
    // make sure user setting is within valid range for device 
    if (userWake.toInteger() <       60) { userWake =       '600' }  // 10 minutes - Mininum
    if (userWake.toInteger() > 604800) { userWake = '604800' }  // 1 week - Maximum
    return userWake.toInteger()
}

Monoprice Z-wave Smoke Detector for $15.75
#2

Nicely done… Glad someone else that know more about this stuffs took a stab at it beside me.


#3

Jim,

Are you able to get the battery status report from this code? I am not seeing battery being reported at all.


(Jim W) #4

It should update the battery once the smoke alarm wakes up. Normally it should wake up every once in awhile, but I never figured out what the time schedule for that was.

Could you try holding down the test button for a couple cycles and see if the battery information updates sometime after you let go? I’ll do some testing myself when I get home.


#5

According to the pamphlet that comes with it, it should wake up every hour. I have mine set at 10 minutes by modifying your code a bit.


(John Essey) #6

@noname4444 – Your device type works great, but I cant get the battery stats to show up either. I’ve tested the smoke alarm several times now, even removing the bracket, but I still dont see any status change. Seems to function fine though, just this one issue :smile:

Any chance you might be able to figure out what’s up?


#7

I made some changes that should send battery status.

UPDATE: fixed battery status always being requested with every wakeupnotification event.

/**
 *  Monoprice Z-Wave Smoke Detector
 *
 *  Copyright 2015 Jim Worley
 *		Updated by Cuboy29: 8/24 - Add ability to change wakeup time.
 *
 *  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.
 *
 */

preferences {
    // manufacturer default wake up is every hour; optionally increase for better battery life
    input "userWakeUpInterval", "number", title: "Wake Up Interval (seconds)", description: "Default 3600 sec (10 minutes - 7 days)", defaultValue: '3600', required: false, displayDuringSetup: true
}

metadata {
	definition (name: "Monoprice Z-Wave Smoke Detector", namespace: "Smartthings", author: "Jim Wolley") {
		capability "Smoke Detector"
		capability "Sensor"
		capability "Battery"

		attribute "alarmState", "string"
		attribute "deviceAlert", "string"
        
        //Support Command Class
        //0x30 Sensor Binary
        //0x71 Alarm/Notification
        //0x72 Manufacture Specific
        //0x86 Version
        //0x85 Association V2
        //0x80 Battery
        //0x84 Wake Up V2

		fingerprint deviceId: "0xA100", inClusters: "0x30,0x71,0x72,0x86,0x85,0x80,0x84"
	}

	simulator {	
		status "smoke/test": "command: 7105, payload: 01 FF 00 FF 01 02 00 00"
        status "clear": "command: 7105, payload: 01 00 00 FF 01 02 00 00"
        status "tamper" : "command: 7105, payload: 01 FF 00 FF 01 FE 00 00"
        status "wakeUp Device" : "command: 8407, payload:"
		status "battery 100%": "command: 8003, payload: 64"
		status "battery 5%": "command: 8003, payload: 05"
	}

	tiles {
		standardTile("smoke", "device.alarmState", width: 2, height: 2) {
			state("clear", label:"clear", icon:"st.alarm.smoke.clear", backgroundColor:"#ffffff")
			state("smoke", label:"SMOKE", icon:"st.alarm.smoke.smoke", backgroundColor:"#e86d13")
            state("tamper", label:"tamper", icon:"st.alarm.smoke.smoke", backgroundColor:"#e86d13")
			state("lowbat", label:"lowbat", icon:"st.alarm.smoke.smoke", backgroundColor:"#e86d13")
		}
		valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat") {
			state "battery", label:'${currentValue}% battery', unit:""
		}

        
		main "smoke"
		details(["smoke", "battery"])
	}
}

// parse events into attributes
def parse(String description) {
    log.debug "Smoke Detector description: $description"
	def results = []
	if (description.startsWith("Err")) {
	    results << createEvent(descriptionText:description, displayed:true)
	} else {
		def cmd = zwave.parse(description, [0x71: 2, 0x84: 2, 0x80: 1])
		if (cmd) {
            //log.debug "Smoke Detector CMD: $cmd"
            //log.debug "Smoke Detector CMD properties:  ${cmd.getProperties()}"
			zwaveEvent(cmd, results)
		}
	}
	log.debug "\"$description\" parsed to ${results.inspect()}"
	return results
}

def createSmokeEvents(name, results) {

    if (name == "smoke") {
		// these are displayed:false because the composite event is the one we want to see in the app
		results << createEvent(name: "smoke", value: "detected", descriptionText: "$device.displayName smoke was detected!", displayed: false)
        // This composite event is used for updating the tile
		results << createEvent(name: "alarmState", value: name, descriptionText: text)
	} else if (name == "clear") {
		results << createEvent(name: "smoke", value: "clear", descriptionText: "$device.displayName smoke is clear!", displayed: false)
        results << createEvent(name: "alarmState", value: name, descriptionText: text)
	} else if (name == "tamper"){
    	results << createEvent(name: "smoke", value: "tamper", descriptionText: "$device.displayName has been tampered with!", displayed: false)
        results << createEvent(name: "alarmState", value: name, descriptionText: text)
		results << createEvent(name: "deviceAlert",value: name, descriptionText:"$device.displayName has been tampered with!")
	} else if (name == "lowbat"){
   		results << createEvent(name: "smoke", value: "lowbat", descriptionText: "$device.displayName has been tampered with!", displayed: false)
    	results << createEvent(name: "alarmState", value: name, descriptionText: text)
        results << createEvent(name: "deviceAlert",value: name,descriptionText:"$device.displayName has a low battery!")
	}
}

def zwaveEvent(physicalgraph.zwave.commands.alarmv2.AlarmReport cmd, results) {
	
    if (cmd.zwaveAlarmEvent == 2){
		createSmokeEvents(cmd.alarmLevel ? "smoke" : "clear", results)
    } else if (cmd.zwaveAlarmEvent == 254){
		createSmokeEvents("tamper", results)
    } else {
    	createSmokeEvents("lowbat", results)
    }
}

def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpNotification cmd, results)
{
        createEvent(descriptionText: "${device.displayName} woke up", isStateChange: false)
        def userWake = getUserWakeUp(userWakeUpInterval)
        
        // Only ask for battery if we haven't had a BatteryReport in a while
        if (!state.lastbatt || (new Date().time) - state.lastbatt > 50*60*1000) {
                results << response(zwave.batteryV1.batteryGet())
                results << response("delay 1500")  // leave time for device to respond to batteryGet
        }
        // If user has changed userWakeUpInterval, send the new interval to the device 
    	if (state.wakeUpInterval != userWake){
       		state.wakeUpInterval = userWake
            log.debug "Setting New WakeUp Interval to: " + state.wakeUpInterval
        	results << response(zwave.wakeUpV2.wakeUpIntervalSet(seconds:state.wakeUpInterval, nodeid:zwaveHubNodeId))
        	results << response("delay 200")
        	results << response(zwave.wakeUpV2.wakeUpIntervalGet())
            results << response("delay 1500")
    	}  
        results << new physicalgraph.device.HubAction(zwave.wakeUpV2.wakeUpNoMoreInformation().format())
		results << createEvent(descriptionText: "$device.displayName woke up", isStateChange: false)
		results << createEvent(name: "deviceAlert",value:"clear",descriptionText:"No more alerts are found!")
}

def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd, results) {
	def map = [ name: "battery", unit: "%" ]
	if (cmd.batteryLevel == 0xFF) {
		map.value = 1
		map.descriptionText = "$device.displayName battery is low!"
	} else {
		map.value = cmd.batteryLevel
        results << createEvent(name: "deviceAlert",value:"clear",descriptionText:"Reported Battery Level: $cmd.batteryLevel")
	}
    state.lastbatt = new Date().time
	results << createEvent(map)
}

def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpIntervalCapabilitiesReport cmd, results) {

    def map = [ name: "defaultWakeUpInterval", unit: "seconds" ]
	map.value = cmd.defaultWakeUpIntervalSeconds
	map.displayed = false
	state.defaultWakeUpInterval = cmd.defaultWakeUpIntervalSeconds
    results << createEvent(map)
}

def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpIntervalReport cmd, results) {

	def map = [ name: "reportedWakeUpInterval", unit: "seconds" ]
	map.value = cmd.seconds
	map.displayed = false
    results << createEvent(map)
}

def zwaveEvent(physicalgraph.zwave.Command cmd, results) {
	def event = [ displayed: false ]
	event.linkText = device.label ?: device.name
	event.descriptionText = "$event.linkText: $cmd"
	results << createEvent(event)
}


private getUserWakeUp(userWake) {

    if (!userWake)                       { userWake =     '3600' }  // set default 1 hr if no user preference 
    // make sure user setting is within valid range for device 
    if (userWake.toInteger() <       60) { userWake =       '600' }  // 10 minutes - Mininum
    if (userWake.toInteger() > 604800) { userWake = '604800' }  // 1 week - Maximum
    return userWake.toInteger()
}

Monoprice Zwave Smoke Detector
(John Essey) #8

@cuboy29 – Thanks! That worked nicely!


(John Foliot) #9

Newbie Question: Is there a way of being notified if/when battery level drops below a certain %? For example, I have 3 devices now, and they are reporting different battery levels. I’d love to have a way where, once a device battery level drops below 15% that I get a notification (SMS would do). Thanks in advance


#10

If the battery is low, the sensor will send a low battery alert but I am not sure at what % remaining. I use the smartapp battery monitor to monitor the battery status for all my sensors.


(Adkhkr1) #11

I have 6 monoprice smoke detectors and the status for 5 of them shows tamper. It hasn’t cleared out and its been awhile. Im using the device type in this thread…anyone else have this too?


(Adam Foster) #12

To clear this in the past, just press the test button so the alarm goes off. This cleared it for me


(Jim W) #13

I’ve updated the original post to include @cuboy29’s code and to point to a different github location that is linked to the smarthub IDE gitub.

Thanks cuboy29 for working on the battery code!


#14
/**
 *  Monoprice Z-Wave Smoke Detector
 *
 *  Copyright 2015 Jim Worley
 *		Updated by Cuboy29: 8/24 - Add ability to change wakeup time.
 *
 *  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.
 *
 */

preferences {
    // manufacturer default wake up is every hour; optionally increase for better battery life
    input "userWakeUpInterval", "number", title: "Wake Up Interval (seconds)", description: "Default 3600 sec (10 minutes - 7 days)", defaultValue: '3600', required: false, displayDuringSetup: true
}

metadata {
	definition (name: "Monoprice Z-Wave Smoke Detector With Device Health", namespace: "Smartthings", author: "Jim Wolley") {
		capability "Smoke Detector"
		capability "Sensor"
		capability "Battery"
        capability "Health Check"

		attribute "alarmState", "string"
		attribute "deviceAlert", "string"   
        attribute "lastCheckin", "string"
		attribute "lastTested", "string"
        
		fingerprint deviceId: "0xA100", inClusters: "0x30,0x71,0x72,0x86,0x85,0x80,0x84"
	}

        tiles(scale: 2) {
		multiAttributeTile(name:"smoke", type: "generic", width: 6, height: 4, canChangeIcon: true){
			tileAttribute ("device.smoke", key: "PRIMARY_CONTROL") {
				attributeState "clear", 
					label:'clear', 
					icon:"st.alarm.smoke.clear", 
					backgroundColor:"#79b821"
				attributeState "detected", 
					label:'smoke', 
					icon:"st.alarm.smoke.smoke", 
					backgroundColor:"#e86d13"
                attributeState "tamper", 
					label:'tamper', 
					icon:"st.alarm.smoke.smoke", 
					backgroundColor:"#e86d13"
                attributeState "lowbat", 
					label:'lowbat', 
					icon:"st.alarm.smoke.smoke", 
					backgroundColor:"#e86d13"
			}
		}	
		valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
			state "battery", label:'${currentValue}% battery', unit:""
		}
    		
		valueTile("lastCheckin", "device.lastCheckin", decoration: "flat", width: 2, height: 2){
			state "lastCheckin", label:'Last Checkin\n\n${currentValue}', unit:""
		}
		
		valueTile("lastTested", "device.lastTested", decoration: "flat", width: 2, height: 2){
			state "lastTested", label:'Last Tested\n\n${currentValue}', unit:""
		}
        
		main "smoke"
		details(["smoke", "battery", "lastCheckin", "lastTested"])
	}
}

def updated() {	
	// This method always gets called twice when preferences are saved.
	if (!isDuplicateCommand(state.lastUpdated, 3000)) {		
		state.lastUpdated = new Date().time
		logTrace "updated()"		
		initializeCheckin()
	}	
}

private initializeCheckin() {
	// The device wakes up every 4 hours by default.  Changing the wakeup interval sometimes causes the device to stop responding so I've removed that functionality.  Sets expected interval to 8 hours and 2 minutes which allows it to get skipped once.
	def checkInterval = state.wakeUpInterval
	logTrace "initializeCheckin()"
	sendEvent(name: "checkInterval", value: checkInterval, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
}

// Required for HealthCheck Capability, but doesn't actually do anything because this device sleeps.
def ping() {
	logDebug "ping()"	
}

private createLastCheckinEvent() {
	logDebug "${device.displayName} has checked in!"
	state.lastCheckinTime = new Date().time
	return createEvent(name: "lastCheckin", value: convertToLocalTimeString(new Date()), displayed: false)
}

private convertToLocalTimeString(dt) {
	return dt.format("MM/dd/yyyy hh:mm:ss a", TimeZone.getTimeZone(location.timeZone.ID))
}

// parse events into attributes
def parse(String description) {
    //log.debug "Smoke Detector description: $description"
	def results = []
	if (description.startsWith("Err")) {
	    results << createEvent(descriptionText:description, displayed:true)
	} else {
		def cmd = zwave.parse(description, [0x71: 2, 0x84: 2, 0x80: 1])
		if (cmd) {
            //log.debug "Smoke Detector CMD: $cmd"
            //log.debug "Smoke Detector CMD properties:  ${cmd.getProperties()}"
			zwaveEvent(cmd, results)
		}
	}
	//log.debug "\"$description\" parsed to ${results.inspect()}"
    if (!isDuplicateCommand(state.lastCheckinTime, 60000)) {
    results << createLastCheckinEvent()
	}
	return results
}

def createSmokeEvents(name, results) {

    if (name == "smoke") {
		// these are displayed:false because the composite event is the one we want to see in the app
		results << createEvent(name: "smoke", value: "detected", descriptionText: "$device.displayName smoke was detected!", displayed: false)
        results << createEvent(name: "lastTested", value: convertToLocalTimeString(new Date()), displayed: false, isStateChange: true)
        // This composite event is used for updating the tile
		results << createEvent(name: "alarmState", value: name, descriptionText: "$device.displayName smoke was detected!")
	} else if (name == "clear") {
		results << createEvent(name: "smoke", value: "clear", descriptionText: "$device.displayName smoke is clear!", displayed: false)
        results << createEvent(name: "alarmState", value: name, descriptionText: "$device.displayName smoke is clear!")
	} else if (name == "tamper"){
    	results << createEvent(name: "smoke", value: "tamper", descriptionText: "$device.displayName has been tampered with!", displayed: false)
        results << createEvent(name: "alarmState", value: name, descriptionText: "$device.displayName has been tampered with!")
		results << createEvent(name: "deviceAlert",value: name, descriptionText:"$device.displayName has been tampered with!")
	} else if (name == "lowbat"){
   		results << createEvent(name: "smoke", value: "lowbat", descriptionText: "$device.displayName has been tampered with!", displayed: false)
    	results << createEvent(name: "alarmState", value: name, descriptionText: "$device.displayName has been tampered with!")
        results << createEvent(name: "deviceAlert",value: name,descriptionText:"$device.displayName has a low battery!")
	}
}

def zwaveEvent(physicalgraph.zwave.commands.alarmv2.AlarmReport cmd, results) {
	
    if (cmd.zwaveAlarmEvent == 2){
		createSmokeEvents(cmd.alarmLevel ? "smoke" : "clear", results)   
        log.debug "${device.displayName} detected smoke!"
    } else if (cmd.zwaveAlarmEvent == 254){
		createSmokeEvents("tamper", results)
        log.debug "${device.displayName} was tampered!"
    } else {
    	createSmokeEvents("lowbat", results)
        log.debug "${device.displayName} is low on battery!"
    }
}

def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpNotification cmd, results){

		def userWake = getUserWakeUp(userWakeUpInterval)
         
        log.debug "${device.displayName} Woke Up"
		if (!state.lastbatt || (now() - state.lastbatt) > 50*60*1000) {
        	log.debug "Getting Battery Status from ${device.displayName}"
            results << response(zwave.batteryV1.batteryGet())
            results << response("delay 1500")  // leave time for device to respond to batteryGet
        }
        // If user has changed userWakeUpInterval, send the new interval to the device 
    	if (state.wakeUpInterval != userWake){
       		state.wakeUpInterval = userWake
            log.debug "Setting New WakeUp Interval to: " + state.wakeUpInterval
        	results << response(zwave.wakeUpV2.wakeUpIntervalSet(seconds:state.wakeUpInterval, nodeid:zwaveHubNodeId).format())
        	results << response("delay 200")
        	results << response(zwave.wakeUpV2.wakeUpIntervalGet().format())
            results << response("delay 1500")
    	}  
        results << response(zwave.wakeUpV1.wakeUpNoMoreInformation().format())
        results << createEvent(descriptionText: "${device.displayName} woke up", isStateChange: false)
		results << createEvent(name: "deviceAlert",value:"clear",descriptionText:"No more alerts are found!")
}

def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd, results) {
	def map = [ name: "battery", unit: "%" ]
	if (cmd.batteryLevel == 0xFF) {
		map.value = 1
		map.descriptionText = "$device.displayName battery is low!"
	} else {
		map.value = cmd.batteryLevel
        results << createEvent(name: "deviceAlert",value:"clear",descriptionText:"Reported Battery Level: $cmd.batteryLevel")
	}
    state.lastbatt = new Date().time
	results << createEvent(map)
}

def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpIntervalCapabilitiesReport cmd, results) {

    def map = [ name: "defaultWakeUpInterval", unit: "seconds" ]
	map.value = cmd.defaultWakeUpIntervalSeconds
	map.displayed = false
	state.defaultWakeUpInterval = cmd.defaultWakeUpIntervalSeconds
    results << createEvent(map)
}

def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpIntervalReport cmd, results) {

	def map = [ name: "reportedWakeUpInterval", unit: "seconds" ]
	map.value = cmd.seconds
	map.displayed = false
    results << createEvent(map)
}

def zwaveEvent(physicalgraph.zwave.Command cmd, results) {
	def event = [ displayed: false ]
	event.linkText = device.label ?: device.name
	event.descriptionText = "$event.linkText: $cmd"
	results << createEvent(event)
}

private getUserWakeUp(userWake) {

    if (!userWake)                       
    	{ userWake = '3600' }  // set default 1 hr if no user preference 
    // make sure user setting is within valid range for device 
    if (userWake.toInteger() < 600) 
    	{ userWake = '600'}  // 10 minutes - Mininum
    if (userWake.toInteger() > 604800) 
    	{ userWake = '604800'}  // 1 week - Maximum
    return userWake.toInteger()
}
private isDuplicateCommand(lastExecuted, allowedMil) {
	!lastExecuted ? false : (lastExecuted + allowedMil > new Date().time) 
}

private logDebug(msg) {
	if (settings?.debugOutput || settings?.debugOutput == null) {
		log.debug "$msg"
	}
}

private logTrace(msg) {
	 log.trace "$msg"
}