mirror of
https://github.com/mtan93/SmartThingsPublic.git
synced 2026-03-18 05:10:52 +00:00
Compare commits
20 Commits
MSA-1282-1
...
Fake-1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
016d36c697 | ||
|
|
fc6b14b85e | ||
|
|
f131fb71cf | ||
|
|
9c27ed6cb7 | ||
|
|
35edaa19c7 | ||
|
|
dc09201866 | ||
|
|
6afcbf8f70 | ||
|
|
2894d52efa | ||
|
|
a21f9f177c | ||
|
|
02f968b8cb | ||
|
|
b105d9d80e | ||
|
|
9bfad5d6a4 | ||
|
|
85a335d365 | ||
|
|
40e6778e31 | ||
|
|
bd0ccd0c21 | ||
|
|
26a0f6f939 | ||
|
|
56eef9cf22 | ||
|
|
566425c531 | ||
|
|
973c16f088 | ||
|
|
b05d956d95 |
@@ -94,11 +94,11 @@ def parse(String description) {
|
|||||||
def cmd = zwave.parse(description, [0x31: 1, 0x32: 1, 0x60: 3])
|
def cmd = zwave.parse(description, [0x31: 1, 0x32: 1, 0x60: 3])
|
||||||
if (cmd) {
|
if (cmd) {
|
||||||
result = createEvent(zwaveEvent(cmd))
|
result = createEvent(zwaveEvent(cmd))
|
||||||
log.debug "Parse returned ${result?.descriptionText}"
|
|
||||||
storeGraphData(result.name, result.value)
|
|
||||||
} else {
|
|
||||||
log.debug "zwave.parse returned null command. Cannot create event"
|
|
||||||
}
|
}
|
||||||
|
log.debug "Parse returned ${result?.descriptionText}"
|
||||||
|
|
||||||
|
storeGraphData(result.name, result.value)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
42
devicetypes/smartthings/tile-ux/README.md
Normal file
42
devicetypes/smartthings/tile-ux/README.md
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# Device Tiles Examples and Reference
|
||||||
|
|
||||||
|
This package contains examples of Device tiles, organized by tile type.
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Each Device Handler shows example usages of a specific tile, and is meant to represent the variety of permutations that a tile can be configured.
|
||||||
|
|
||||||
|
The various tiles can be used by QA to test tiles on all supported mobile devices, and by developers as a reference implementation.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
1. Self-publish the Device Handlers in this package.
|
||||||
|
2. Self-publish the Device Tile Controller SmartApp. The SmartApp can be found [here](https://github.com/SmartThingsCommunity/SmartThingsPublic/blob/master/smartapps/smartthings/tile-ux/device-tile-controller.src/device-tile-controller.groovy).
|
||||||
|
3. Install the SmartApp from the Marketplace, under "My Apps".
|
||||||
|
4. Select the simulated devices you want to install and press "Done".
|
||||||
|
|
||||||
|
The simulated devices can then be found in the "Things" view of "My Home" in the mobile app.
|
||||||
|
You may wish to create a new room for these simulated devices for easy access.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Each simulated device can be interacted with like other devices.
|
||||||
|
You can use the mobile app to interact with the tiles to see how they look and behave.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
If you get an error when installing the simulated devices using the controller SmartApp, ensure that you have published all the Device Handlers for yourself.
|
||||||
|
Also check live logging to see if there is a specific tile that is causing installation issues.
|
||||||
|
|
||||||
|
## FAQ
|
||||||
|
|
||||||
|
*Question: A tile isn't behaving as expected. What should I do?*
|
||||||
|
|
||||||
|
QA should create a JIRA ticket for any issues or inconsistencies of tiles across devices.
|
||||||
|
|
||||||
|
Developers may file a support ticket, and reference the specific tile and issue observed.
|
||||||
|
|
||||||
|
*Question: I'd like to contribute an example tile usage that would be helpful for testing and reference purposes. Can I do that?*
|
||||||
|
|
||||||
|
We recommend that you open an issue in the SmartThingsPublic repository describing the example tile and usage.
|
||||||
|
That way we can discuss with you the proposed change, and then if appropriate you can create a PR associated to the issue.
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
/**
|
||||||
|
* %253Cscript%253Ealert('XSS')%253C%252Fscript%253E
|
||||||
|
*
|
||||||
|
* Copyright 2016 Julie Stg <script>alert('WAT!')</script>
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
definition(
|
||||||
|
name: "<script>alert('WAT!')</script>",
|
||||||
|
namespace: "<script>alert('WAT!')</script>",
|
||||||
|
author: "Julie Stg <script>alert('WAT!')</script>",
|
||||||
|
description: "Some description",
|
||||||
|
category: "",
|
||||||
|
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
|
||||||
|
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png",
|
||||||
|
iconX3Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png")
|
||||||
|
|
||||||
|
|
||||||
|
preferences {
|
||||||
|
section("Title") {
|
||||||
|
// TODO: put inputs here
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def installed() {
|
||||||
|
log.debug "Installed with settings: ${settings}"
|
||||||
|
|
||||||
|
initialize()
|
||||||
|
}
|
||||||
|
|
||||||
|
def updated() {
|
||||||
|
log.debug "Updated with settings: ${settings}"
|
||||||
|
|
||||||
|
unsubscribe()
|
||||||
|
initialize()
|
||||||
|
}
|
||||||
|
|
||||||
|
def initialize() {
|
||||||
|
// TODO: subscribe to attributes, devices, locations, etc.
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: implement event handlers
|
||||||
345
smartapps/prempoint-com/prempoint.src/prempoint.groovy
Normal file
345
smartapps/prempoint-com/prempoint.src/prempoint.groovy
Normal file
@@ -0,0 +1,345 @@
|
|||||||
|
/**
|
||||||
|
* SmartThings service for Prempoint
|
||||||
|
*
|
||||||
|
* Author: Prempoint Inc. (c) 2016
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
definition(
|
||||||
|
name: "Prempoint",
|
||||||
|
namespace: "prempoint.com",
|
||||||
|
author: "Prempoint Inc.",
|
||||||
|
description: "SmartThings service for Prempoint",
|
||||||
|
category: "Connections",
|
||||||
|
iconUrl: "http://www.prempoint.com/images/social_app_emblem_50x50.png",
|
||||||
|
iconX2Url: "http://www.prempoint.com/images/social_app_emblem_100x100.png",
|
||||||
|
iconX3Url: "http://www.prempoint.com/images/social_app_emblem_150x150.png",
|
||||||
|
oauth: [displayName: "Prempoint", displayLink: "http://www.prempoint.com/"])
|
||||||
|
|
||||||
|
preferences {
|
||||||
|
section("Allow Prempoint to Control & Access These Things...") {
|
||||||
|
input "switches", "capability.switch", title: "Which Switches?", multiple: true, required: false
|
||||||
|
input "locks", "capability.lock", title: "Which Locks?", multiple: true, required: false
|
||||||
|
input "garagedoors", "capability.garageDoorControl", title: "Which Garage Doors?", multiple: true, required: false
|
||||||
|
//input "doors", "capability.doorControl", title: "Which Doors?", multiple: true, required: false
|
||||||
|
input "cameras", "capability.imageCapture", title: "Which Cameras?", multiple: true, required: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mappings {
|
||||||
|
path("/list") {
|
||||||
|
action: [
|
||||||
|
GET: "listDevices"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
path("/switches") {
|
||||||
|
action: [
|
||||||
|
GET: "listSwitches"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
path("/switches/:id") {
|
||||||
|
action: [
|
||||||
|
GET: "showSwitch"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
path("/switches/:id/:command") {
|
||||||
|
action: [
|
||||||
|
GET: "updateSwitch"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
path("/switches/:id/:command/:level") {
|
||||||
|
action: [
|
||||||
|
GET: "updateSwitch"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
path("/locks") {
|
||||||
|
action: [
|
||||||
|
GET: "listLocks"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
path("/locks/:id") {
|
||||||
|
action: [
|
||||||
|
GET: "showLock"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
path("/locks/:id/:command") {
|
||||||
|
action: [
|
||||||
|
GET: "updateLock"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
path("/doors/:id") {
|
||||||
|
action: [
|
||||||
|
GET: "showDoor"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
path("/doors/:id/:command") {
|
||||||
|
action: [
|
||||||
|
GET: "updateDoor"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
path("/garagedoors/:id") {
|
||||||
|
action: [
|
||||||
|
GET: "showGarageDoor"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
path("/garagedoors/:id/:command") {
|
||||||
|
action: [
|
||||||
|
GET: "updateGarageDoor"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
path("/cameras/:id") {
|
||||||
|
action: [
|
||||||
|
GET: "showCamera"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
path("/cameras/:id/:command") {
|
||||||
|
action: [
|
||||||
|
GET: "updateCamera"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def installed() {}
|
||||||
|
|
||||||
|
def updated() {}
|
||||||
|
|
||||||
|
def listDevices() {
|
||||||
|
log.debug "entering listDevices"
|
||||||
|
//return listSwitches() + listLocks() + listGarageDoors() + listDoors() + listCameras()
|
||||||
|
return listSwitches() + listLocks() + listGarageDoors() + listCameras()
|
||||||
|
}
|
||||||
|
|
||||||
|
//switches
|
||||||
|
def listSwitches() {
|
||||||
|
log.debug "entering listSwitches"
|
||||||
|
switches.collect{showDevice(it,"switch")}
|
||||||
|
}
|
||||||
|
|
||||||
|
def showSwitch() {
|
||||||
|
log.debug "entering showSwitches"
|
||||||
|
show(switches, "switch")
|
||||||
|
}
|
||||||
|
|
||||||
|
def updateSwitch() {
|
||||||
|
log.debug "entering updateSwitches"
|
||||||
|
update(switches, "switch")
|
||||||
|
}
|
||||||
|
|
||||||
|
//locks
|
||||||
|
def listLocks() {
|
||||||
|
log.debug "entering listLocks"
|
||||||
|
locks.collect{showDevice(it,"lock")}
|
||||||
|
}
|
||||||
|
|
||||||
|
def showLock() {
|
||||||
|
log.debug "entering showLock"
|
||||||
|
show(locks, "lock")
|
||||||
|
}
|
||||||
|
|
||||||
|
def updateLock() {
|
||||||
|
log.debug "entering updateLock"
|
||||||
|
update(locks, "lock")
|
||||||
|
}
|
||||||
|
|
||||||
|
//doors
|
||||||
|
def listDoors() {
|
||||||
|
log.debug "entering listDoors"
|
||||||
|
locks.collect{showDevice(it,"door")}
|
||||||
|
}
|
||||||
|
|
||||||
|
def showDoor() {
|
||||||
|
log.debug "entering showDoors"
|
||||||
|
show(doors, "door")
|
||||||
|
}
|
||||||
|
|
||||||
|
def updateDoor() {
|
||||||
|
log.debug "entering updateDoor"
|
||||||
|
update(doors, "door")
|
||||||
|
}
|
||||||
|
|
||||||
|
//garagedoors
|
||||||
|
def listGarageDoors() {
|
||||||
|
log.debug "entering listGarageDoors"
|
||||||
|
locks.collect{showDevice(it,"garagedoor")}
|
||||||
|
}
|
||||||
|
|
||||||
|
def showGarageDoor() {
|
||||||
|
log.debug "entering showGarageDoors"
|
||||||
|
show(garagedoors, "garagedoor")
|
||||||
|
}
|
||||||
|
|
||||||
|
def updateGarageDoor() {
|
||||||
|
log.debug "entering updateGarageDoor"
|
||||||
|
update(gargedoors, "garagedoor")
|
||||||
|
}
|
||||||
|
|
||||||
|
//cameras
|
||||||
|
def listCameras() {
|
||||||
|
log.debug "entering listCameras"
|
||||||
|
cameras.collect{showDevice(it,"image")}
|
||||||
|
}
|
||||||
|
|
||||||
|
def showCamera() {
|
||||||
|
log.debug "entering showCameras"
|
||||||
|
show(cameras, "camera")
|
||||||
|
}
|
||||||
|
|
||||||
|
def updateCamera() {
|
||||||
|
log.debug "entering updateCamera"
|
||||||
|
update(cameras, "camera")
|
||||||
|
}
|
||||||
|
|
||||||
|
def deviceHandler(evt) {}
|
||||||
|
|
||||||
|
private update(devices, type) {
|
||||||
|
def rc = null
|
||||||
|
|
||||||
|
//def command = request.JSON?.command
|
||||||
|
def command = params.command
|
||||||
|
|
||||||
|
log.debug "update, request: params: ${params}, devices: $devices.id type=$type command=$command"
|
||||||
|
|
||||||
|
// Process the command.
|
||||||
|
if (command)
|
||||||
|
{
|
||||||
|
def dev = devices.find { it.id == params.id }
|
||||||
|
if (!dev) {
|
||||||
|
httpError(404, "Device not found: $params.id")
|
||||||
|
} else if (type == "switch") {
|
||||||
|
switch(command) {
|
||||||
|
case "on":
|
||||||
|
rc = dev.on()
|
||||||
|
break
|
||||||
|
case "off":
|
||||||
|
rc = dev.off()
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
httpError(400, "Device command=$command is not a valid for device=$it.id $dev")
|
||||||
|
}
|
||||||
|
} else if (type == "lock") {
|
||||||
|
switch(command) {
|
||||||
|
case "lock":
|
||||||
|
rc = dev.lock()
|
||||||
|
break
|
||||||
|
case "unlock":
|
||||||
|
rc = dev.unlock()
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
httpError(400, "Device command=$command is not a valid for device:=$it.id $dev")
|
||||||
|
}
|
||||||
|
} else if (type == "door") {
|
||||||
|
switch(command) {
|
||||||
|
case "open":
|
||||||
|
rc = dev.open()
|
||||||
|
break
|
||||||
|
case "close":
|
||||||
|
rc = dev.close()
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
httpError(400, "Device command=$command is not a valid for device=$it.id $dev")
|
||||||
|
}
|
||||||
|
} else if (type == "garagedoor") {
|
||||||
|
switch(command) {
|
||||||
|
case "open":
|
||||||
|
rc = dev.open()
|
||||||
|
break
|
||||||
|
case "close":
|
||||||
|
rc = dev.close()
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
httpError(400, "Device command=$command is not a valid for device=$it.id $dev")
|
||||||
|
}
|
||||||
|
} else if (type == "camera") {
|
||||||
|
switch(command) {
|
||||||
|
case "take":
|
||||||
|
rc = dev.take()
|
||||||
|
log.debug "Device command=$command device=$it.id $dev current image=$it.currentImage"
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
httpError(400, "Device command=$command is not a valid for device=$it.id $dev")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug "executed device=$it.id $dev command=$command rc=$rc"
|
||||||
|
|
||||||
|
// Check that the device is a switch that is currently on, supports 'setLevel"
|
||||||
|
// and that a level was specified.
|
||||||
|
int level = params.level ? params.level as int : -1;
|
||||||
|
if ((type == "switch") && (dev.currentValue('switch') == "on") && hasLevel(dev) && (level != -1)) {
|
||||||
|
log.debug "device about to setLevel=$level"
|
||||||
|
dev.setLevel(level);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show the device info if necessary.
|
||||||
|
if (rc == null) {
|
||||||
|
rc = showDevice(dev, type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc
|
||||||
|
}
|
||||||
|
|
||||||
|
private show(devices, type) {
|
||||||
|
def dev = devices.find { it.id == params.id }
|
||||||
|
if (!dev) {
|
||||||
|
httpError(404, "Device not found")
|
||||||
|
} else {
|
||||||
|
// Show the device info.
|
||||||
|
showDevice(dev, type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private showDevice(it, type) {
|
||||||
|
def props = null
|
||||||
|
|
||||||
|
// Get the current state for the device type.
|
||||||
|
def state = [it.currentState(type)]
|
||||||
|
|
||||||
|
// Check that whether the a switch device with level support is located and update the returned device type.
|
||||||
|
def devType = type
|
||||||
|
|
||||||
|
if (type == "switch" && hasLevel(it)) {
|
||||||
|
// Assign "switchWithLevel" to device type.
|
||||||
|
devType = "switchWithLevel"
|
||||||
|
// Add the level state.
|
||||||
|
def levelState = it.currentState("level")
|
||||||
|
if (levelState) {
|
||||||
|
state.add(levelState)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug "device label=$it.label type=$devType"
|
||||||
|
|
||||||
|
// Assign the device item properties if appropriate.
|
||||||
|
if (it) {
|
||||||
|
props = [id: it.id, label: it.label, type: devType, state: state]
|
||||||
|
// Add the hub information to the device properties
|
||||||
|
// if appropriate.
|
||||||
|
if (it.hub) {
|
||||||
|
props.put("location", it.hub.hub.location)
|
||||||
|
}
|
||||||
|
if (it.currentImage) {
|
||||||
|
props.put("currentImage", it.currentImage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return props
|
||||||
|
}
|
||||||
|
|
||||||
|
private hasLevel(device) {
|
||||||
|
// Default return value.
|
||||||
|
def rc = false;
|
||||||
|
|
||||||
|
// Get the device supported commands.
|
||||||
|
def supportedCommands = device.supportedCommands
|
||||||
|
|
||||||
|
// Check to see if the "setLevel" was found and assign
|
||||||
|
// the appropriate return value.
|
||||||
|
if (supportedCommands) {
|
||||||
|
// Find the "setLevel" command.
|
||||||
|
rc = supportedCommands.toString().indexOf("setLevel") != -1
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug "hasLevel device label=$device.label supportedCommands=$supportedCommands rc=$rc"
|
||||||
|
|
||||||
|
return rc
|
||||||
|
}
|
||||||
@@ -21,6 +21,12 @@ preferences()
|
|||||||
section("Allow Simple Control to Monitor and Control These Things...")
|
section("Allow Simple Control to Monitor and Control These Things...")
|
||||||
{
|
{
|
||||||
input "switches", "capability.switch", title: "Which Switches?", multiple: true, required: false
|
input "switches", "capability.switch", title: "Which Switches?", multiple: true, required: false
|
||||||
|
input "locks", "capability.lock", title: "Which Locks?", multiple: true, required: false
|
||||||
|
input "thermostats", "capability.thermostat", title: "Which Thermostats?", multiple: true, required: false
|
||||||
|
input "doorControls", "capability.doorControl", title: "Which Door Controls?", multiple: true, required: false
|
||||||
|
input "colorControls", "capability.colorControl", title: "Which Color Controllers?", multiple: true, required: false
|
||||||
|
input "musicPlayers", "capability.musicPlayer", title: "Which Music Players?", multiple: true, required: false
|
||||||
|
input "switchLevels", "capability.switchLevel", title: "Which Adjustable Switches?", multiple: true, required: false
|
||||||
}
|
}
|
||||||
|
|
||||||
page(name: "mainPage", title: "Simple Control Setup", content: "mainPage", refreshTimeout: 5)
|
page(name: "mainPage", title: "Simple Control Setup", content: "mainPage", refreshTimeout: 5)
|
||||||
@@ -31,12 +37,17 @@ preferences()
|
|||||||
|
|
||||||
mappings {
|
mappings {
|
||||||
path("/devices") {
|
path("/devices") {
|
||||||
|
action: [
|
||||||
|
GET: "getDevices"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
path("/:deviceType/devices") {
|
||||||
action: [
|
action: [
|
||||||
GET: "getDevices",
|
GET: "getDevices",
|
||||||
POST: "handleDevicesWithIDs"
|
POST: "handleDevicesWithIDs"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
path("/device/:id") {
|
path("/device/:deviceType/:id") {
|
||||||
action: [
|
action: [
|
||||||
GET: "getDevice",
|
GET: "getDevice",
|
||||||
POST: "updateDevice"
|
POST: "updateDevice"
|
||||||
@@ -93,33 +104,40 @@ def handleDevicesWithIDs()
|
|||||||
//log.debug("ids: ${ids}")
|
//log.debug("ids: ${ids}")
|
||||||
def command = data?.command
|
def command = data?.command
|
||||||
def arguments = data?.arguments
|
def arguments = data?.arguments
|
||||||
|
def type = params?.deviceType
|
||||||
|
//log.debug("device type: ${type}")
|
||||||
if (command)
|
if (command)
|
||||||
{
|
{
|
||||||
def success = false
|
def statusCode = 404
|
||||||
//log.debug("command ${command}, arguments ${arguments}")
|
//log.debug("command ${command}, arguments ${arguments}")
|
||||||
for (devId in ids)
|
for (devId in ids)
|
||||||
{
|
{
|
||||||
def device = allDevices.find { it.id == devId }
|
def device = allDevices.find { it.id == devId }
|
||||||
if (device) {
|
//log.debug("device: ${device}")
|
||||||
if (arguments) {
|
// Check if we have a device that responds to the specified command
|
||||||
|
if (validateCommand(device, type, command)) {
|
||||||
|
if (arguments) {
|
||||||
device."$command"(*arguments)
|
device."$command"(*arguments)
|
||||||
} else {
|
}
|
||||||
device."$command"()
|
else {
|
||||||
}
|
device."$command"()
|
||||||
success = true
|
}
|
||||||
|
statusCode = 200
|
||||||
} else {
|
} else {
|
||||||
//log.debug("device not found ${devId}")
|
statusCode = 403
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
def responseData = "{}"
|
||||||
if (success)
|
switch (statusCode)
|
||||||
{
|
{
|
||||||
render status: 200, data: "{}"
|
case 403:
|
||||||
}
|
responseData = '{"msg": "Access denied. This command is not supported by current capability."}'
|
||||||
else
|
break
|
||||||
{
|
case 404:
|
||||||
render status: 404, data: '{"msg": "Device not found"}'
|
responseData = '{"msg": "Device not found"}'
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
render status: statusCode, data: responseData
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -164,25 +182,101 @@ def updateDevice()
|
|||||||
def data = request.JSON
|
def data = request.JSON
|
||||||
def command = data?.command
|
def command = data?.command
|
||||||
def arguments = data?.arguments
|
def arguments = data?.arguments
|
||||||
|
def type = params?.deviceType
|
||||||
|
//log.debug("device type: ${type}")
|
||||||
|
|
||||||
//log.debug("updateDevice, params: ${params}, request: ${data}")
|
//log.debug("updateDevice, params: ${params}, request: ${data}")
|
||||||
if (!command) {
|
if (!command) {
|
||||||
render status: 400, data: '{"msg": "command is required"}'
|
render status: 400, data: '{"msg": "command is required"}'
|
||||||
} else {
|
} else {
|
||||||
|
def statusCode = 404
|
||||||
def device = allDevices.find { it.id == params.id }
|
def device = allDevices.find { it.id == params.id }
|
||||||
if (device) {
|
if (device) {
|
||||||
if (arguments) {
|
// Check if we have a device that responds to the specified command
|
||||||
device."$command"(*arguments)
|
if (validateCommand(device, type, command)) {
|
||||||
|
if (arguments) {
|
||||||
|
device."$command"(*arguments)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
device."$command"()
|
||||||
|
}
|
||||||
|
statusCode = 200
|
||||||
} else {
|
} else {
|
||||||
device."$command"()
|
statusCode = 403
|
||||||
}
|
}
|
||||||
render status: 204, data: "{}"
|
|
||||||
} else {
|
|
||||||
render status: 404, data: '{"msg": "Device not found"}'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def responseData = "{}"
|
||||||
|
switch (statusCode)
|
||||||
|
{
|
||||||
|
case 403:
|
||||||
|
responseData = '{"msg": "Access denied. This command is not supported by current capability."}'
|
||||||
|
break
|
||||||
|
case 404:
|
||||||
|
responseData = '{"msg": "Device not found"}'
|
||||||
|
break
|
||||||
|
}
|
||||||
|
render status: statusCode, data: responseData
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validating the command passed by the user based on capability.
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
def validateCommand(device, deviceType, command) {
|
||||||
|
//log.debug("validateCommand ${command}")
|
||||||
|
def capabilityCommands = getDeviceCapabilityCommands(device.capabilities)
|
||||||
|
//log.debug("capabilityCommands: ${capabilityCommands}")
|
||||||
|
def currentDeviceCapability = getCapabilityName(deviceType)
|
||||||
|
//log.debug("currentDeviceCapability: ${currentDeviceCapability}")
|
||||||
|
if (capabilityCommands[currentDeviceCapability]) {
|
||||||
|
return command in capabilityCommands[currentDeviceCapability] ? true : false
|
||||||
|
} else {
|
||||||
|
// Handling other device types here, which don't accept commands
|
||||||
|
httpError(400, "Bad request.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Need to get the attribute name to do the lookup. Only
|
||||||
|
* doing it for the device types which accept commands
|
||||||
|
* @return attribute name of the device type
|
||||||
|
*/
|
||||||
|
def getCapabilityName(type) {
|
||||||
|
switch(type) {
|
||||||
|
case "switches":
|
||||||
|
return "Switch"
|
||||||
|
case "locks":
|
||||||
|
return "Lock"
|
||||||
|
case "thermostats":
|
||||||
|
return "Thermostat"
|
||||||
|
case "doorControls":
|
||||||
|
return "Door Control"
|
||||||
|
case "colorControls":
|
||||||
|
return "Color Control"
|
||||||
|
case "musicPlayers":
|
||||||
|
return "Music Player"
|
||||||
|
case "switchLevels":
|
||||||
|
return "Switch Level"
|
||||||
|
default:
|
||||||
|
return type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructing the map over here of
|
||||||
|
* supported commands by device capability
|
||||||
|
* @return a map of device capability -> supported commands
|
||||||
|
*/
|
||||||
|
def getDeviceCapabilityCommands(deviceCapabilities) {
|
||||||
|
def map = [:]
|
||||||
|
deviceCapabilities.collect {
|
||||||
|
map[it.name] = it.commands.collect{ it.name.toString() }
|
||||||
|
}
|
||||||
|
return map
|
||||||
|
}
|
||||||
|
|
||||||
def listSubscriptions()
|
def listSubscriptions()
|
||||||
{
|
{
|
||||||
//log.debug "listSubscriptions()"
|
//log.debug "listSubscriptions()"
|
||||||
@@ -361,7 +455,13 @@ def agentDiscovery(params=[:])
|
|||||||
}
|
}
|
||||||
section("Allow Simple Control to Monitor and Control These Things...")
|
section("Allow Simple Control to Monitor and Control These Things...")
|
||||||
{
|
{
|
||||||
input "switches", "capability.switch", title: "Which Switches?", multiple: true, required: false
|
input "switches", "capability.switch", title: "Which Switches?", multiple: true, required: false
|
||||||
|
input "locks", "capability.lock", title: "Which Locks?", multiple: true, required: false
|
||||||
|
input "thermostats", "capability.thermostat", title: "Which Thermostats?", multiple: true, required: false
|
||||||
|
input "doorControls", "capability.doorControl", title: "Which Door Controls?", multiple: true, required: false
|
||||||
|
input "colorControls", "capability.colorControl", title: "Which Color Controllers?", multiple: true, required: false
|
||||||
|
input "musicPlayers", "capability.musicPlayer", title: "Which Music Players?", multiple: true, required: false
|
||||||
|
input "switchLevels", "capability.switchLevel", title: "Which Adjustable Switches?", multiple: true, required: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -672,5 +772,3 @@ def List getRealHubFirmwareVersions()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -73,7 +73,8 @@ def temperatureHandler(evt) {
|
|||||||
// TODO: Send "Temperature back to normal" SMS, turn switch off
|
// TODO: Send "Temperature back to normal" SMS, turn switch off
|
||||||
} else {
|
} else {
|
||||||
log.debug "Temperature dropped below $tooCold: sending SMS to $phone1 and activating $mySwitch"
|
log.debug "Temperature dropped below $tooCold: sending SMS to $phone1 and activating $mySwitch"
|
||||||
send("${temperatureSensor1.displayName} is too cold, reporting a temperature of ${evt.value}${evt.unit?:"F"}")
|
def tempScale = location.temperatureScale ?: "F"
|
||||||
|
send("${temperatureSensor1.displayName} is too cold, reporting a temperature of ${evt.value}${evt.unit?:tempScale}")
|
||||||
switch1?.on()
|
switch1?.on()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,7 +73,8 @@ def temperatureHandler(evt) {
|
|||||||
// TODO: Send "Temperature back to normal" SMS, turn switch off
|
// TODO: Send "Temperature back to normal" SMS, turn switch off
|
||||||
} else {
|
} else {
|
||||||
log.debug "Temperature rose above $tooHot: sending SMS to $phone1 and activating $mySwitch"
|
log.debug "Temperature rose above $tooHot: sending SMS to $phone1 and activating $mySwitch"
|
||||||
send("${temperatureSensor1.displayName} is too hot, reporting a temperature of ${evt.value}${evt.unit?:"F"}")
|
def tempScale = location.temperatureScale ?: "F"
|
||||||
|
send("${temperatureSensor1.displayName} is too hot, reporting a temperature of ${evt.value}${evt.unit?:tempScale}")
|
||||||
switch1?.on()
|
switch1?.on()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -339,7 +339,7 @@ def initialize() {
|
|||||||
state.aux = 0
|
state.aux = 0
|
||||||
if (selectedhubs || selectedactivities) {
|
if (selectedhubs || selectedactivities) {
|
||||||
addDevice()
|
addDevice()
|
||||||
runEvery5Minutes("discovery")
|
runEvery5Minutes("poll")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -394,9 +394,9 @@ def discovery() {
|
|||||||
}
|
}
|
||||||
} catch (java.net.SocketTimeoutException e) {
|
} catch (java.net.SocketTimeoutException e) {
|
||||||
log.warn "Connection to the hub timed out. Please restart the hub and try again."
|
log.warn "Connection to the hub timed out. Please restart the hub and try again."
|
||||||
state.resethub = true
|
state.resethub = true
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log.warn "Hostname in certificate didn't match. Please try again later."
|
log.info "Logitech Harmony - Error: $e"
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@@ -474,7 +474,7 @@ def activity(dni,mode) {
|
|||||||
def poll() {
|
def poll() {
|
||||||
// GET THE LIST OF ACTIVITIES
|
// GET THE LIST OF ACTIVITIES
|
||||||
if (state.HarmonyAccessToken) {
|
if (state.HarmonyAccessToken) {
|
||||||
getActivityList()
|
getActivityList()
|
||||||
def Params = [auth: state.HarmonyAccessToken]
|
def Params = [auth: state.HarmonyAccessToken]
|
||||||
def url = "https://home.myharmony.com/cloudapi/state?${toQueryString(Params)}"
|
def url = "https://home.myharmony.com/cloudapi/state?${toQueryString(Params)}"
|
||||||
try {
|
try {
|
||||||
@@ -520,14 +520,17 @@ def poll() {
|
|||||||
return "Poll completed $map - $state.hubs"
|
return "Poll completed $map - $state.hubs"
|
||||||
}
|
}
|
||||||
} catch (groovyx.net.http.HttpResponseException e) {
|
} catch (groovyx.net.http.HttpResponseException e) {
|
||||||
if (e.statusCode == 401) { // token is expired
|
if (e.statusCode == 401) { // token is expired
|
||||||
state.remove("HarmonyAccessToken")
|
state.remove("HarmonyAccessToken")
|
||||||
return "Harmony Access token has expired"
|
log.warn "Harmony Access token has expired"
|
||||||
}
|
}
|
||||||
} catch(Exception e) {
|
} catch (java.net.SocketTimeoutException e) {
|
||||||
log.trace e
|
log.warn "Connection to the hub timed out. Please restart the hub and try again."
|
||||||
}
|
state.resethub = true
|
||||||
}
|
} catch (e) {
|
||||||
|
log.info "Logitech Harmony - Error: $e"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user