Skip to content

Instantly share code, notes, and snippets.

Created November 28, 2016 02:18
Show Gist options
  • Save anonymous/8e21898568f959d2078d44afe20db6a0 to your computer and use it in GitHub Desktop.
Save anonymous/8e21898568f959d2078d44afe20db6a0 to your computer and use it in GitHub Desktop.
Scout Alarm Integration for SmartThings
/**
* Scout Alarm Integration for SmartThings
*/
import groovy.json.*
import groovy.time.*
definition(
name: "Scout Alarm",
namespace: "scoutalarm",
description: "Scout Alarm Client",
category: "Safety & Security",
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/[email protected]",
iconX3Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/[email protected]"
)
preferences {
page(name: "accountPage", title: "Enter your Scout account information", nextPage: "stModesSelection", uninstall: true) {
section {
input(
name: "email",
type: "email",
required: true,
title: "Email Address"
)
input(
name: "password",
type: "password",
required: true,
title: "Password"
)
}
}
page(name: "stModesSelection", title: "Select StartThings modes to run on", nextPage: "scoutLocationPage") {
section {
input(
name: "linkedStModes",
type: "enum",
title: "SmartThings modes to run on",
multiple: true,
required: true,
options: location.modes.name
)
}
}
page(name: "scoutLocationPage")
page(name: "preferencesPage", install: true)
}
def scoutLocationPage() {
dynamicPage(name: "scoutLocationPage", nextPage: "preferencesPage") {
section("Scout Location") {
log.debug("Running scoutLocationPage")
def scoutMemberId = getScoutMemberId()
def scoutLocations = getScoutLocations(scoutMemberId)
input(
name: "scoutLocationName",
type: "enum",
required: true,
title: "Scout Location:",
options: scoutLocations?.name
)
}
}
}
def preferencesPage() {
dynamicPage(name: "preferencesPage") {
section("Preferences") {
log.debug("Running preferencesPage")
def scoutLocationModes = getScoutLocationModes(getScoutLocationByName(settings.scoutLocationName)?.id)
log.debug("Selected Modes ${settings.linkedStModes}")
log.debug("Scout Modes ${scoutLocationModes.name}")
settings.linkedStModes.each {stMode ->
input(
name: "onSTMode_${stMode}",
type: "enum",
required: true,
title: "When ST mode ${stMode}, set:",
options: scoutLocationModes?.name
)
}
}
}
}
def installed() {
log.debug "Installed with settings: ${settings}"
initialize()
}
def updated() {
log.debug "Updated with settings: ${settings}"
unsubscribe()
initialize()
}
def initialize() {
atomicState.scoutMemberId = getScoutMemberId()
atomicState.scoutLocationId = getScoutLocationByName(settings.scoutLocationName)?.id
subscribe(location, "mode", modeChangeHandler)
}
def getApiBase() { return "https://api.scoutalarm.com/" }
def getDefaultHeaders() {
return [
"User-Agent": "okhttp/3.2.0",
"Content-Type": "application/json"
]
}
def getDefaultAuthHeaders() {
def headers = getDefaultHeaders()
headers["Authorization"] = atomicState?.jwt
return headers
}
def getScoutMemberId() {
if (!isLoggedIn()) {
return login()
}
return atomicState.scoutMemberId
}
def getScoutModeForStModeName(stModeName) {
log.debug("Getting setting for onSTMode_${stModeName}: ${settings["onSTMode_${stModeName}"]}")
return settings["onSTMode_${stModeName}"]
}
def modeChangeHandler(evt) {
log.debug "mode changed to ${evt.value}"
if (isSTModeLinked(evt.value)) {
def scoutMode = getScoutModeForStModeName(evt.value)
if (scoutMode == 'Disarmed') {
disarm()
} else {
arm(scoutMode)
}
}
}
def disarm() {
log.debug("disarm: Disarming Scout")
def armedMode = getArmedMode(atomicState.scoutLocationId)
if (armedMode) {
def body = new groovy.json.JsonOutput().toJson([
"state": "disarm",
])
def params = [
uri: getApiBase(),
path: "modes/${armedMode.id}",
headers: getDefaultAuthHeaders(),
body: body
]
try {
log.debug("disarm: Trying to disarm.")
httpPostJson(params) { resp ->
if (resp?.status == 200) {
log.debug("disarm: Successful")
} else {
log.error("disarm: 'Unexpected' Response: ${resp?.status}: ${resp?.data}")
}
}
} catch (ex) {
log.error("disarm Exception:", ex)
}
} else {
log.debug("arm: Scout is already disarmed")
}
}
def arm(scoutModeName) {
log.debug("arm: Arming Scout: ${scoutModeName}")
def armedMode = getArmedMode(atomicState.scoutLocationId)
def scoutModeToArm = getScoutModeByName(scoutModeName)
if (!armedMode) {
def body = new groovy.json.JsonOutput().toJson([
"state": "arming",
])
def params = [
uri: getApiBase(),
path: "modes/${scoutModeToArm.id}",
headers: getDefaultAuthHeaders(),
body: body
]
try {
log.debug("arm: Trying to arm.")
httpPostJson(params) { resp ->
if (resp?.status == 200) {
log.debug("arm: Successful")
} else {
log.error("arm: 'Unexpected' Response: ${resp?.status}: ${resp?.data}")
}
}
} catch (ex) {
log.error("arm Exception:", ex)
}
} else {
log.debug("arm: Scout is already armed in ${armedMode.name}")
}
}
def login() {
def scoutMemberId = null;
def body = new groovy.json.JsonOutput().toJson([
"email" : settings.email,
"password": settings.password
])
def params = [
uri: getApiBase(),
path: "auth",
headers: getDefaultHeaders(),
body: body
]
try {
log.debug("getJWT: Trying to login.")
httpPostJson(params) { resp ->
if (resp?.status == 200) {
log.debug("getJWT: Successful")
atomicState.jwt = resp.data.jwt
def jsonSlurper = new JsonSlurper()
def parsedJwt = resp.data.jwt.split(/\./)[1]
parsedJwt = new String(parsedJwt?.decodeBase64(), "UTF-8")
parsedJwt = jsonSlurper.parseText(parsedJwt)
scoutMemberId = parsedJwt?.id
atomicState.jwtExpireTime = parsedJwt?.exp
atomicState.scoutMemberId = scoutMemberId
} else {
log.error("getJWT: 'Unexpected' Response: ${resp?.status}: ${resp?.data}")
}
}
} catch (ex) {
log.error("getJWT Exception:", ex)
}
return scoutMemberId
}
def isLoggedIn() {
if (atomicState?.scoutMemberId == null || atomicState?.jwtExpireTime == null || atomicState?.jwtExpireTime <= (new Date().getTime() / 1000l)) {
return false
}
return true
}
def isSTModeLinked(stModeName) {
return settings.linkedStModes.find { stModeName }
}
def getScoutLocations(scoutMemberId) {
def scoutLocations = null
def params = [
uri: getApiBase(),
path: "members/${scoutMemberId}/locations",
headers: getDefaultAuthHeaders(),
]
try {
httpGet(params) { resp ->
if (resp?.status == 200) {
log.debug("getLocations: Successful")
scoutLocations = resp.data
atomicState.scoutLocations = scoutLocations
} else {
log.warn("getLocations: 'Unexpected' Response: ${resp?.status}: ${resp?.data}")
}
}
} catch (groovyx.net.http.HttpResponseException ex) {
if (ex.getStatusCode() == 401) {
log.error("getLocations: Authentication failed")
} else {
log.error("getLocations: Exception ${ex.getStatusCode()}:", ex)
}
}
return scoutLocations
}
def getArmedMode(scoutLocationId) {
def scoutModes = getScoutLocationModes(scoutLocationId)
def armedMode = scoutModes.find { scoutMode -> scoutMode.state == 'armed' || scoutMode.state == 'arming' }
log.debug("Found armedMode: ${armedMode}")
return armedMode;
}
def getScoutModeByName(scoutModeName) {
return atomicState.scoutModes.find { scoutMode -> scoutMode.name == scoutModeName }
}
def getScoutLocationByName(scoutLocationName) {
return atomicState.scoutLocations.find {
name: scoutLocationName
}
}
def getScoutLocationModes(scoutLocationId) {
if (scoutLocationId == null) {
log.warn('getLocationModes: location id was null.')
return null
}
def scoutModes = null
def params = [
uri: getApiBase(),
path: "locations/${scoutLocationId}/modes",
headers: getDefaultAuthHeaders(),
]
try {
httpGet(params) { resp ->
if (resp?.status == 200) {
log.debug("getLocationModes: Successful")
scoutModes = resp.data
scoutModes << [name: 'Disarmed']
atomicState.scoutModes = scoutModes
} else {
log.warn("getLocationModes: 'Unexpected' Response: ${resp?.status}: ${resp?.data}")
}
}
} catch (groovyx.net.http.HttpResponseException ex) {
if (ex.getStatusCode() == 401) {
log.error("getLocationModes: Authentication failed")
} else {
log.error("getLocationModes: Exception ${ex.getStatusCode()}:", ex)
}
}
return scoutModes
}
0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment