Nest Protect Device Type

@wakkigy @tdh I have a working solution to the – problem and icons not displaying for smoke, CO and battery. The main issue with the value tiles displaying – is that Nest Protect doesn’t report a value for battery %, just “OK” or “low,” so there’s no way to monitor the actual battery percentage. Also, apparently there’s no icon for battery, so I didn’t code an icon, just green/red with a label.

I modified SmartThings’ code for the Z-Wave Smoke Alarm (just added in the Nest login), so this should be 100% up to their specs.

The installation steps are the same as the original code, just update your device code with this and you’ll be set.

Here’s the code, enjoy!

preferences {
    input("username", "text", title: "Username", description: "Your Nest username (usually an email address)")
    input("password", "password", title: "Password", description: "Your Nest password")
    input("mac", "text", title: "MAC Address", description: "The MAC Address of your Nest Protect")
}

metadata {
	// Automatically generated. Make future change here.
	definition (name: "Nest Protect", namespace: "NP", author: "jcbannon") {
		capability "Smoke Detector"
		capability "Carbon Monoxide Detector"
		capability "Sensor"
		capability "Battery"
		capability "Polling"

		attribute "alarmState", "string"

		fingerprint deviceId: "0xA100", inClusters: "0x20,0x80,0x70,0x85,0x71,0x72,0x86"
	}

	simulator {
		status "smoke": "command: 7105, payload: 01 FF"
		status "clear": "command: 7105, payload: 01 00"
		status "test": "command: 7105, payload: 0C FF"
		status "carbonMonoxide": "command: 7105, payload: 02 FF"
		status "carbonMonoxide clear": "command: 7105, payload: 02 00"
		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:"Smoke clear", icon:"st.alarm.smoke.clear", backgroundColor:"#44B621")
			state("smoke", label:"SMOKE", icon:"st.alarm.smoke.smoke", backgroundColor:"#e86d13")
			state("tested", label:"TEST", icon:"st.alarm.smoke.test", backgroundColor:"#e86d13")
		}
        
        standardTile("carbonMonoxide", "device.carbonMonoxide"){
        	state("clear", label:"CO clear", icon:"st.particulate.particulate.particulate", backgroundColor:"#44B621")
			state("smoke", label:"CO", icon:"st.particulate.particulate.particulate", backgroundColor:"#e86d13")
			state("tested", label:"TEST", icon:"st.particulate.particulate.particulate", backgroundColor:"#e86d13")
		}
        
        standardTile("battery", "device.battery") {
			state("OK", label: "Battery OK", backgroundColor: "#44B621")
            state("low", label: "Battery Low", backgroundColor: "#e86d13")
		}

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

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


def createSmokeOrCOEvents(name, results) {
	def text = null
	if (name == "smoke") {
		text = "$device.displayName smoke was detected!"
		// 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: text, displayed: false)
	} else if (name == "carbonMonoxide") {
		text = "$device.displayName carbon monoxide was detected!"
		results << createEvent(name: "carbonMonoxide", value: "detected", descriptionText: text, displayed: false)
	} else if (name == "tested") {
		text = "$device.displayName was tested"
		results << createEvent(name: "smoke",          value: "tested", descriptionText: text, displayed: false)
		results << createEvent(name: "carbonMonoxide", value: "tested", descriptionText: text, displayed: false)
	} else if (name == "smokeClear") {
		text = "$device.displayName smoke is clear"
		results << createEvent(name: "smoke",          value: "clear", descriptionText: text, displayed: false)
		name = "clear"
	} else if (name == "carbonMonoxideClear") {
		text = "$device.displayName carbon monoxide is clear"
		results << createEvent(name: "carbonMonoxide", value: "clear", descriptionText: text, displayed: false)
		name = "clear"
	} else if (name == "testClear") {
		text = "$device.displayName smoke is clear"
		results << createEvent(name: "smoke",          value: "clear", descriptionText: text, displayed: false)
		results << createEvent(name: "carbonMonoxide", value: "clear", displayed: false)
		name = "clear"
	}
	// This composite event is used for updating the tile
	results << createEvent(name: "alarmState", value: name, descriptionText: text)
}

def zwaveEvent(physicalgraph.zwave.commands.alarmv2.AlarmReport cmd, results) {
	if (cmd.zwaveAlarmType == physicalgraph.zwave.commands.alarmv2.AlarmReport.ZWAVE_ALARM_TYPE_SMOKE) {
		if (cmd.zwaveAlarmEvent == 3) {
			createSmokeOrCOEvents("tested", results)
		} else {
			createSmokeOrCOEvents((cmd.zwaveAlarmEvent == 1 || cmd.zwaveAlarmEvent == 2) ? "smoke" : "smokeClear", results)
		}
	} else if (cmd.zwaveAlarmType == physicalgraph.zwave.commands.alarmv2.AlarmReport.ZWAVE_ALARM_TYPE_CO) {
		createSmokeOrCOEvents((cmd.zwaveAlarmEvent == 1 || cmd.zwaveAlarmEvent == 2) ? "carbonMonoxide" : "carbonMonoxideClear", results)
	} else switch(cmd.alarmType) {
		case 1:
			createSmokeOrCOEvents(cmd.alarmLevel ? "smoke" : "smokeClear", results)
			break
		case 2:
			createSmokeOrCOEvents(cmd.alarmLevel ? "carbonMonoxide" : "carbonMonoxideClear", results)
			break
		case 12:  // test button pressed
			createSmokeOrCOEvents(cmd.alarmLevel ? "tested" : "testClear", results)
			break
		case 13:  // sent every hour -- not sure what this means, just a wake up notification?
			if (cmd.alarmLevel != 255) {
				results << createEvent(descriptionText: "$device.displayName code 13 is $cmd.alarmLevel", displayed: true)
			}
			
			// Clear smoke in case they pulled batteries and we missed the clear msg
			if(device.currentValue("smoke") != "clear") {
				createSmokeOrCOEvents("smokeClear", results)
			}
			
			// Check battery if we don't have a recent battery event
			def prevBattery = device.currentState("battery")
			if (!prevBattery || (new Date().time - prevBattery.date.time)/60000 >= 60 * 53) {
				results << new physicalgraph.device.HubAction(zwave.batteryV1.batteryGet().format())
			}
			break
		default:
			results << createEvent(displayed: true, descriptionText: "Alarm $cmd.alarmType ${cmd.alarmLevel == 255 ? 'activated' : cmd.alarmLevel ?: 'deactivated'}".toString())
			break
	}
}

// SensorBinary and SensorAlarm aren't tested, but included to preemptively support future smoke alarms
//
def zwaveEvent(physicalgraph.zwave.commands.sensorbinaryv2.SensorBinaryReport cmd, results) {
	if (cmd.sensorType == physicalgraph.zwave.commandclasses.SensorBinaryV2.SENSOR_TYPE_SMOKE) {
		createSmokeOrCOEvents(cmd.sensorValue ? "smoke" : "smokeClear", results)
	} else if (cmd.sensorType == physicalgraph.zwave.commandclasses.SensorBinaryV2.SENSOR_TYPE_CO) {
		createSmokeOrCOEvents(cmd.sensorValue ? "carbonMonoxide" : "carbonMonoxideClear", results)
	}
}

def zwaveEvent(physicalgraph.zwave.commands.sensoralarmv1.SensorAlarmReport cmd, results) {
	if (cmd.sensorType == 1) {
		createSmokeOrCOEvents(cmd.sensorState ? "smoke" : "smokeClear", results)
	} else if (cmd.sensorType == 2) {
		createSmokeOrCOEvents(cmd.sensorState ? "carbonMonoxide" : "carbonMonoxideClear", results)
	}
	
}

def zwaveEvent(physicalgraph.zwave.commands.wakeupv1.WakeUpNotification cmd, results) {
	results << new physicalgraph.device.HubAction(zwave.wakeUpV1.wakeUpNoMoreInformation().format())
	results << createEvent(descriptionText: "$device.displayName woke up", isStateChange: false)
}

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(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)
}

// Need to be logged in before this is called. So don't call this. Call api.
def doRequest(uri, args, type, success) {
    log.debug "Calling $type : $uri : $args"
    
    if(uri.charAt(0) == '/') {
        uri = "${data.auth.urls.transport_url}${uri}"
    }
    
    def params = [
        uri: uri,
        headers: [
            'X-nl-protocol-version': 1,
            'X-nl-user-id': data.auth.userid,
            'Authorization': "Basic ${data.auth.access_token}"
        ],
        body: args
    ]
    
    if(type == 'post') {
        httpPostJson(params, success)
    } else if (type == 'get') {
        httpGet(params, success)
    }
}
 
def login(method = null, args = [], success = {}) {    
    def params = [
        uri: 'https://home.nest.com/user/login',
        body: [username: settings.username, password: settings.password]
    ]        
    
    httpPost(params) {response -> 
        data.auth = response.data
        data.auth.expires_in = Date.parse('EEE, dd-MMM-yyyy HH:mm:ss z', response.data.expires_in).getTime()
        log.debug data.auth
        
        api(method, args, success)
    }
}
 
def isLoggedIn() {
    if(!data.auth) {
        log.debug "No data.auth"
        return false
    }
    
    def now = new Date().getTime();
    return data.auth.expires_in > now
}
7 Likes

Hi @jgm1937, with this code, you wouldn’t change your login credentials in the code. Where you put in your email address is actually the code item that creates the username variable in the script. You need to follow the installation steps and just copy/paste the code. The steps weren’t super obvious to me when I started, so here’s a clearer explanation:

  1. Click “My Device Types” in the top navigation menu, and click on the green “New SmartDevice” button to the right of the page. Click the “From Code” tab and copy/paste the code into the box. Click “Create.”

  2. Click “My Devices” in the top nav and click “New Device” button. Name the device whatever you want, create whatever network ID you want, and then choose “Nest Protect” as the device type. Set “Location” and “Hub” based on your SmartThings setup. Click “Create.”

  3. Choose your new NP Device, and under the “Preferences” attribute, you should see (edit) as a link. Click on that, and enter in your Nest username, password, and the 802.15.4 MAC address from your Nest account settings.

You should then see the Nest in your SmartThings. Don’t get discouraged if it says “inactive,” because it appears that the Nest only pushes updates periodically, so it won’t display as “active” all the time, even if it’s connected. You can see the updates in the log.

5 Likes

Thanks! I was able to play around a bit on the Groove designer and eventually found out the values for the username/password. Thanks for the info, as I was a bit worried until I saw active from the Nest Protect.

Tried @jcbannon code and got the icons to show up now :slight_smile:

One thing for others who want to try. I kept getting error when creating the Device Type when I copy the code from the thread and paste code section (complains about ^1) I ended up just copying it to notepad and then redo copy/paste from there.

I then swapped the device type on my existing Nest Protects and Icons are updated.

1 Like

Glad you got it to work, too!

@jcbannon I was having intermittent issues with the older code, but this one works better! Many thanks!

2 Likes

Does the Nest Protect show more battery info When using it with ActiON Dashboard 4.6.3 is here! (Now SmartTiles.click) it only displays NULL.

Does this code work with the AC powered version of Nest Protect ?

Yes.

(20 characters)

Thanks

Wanted to make sure before I buy 4

Is there a way to poll the AC power status - ie lost AC or power failure ?

Monitoring a remote location and I can’t tell whether the Nest is running on the backup batteries

@jcbannon

This solved the tile display issues on my Android devices, thank you very much.

Has anyone tested this to see if you receive the alarm event? I can’t test it right now without waking up the family.

@hondohudson I don’t know what the variable is for the Nest’s “power out” message, so I don’t know how to add it in. Would be very useful though!

@korban_hadley If that dashboard is expecting a % battery level, than it will always show NULL. That’s a problem with the dashboard, not with the Nest Protect or with this code. Nest Protect doesn’t deliver a specific battery %, so if the tile is set as a valueTile instead of a standardTile, it will show NULL.

Im talking about when the unit (mine is the AC powered unit) correctly shows OK or Low for the backup battery. I trying to get it to show if the AC power is lost. There is not a “tile” for AC power currently.

1 Like

@jcbannon - used your code and now have my 3 Nest Protects displaying in ST.

I just tested (lit a newspaper under one of the NPs) - it went into alarm mode but nothing was sent to smartthings. The icon says “Smoke Clear”, and the activity log is blank. Any ideas?

1 Like

@jcbannon first I wanted to say great job. I just installed this and everything looks good.

Question… I have 2 NEST Protects! Will entering the code 1 time work for both? Or do I need to add another device? I only see 1 in my things and I don’t know which one it is. My goal is to have both showing if possible.

Thanks

Hi @antman2 - You’ll need to add a new device for each Nest Protect in your home. I would suggest naming them for the room they’re in.

1 Like

@antman2 - have you been able to get alters from your nest protect? Also using @jcbannon code (thank you!), but it doesn’t seem to alert on a smoke event.

@hondohudson I don’t know what message the Nest Protect pushes for AC power status. I know it’s there, but I have no idea how to integrate it into SmartThings. I tried to look at other products that have AC power to see if I could find one that had a code string I could use, but I didn’t find one. SmartThings doesn’t officially support any smoke detectors with AC power.

And really, if you think about it, if your AC power is out, so is your SmartThings, unless you have it on a battery backup.

@tuffcalc I haven’t tested my Nest Protects but I will try to see if I can look at the alarm tile at some point this week. It’s possible that SmartThings isn’t updating fast enough, if the alarm only goes off for a minute. I’ve seen SmartThings have a pretty long delay on updating. Does the tile display Test status?

1 Like