Custom Commands 1.1.4.119

I have three custom commands for my SimpliSafe Alarm. One for away, one for home and one for off. All three stopped working in RM after my firmware update. The custom commands appear to work when I select a test device in custom command configuration. But when I try to trigger it via an RM, nothin happens. I don't even see a log entry.

As a test, I rolled by HE back to 1.1.3.116 and my custom commands work just fine. Here is the code for the device driver:

/**
 *  SimpliSafe integration for SmartThings
 *
 *  Copyright 2015 Felix Gorodishter
 *  Modifications by Scott Silence
 *	Modifications by Toby Harris - 2/10/2018
 *
 *  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: "SimpliSafe Username")
	input(name: "password", type: "password", title: "Password", required: "true", description: "SimpliSafe Password")
	input(name: "ssversion", type: "enum", title: "SimpliSafe Version", required: "true", description: "Alarm system version", options: ["ss1", "ss2", "ss3"])
}

metadata {	
	definition (name: "SimpliSafe", namespace: "tobycth3", author: "Toby Harris") {
		capability "Alarm"
		capability "Polling"
       // capability "Contact Sensor"
		capability "Carbon Monoxide Detector"
		capability "Presence Sensor"
		capability "Smoke Detector"
        capability "Temperature Measurement"
        capability "Water Sensor"
		command "off"
		command "home"
		command "away"
		command "update_state"
		attribute "events", "string"
		attribute "messages", "string"
		attribute "status", "string"
	}
}

def installed() {
  init()
}

def updated() {
  unschedule()
  init()
}
  
def init() {
	log.info "Setting up Schedule (every 5 minutes)..."
    runEvery5Minutes(poll)
}

// handle commands
def off() {
	log.info "Setting SimpliSafe mode to 'Off'"
	setState ('off')
}

def home() { 
	log.info "Setting SimpliSafe mode to 'Home'"
	setState ('home')
}

def away() {
	log.info "Setting SimpliSafe mode to 'Away'"
	setState ('away')
}

def update_state() {
	log.info "Refreshing SimpliSafe state..."
	poll()
}

def setState (alState){
	//Check Auth first
	checkAuth()
    def timeout = false;
    
    if (alState == "off")
    {
    	try {
        	httpPost([ uri: getAPIUrl("alarmOff"), headers: state.auth.respAuthHeader, contentType: "application/json; charset=utf-8" ]){response ->}
        } catch (e) {
        	timeout = true;
        	log.debug "Alarm SET to OFF Error: $e"
        }
    }
    else if (alState == "home")
    {
    	try {
        	httpPost([ uri: getAPIUrl("alarmHome"), headers: state.auth.respAuthHeader, contentType: "application/json; charset=utf-8" ]){response ->}
        } catch (e) {
        	timeout = true;
        	log.debug "Alarm SET to HOME Error: $e"
        }
    }
    else if (alState == "away")
    {
    	try {
        	httpPost([ uri: getAPIUrl("alarmAway"), headers: state.auth.respAuthHeader, contentType: "application/json; charset=utf-8" ]){response ->}
        } catch (e) {
        	timeout = true;
        	log.debug "Alarm SET to AWAY Error: $e"
        }
    }
    else
    {
        log.info "Invalid state requested."
    }
    
    //If not a timeout, we can poll immediately, otherwise wait 10 seconds
    if (!timeout) {
    	poll()
    } else {
    	//There was a timeout, so we can't poll right away. Wait 10 seconds and try polling.
    	runIn(10, poll)
    }
}

def poll() {
	//Check Auth first
	checkAuth()

    log.info "Executing polling..."
   
	httpGet ([uri: getAPIUrl("refresh"), headers: state.auth.respAuthHeader, contentType: "application/json; charset=utf-8"]) { response ->
        
		//Check alarm state
		sendEvent(name: "alarm", value: response.data.subscription.location.system.alarmState)
		sendEvent(name: "status", value: response.data.subscription.location.system.alarmState)
		log.info "Alarm State1: $response.data.subscription.location.system.alarmState"
		
		//Check temperature
		if (response.data.subscription.location.system.temperature != null) {
		sendEvent(name: "temperature", value: response.data.subscription.location.system.temperature, unit: "dF")
		log.info "Temperature: $response.data.subscription.location.system.temperature"
		}
		
		//Check messages
       	if (settings.ssversion == "ss3") {
            if (response.data.subscription.location.system.messages[0] != null)
            {
				sendEvent(name: "status", value: "alert")
                sendEvent(name: "messages", value: response.data.subscription.location.system.messages[0].text)
                log.info "Messages: ${response.data.subscription.location.system.messages[0].text}"
				
				//Check for alerts
				if (response.data.subscription.location.system.messages[0].category == "alarm")
				{
				sendEvent(name: "status", value: "alarm")
				log.info "Message category: ${response.data.subscription.location.system.messages[0].category}"
				
					//Carbon Monoxide sensor alerts
					if (response.data.subscription.location.system.messages[0].data.sensorType == "C0 Detector")
					{
					sendEvent(name: "carbonMonoxide", value: "detected")
					sendEvent(name: "status", value: "carbonMonoxide")
					log.info "Message sensor: ${response.data.subscription.location.system.messages[0].data.sensorType}"
					}
					
					//Smoke sensor alerts
					if (response.data.subscription.location.system.messages[0].data.sensorType == "Smoke Detector")
					{
					sendEvent(name: "smoke", value: "detected")
					sendEvent(name: "status", value: "smoke")
					log.info "Message sensor: ${response.data.subscription.location.system.messages[0].data.sensorType}"
					}
					
				
					//Temperature sensor alerts
					//if (response.data.subscription.location.system.messages[0].data.sensorType == "Freeze Sensor")
					//{
					//sendEvent(name: "temperature", value: "??")
					//sendEvent(name: "status", value: "temperature")
					//log.info "Message sensor: ${response.data.subscription.location.system.messages[0].data.sensorType}"
					//}

					//Water sensor alerts
					if (response.data.subscription.location.system.messages[0].data.sensorType == "Water Sensor")
					{
					sendEvent(name: "water", value: "wet")
					sendEvent(name: "status", value: "water")
					log.info "Message sensor: ${response.data.subscription.location.system.messages[0].data.sensorType}"
					}
				}	
            }
            else
            {
				sendEvent(name: "messages", value: "none")
				sendEvent(name: "carbonMonoxide", value: "clear")
				sendEvent(name: "smoke", value: "clear")
				//sendEvent(name: "temperature", value: "??")
				sendEvent(name: "water", value: "dry")
                log.info "Messages: ${response.data.subscription.location.system.messages}"
            }
      	}
    }
	
		//Check events
		httpGet ([uri: getAPIUrl("events"), headers: state.auth.respAuthHeader, contentType: "application/json; charset=utf-8"]) { response ->
		if (response.data.events[0] != null) {
			sendEvent(name: "events", value: response.data.events[0].messageBody)
			log.info "Events: ${response.data.events[0].messageBody}"
    }
}	
	
	//Set presence
	def alarm_state = device.currentValue("alarm")
    log.debug "presence alarm_state: ${alarm_state}"
	def alarm_presence = ['OFF':'present', 'HOME':'present', 'AWAY':'not present']
    def presence_value = alarm_presence.getAt(alarm_state)
    log.debug "presence value ${presence_value}"
	sendEvent(name: 'presence', value: alarm_presence.getAt(alarm_state))
	

    //log.info "Alarm State2: $response"
    //apiLogout()
}

def apiLogin() {
	//Login to the system
    log.info "Executing Login..."
   
   	//Define the login Auth Body and Header Information
    def authBody = [ "grant_type":"password",
    				"device_id":"WebApp",
                    "username":settings.username,
                    "password": settings.password ]                    
    def authHeader = [ "Authorization":"Basic NGRmNTU2MjctNDZiMi00ZTJjLTg2NmItMTUyMWIzOTVkZWQyLjEtMC0wLldlYkFwcC5zaW1wbGlzYWZlLmNvbTo="	]
    
    try {
        httpPost([ uri: getAPIUrl("initAuth"), headers: authHeader, contentType: "application/json; charset=utf-8", body: authBody ]) { response ->
        	state.auth = response.data
            state.auth.respAuthHeader = ["Authorization":state.auth.token_type + " " + state.auth.access_token]
            state.auth.tokenExpiry = now() + 3600000
        }
 	} catch (e) {
    	//state.token = 
    }
    
    //Check for valid UID, and if not get it
    if (!state.uid)
   	{
    	getUserId()
   	}
    
    //Check for valid Subscription ID, and if not get it
    //Might be able to expand this to multiple systems
    if (!state.subscriptionId)
    {
    	getSubscriptionId()
    }
}

def getUserId() {
	//check auth and get uid    
    httpGet ([uri: getAPIUrl("authCheck"), headers: state.auth.respAuthHeader, contentType: "application/json; charset=utf-8"]) { response ->
        state.uid = response.data.userId
    }
    log.info "User ID: $state.uid"
}

def getSubscriptionId() {
	//get subscription id
    httpGet ([uri: getAPIUrl("subId"), headers: state.auth.respAuthHeader, contentType: "application/json; charset=utf-8"]) { response ->
    	String tsid = response.data.subscriptions.location.sid
		state.subscriptionId = tsid.substring(1, tsid.length() - 1)
    }
    log.info "Subscription ID: $state.subscriptionId"
}

def checkAuth()
{
	log.info "Checking to see if time has expired...."
        
    //If no State Auth, or now Token Expiry, or time has expired, need to relogin
    //log.info "Expiry time: $state.auth.tokenExpiry"
    if (!state.auth || !state.auth.tokenExpiry || now() > state.auth.tokenExpiry) {    
    	log.info"Token Time has expired, excecuting re-login..."
        apiLogin()
    }
    
	//Check Auth
    try {
        httpGet ([uri: getAPIUrl("authCheck"), headers: state.auth.respAuthHeader, contentType: "application/json; charset=utf-8"]) { response ->
            return response.status        
        }
    } catch (e) {
        state.clear()
        apiLogin()
        httpGet ([uri: getAPIUrl("authCheck"), headers: state.auth.respAuthHeader, contentType: "application/json; charset=utf-8"]) { response ->
            return response.status        
    }
}
}

def apiLogout() {
    httpDelete([ uri: getAPIUrl("initAuth"), headers: state.auth.respAuthHeader, contentType: "application/json; charset=utf-8" ]) { response ->
        if (response.status == 200) {
            state.subscriptionId = null
            log.info "Logged out from API."
        }
    }
}

def getTime()
{
	def tDate = new Date()
    return tDate.getTime()
}

def getAPIUrl(urlType) {
	if (urlType == "initAuth")
    {
    	return "https://api.simplisafe.com/v1/api/token"
    }
    else if (urlType == "authCheck")
    {
    	return "https://api.simplisafe.com/v1/api/authCheck"
    }
    else if (urlType == "subId" )
    {
    	return "https://api.simplisafe.com/v1/users/$state.uid/subscriptions?activeOnly=false"
    }
    else if (urlType == "alarmOff" )
    {
    	if (settings.ssversion == "ss3") 
        {
    		return "https://api.simplisafe.com/v1/$settings.ssversion/subscriptions/$state.subscriptionId/state/off"
        }
        else
        if (settings.ssversion == "ss2")
        {
        	return "https://api.simplisafe.com/v1/subscriptions/$state.subscriptionId/state?state=off"
        }       
    }
    else if (urlType == "alarmHome" )
    {
   		if (settings.ssversion == "ss3") 
        {
    		return "https://api.simplisafe.com/v1/$settings.ssversion/subscriptions/$state.subscriptionId/state/home"
        }
        else
        if (settings.ssversion == "ss2")
        {
        	return "https://api.simplisafe.com/v1/subscriptions/$state.subscriptionId/state?state=home"
        }
    }
    else if (urlType == "alarmAway" )
    {
   		if (settings.ssversion == "ss3") 
        {
    		return "https://api.simplisafe.com/v1/$settings.ssversion/subscriptions/$state.subscriptionId/state/away"
        }
        else
        {
        	return "https://api.simplisafe.com/v1/subscriptions/$state.subscriptionId/state?state=away"
        }
    }
    else if (urlType == "refresh")
    {
    	return "https://api.simplisafe.com/v1/subscriptions/$state.subscriptionId/"
    }
    else if (urlType == "events")
    {
    	return "https://api.simplisafe.com/v1/subscriptions/$state.subscriptionId/events?numEvents=1"
    }
    else
    {
    	log.info "Invalid URL type"
    }
}

Here is the rule:

Here is a screen shot of the events. During the time between 6:02 and 6:14, I tested the alarm on/off switch 3 times. The driver was never activated.

I can confirm that this has been broken as well.

I have created custom commands to fire off ifttt webhooks to set the programs on my ecobee. All stopped working via RM. I can fire them off manually with no problem. Same with other custom commands I have also created.

I have tried recreating them as well as the RM rules with no luck.

Looks like its broken.

This bug has been found and will be fixed for next hot fix release.

4 Likes

I have 8 custom commands triggered by virtual switches, and all no longer working.
I removed all rules in RM and commands, even my speakers and reconfigured all again.

Yet using the Samsung Speaker Driver to execute a preset works fine. This was working up until the update.

Thank you!!

Custom commands are fixed in the latest release:

2 Likes