SimpliSafe Alarm Integration (cloud to cloud)

dth_security
dth_alerts
project_security

(Toby Cth3) #1

I took the original (and great) code by @fgorodishter and made a couple of enhancements. You can now pull the status of the alarm, fire, CO2, freeze and water sensors (just like the SimpliSafe app). These are also exposed and can be used throughout Dashboards and the new Smart Home Monitor to trigger alerts or other actions. I’ve tested it with my setup and all is working pretty good. Please pass along your feedback or let me know if you run into any issues.


5/10/16
Updated device type

  • Uses new multitile layout
  • Displays most recent event in main tile
  • Refresh temperature and alarm status by clicking on their icons
  • Reduced IDE logging to more relevant info

3/7/16
All new companion SmartApp

  • Completely re-written
  • Resolved duplicate notification issues
  • Improved push notifications and error logging
  • Removed modes trigger since it was redundant now that Routines can set SHM
    - This is a totally new SmartApp and I strongly recommend uninstalling the existing app before installing the new version.

2/24/16
Updated companion SmartApp

  • Push notifications will now only send on SimpliSafe state changes and not SHM changes

2/15/16
Added new companion SmartApp:

  • Syncs status & controls SimpliSafe though Smart Home Monitor
  • Sets SimpliSafe alarm based off of location modes
  • Turns on/off switches based on SimpliSafe state

DEVICE TYPE

/**
 *  SimpliSafe integration for SmartThings
 *
 *  Copyright 2015 Felix Gorodishter
 *	Modifications by Toby Harris - 5/10/2016
 *
 *  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 {
	input(name: "username", type: "text", title: "Username", required: "true", description: "Your SimpliSafe username")
	input(name: "password", type: "password", title: "Password", required: "true", description: "Your SimpliSafe password")
}

metadata {
	// Automatically generated. Make future change here.
	definition (name: "SimpliSafe", namespace: "tobycth3", author: "Toby Harris") {
		capability "Alarm"
		capability "Polling"
		capability "Acceleration Sensor"
        capability "Contact Sensor"
		capability "Carbon Monoxide Detector"
        capability "Lock"
		capability "Presence Sensor"
		capability "Smoke Detector"
        capability "Temperature Measurement"
        capability "Water Sensor"
		command "home"
		command "away"
		command "off"
		command "update_state"
		command "update_temp"
		attribute "events", "string"
		attribute "recent_alarm", "string" 
		attribute "recent_fire", "string" 
		attribute "recent_co", "string" 
		attribute "recent_flood", "string" 
		attribute "warnings", "string"        
	}

	simulator {
		// TODO: define status and reply messages here
	}


tiles(scale: 2) {
    multiAttributeTile(name:"alarm", type: "generic", width: 6, height: 4){
        tileAttribute ("device.alarm", key: "PRIMARY_CONTROL") {
            attributeState "off", label:'${name}', icon: "st.security.alarm.off", backgroundColor: "#505050"
            attributeState "home", label:'${name}', icon: "st.Home.home4", backgroundColor: "#00BEAC"
            attributeState "away", label:'${name}', icon: "st.security.alarm.on", backgroundColor: "#008CC1"
			attributeState "pending off", label:'${name}', icon: "st.security.alarm.off", backgroundColor: "#ffffff"
			attributeState "pending away", label:'${name}', icon: "st.Home.home4", backgroundColor: "#ffffff"
			attributeState "pending home", label:'${name}', icon: "st.security.alarm.on", backgroundColor: "#ffffff"
			attributeState "failed set", label:'error', icon: "st.secondary.refresh", backgroundColor: "#d44556"
        }
		
		tileAttribute("device.events", key: "SECONDARY_CONTROL", wordWrap: true) {
			attributeState("default", label:'${currentValue}')
		}
    }	
	
    standardTile("off", "device.alarm", width: 2, height: 2, canChangeIcon: false, inactiveLabel: true, canChangeBackground: false) {
        state ("off", label:"off", action:"off", icon: "st.security.alarm.off", backgroundColor: "#008CC1", nextState: "pending")
        state ("away", label:"off", action:"off", icon: "st.security.alarm.off", backgroundColor: "#505050", nextState: "pending")
        state ("home", label:"off", action:"off", icon: "st.security.alarm.off", backgroundColor: "#505050", nextState: "pending")
        state ("pending", label:"pending", icon: "st.security.alarm.off", backgroundColor: "#ffffff")
	}
	
    standardTile("away", "device.alarm", width: 2, height: 2, canChangeIcon: false, inactiveLabel: true, canChangeBackground: false) {
        state ("off", label:"away", action:"away", icon: "st.security.alarm.on", backgroundColor: "#505050", nextState: "pending") 
		state ("away", label:"away", action:"away", icon: "st.security.alarm.on", backgroundColor: "#008CC1", nextState: "pending")
        state ("home", label:"away", action:"away", icon: "st.security.alarm.on", backgroundColor: "#505050", nextState: "pending")
		state ("pending", label:"pending", icon: "st.security.alarm.on", backgroundColor: "#ffffff")
	}
	
    standardTile("home", "device.alarm", width: 2, height: 2, canChangeIcon: false, inactiveLabel: true, canChangeBackground: false) {
        state ("off", label:"home", action:"home", icon: "st.Home.home4", backgroundColor: "#505050", nextState: "pending")
        state ("away", label:"home", action:"home", icon: "st.Home.home4", backgroundColor: "#505050", nextState: "pending")
		state ("home", label:"home", action:"home", icon: "st.Home.home4", backgroundColor: "#008CC1", nextState: "pending")
		state ("pending", label:"pending", icon: "st.Home.home4", backgroundColor: "#ffffff")
	}
    
		standardTile("recent_alarm", "device.contact", inactiveLabel: false, width: 2, height: 2) {
			state "closed", label:'Alarm', icon: "st.security.alarm.clear", backgroundColor: "#50C65F"
			state "open", label:'ALARM', icon: "st.security.alarm.alarm", backgroundColor: "#d44556"
		}
		standardTile("freeze", "device.freeze_status", width: 2, height: 2, canChangeIcon: false, inactiveLabel: true, canChangeBackground: false) {
			state ("no alert", label:'Temp', action:"update_temp", icon: "st.alarm.temperature.normal", backgroundColor: "#50C65F", nextState: "updating")
			state ("alert", label:'TEMP', action:"update_temp", icon: "st.alarm.temperature.freeze", backgroundColor: "#d44556", nextState: "updating")
			state ("updating", label:"updating", icon: "st.alarm.temperature.normal", backgroundColor: "#ffffff")
		}
		standardTile("recent_fire", "device.smoke", inactiveLabel: false, width: 2, height: 2) {
			state "clear", label:'Fire', icon: "st.alarm.smoke.clear", backgroundColor: "#50C65F"
			state "detected", label:'FIRE', icon: "st.alarm.smoke.smoke", backgroundColor: "#d44556"
		}
		standardTile("recent_co", "device.carbonMonoxide", inactiveLabel: false, width: 2, height: 2) {
			state "clear", label:'CO2', icon: "st.alarm.carbon-monoxide.clear", backgroundColor: "#50C65F"
			state "detected", label:'CO2', icon: "st.alarm.carbon-monoxide.carbon-monoxide", backgroundColor: "#d44556"
		}
		standardTile("recent_flood", "device.water", inactiveLabel: false, width: 2, height: 2) {
			state "dry", label:'Flood', icon: "st.alarm.water.dry", backgroundColor: "#50C65F"
			state "wet", label:'FLOOD', icon: "st.alarm.water.wet", backgroundColor: "#d44556"
		}
		standardTile("warnings", "device.acceleration", width: 2, height: 2, canChangeIcon: false, inactiveLabel: true, canChangeBackground: false) {
			state ("inactive", label:'Base', action:"update_state", icon: "st.Kids.kids15", backgroundColor: "#50C65F", nextState: "updating")
			state ("active", label:'BASE', action:"update_state", icon: "st.Kids.kids15", backgroundColor: "#d44556", nextState: "updating")
			state ("updating", label:"updating", icon: "st.Kids.kids15", backgroundColor: "#ffffff")
		}
		standardTile("refresh", "device.alarm", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
			state "default", action:"polling.poll", icon:"st.secondary.refresh"
		}

		main(["alarm"])
		details(["alarm","off", "away", "home", "recent_alarm", "freeze", "recent_fire", "recent_co", "recent_flood", "warnings", "refresh"])
	}
}

// parse events into attributes
def parse(String description) {
	log.debug "Parsing '${description}'"
	// TODO: handle 'alarm' attribute

}

// handle commands
def off() {
	log.info "Executing 'off'"
	api('set-state', [state: off, mobile: 1]) { response ->
	//	log.trace "Set-state response $response.status $response.data"
	}
	// refresh status
	poll()
}

def home() { 
	log.info "Executing 'home'"
	api('set-state', [state: home, mobile: 1]) { response ->
	//	log.trace "Set-state response $response.status $response.data"
	}
	// refresh status
	poll()	
}

def away() {
	log.info "Executing 'away'"
	api('set-state', [state: away, mobile: 1]) { response ->
	//	log.trace "Set-state response $response.status $response.data"
	}
	// refresh status
	poll()
}

def update_state() {
		log.info "Updating state from base station"
	api('get-state', []) { response ->
	//	log.trace "Get-state response $response.status $response.data"
	}
	// refresh status
	poll()   
  }
	
def update_temp() {
		log.info "Updating temperature from base station"
	api('update-freeze', []) { response ->
	//	log.trace "Update-freeze response $response.status $response.data"
	}
	// refresh status
	poll()
}

def setAlarmMode(mode) {
	// TODO
}

def strobe() {
	log.info "Executing 'strobe'"
	// TODO: handle 'strobe' command
}

def siren() {
	log.info "Executing 'siren'"
	// TODO: handle 'siren' command
}

def both() {
	log.info "Executing 'both'"
	// TODO: handle 'both' command
}

def poll() {
	log.info "Executing 'poll'"

	log.info "Executing 'status'"
	api('status', []) { response ->
	//	log.trace "Status response $response.status $response.data"

		if (response.data.return_code < 1) {
			return
		}

		def location = response.data.location

		def new_state = location.system.state
		def old_state = device.currentValue("alarm")
		def state_changed = new_state != old_state

        def new_recent_alarm = location.monitoring.recent_alarm.text
		def old_recent_alarm = device.currentValue("recent_alarm")
		def recent_alarm_changed = new_recent_alarm != old_recent_alarm

        def new_recent_fire = location.monitoring.recent_fire.text
		def old_recent_fire = device.currentValue("recent_fire")
		def recent_fire_changed = new_recent_fire != old_recent_fire

        def new_recent_co = location.monitoring.recent_co.text
		def old_recent_co = device.currentValue("recent_co")
		def recent_co_changed = new_recent_co != old_recent_co

		def new_recent_flood = location.monitoring.recent_flood.text
		def old_recent_flood = device.currentValue("recent_flood")
		def recent_flood_changed = new_recent_flood != old_recent_flood

        def new_freeze = location.monitoring.freeze.temp
		if (new_freeze != "?" || null) { 
        def old_freeze = device.currentValue("temperature")
        def freeze_changed = new_freeze != old_freeze }
		else { 
		log.debug "No freeze sensor data received - aborting" 
		new_freeze = "0"
		def old_freeze = "0"
		def freeze_changed = new_freeze != old_freeze } 

        def new_warnings = location.monitoring.warnings
        if (new_warnings == null) { new_warnings = "No Alert" }
		def old_warnings = device.currentValue("warnings")
		def warnings_changed = new_warnings != old_warnings


		def alarm_presence = ['off':'present', 'home':'present', 'away':'not present']
		def presence_state_changed = device.currentValue("presence") != alarm_presence.getAt(new_state)

		def alarm_armed = ['off':'unlocked', 'home':'locked', 'away':'locked']
		def armed_state_changed = device.currentValue("lock") != alarm_armed.getAt(new_state)

		log.debug "Alarm State: $new_state"
		log.debug "Alarm: $new_recent_alarm"
		log.debug "Fire: $new_recent_fire"
		log.debug "CO2: $new_recent_co"
		log.debug "Flood: $new_recent_flood"
		log.debug "Freeze: $new_freeze"
		log.debug "Warnings: $new_warnings"

		sendEvent(name: 'presence', value: alarm_presence.getAt(new_state), displayed: presence_state_changed, isStateChange: presence_state_changed)
		sendEvent(name: 'lock', value: alarm_armed.getAt(new_state), displayed: armed_state_changed, isStateChange: armed_state_changed)
		sendEvent(name: 'alarm', value: new_state, displayed: state_changed, isStateChange: state_changed)
		sendEvent(name: 'recent_alarm', value: new_recent_alarm, displayed: recent_alarm_changed, isStateChange: recent_alarm_changed)
		sendEvent(name: 'recent_fire', value: new_recent_fire, displayed: recent_fire_changed, isStateChange: recent_fire_changed)
		sendEvent(name: 'recent_co', value: new_recent_co, displayed: recent_co_changed, isStateChange: recent_co_changed)
		sendEvent(name: 'recent_flood', value: new_recent_flood, displayed: recent_flood_changed, isStateChange: recent_flood_changed)
		sendEvent(name: 'temperature', value: new_freeze, displayed: freeze_changed, isStateChange: freeze_changed)
		sendEvent(name: 'warnings', value: new_warnings, displayed: warnings_changed, isStateChange: warnings_changed)

	if (new_recent_alarm != "No Alert") { 
	sendEvent(name: 'contact', value: "open", displayed: recent_alarm_changed, isStateChange: recent_alarm_changed) }
		else {
	sendEvent(name: 'contact', value: "closed", displayed: recent_alarm_changed, isStateChange: recent_alarm_changed)
		}
	if (new_recent_fire != "No Alert") { 
	sendEvent(name: 'smoke', value: "detected", displayed: recent_fire_changed, isStateChange: recent_fire_changed) }
		else {
	sendEvent(name: 'smoke', value: "clear", displayed: recent_fire_changed, isStateChange: recent_fire_changed)
		}
	if (new_recent_co != "No Alert") { 
	sendEvent(name: 'carbonMonoxide', value: "detected", displayed: recent_co_changed, isStateChange: recent_co_changed) }
		else {
	sendEvent(name: 'carbonMonoxide', value: "clear", displayed: recent_co_changed, isStateChange: recent_co_changed)
		}
	if (new_recent_flood != "No Alert") { 
	sendEvent(name: 'water', value: "wet", displayed: recent_flood_changed, isStateChange: recent_flood_changed) }
		else {
	sendEvent(name: 'water', value: "dry", displayed: recent_flood_changed, isStateChange: recent_flood_changed)
		}
	if (new_freeze <= 41) { 
	sendEvent(name: 'freeze_status', value: "alert", displayed: freeze_changed, isStateChange: freeze_changed) }
		else {
	sendEvent(name: 'freeze_status', value: "no alert", displayed: freeze_changed, isStateChange: freeze_changed)
		}		
	if (new_warnings != "No Alert") { 
	sendEvent(name: 'acceleration', value: "active", displayed: warnings_changed, isStateChange: warnings_changed) }
		else {
	sendEvent(name: 'acceleration', value: "inactive", displayed: warnings_changed, isStateChange: warnings_changed)
		}
 }

	log.info "Executing 'events'" 
	api('events', []) { response ->
	//	log.trace "Events response $response.status $response.data"

		if (response.data.return_code < 1) {
			return
		} 
        
		def raw_event_desc = response.data.events.event_desc[0]
		if (raw_event_desc.find(/System Armed|System Disarmed/)) {
			def parsed_event_desc = raw_event_desc.findAll(/Armed|Disarmed|[(]\w*\s+\w+[)]/)
			parsed_event_desc = parsed_event_desc.join(' in ')
			state.parsed_event_desc = parsed_event_desc.replaceAll("[()]","") + " - "
		} else if (raw_event_desc.size() <=26 ) {
			state.parsed_event_desc = raw_event_desc + " - "
			} else {
			def parsed_event_desc = raw_event_desc.take(26) + "... "	
			state.parsed_event_desc = parsed_event_desc
		}
		
		def new_events = state.parsed_event_desc + response.data.events.event_time[0] + " " + response.data.events.event_date[0]
		def old_events = device.currentValue("events")
		def events_changed = new_events != old_events
      
		log.debug "Events: $new_events"
        
		sendEvent(name: 'events', value: new_events, displayed: events_changed, isStateChange: events_changed)       
 }        
 	// log out session	
	logout()
}

def api(method, args = [], success = {}) {
	log.info "Executing 'api'"

	if(!isLoggedIn()) {
		log.debug "Need to login"
		login(method, args, success)
		return
	}

	// SimpliSafe requires this funkiness
	def existing_args = args
	def required_payload = [
		no_persist: 0,
		XDEBUG_SESSION_START: 'session_name',
	]

	// append it to the args
	if (existing_args != [])
		{
		args = existing_args + required_payload
		} 
		else {
		args = required_payload
		}

	def methods = [
		'locations': [uri: "https://simplisafe.com/mobile/$state.auth.uid/locations", type: 'post'],
		'status': [uri: "https://simplisafe.com/mobile/$state.auth.uid/sid/$state.locationID/dashboard", type: 'post'],
		'events': [uri: "https://simplisafe.com/mobile/$state.auth.uid/sid/$state.locationID/events", type: 'post'],
		'get-state': [uri: "https://simplisafe.com/mobile/$state.auth.uid/sid/$state.locationID/get-state", type: 'post'],
		'set-state': [uri: "https://simplisafe.com/mobile/$state.auth.uid/sid/$state.locationID/set-state", type: 'post'],
		'update-freeze': [uri: "https://simplisafe.com/account2/$state.auth.uid/sid/$state.locationID/control-panel/utility/update-freeze", type: 'post'],
		'logout': [uri: "https://simplisafe.com/mobile/logout", type: 'post']
	]

	def request = methods.getAt(method)

	log.debug "Starting $method : $args"
	doRequest(request.uri, args, request.type, success)
}

// 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"

	def params = [
		uri: uri,
		headers: [
			'Cookie': state.cookiess
		],
		body: args
	]

//	log.trace params

	try {
		if (type == 'post') {
			httpPost(params, success)
		} else if (type == 'get') {
			httpGet(params, success)
		}

	} catch (e) {
		log.debug "something went wrong: $e"
	}
}

def login(method = null, args = [], success = {}) { 
	log.info "Executing 'login'"
	def params = [
		uri: 'https://simplisafe.com/mobile/login',
		body: [
			name: settings.username, 
			pass: settings.password, 
			device_name: "SimpliSafe", 
			device_uuid: "SimpliSafe",
			version: 1200,
			no_persist: 1,
			XDEBUG_SESSION_START: 'session_name'
		]
	]

	state.cookiess = ''

	httpPost(params) {response ->
	//	log.trace "Login response, $response.status $response.data"
	//	log.trace response.headers

		state.auth = response.data

		// set the expiration to 10 minutes
		state.auth.expires_at = new Date().getTime() + 600000;

		response.getHeaders('Set-Cookie').each {
			String cookie = it.value.split(';|,')[0]
		//	log.trace "Adding cookie to collection: $cookie"
			state.cookiess = state.cookiess+cookie+';'
		}
	//	log.trace "cookies: $state.cookiess"

		// get location ID
		locations()

		api(method, args, success)

	}
}

def locations() {
	log.info "Executing 'locations'"

	api('locations', []) { response ->
	//	log.trace "Locations response $response.status $response.data"

	if (response.data.num_locations < 1) {
			return
		}

		def locations = response.data.locations
		state.locationID = locations.keySet()[0]
 }
}

def logout() { 
	log.info "Executing 'logout'"
	api('logout', []) { response ->
	//	log.trace "Logout response $response.status $response.data"
	}	
	state.auth = false		
}

def isLoggedIn() {
	if(!state.auth) {
		log.debug "No state.auth"
		return false
	}

//	log.trace state.auth.uid

	def now = new Date().getTime();
//	log.trace now
//	log.trace state.auth.expires_at
	return state.auth.expires_at > now
}

COMPANION SMARTAPP

/**
 *  SimpliSafe Monitor
 *
 *  Author: toby@cth3.com
 *  Date: 3/5/16
 *
 *  Monitors and controlls the state of a SimpliSafe alarm system, syncs with Smart Home Monitor and can turn on/off switchs based on SimpliSafe state.
 *  Works in conjunction with SimpliSafe Alarm Integration device type.
 */


// Automatically generated. Make future change here.
definition(
    name: "SimpliSafe Monitor",
    namespace: "tobycth3",
    author: "toby@cth3.com",
    description: "Monitors and controlls the state of a SimpliSafe alarm system, syncs with Smart Home Monitor and can turn on/off switchs based on SimpliSafe state. Works in conjunction with SimpliSafe Alarm Integration device type.",
    category: "Safety & Security",
    iconUrl: "https://pbs.twimg.com/profile_images/594250179215241217/LOjVA4Yf.jpg",
    iconX2Url: "https://pbs.twimg.com/profile_images/594250179215241217/LOjVA4Yf.jpg")

preferences {
  section("Monitor and control this SimpliSafe alarm system") {
    input "alarmsystem", "capability.alarm", title: "Select alarm system"
  }
  
  section("Control these switchs") {
	input "alarmtile", "capability.switch", title: "Select switches", multiple: true, required: false  } 
  
  section("Turn on switchs when SimpliSafe state matches") {
    input "alarmon", "enum", title: "Select on state", multiple: true, required: false, metadata:[values:["off", "away", "home"]]
  }
  
  section("Turn off switchs when SimpliSafe state matches") {
    input "alarmoff", "enum", title: "Select off state", multiple: true, required: false, metadata:[values:["off", "away", "home"]]
  }
   
  section("Notifications") {
    input "sendPushMessage", "enum", title: "Send a push notification?", metadata:[values:["Yes","No"]], required: false
   }
  }

def installed() {
  init()
  }

def updated() {
  unsubscribe()
  unschedule()
  init()
  }
  
def init() {
  subscribe(alarmsystem, "alarm", alarmstate)
  subscribe(location, "alarmSystemStatus", shmaction)
  }
  
def updatestate() {
	log.info("Checking SimpliSafe and Smart Home Monitor state")
	state.alarmstate = alarmsystem.currentState("alarm").value.toLowerCase()
	state.shmstate = location.currentState("alarmSystemStatus").value.toLowerCase()
	log.debug("SimpliSafe: '$state.alarmstate', Smart Home Monitor: '$state.shmstate'")
	
}

def shmaction(evt) {
log.info "Smart Home Monitor: $evt.displayName - $evt.value"
state.shmstate = evt.value

  if(shmOff && !alarmOff) {
    log.debug("Smart Home Monitor: '$state.shmstate', SimpliSafe: '$state.alarmstate'")
     setalarmoff()
  }
 else {
  if(shmAway && !alarmAway) {
    log.debug("Smart Home Monitor: '$state.shmstate', SimpliSafe: '$state.alarmstate'")
     setalarmaway()
  }
 else {
  if(shmStay && !alarmHome) {
    log.debug("Smart Home Monitor: '$state.shmstate', SimpliSafe: '$state.alarmstate'")
     setalarmhome()
  }
  else {
    log.debug("No condition match Smart Home Monitor: '$state.shmstate', SimpliSafe: '$state.alarmstate'")
   }  
  }
 }
}

def alarmstate(evt) {
log.info "SimpliSafe Alarm: $evt.displayName - $evt.value"
state.alarmstate = evt.value

  if (alarmOff && !shmOff) {
    log.debug("SimpliSafe: '$state.alarmstate', Smart Home Monitor: '$state.shmstate'")
     setshmoff()
  }
 else {
  if (alarmAway && !shmAway) {
    log.debug("SimpliSafe: '$state.alarmstate', Smart Home Monitor: '$state.shmstate'")
     setshmaway()
  }
 else {
  if (alarmHome && !shmStay) {
    log.debug("SimpliSafe: '$state.alarmstate', Smart Home Monitor: '$state.shmstate'")
     setshmstay()
  }
  else {
    log.debug("No condition match SimpliSafe: '$state.alarmstate', Smart Home Monitor: '$state.shmstate'")
  }
 }
}
  
  if (evt.value in alarmon) {
    log.debug("SimpliSafe state: $state.alarmstate")
     alarmstateon()
  }
 else {
  if (evt.value in alarmoff) {
    log.debug("SimpliSafe state: $state.alarmstate")
     alarmstateoff()
  }
 else {
    log.debug("No switch actions set for SimpliSafe state '${state.alarmstate}'")
    }
   }  
  }

def setalarmoff() {
	updatestate()
      log.debug("SimpliSafe: '$state.alarmstate'")
      if (!alarmOff) {
      def message = "Setting SimpliSafe to Off"
      log.info(message)
      send(message)
      alarmsystem.off()
  }
  else {
	 if (alarmOff) {  
     log.debug("SimpliSafe already set to '$state.alarmstate'")
  }
 }
}
  
def setalarmaway() {
	updatestate()
      log.debug("SimpliSafe: '$state.alarmstate'")
      if (!alarmAway) {
      def message = "Setting SimpliSafe to Away"
      log.info(message)
      send(message)
      alarmsystem.away()
  }
  else {
	 if (alarmAway) {  
     log.debug("SimpliSafe already set to '$state.alarmstate'")
  }
 }
}
  
def setalarmhome() {
	updatestate()
      log.debug("SimpliSafe: '$state.alarmstate'")
      if (!alarmHome) {
      def message = "Setting SimpliSafe to Home"
      log.info(message)
      send(message)
      alarmsystem.home()
  }
  else {
	 if (alarmHome) {  
     log.debug("SimpliSafe already set to '$state.alarmstate'")
  }
 }
}

def setshmoff() {
	updatestate()
      log.debug("Smart Home Monitor: '$state.shmstate'")
      if (!shmOff) {
      def message = "Setting Smart Home Monitor to Off"
      log.info(message)
      send(message)
      sendLocationEvent(name: "alarmSystemStatus", value: "off")
  }
  else {
	 if (shmOff) {  
     log.debug("Smart Home Monitor already set to '$state.shmstate'")
  }
 }
}
  
def setshmaway() {
	updatestate()
      log.debug("Smart Home Monitor: '$state.shmstate'")
      if (!shmAway) {
      def message = "Setting Smart Home Monitor to Away"
      log.info(message)
      send(message)
      sendLocationEvent(name: "alarmSystemStatus", value: "away")
  }
  else {
	 if (shmAway) {  
     log.debug("Smart Home Monitor already set to '$state.shmstate'")
  }
 }
}
  
def setshmstay() {
	updatestate()
      log.debug("Smart Home Monitor: '$state.shmstate'")
      if (!shmStay) {
      def message = "Setting Smart Home Monitor to Stay"
      log.info(message)
      send(message)
      sendLocationEvent(name: "alarmSystemStatus", value: "stay")
  }
  else {
	 if (shmStay) {  
     log.debug("Smart Home Monitor already set to '$state.shmstate'")
  }
 }
}

def alarmstateon() {
    log.debug ("Setting switches to on")
      settings.alarmtile.on()
  }
  
def alarmstateoff() {
    log.debug ("Setting switches to off")
      settings.alarmtile.off()
  } 
  
// TODO - centralize somehow
private getalarmOff() {
	def result = false
	if (state.alarmstate == "off") {
	result = true }
	log.trace "alarmOff = $result"
	result
}

private getalarmAway() {
	def result = false
	if (state.alarmstate == "away") {
	result = true }
	log.trace "alarmAway = $result"
	result
}

private getalarmHome() {
	def result = false
	if (state.alarmstate == "home") {
	result = true }
	log.trace "alarmHome = $result"
	result
}

private getshmOff() {
	def result = false
	if (state.shmstate == "off") {
	result = true }
	log.trace "shmOff = $result"
	result
}

private getshmAway() {
	def result = false
	if (state.shmstate == "away") {
	result = true }
	log.trace "shmAway = $result"
	result
}

private getshmStay() {
	def result = false
	if (state.shmstate == "stay") {
	result = true }
	log.trace "shmStay = $result"
	result
}
  
private send(msg) {
  if(sendPushMessage != "No") {
    log.debug("Sending push message")
    sendPush(msg)
   }
  log.debug(msg)
  }


What would I need to create a home alarm system
Smartthings+home security integration
Which battery powered security sensors / monitors, alarms / sirens, etc. should I add to Hub + Schlage Connect deadbolt locks?
Looking for Ideas on Simpi Safe
[DEPRECATED THREAD] webCoRE design process
Looking For Stable, Reliable Security Solution
Security System Integration
setLocationMode in Device Type possible?
Integrating SmartThings with SimpliSafe
My home is wired for a security system. Any way to take advantage of this to integrate a security system into ST?
SnarthThings + Security Monitoring
Home Security With Some Automation - ST + Abode (or Scout or ?)
Brand New Simplisafe Gear for Sale
(Geko) #2

You’d need to write a custom device handler with three tiles for “Home”, “Away” and “Off” commands and a tile to show alarm status. The device handler would issue HTTP requests to the web service when you tap a command tile and update status tile when it receives a reply. Pretty basic stuff. Read the docs and start coding. :smile:


(Marc) #3

I am very interested this also. I am extremely disappointed in the fact SimpliSafe shows no interest in tying into any sort of home automation such as IFTTT. ADT is going to be integrating with IFTTT soon and that will really put the pressure on them to.


(Toby Cth3) #4

Yup, understand that part but I’m more so having trouble keeping the session alive during the login. I first need to call a login page and pass my credentials. I then need to make a second call to actually do something. The problem is from my first login call to my second action call, I loose the cookie/session/authentication.

def params = [
    uri: "https://simplisafe.com/mobile/login/",
    body: [name: "REMOVED", pass: "REMOVED", device_name: "SmartThings_API", device_uuid: "REMOVED", version: "1200", no_persist: "1", XDEBUG_SESSION_START: "session_name"]
]   

httpPost(params) {response -> 
    data.auth = response.data
    log.debug data.auth
    

}

Then I try to call this but it doesn’t work:

def params = [
    uri: "https://simplisafe.com/mobile/REMOVED/sid/REMOVED/set-state/",
    body: [state: "home", mobile: "1", no_persist: "1", XDEBUG_SESSION_START: "session_name"]
]   

httpPost(params) {response -> 
    data.auth = response.data
    log.debug data.auth
    

}

(Marc) #5

Not sure this will help you, but this person got it working with Java:

This in python and specifically mentions avoiding the login cookie timeout:


(Felix Gorodishter) #6

I started on an integration (and in parallel learning some groovy to mess with SimpliSafe) - https://github.com/bobcat0070/simplisafe-smartthings

Just load it up as a new Device Type and set your SimpliSafe Username / Password on a new Device.

Its currently hard-coded to only support one location (the first one returned) and currently displays the SimpliSafe alarm status as a tile.

I’ll be implementing support to change status (off/away/home) when I get a chance. I still have to figure out if there is a trimode capability or if it will have to be a switch with a cobbled together On state for Armed/Home. Any advice certainly welcome.


Integrating SmartThings with SimpliSafe
(Marc) #7

Awesome!!! I got it up and running and so far is reading my status fine. How often does it poll for status? I setup pollster to pole every minute for now.

Also, it would be cool if we can get notifications when there is a status change (home, away or off).

Can’t wait for the status changes being performed from ST!


(Toby Cth3) #8

@fgorodishter
This is exactly what I was trying to figure out how to do. Excellent work! Thank you so much for your effort :smile:


(Felix Gorodishter) #9

Happy to hear its working for you :slight_smile:

It is currently exposing the Poll capability and in turn SmartThings calls poll() every 10 or so minutes. It is certainly not consistent and I may add a scheduled Poll call in there.

I hadn’t heard of Pollster before, but definitely looks to solve some of this inconsistency and should work with this integration without problems. Thanks for the tip.


(Bob Solimine) #10

Thank you for this device type! I have started working on my Smart App to set my “Hello, Home” based on my alarm’s state. I’m having a little trouble, though. Can anyone please take a look at my code and see what I’ve missed? The app simply doesn’t do anything. I’ve gone through several iterations of the code without luck.

preferences {
section(“Monitor this alarm”)
{
input “Alarm”, “capability.alarm”, title: “Which?”, multiple: false
}
}

def installed()
{
subscribe(Alarm, “alarmMode”, alarmHandler)
}

def updated()
{
unsubscribe()
subscribe(Alarm, “alarmMode”, alarmHandler)
}

def alarmHandler(evt)
{
def currentState = Alarm.alarmMode

If(evt.value == "Off")
{
	location.helloHome.execute("I'm Back!")
    log.debug("Alarm is off.")
}
Else
{
	If(evt.value == "Away")
    {
    	location.helloHome.execute("Good Bye!")
        log.debug("Alarm is Armed; Away.")
    }
    Else
    {
    	location.helloHome.execute("Good Night!")
        log.debug("Alarm is Armed; Home")
    }
}

}


(Felix Gorodishter) #11

@robert_solimine
While I haven’t tried it for myself yet, there’s two things I see that you will want to look into.

One of my gripes with SmartThings so far (only had it for 1 week) is the restrictive and inconsistent capabilities matrix when it comes to these more edge cases like integration with SimpliSafe. The capability.alarm (while I have not yet really implemented it in my integration), doesn’t directly do what what we will need since its focused on the modes of a Siren/Strobe rather than an external alarm with Off/Home/Away attributes - https://graph.api.smartthings.com/ide/doc/capabilities
It seems like I can give it the attributes I want, and just keep empty the 3/4 required commands since they make no sense in this context.

All that being said, you are currently subscribing to changes in “alarmMode” (which I likely incorrectly named) whereas the dictated attribute name is alarm", so maybe no event would come through for custom attributes? You can try changing the name of the attribute to alarm and report back and/or a pull request if that fixes it.

While it doesn’t seem well documented (only talked about here: http://docs.smartthings.com/en/latest/smartapp-developers-guide/example-bon-voyage.html?highlight=setlocationmode), there is a function called setLocationMode that you should call to actually do the Mode change.

Hope that helps.


(Marc) #12

See I envision using the integration to arm and disarm SimpliSafe based on my ST Home/Away status and mode changes. For example:

  1. When the house is in Away Mode in ST: Enable Away Mode in SS.
  2. When I put the house into Sleep mode every night in ST: Enable Home Mode in SS
  3. When I get up each morning according to ST, Disable the alarm in SS.

With those 3 rules, I would never need to manually enable and disable my alarm, I would imagine this would be possible, right? Eventually, I would like to also re-use some of my ST sensors also to trigger SS alarm modes, so I can save on costs of hardware and aesthetics as I hate seeing multiple sensors on each door.


(Felix Gorodishter) #13

You will be able to do those 3 mode rules in a SmartApp once we get the next bit of functionality implemented. You will also be able to have ST sensors driving SS state (off/home/away).

That being said, since there isn’t a known public accessible API for SimpliSafe to trigger the alarm itself, I don’t know of a way you would be able to not have the SS door/motion/glass-break sensors driving the alarming functionality.


(Marc) #14

Thanks! Being how unstable ST has been since its inception, I’d rather rely on SimpliSafe to handle alarm triggers. For now I’d be happy with my 3 use cases. Let me know when you need me to test!


(Bob Solimine) #15

Ah! You were correct! I modified the device type from alarmMode to “alarm” and the SmartApp now receives the subscribed events. I am now getting errors in my simulation. I will debug a bit later.

error groovy.lang.MissingMethodException: No signature of method: script14286817495671448426773.If() is applicable for argument types: (java.lang.Boolean, script14286817495671448426773$_alarmHandler_closure2) values: [false, script14286817495671448426773$_alarmHandler_closure2@227099f7]
Possible solutions: now(), url(), run(), any(), find(), is(java.lang.Object) @ line 50


(Bob Solimine) #16

My thinking is that I don’t want my alarm connected to the Internet. I don’t feel the IoT is mature enough to allow control of my alarm to an API. This is part of the reason I purchased SimpliSafe. I love the idea of directing ST based on my alarm state, though. I love the device type that reads the state but does nothing more.


(Roberto Pedro Martinez) #17

Hey Felix how do I load up those files that you linked to as a new Device?


(Marc) #18

In the IDE, Go to Device Types and add Felix’s code. Then you go Devices, add a new device and use the new device type you just created.


(Roberto Pedro Martinez) #19

Thanks for the quick reply Mbhforum… but where do I enter my username and password for simplisafe?
In the code or in the app once it detects its it and I add it?


(Marc) #20

Once the Device shows up in your “Things”, you will see a Preferences button where you enter in your credentials.