mirror of
https://github.com/mtan93/SmartThingsPublic.git
synced 2026-03-09 05:11:52 +00:00
Compare commits
24 Commits
MSA-2038-2
...
MSA-2062-1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
513e44912c | ||
|
|
505ec4ff0f | ||
|
|
fe0f555f10 | ||
|
|
6e1701f955 | ||
|
|
9f5378c2b6 | ||
|
|
39e828b16d | ||
|
|
42353044e6 | ||
|
|
78f06a0b9d | ||
|
|
7f13dd356d | ||
|
|
4042bbbf98 | ||
|
|
20407441d9 | ||
|
|
52925d6d99 | ||
|
|
e6c17131af | ||
|
|
cc70865fbf | ||
|
|
91a77afb2d | ||
|
|
006ffa8a0b | ||
|
|
17465c87c0 | ||
|
|
370b435874 | ||
|
|
be9bdae4cc | ||
|
|
c278e035f6 | ||
|
|
e53d9c910c | ||
|
|
c13014936b | ||
|
|
ea3abb26c0 | ||
|
|
c4c2a3ffd7 |
18
build.gradle
18
build.gradle
@@ -9,7 +9,7 @@ apply plugin: 'smartthings-slack'
|
||||
|
||||
buildscript {
|
||||
dependencies {
|
||||
classpath "com.smartthings.deployment:executable-deployment-scripts:1.0.11"
|
||||
classpath "com.smartthings.deployment:executable-deployment-scripts:1.0.12"
|
||||
}
|
||||
repositories {
|
||||
mavenLocal()
|
||||
@@ -19,7 +19,7 @@ buildscript {
|
||||
username smartThingsArtifactoryUserName
|
||||
password smartThingsArtifactoryPassword
|
||||
}
|
||||
url "https://artifactory.smartthings.com/libs-release-local"
|
||||
url "https://smartthings.jfrog.io/smartthings/libs-release-local"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ repositories {
|
||||
username smartThingsArtifactoryUserName
|
||||
password smartThingsArtifactoryPassword
|
||||
}
|
||||
url "https://artifactory.smartthings.com/libs-release-local"
|
||||
url "https://smartthings.jfrog.io/smartthings/libs-release-local"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,10 +51,10 @@ sourceSets {
|
||||
|
||||
dependencies {
|
||||
devicetypesCompile 'org.codehaus.groovy:groovy-all:2.4.7'
|
||||
devicetypesCompile 'smartthings:appengine-z-wave:0.1.2'
|
||||
devicetypesCompile 'smartthings:appengine-zigbee:0.1.11'
|
||||
devicetypesCompile 'smartthings:appengine-z-wave:0.1.3'
|
||||
devicetypesCompile 'smartthings:appengine-zigbee:0.1.12'
|
||||
smartappsCompile 'org.codehaus.groovy:groovy-all:2.4.7'
|
||||
smartappsCompile 'smartthings:appengine-common:0.1.8'
|
||||
smartappsCompile 'smartthings:appengine-common:0.1.9'
|
||||
smartappsCompile 'org.codehaus.groovy.modules.http-builder:http-builder:0.7.1'
|
||||
smartappsCompile 'org.grails:grails-web:2.3.11'
|
||||
smartappsCompile 'org.json:json:20140107'
|
||||
@@ -74,19 +74,19 @@ slackSendMessage {
|
||||
String username
|
||||
switch (branch) {
|
||||
case 'master':
|
||||
username = 'Hickory'
|
||||
username = 'DEV'
|
||||
iconUrl = wolverine
|
||||
color = '#35D0F2'
|
||||
messageText = 'Began deployment of _SmartThingsPublic[master]_ branch to the _Dev_ environments.'
|
||||
break
|
||||
case 'staging':
|
||||
username = 'Dickory'
|
||||
username = 'STG'
|
||||
iconUrl = beach
|
||||
color = '#FFDE20'
|
||||
messageText = 'Began deployment of _SmartThingsPublic[staging]_ branch to the _Staging_ environments.'
|
||||
break
|
||||
case 'production':
|
||||
username = 'Dock'
|
||||
username = 'PRD'
|
||||
iconUrl = drinks
|
||||
color = '#FF1D23'
|
||||
messageText = 'Began deployment of _SmartThingsPublic[production]_ branch to the _Prod_ environments.'
|
||||
|
||||
2
devicetypes/smartthings/aeon-key-fob.src/.st-ignore
Normal file
2
devicetypes/smartthings/aeon-key-fob.src/.st-ignore
Normal file
@@ -0,0 +1,2 @@
|
||||
.st-ignore
|
||||
README.md
|
||||
34
devicetypes/smartthings/aeon-key-fob.src/README.md
Normal file
34
devicetypes/smartthings/aeon-key-fob.src/README.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# Aeon Labs Key Fob
|
||||
|
||||
Cloud Execution
|
||||
|
||||
Works with:
|
||||
|
||||
* [Aeon Labs Key Fob](http://aeotec.com/z-wave-key-fob-remote-control)
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Capabilities](#capabilities)
|
||||
* [Health](#device-health)
|
||||
* [Troubleshooting](#troubleshooting)
|
||||
|
||||
## Capabilities
|
||||
|
||||
* **Actuator** - represents device has commands
|
||||
* **Button** - represents a device with one or more buttons
|
||||
* **Holdable Button** - represents a device with one or more holdable buttons
|
||||
* **Configuration** - allows for configuration of devices
|
||||
* **Sensor** - detects sensor events
|
||||
* **Battery** - defines device uses a battery
|
||||
* **Health Check** - indicates ability to get device health notifications
|
||||
|
||||
## Device Health
|
||||
|
||||
Aeon Key Fob is a ZWave totally sleepy device and is marked offline only in the case when Hub is offline.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the sensor is out of range.
|
||||
Pairing needs to be tried again by placing the sensor closer to the hub.
|
||||
Instructions related to pairing, resetting and removing the Aeon Labs Key Fob from SmartThings can be found in the following link:
|
||||
* [Aeotec Key Fob Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/202294120-Aeon-Labs-Key-Fob)
|
||||
@@ -1,3 +1,4 @@
|
||||
import groovy.json.JsonOutput
|
||||
/**
|
||||
* Copyright 2015 SmartThings
|
||||
*
|
||||
@@ -19,6 +20,7 @@ metadata {
|
||||
capability "Configuration"
|
||||
capability "Sensor"
|
||||
capability "Battery"
|
||||
capability "Health Check"
|
||||
|
||||
fingerprint deviceId: "0x0101", inClusters: "0x86,0x72,0x70,0x80,0x84,0x85"
|
||||
fingerprint mfr: "0086", prod: "0001", model: "0026", deviceJoinName: "Aeon Panic Button"
|
||||
@@ -131,6 +133,9 @@ def updated() {
|
||||
}
|
||||
|
||||
def initialize() {
|
||||
// Arrival sensors only goes OFFLINE when Hub is off
|
||||
sendEvent(name: "DeviceWatch-Enroll", value: JsonOutput.toJson([protocol: "zigbee", scheme:"untracked"]), displayed: false)
|
||||
|
||||
def zwMap = getZwaveInfo()
|
||||
def buttons = 4 // Default for Key Fob
|
||||
|
||||
|
||||
2
devicetypes/smartthings/aeon-minimote.src/.st-ignore
Normal file
2
devicetypes/smartthings/aeon-minimote.src/.st-ignore
Normal file
@@ -0,0 +1,2 @@
|
||||
.st-ignore
|
||||
README.md
|
||||
33
devicetypes/smartthings/aeon-minimote.src/README.md
Normal file
33
devicetypes/smartthings/aeon-minimote.src/README.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# Aeon Minimote
|
||||
|
||||
Cloud Execution
|
||||
|
||||
Works with:
|
||||
|
||||
* [Aeotec Minimote](http://aeotec.com/small-z-wave-remote-control)
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Capabilities](#capabilities)
|
||||
* [Health](#device-health)
|
||||
* [Troubleshooting](#troubleshooting)
|
||||
|
||||
## Capabilities
|
||||
|
||||
* **Actuator** - represents device has commands
|
||||
* **Button** - represents a device with one or more buttons
|
||||
* **Holdable Button** - represents a device with one or more holdable buttons
|
||||
* **Configuration** - allows for configuration of devices
|
||||
* **Sensor** - detects sensor events
|
||||
* **Health Check** - indicates ability to get device health notifications
|
||||
|
||||
## Device Health
|
||||
|
||||
Aeon Minimote is a ZWave totally sleepy device and is marked offline only in the case when Hub is offline.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the sensor is out of range.
|
||||
Pairing needs to be tried again by placing the sensor closer to the hub.
|
||||
Instructions related to pairing, resetting and removing the Aeotec Minimote from SmartThings can be found in the following link:
|
||||
* [Aeotec Minimote Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/202087904-Aeotec-Minimote)
|
||||
@@ -1,3 +1,4 @@
|
||||
import groovy.json.JsonOutput
|
||||
/**
|
||||
* Copyright 2015 SmartThings
|
||||
*
|
||||
@@ -18,6 +19,7 @@ metadata {
|
||||
capability "Holdable Button"
|
||||
capability "Configuration"
|
||||
capability "Sensor"
|
||||
capability "Health Check"
|
||||
|
||||
fingerprint deviceId: "0x0101", inClusters: "0x86,0x72,0x70,0x9B", outClusters: "0x26,0x2B"
|
||||
fingerprint deviceId: "0x0101", inClusters: "0x86,0x72,0x70,0x9B,0x85,0x84", outClusters: "0x26" // old style with numbered buttons
|
||||
@@ -119,5 +121,7 @@ def updated() {
|
||||
}
|
||||
|
||||
def initialize() {
|
||||
// Arrival sensors only goes OFFLINE when Hub is off
|
||||
sendEvent(name: "DeviceWatch-Enroll", value: JsonOutput.toJson([protocol: "zigbee", scheme:"untracked"]), displayed: false)
|
||||
sendEvent(name: "numberOfButtons", value: 4)
|
||||
}
|
||||
|
||||
@@ -300,15 +300,21 @@ def setColor(value) {
|
||||
value.hex = "#${hex(value.red)}${hex(value.green)}${hex(value.blue)}"
|
||||
}
|
||||
|
||||
sendEvent(name: "hue", value: value.hue, displayed: false)
|
||||
sendEvent(name: "saturation", value: value.saturation, displayed: false)
|
||||
sendEvent(name: "color", value: value.hex, displayed: false)
|
||||
if (value.level) {
|
||||
sendEvent(name: "level", value: value.level)
|
||||
}
|
||||
if (value.switch) {
|
||||
sendEvent(name: "switch", value: value.switch)
|
||||
}
|
||||
if(value.hue) {
|
||||
sendEvent(name: "hue", value: value.hue, displayed: false)
|
||||
}
|
||||
if(value.saturation) {
|
||||
sendEvent(name: "saturation", value: value.saturation, displayed: false)
|
||||
}
|
||||
if(value.hex?.trim()) {
|
||||
sendEvent(name: "color", value: value.hex, displayed: false)
|
||||
}
|
||||
if (value.level) {
|
||||
sendEvent(name: "level", value: value.level)
|
||||
}
|
||||
if (value.switch?.trim()) {
|
||||
sendEvent(name: "switch", value: value.switch)
|
||||
}
|
||||
|
||||
sendRGB(value.rh, value.gh, value.bh)
|
||||
}
|
||||
|
||||
@@ -35,8 +35,8 @@ metadata {
|
||||
|
||||
// tile definitions
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"valve", type: "generic", width: 6, height: 4, canChangeIcon: true){
|
||||
tileAttribute ("device.valve", key: "PRIMARY_CONTROL") {
|
||||
multiAttributeTile(name:"contact", type: "generic", width: 6, height: 4, canChangeIcon: true){
|
||||
tileAttribute ("device.contact", key: "PRIMARY_CONTROL") {
|
||||
attributeState "open", label: '${name}', action: "valve.close", icon: "st.valves.water.open", backgroundColor: "#00A0DC", nextState:"closing"
|
||||
attributeState "closed", label: '${name}', action: "valve.open", icon: "st.valves.water.closed", backgroundColor: "#ffffff", nextState:"opening"
|
||||
attributeState "opening", label: '${name}', action: "valve.close", icon: "st.valves.water.open", backgroundColor: "#00A0DC"
|
||||
@@ -48,8 +48,8 @@ metadata {
|
||||
state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
|
||||
}
|
||||
|
||||
main "valve"
|
||||
details(["valve","refresh"])
|
||||
main "contact"
|
||||
details(["contact","refresh"])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,8 +38,8 @@ metadata {
|
||||
}
|
||||
|
||||
tiles(scale: 2) {
|
||||
multiAttributeTile(name:"valve", type: "generic", width: 6, height: 4, canChangeIcon: true){
|
||||
tileAttribute ("device.valve", key: "PRIMARY_CONTROL") {
|
||||
multiAttributeTile(name:"contact", type: "generic", width: 6, height: 4, canChangeIcon: true){
|
||||
tileAttribute ("device.contact", key: "PRIMARY_CONTROL") {
|
||||
attributeState "open", label: '${name}', action: "valve.close", icon: "st.valves.water.open", backgroundColor: "#00A0DC", nextState:"closing"
|
||||
attributeState "closed", label: '${name}', action: "valve.open", icon: "st.valves.water.closed", backgroundColor: "#ffffff", nextState:"opening"
|
||||
attributeState "opening", label: '${name}', action: "valve.close", icon: "st.valves.water.open", backgroundColor: "#00A0DC", nextState:"closing"
|
||||
@@ -58,8 +58,8 @@ metadata {
|
||||
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
|
||||
}
|
||||
|
||||
main(["valve"])
|
||||
details(["valve", "battery", "refresh"])
|
||||
main(["contact"])
|
||||
details(["contact", "battery", "refresh"])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -195,7 +195,10 @@ def registerDeviceChange() {
|
||||
state.deviceSubscriptionMap.put(deviceId, [subscriptionEndpt])
|
||||
log.info "Added subscription URL: ${subscriptionEndpt} for ${myDevice.displayName}"
|
||||
} else if (!state.deviceSubscriptionMap[deviceId].contains(subscriptionEndpt)) {
|
||||
state.deviceSubscriptionMap[deviceId] << subscriptionEndpt
|
||||
// state.deviceSubscriptionMap[deviceId] << subscriptionEndpt
|
||||
// For now, we will only have one subscription endpoint per device
|
||||
state.deviceSubscriptionMap.remove(deviceId)
|
||||
state.deviceSubscriptionMap.put(deviceId, [subscriptionEndpt])
|
||||
log.info "Added subscription URL: ${subscriptionEndpt} for ${myDevice.displayName}"
|
||||
}
|
||||
|
||||
|
||||
@@ -1,447 +0,0 @@
|
||||
/**
|
||||
* JSON Complete API
|
||||
*
|
||||
* Copyright 2017 Paul Lovelace
|
||||
*
|
||||
*/
|
||||
definition(
|
||||
name: "JSON Complete API",
|
||||
namespace: "smartthings",
|
||||
author: "Ashok Malhotra",
|
||||
description: "API for JSON with complete set of devices",
|
||||
category: "SmartThings Labs",
|
||||
iconUrl: "https://raw.githubusercontent.com/pdlove/homebridge-smartthings/master/smartapps/JSON%401.png",
|
||||
iconX2Url: "https://raw.githubusercontent.com/pdlove/homebridge-smartthings/master/smartapps/JSON%402.png",
|
||||
iconX3Url: "https://raw.githubusercontent.com/pdlove/homebridge-smartthings/master/smartapps/JSON%403.png",
|
||||
oauth: true)
|
||||
|
||||
|
||||
preferences {
|
||||
page(name: "copyConfig")
|
||||
}
|
||||
|
||||
//When adding device groups, need to add here
|
||||
def copyConfig() {
|
||||
if (!state.accessToken) {
|
||||
createAccessToken()
|
||||
}
|
||||
dynamicPage(name: "copyConfig", title: "Configure Devices", install:true, uninstall:true) {
|
||||
section("Select devices to include in the /devices API call") {
|
||||
paragraph "Version 0.5.5"
|
||||
input "deviceList", "capability.refresh", title: "Most Devices", multiple: true, required: false
|
||||
input "sensorList", "capability.sensor", title: "Sensor Devices", multiple: true, required: false
|
||||
input "switchList", "capability.switch", title: "All Switches", multiple: true, required: false
|
||||
//paragraph "Devices Selected: ${deviceList ? deviceList?.size() : 0}\nSensors Selected: ${sensorList ? sensorList?.size() : 0}\nSwitches Selected: ${switchList ? switchList?.size() : 0}"
|
||||
}
|
||||
section("Configure Pubnub") {
|
||||
input "pubnubSubscribeKey", "text", title: "PubNub Subscription Key", multiple: false, required: false
|
||||
input "pubnubPublishKey", "text", title: "PubNub Publish Key", multiple: false, required: false
|
||||
input "subChannel", "text", title: "Channel (Can be anything)", multiple: false, required: false
|
||||
}
|
||||
section() {
|
||||
paragraph "View this SmartApp's configuration to use it in other places."
|
||||
href url:"${apiServerUrl("/api/smartapps/installations/${app.id}/config?access_token=${state.accessToken}")}", style:"embedded", required:false, title:"Config", description:"Tap, select, copy, then click \"Done\""
|
||||
}
|
||||
|
||||
section() {
|
||||
paragraph "View the JSON generated from the installed devices."
|
||||
href url:"${apiServerUrl("/api/smartapps/installations/${app.id}/devices?access_token=${state.accessToken}")}", style:"embedded", required:false, title:"Device Results", description:"View accessories JSON"
|
||||
}
|
||||
section() {
|
||||
paragraph "Enter the name you would like shown in the smart app list"
|
||||
label title:"SmartApp Label (optional)", required: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def renderDevices() {
|
||||
def deviceData = []
|
||||
deviceList.each {
|
||||
try {
|
||||
deviceData << [name: it.displayName,
|
||||
basename: it.name,
|
||||
deviceid: it.id,
|
||||
status: it.status,
|
||||
manufacturerName: it.getManufacturerName(),
|
||||
modelName: it.getModelName(),
|
||||
lastTime: it.getLastActivity(),
|
||||
capabilities: deviceCapabilityList(it),
|
||||
commands: deviceCommandList(it),
|
||||
attributes: deviceAttributeList(it)
|
||||
]
|
||||
} catch (e) {
|
||||
log.error("Error Occurred Parsing Device "+it.displayName+", Error " + e)
|
||||
}
|
||||
}
|
||||
sensorList.each {
|
||||
try {
|
||||
deviceData << [name: it.displayName,
|
||||
basename: it.name,
|
||||
deviceid: it.id,
|
||||
status: it.status,
|
||||
manufacturerName: it.getManufacturerName(),
|
||||
modelName: it.getModelName(),
|
||||
lastTime: it.getLastActivity(),
|
||||
capabilities: deviceCapabilityList(it),
|
||||
commands: deviceCommandList(it),
|
||||
attributes: deviceAttributeList(it)
|
||||
]
|
||||
} catch (e) {
|
||||
log.error("Error Occurred Parsing Device "+it.displayName+", Error " + e)
|
||||
}
|
||||
}
|
||||
switchList.each {
|
||||
try {
|
||||
deviceData << [name: it.displayName,
|
||||
basename: it.name,
|
||||
deviceid: it.id,
|
||||
status: it.status,
|
||||
manufacturerName: it.getManufacturerName(),
|
||||
modelName: it.getModelName(),
|
||||
lastTime: it.getLastActivity(),
|
||||
capabilities: deviceCapabilityList(it),
|
||||
commands: deviceCommandList(it),
|
||||
attributes: deviceAttributeList(it)
|
||||
]
|
||||
} catch (e) {
|
||||
log.error("Error Occurred Parsing Device "+it.displayName+", Error " + e)
|
||||
}
|
||||
}
|
||||
return deviceData
|
||||
}
|
||||
|
||||
def findDevice(paramid) {
|
||||
def device = deviceList.find { it.id == paramid }
|
||||
if (device) return device
|
||||
device = sensorList.find { it.id == paramid }
|
||||
if (device) return device
|
||||
device = switchList.find { it.id == paramid }
|
||||
|
||||
return device
|
||||
}
|
||||
//No more individual device group definitions after here.
|
||||
|
||||
|
||||
def installed() {
|
||||
log.debug "Installed with settings: ${settings}"
|
||||
initialize()
|
||||
}
|
||||
|
||||
def updated() {
|
||||
log.debug "Updated with settings: ${settings}"
|
||||
unsubscribe()
|
||||
initialize()
|
||||
}
|
||||
|
||||
def initialize() {
|
||||
if(!state.accessToken) {
|
||||
createAccessToken()
|
||||
}
|
||||
registerAll()
|
||||
state.subscriptionRenewed = 0
|
||||
subscribe(location, null, HubResponseEvent, [filterEvents:false])
|
||||
log.debug "0.5.5"
|
||||
}
|
||||
|
||||
def authError() {
|
||||
[error: "Permission denied"]
|
||||
}
|
||||
def renderConfig() {
|
||||
def configJson = new groovy.json.JsonOutput().toJson([
|
||||
description: "JSON API",
|
||||
platforms: [
|
||||
[
|
||||
platform: "SmartThings",
|
||||
name: "SmartThings",
|
||||
app_url: apiServerUrl("/api/smartapps/installations/"),
|
||||
app_id: app.id,
|
||||
access_token: state.accessToken
|
||||
]
|
||||
],
|
||||
])
|
||||
|
||||
def configString = new groovy.json.JsonOutput().prettyPrint(configJson)
|
||||
render contentType: "text/plain", data: configString
|
||||
}
|
||||
def renderLocation() {
|
||||
[
|
||||
latitude: location.latitude,
|
||||
longitude: location.longitude,
|
||||
mode: location.mode,
|
||||
name: location.name,
|
||||
temperature_scale: location.temperatureScale,
|
||||
zip_code: location.zipCode,
|
||||
hubIP: location.hubs[0].localIP,
|
||||
smartapp_version: '0.5.5'
|
||||
]
|
||||
}
|
||||
def CommandReply(statusOut, messageOut) {
|
||||
def replyData =
|
||||
[
|
||||
status: statusOut,
|
||||
message: messageOut
|
||||
]
|
||||
|
||||
def replyJson = new groovy.json.JsonOutput().toJson(replyData)
|
||||
render contentType: "application/json", data: replyJson
|
||||
}
|
||||
def deviceCommand() {
|
||||
log.info("Command Request")
|
||||
def device = findDevice(params.id)
|
||||
def command = params.command
|
||||
|
||||
if (!device) {
|
||||
log.error("Device Not Found")
|
||||
CommandReply("Failure", "Device Not Found")
|
||||
} else if (!device.hasCommand(command)) {
|
||||
log.error("Device "+device.displayName+" does not have the command "+command)
|
||||
CommandReply("Failure", "Device "+device.displayName+" does not have the command "+command)
|
||||
} else {
|
||||
def value1 = request.JSON?.value1
|
||||
def value2 = request.JSON?.value2
|
||||
try {
|
||||
if (value2) {
|
||||
device."$command"(value1,value2)
|
||||
} else if (value1) {
|
||||
device."$command"(value1)
|
||||
} else {
|
||||
device."$command"()
|
||||
}
|
||||
log.info("Command Successful for Device "+device.displayName+", Command "+command)
|
||||
CommandReply("Success", "Device "+device.displayName+", Command "+command)
|
||||
} catch (e) {
|
||||
log.error("Error Occurred For Device "+device.displayName+", Command "+command)
|
||||
CommandReply("Failure", "Error Occurred For Device "+device.displayName+", Command "+command)
|
||||
}
|
||||
}
|
||||
}
|
||||
def deviceAttribute() {
|
||||
def device = findDevice(params.id)
|
||||
def attribute = params.attribute
|
||||
if (!device) {
|
||||
httpError(404, "Device not found")
|
||||
} else {
|
||||
def currentValue = device.currentValue(attribute)
|
||||
[currentValue: currentValue]
|
||||
}
|
||||
}
|
||||
def deviceQuery() {
|
||||
def device = findDevice(params.id)
|
||||
if (!device) {
|
||||
device = null
|
||||
httpError(404, "Device not found")
|
||||
}
|
||||
|
||||
if (result) {
|
||||
def jsonData =
|
||||
[
|
||||
name: device.displayName,
|
||||
deviceid: device.id,
|
||||
capabilities: deviceCapabilityList(device),
|
||||
commands: deviceCommandList(device),
|
||||
attributes: deviceAttributeList(device)
|
||||
]
|
||||
def resultJson = new groovy.json.JsonOutput().toJson(jsonData)
|
||||
render contentType: "application/json", data: resultJson
|
||||
}
|
||||
}
|
||||
def deviceCapabilityList(device) {
|
||||
def i=0
|
||||
device.capabilities.collectEntries { capability->
|
||||
[
|
||||
(capability.name):1
|
||||
]
|
||||
}
|
||||
}
|
||||
def deviceCommandList(device) {
|
||||
def i=0
|
||||
device.supportedCommands.collectEntries { command->
|
||||
[
|
||||
(command.name): (command.arguments)
|
||||
]
|
||||
}
|
||||
}
|
||||
def deviceAttributeList(device) {
|
||||
device.supportedAttributes.collectEntries { attribute->
|
||||
try {
|
||||
[
|
||||
(attribute.name): device.currentValue(attribute.name)
|
||||
]
|
||||
} catch(e) {
|
||||
[
|
||||
(attribute.name): null
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
def getAllData() {
|
||||
//Since we're about to send all of the data, we'll count this as a subscription renewal and clear out pending changes.
|
||||
state.subscriptionRenewed = now()
|
||||
state.devchanges = []
|
||||
|
||||
|
||||
def deviceData =
|
||||
[ location: renderLocation(),
|
||||
deviceList: renderDevices() ]
|
||||
def deviceJson = new groovy.json.JsonOutput().toJson(deviceData)
|
||||
render contentType: "application/json", data: deviceJson
|
||||
}
|
||||
def startSubscription() {
|
||||
//This simply registers the subscription.
|
||||
state.subscriptionRenewed = now()
|
||||
def deviceJson = new groovy.json.JsonOutput().toJson([status: "Success"])
|
||||
render contentType: "application/json", data: deviceJson
|
||||
}
|
||||
def endSubscription() {
|
||||
//Because it takes too long to register for an api command, we don't actually unregister.
|
||||
//We simply blank the devchanges and change the subscription renewal to two hours ago.
|
||||
state.devchanges = []
|
||||
state.subscriptionRenewed = 0
|
||||
def deviceJson = new groovy.json.JsonOutput().toJson([status: "Success"])
|
||||
render contentType: "application/json", data: deviceJson
|
||||
}
|
||||
def registerAll() {
|
||||
//This has to be done at startup because it takes too long for a normal command.
|
||||
log.debug "Registering All Events"
|
||||
state.devchanges = []
|
||||
registerChangeHandler(deviceList)
|
||||
registerChangeHandler(sensorList)
|
||||
registerChangeHandler(switchList)
|
||||
}
|
||||
def registerChangeHandler(myList) {
|
||||
myList.each { myDevice ->
|
||||
def theAtts = myDevice.supportedAttributes
|
||||
theAtts.each {att ->
|
||||
subscribe(myDevice, att.name, changeHandler)
|
||||
log.debug "Registering ${myDevice.displayName}.${att.name}"
|
||||
}
|
||||
}
|
||||
}
|
||||
def changeHandler(evt) {
|
||||
//Send to Pubnub if we need to.
|
||||
if (pubnubPublishKey!=null) {
|
||||
def deviceData = [device: evt.deviceId, attribute: evt.name, value: evt.value, date: evt.date]
|
||||
def changeJson = new groovy.json.JsonOutput().toJson(deviceData)
|
||||
def changeData = URLEncoder.encode(changeJson)
|
||||
def uri = "http://pubsub.pubnub.com/publish/${pubnubPublishKey}/${pubnubSubscribeKey}/0/${subChannel}/0/${changeData}"
|
||||
log.debug "${uri}"
|
||||
httpGet(uri)
|
||||
}
|
||||
|
||||
if (state.directIP!="") {
|
||||
//Send Using the Direct Mechanism
|
||||
def deviceData = [device: evt.deviceId, attribute: evt.name, value: evt.value, date: evt.date]
|
||||
//How do I control the port?!?
|
||||
log.debug "Sending Update to ${state.directIP}:${state.directPort}"
|
||||
def result = new physicalgraph.device.HubAction(
|
||||
method: "GET",
|
||||
path: "/update",
|
||||
headers: [
|
||||
HOST: "${state.directIP}:${state.directPort}",
|
||||
change_device: evt.deviceId,
|
||||
change_attribute: evt.name,
|
||||
change_value: evt.value,
|
||||
change_date: evt.date
|
||||
]
|
||||
)
|
||||
sendHubCommand(result)
|
||||
}
|
||||
|
||||
//Only add to the state's devchanges if the endpoint has renewed in the last 10 minutes.
|
||||
if (state.subscriptionRenewed>(now()-(1000*60*10))) {
|
||||
if (evt.isStateChange()) {
|
||||
state.devchanges << [device: evt.deviceId, attribute: evt.name, value: evt.value, date: evt.date]
|
||||
}
|
||||
} else if (state.subscriptionRenewed>0) { //Otherwise, clear it
|
||||
log.debug "Endpoint Subscription Expired. No longer storing changes for devices."
|
||||
state.devchanges=[]
|
||||
state.subscriptionRenewed=0
|
||||
}
|
||||
}
|
||||
def getChangeEvents() {
|
||||
//Store the changes so we can swap it out very quickly and eliminate the possibility of losing any.
|
||||
//This is mainly to make this thread safe because I'm willing to bet that a change event can fire
|
||||
//while generating/sending the JSON.
|
||||
def oldchanges = state.devchanges
|
||||
state.devchanges=[]
|
||||
state.subscriptionRenewed = now()
|
||||
if (oldchanges.size()==0) {
|
||||
def deviceJson = new groovy.json.JsonOutput().toJson([status: "None"])
|
||||
render contentType: "application/json", data: deviceJson
|
||||
} else {
|
||||
def changeJson = new groovy.json.JsonOutput().toJson([status: "Success", attributes:oldchanges])
|
||||
render contentType: "application/json", data: changeJson
|
||||
}
|
||||
}
|
||||
def enableDirectUpdates() {
|
||||
log.debug("Command Request")
|
||||
state.directIP = params.ip
|
||||
state.directPort = params.port
|
||||
log.debug("Trying ${state.directIP}:${state.directPort}")
|
||||
def result = new physicalgraph.device.HubAction(
|
||||
method: "GET",
|
||||
path: "/initial",
|
||||
headers: [
|
||||
HOST: "${state.directIP}:${state.directPort}"
|
||||
],
|
||||
query: deviceData
|
||||
)
|
||||
sendHubCommand(result)
|
||||
}
|
||||
|
||||
def HubResponseEvent(evt) {
|
||||
log.debug(evt.description)
|
||||
}
|
||||
|
||||
def locationHandler(evt) {
|
||||
def description = evt.description
|
||||
def hub = evt?.hubId
|
||||
|
||||
log.debug "cp desc: " + description
|
||||
if (description.count(",") > 4)
|
||||
{
|
||||
def bodyString = new String(description.split(',')[5].split(":")[1].decodeBase64())
|
||||
log.debug(bodyString)
|
||||
}
|
||||
}
|
||||
|
||||
def getSubscriptionService() {
|
||||
def replyData =
|
||||
[
|
||||
pubnub_publishkey: pubnubPublishKey,
|
||||
pubnub_subscribekey: pubnubSubscribeKey,
|
||||
pubnub_channel: subChannel
|
||||
]
|
||||
|
||||
def replyJson = new groovy.json.JsonOutput().toJson(replyData)
|
||||
render contentType: "application/json", data: replyJson
|
||||
}
|
||||
|
||||
mappings {
|
||||
if (!params.access_token || (params.access_token && params.access_token != state.accessToken)) {
|
||||
path("/devices") { action: [GET: "authError"] }
|
||||
path("/config") { action: [GET: "authError"] }
|
||||
path("/location") { action: [GET: "authError"] }
|
||||
path("/:id/command/:command") { action: [POST: "authError"] }
|
||||
path("/:id/query") { action: [GET: "authError"] }
|
||||
path("/:id/attribute/:attribute") { action: [GET: "authError"] }
|
||||
path("/subscribe") { action: [GET: "authError"] }
|
||||
path("/getUpdates") { action: [GET: "authError"] }
|
||||
path("/unsubscribe") { action: [GET: "authError"] }
|
||||
path("/startDirect/:ip/:port") { action: [GET: "authError"] }
|
||||
path("/getSubcriptionService") { action: [GET: "authError"] }
|
||||
|
||||
} else {
|
||||
path("/devices") { action: [GET: "getAllData"] }
|
||||
path("/config") { action: [GET: "renderConfig"] }
|
||||
path("/location") { action: [GET: "renderLocation"] }
|
||||
path("/:id/command/:command") { action: [POST: "deviceCommand"] }
|
||||
path("/:id/query") { action: [GET: "deviceQuery"] }
|
||||
path("/:id/attribute/:attribute") { action: [GET: "deviceAttribute"] }
|
||||
path("/subscribe") { action: [GET: "startSubscription"] }
|
||||
path("/getUpdates") { action: [GET: "getChangeEvents"] }
|
||||
path("/unsubscribe") { action: [GET: "endSubscription"] }
|
||||
path("/startDirect/:ip/:port") { action: [GET: "enableDirectUpdates"] }
|
||||
path("/getSubcriptionService") { action: [GET: "getSubscriptionService"] }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user