Merge pull request #268 from SmartThingsCommunity/master

Master -> Staging
This commit is contained in:
Juan Pablo Risso
2015-11-09 16:31:21 -05:00
5 changed files with 198 additions and 119 deletions

View File

@@ -0,0 +1,81 @@
/**
* Logitech Harmony Activity
*
* Copyright 2015 Juan Risso
*
* 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.
*
*/
metadata {
definition (name: "Logitech Harmony Activity", namespace: "smartthings", author: "Juan Risso") {
capability "Switch"
capability "Actuator"
capability "Refresh"
command "huboff"
command "alloff"
command "refresh"
}
// simulator metadata
simulator {
}
// UI tile definitions
tiles {
standardTile("button", "device.switch", width: 2, height: 2, canChangeIcon: true) {
state "off", label: 'Off', action: "switch.on", icon: "st.harmony.harmony-hub-icon", backgroundColor: "#ffffff", nextState: "on"
state "on", label: 'On', action: "switch.off", icon: "st.harmony.harmony-hub-icon", backgroundColor: "#79b821", nextState: "off"
}
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
}
standardTile("forceoff", "device.switch", inactiveLabel: false, decoration: "flat") {
state "default", label:'Force End', action:"switch.off", icon:"st.secondary.off"
}
standardTile("huboff", "device.switch", inactiveLabel: false, decoration: "flat") {
state "default", label:'End Hub Action', action:"huboff", icon:"st.harmony.harmony-hub-icon"
}
standardTile("alloff", "device.switch", inactiveLabel: false, decoration: "flat") {
state "default", label:'All Actions', action:"alloff", icon:"st.secondary.off"
}
main "button"
details(["button", "refresh", "forceoff", "huboff", "alloff"])
}
}
def parse(String description) {
}
def on() {
sendEvent(name: "switch", value: "on")
log.trace parent.activity(device.deviceNetworkId,"start")
}
def off() {
sendEvent(name: "switch", value: "off")
log.trace parent.activity(device.deviceNetworkId,"end")
}
def huboff() {
sendEvent(name: "switch", value: "off")
log.trace parent.activity(device.deviceNetworkId,"hub")
}
def alloff() {
sendEvent(name: "switch", value: "off")
log.trace parent.activity("all","end")
}
def refresh() {
log.debug "Executing 'refresh'"
log.trace parent.poll()
}

View File

@@ -18,7 +18,6 @@ definition(
) { ) {
appSetting "clientId" appSetting "clientId"
appSetting "clientSecret" appSetting "clientSecret"
appSetting "serverUrl"
} }
preferences { preferences {
@@ -29,16 +28,13 @@ mappings {
path("/receivedToken") { action: [ POST: "receivedToken", GET: "receivedToken"] } path("/receivedToken") { action: [ POST: "receivedToken", GET: "receivedToken"] }
path("/receiveToken") { action: [ POST: "receiveToken", GET: "receiveToken"] } path("/receiveToken") { action: [ POST: "receiveToken", GET: "receiveToken"] }
path("/hookCallback") { action: [ POST: "hookEventHandler", GET: "hookEventHandler"] } path("/hookCallback") { action: [ POST: "hookEventHandler", GET: "hookEventHandler"] }
path("/oauth/initialize") {action: [GET: "oauthInitUrl"]}
path("/oauth/callback") { action: [ GET: "callback" ] } path("/oauth/callback") { action: [ GET: "callback" ] }
} }
def getSmartThingsClientId() { def getServerUrl() { return "https://graph.api.smartthings.com" }
return appSettings.clientId def getBuildRedirectUrl() { "${serverUrl}/oauth/initialize?appId=${app.id}&access_token=${state.accessToken}&apiServerUrl=${apiServerUrl}" }
} def buildRedirectUrl(page) { return buildActionUrl(page) }
def getSmartThingsClientSecret() {
return appSettings.clientSecret
}
def callback() { def callback() {
def redirectUrl = null def redirectUrl = null
@@ -64,9 +60,8 @@ def callback() {
// SmartThings code, which we ignore, as we don't need to exchange for an access token. // SmartThings code, which we ignore, as we don't need to exchange for an access token.
// Instead, go initiate the Jawbone OAuth flow. // Instead, go initiate the Jawbone OAuth flow.
log.debug "Executing callback redirect to auth page" log.debug "Executing callback redirect to auth page"
def stcid = getSmartThingsClientId()
state.oauthInitState = UUID.randomUUID().toString() state.oauthInitState = UUID.randomUUID().toString()
def oauthParams = [response_type: "code", client_id: stcid, scope: "move_read sleep_read", redirect_uri: "${serverUrl}/oauth/callback"] def oauthParams = [response_type: "code", client_id: appSettings.clientId, scope: "move_read sleep_read", redirect_uri: "${serverUrl}/oauth/callback"]
redirect(location: "https://jawbone.com/auth/oauth2/auth?${toQueryString(oauthParams)}") redirect(location: "https://jawbone.com/auth/oauth2/auth?${toQueryString(oauthParams)}")
} }
} else { } else {
@@ -85,10 +80,11 @@ def authPage() {
createAccessToken() createAccessToken()
} }
description = "Click to enter Jawbone Credentials" description = "Click to enter Jawbone Credentials"
def redirectUrl = oauthInitUrl() def redirectUrl = buildRedirectUrl
// log.debug "RedirectURL = ${redirectUrl}" log.debug "RedirectURL = ${redirectUrl}"
return dynamicPage(name: "Credentials", title: "Jawbone UP", nextPage: null, uninstall: true, install:false) { def donebutton= state.JawboneAccessToken != null
section { href url:redirectUrl, style:"embedded", required:true, title:"Jawbone UP", description:description } return dynamicPage(name: "Credentials", title: "Jawbone UP", nextPage: null, uninstall: true, install: donebutton) {
section { href url:redirectUrl, style:"embedded", required:true, title:"Jawbone UP", state: hast ,description:description }
} }
} else { } else {
description = "Jawbone Credentials Already Entered." description = "Jawbone Credentials Already Entered."
@@ -100,17 +96,14 @@ def authPage() {
def oauthInitUrl() { def oauthInitUrl() {
log.debug "oauthInitUrl" log.debug "oauthInitUrl"
def stcid = getSmartThingsClientId()
state.oauthInitState = UUID.randomUUID().toString() state.oauthInitState = UUID.randomUUID().toString()
def oauthParams = [ response_type: "code", client_id: stcid, scope: "move_read sleep_read", redirect_uri: buildRedirectUrl("receiveToken") ] def oauthParams = [ response_type: "code", client_id: appSettings.clientId, scope: "move_read sleep_read", redirect_uri: "${serverUrl}/oauth/callback" ]
return "https://jawbone.com/auth/oauth2/auth?${toQueryString(oauthParams)}" redirect(location: "https://jawbone.com/auth/oauth2/auth?${toQueryString(oauthParams)}")
} }
def receiveToken(redirectUrl = null) { def receiveToken(redirectUrl = null) {
log.debug "receiveToken" log.debug "receiveToken"
def stcid = getSmartThingsClientId() def oauthParams = [ client_id: appSettings.clientId, client_secret: appSettings.clientSecret, grant_type: "authorization_code", code: params.code ]
def oauthClientSecret = getSmartThingsClientSecret()
def oauthParams = [ client_id: stcid, client_secret: oauthClientSecret, grant_type: "authorization_code", code: params.code ]
def params = [ def params = [
uri: "https://jawbone.com/auth/oauth2/token?${toQueryString(oauthParams)}", uri: "https://jawbone.com/auth/oauth2/token?${toQueryString(oauthParams)}",
] ]
@@ -232,18 +225,10 @@ String toQueryString(Map m) {
return m.collect { k, v -> "${k}=${URLEncoder.encode(v.toString())}" }.sort().join("&") return m.collect { k, v -> "${k}=${URLEncoder.encode(v.toString())}" }.sort().join("&")
} }
def getServerUrl() { return appSettings.serverUrl ?: "https://graph.api.smartthings.com" }
def buildRedirectUrl(page) {
// log.debug "buildRedirectUrl"
// /api/token/:st_token/smartapps/installations/:id/something
return "${serverUrl}/api/token/${state.accessToken}/smartapps/installations/${app.id}/${page}"
}
def validateCurrentToken() { def validateCurrentToken() {
log.debug "validateCurrentToken" log.debug "validateCurrentToken"
def url = "https://jawbone.com/nudge/api/v.1.1/users/@me/refreshToken" def url = "https://jawbone.com/nudge/api/v.1.1/users/@me/refreshToken"
def requestBody = "secret=${getSmartThingsClientSecret()}" def requestBody = "secret=${appSettings.clientSecret}"
try { try {
httpPost(uri: url, headers: ["Authorization": "Bearer ${state.JawboneAccessToken}" ], body: requestBody) {response -> httpPost(uri: url, headers: ["Authorization": "Bearer ${state.JawboneAccessToken}" ], body: requestBody) {response ->
@@ -257,9 +242,7 @@ def validateCurrentToken() {
if (e.statusCode == 401) { // token is expired if (e.statusCode == 401) { // token is expired
log.debug "Access token is expired" log.debug "Access token is expired"
if (state.refreshToken) { // if we have this we are okay if (state.refreshToken) { // if we have this we are okay
def stcid = getSmartThingsClientId() def oauthParams = [client_id: appSettings.clientId, client_secret: appSettings.clientSecret, grant_type: "refresh_token", refresh_token: state.refreshToken]
def oauthClientSecret = getSmartThingsClientSecret()
def oauthParams = [client_id: stcid, client_secret: oauthClientSecret, grant_type: "refresh_token", refresh_token: state.refreshToken]
def tokenUrl = "https://jawbone.com/auth/oauth2/token?${toQueryString(oauthParams)}" def tokenUrl = "https://jawbone.com/auth/oauth2/token?${toQueryString(oauthParams)}"
def params = [ def params = [
uri: tokenUrl uri: tokenUrl
@@ -288,9 +271,10 @@ def validateCurrentToken() {
} }
def initialize() { def initialize() {
def hookUrl = "${serverUrl}/api/token/${state.accessToken}/smartapps/installations/${app.id}/hookCallback" log.debug "Callback URL - Webhook"
def webhook = "https://jawbone.com/nudge/api/v.1.1/users/@me/pubsub?webhook=$hookUrl" def localServerUrl = getApiServerUrl()
log.debug "Callback URL: $webhook" def hookUrl = "${localServerUrl}/api/token/${state.accessToken}/smartapps/installations/${app.id}/hookCallback"
def webhook = "https://jawbone.com/nudge/api/v.1.1/users/@me/pubsub?webhook=$hookUrl"
httpPost(uri: webhook, headers: ["Authorization": "Bearer ${state.JawboneAccessToken}" ]) httpPost(uri: webhook, headers: ["Authorization": "Bearer ${state.JawboneAccessToken}" ])
} }
@@ -328,7 +312,6 @@ def setup() {
} }
def installed() { def installed() {
enableCallback()
if (!state.accessToken) { if (!state.accessToken) {
log.debug "About to create access token" log.debug "About to create access token"
@@ -341,7 +324,6 @@ def installed() {
} }
def updated() { def updated() {
enableCallback()
if (!state.accessToken) { if (!state.accessToken) {
log.debug "About to create access token" log.debug "About to create access token"
@@ -500,4 +482,4 @@ def hookEventHandler() {
def html = """{"code":200,"message":"OK"}""" def html = """{"code":200,"message":"OK"}"""
render contentType: 'application/json', data: html render contentType: 'application/json', data: html
} }

View File

@@ -724,8 +724,11 @@ private getBridgeIP() {
host = d.latestState('networkAddress').stringValue host = d.latestState('networkAddress').stringValue
} }
if (host == null || host == "") { if (host == null || host == "") {
def macAddress = selectedHue def serialNumber = selectedHue
def bridge = getHueBridges().find { it?.value?.mac?.equalsIgnoreCase(macAddress) }?.value def bridge = getHueBridges().find { it?.value?.serialNumber?.equalsIgnoreCase(serialNumber) }?.value
if (!bridge) {
bridge = getHueBridges().find { it?.value?.mac?.equalsIgnoreCase(serialNumber) }?.value
}
if (bridge?.ip && bridge?.port) { if (bridge?.ip && bridge?.port) {
if (bridge?.ip.contains(".")) if (bridge?.ip.contains("."))
host = "${bridge?.ip}:${bridge?.port}" host = "${bridge?.ip}:${bridge?.port}"

View File

@@ -98,6 +98,15 @@ def installed() {
} }
def updated() { def updated() {
def currentDeviceIds = settings.collect { k, devices -> devices }.flatten().collect { it.id }.unique()
def subscriptionDevicesToRemove = app.subscriptions*.device.findAll { device ->
!currentDeviceIds.contains(device.id)
}
subscriptionDevicesToRemove.each { device ->
log.debug "Removing $device.displayName subscription"
state.remove(device.id)
unsubscribe(device)
}
log.debug settings log.debug settings
} }

View File

@@ -34,7 +34,7 @@
* locks | lock | lock, unlock | locked, unlocked * locks | lock | lock, unlock | locked, unlocked
* ---------------------+-------------------+-----------------------------+------------------------------------ * ---------------------+-------------------+-----------------------------+------------------------------------
*/ */
definition( definition(
name: "Logitech Harmony (Connect)", name: "Logitech Harmony (Connect)",
namespace: "smartthings", namespace: "smartthings",
@@ -48,7 +48,6 @@ definition(
){ ){
appSetting "clientId" appSetting "clientId"
appSetting "clientSecret" appSetting "clientSecret"
appSetting "callbackUrl"
} }
preferences(oauthPage: "deviceAuthorization") { preferences(oauthPage: "deviceAuthorization") {
@@ -90,16 +89,18 @@ mappings {
} }
def getServerUrl() { return "https://graph.api.smartthings.com" } def getServerUrl() { return "https://graph.api.smartthings.com" }
def getCallbackUrl() { "https://graph.api.smartthings.com/oauth/callback" }
def getBuildRedirectUrl() { "${serverUrl}/oauth/initialize?appId=${app.id}&access_token=${state.accessToken}&apiServerUrl=${apiServerUrl}" }
def authPage() { def authPage() {
def description = null def description = null
if (!state.HarmonyAccessToken) { if (!state.HarmonyAccessToken) {
if (!state.accessToken) { if (!state.accessToken) {
log.debug "About to create access token" log.debug "About to create access token"
createAccessToken() createAccessToken()
} }
description = "Click to enter Harmony Credentials" description = "Click to enter Harmony Credentials"
def redirectUrl = "${serverUrl}/oauth/initialize?appId=${app.id}&access_token=${state.accessToken}" def redirectUrl = buildRedirectUrl
return dynamicPage(name: "Credentials", title: "Harmony", nextPage: null, uninstall: true, install:false) { return dynamicPage(name: "Credentials", title: "Harmony", nextPage: null, uninstall: true, install:false) {
section { href url:redirectUrl, style:"embedded", required:true, title:"Harmony", description:description } section { href url:redirectUrl, style:"embedded", required:true, title:"Harmony", description:description }
} }
@@ -111,7 +112,7 @@ def authPage() {
def huboptions = state.HarmonyHubs ?: [] def huboptions = state.HarmonyHubs ?: []
def actoptions = state.HarmonyActivities ?: [] def actoptions = state.HarmonyActivities ?: []
def numFoundHub = huboptions.size() ?: 0 def numFoundHub = huboptions.size() ?: 0
def numFoundAct = actoptions.size() ?: 0 def numFoundAct = actoptions.size() ?: 0
if((deviceRefreshCount % 5) == 0) { if((deviceRefreshCount % 5) == 0) {
@@ -121,13 +122,14 @@ def authPage() {
section("Please wait while we discover your Harmony Hubs and Activities. Discovery can take five minutes or more, so sit back and relax! Select your device below once discovered.") { section("Please wait while we discover your Harmony Hubs and Activities. Discovery can take five minutes or more, so sit back and relax! Select your device below once discovered.") {
input "selectedhubs", "enum", required:false, title:"Select Harmony Hubs (${numFoundHub} found)", multiple:true, options:huboptions input "selectedhubs", "enum", required:false, title:"Select Harmony Hubs (${numFoundHub} found)", multiple:true, options:huboptions
} }
if (numFoundHub > 0 && numFoundAct > 0 && false) // Virtual activity flag
if (numFoundHub > 0 && numFoundAct > 0 && true)
section("You can also add activities as virtual switches for other convenient integrations") { section("You can also add activities as virtual switches for other convenient integrations") {
input "selectedactivities", "enum", required:false, title:"Select Harmony Activities (${numFoundAct} found)", multiple:true, options:actoptions input "selectedactivities", "enum", required:false, title:"Select Harmony Activities (${numFoundAct} found)", multiple:true, options:actoptions
} }
if (state.resethub) if (state.resethub)
section("Connection to the hub timed out. Please restart the hub and try again.") {} section("Connection to the hub timed out. Please restart the hub and try again.") {}
} }
} }
} }
@@ -139,7 +141,7 @@ def callback() {
} else { } else {
log.warn "No authQueryString" log.warn "No authQueryString"
} }
if (state.HarmonyAccessToken) { if (state.HarmonyAccessToken) {
log.debug "Access token already exists" log.debug "Access token already exists"
discovery() discovery()
@@ -164,8 +166,8 @@ def callback() {
def init() { def init() {
log.debug "Requesting Code" log.debug "Requesting Code"
def oauthParams = [client_id: "${appSettings.clientId}", scope: "remote", response_type: "code", redirect_uri: "${appSettings.callbackUrl}" ] def oauthParams = [client_id: "${appSettings.clientId}", scope: "remote", response_type: "code", redirect_uri: "${callbackUrl}" ]
redirect(location: "https://home.myharmony.com/oauth2/authorize?${toQueryString(oauthParams)}") redirect(location: "https://home.myharmony.com/oauth2/authorize?${toQueryString(oauthParams)}")
} }
def receiveToken(redirectUrl = null) { def receiveToken(redirectUrl = null) {
@@ -175,7 +177,7 @@ def receiveToken(redirectUrl = null) {
uri: "https://home.myharmony.com/oauth2/token?${toQueryString(oauthParams)}", uri: "https://home.myharmony.com/oauth2/token?${toQueryString(oauthParams)}",
] ]
try { try {
httpPost(params) { response -> httpPost(params) { response ->
state.HarmonyAccessToken = response.data.access_token state.HarmonyAccessToken = response.data.access_token
} }
} catch (java.util.concurrent.TimeoutException e) { } catch (java.util.concurrent.TimeoutException e) {
@@ -222,7 +224,7 @@ def connectionStatus(message, redirectUrl = null) {
<meta http-equiv="refresh" content="3; url=${redirectUrl}" /> <meta http-equiv="refresh" content="3; url=${redirectUrl}" />
""" """
} }
def html = """ def html = """
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
@@ -303,30 +305,28 @@ def buildRedirectUrl(page) {
} }
def installed() { def installed() {
enableCallback()
if (!state.accessToken) { if (!state.accessToken) {
log.debug "About to create access token" log.debug "About to create access token"
createAccessToken() createAccessToken()
} else { } else {
initialize() initialize()
} }
} }
def updated() { def updated() {
unsubscribe() unsubscribe()
unschedule() unschedule()
enableCallback()
if (!state.accessToken) { if (!state.accessToken) {
log.debug "About to create access token" log.debug "About to create access token"
createAccessToken() createAccessToken()
} else { } else {
initialize() initialize()
} }
} }
def uninstalled() { def uninstalled() {
if (state.HarmonyAccessToken) { if (state.HarmonyAccessToken) {
try { try {
state.HarmonyAccessToken = "" state.HarmonyAccessToken = ""
log.debug "Success disconnecting Harmony from SmartThings" log.debug "Success disconnecting Harmony from SmartThings"
} catch (groovyx.net.http.HttpResponseException e) { } catch (groovyx.net.http.HttpResponseException e) {
@@ -340,7 +340,7 @@ def initialize() {
if (selectedhubs || selectedactivities) { if (selectedhubs || selectedactivities) {
addDevice() addDevice()
runEvery5Minutes("discovery") runEvery5Minutes("discovery")
} }
} }
def getHarmonydevices() { def getHarmonydevices() {
@@ -360,20 +360,20 @@ Map discoverDevices() {
def hubname = getHubName(it.key) def hubname = getHubName(it.key)
def hubvalue = "${hubname}" def hubvalue = "${hubname}"
hubs["harmony-${hubkey}"] = hubvalue hubs["harmony-${hubkey}"] = hubvalue
it.value.response.data.activities.each { it.value.response.data.activities.each {
def value = "${it.value.name}" def value = "${it.value.name}"
def key = "harmony-${hubkey}-${it.key}" def key = "harmony-${hubkey}-${it.key}"
activities["${key}"] = value activities["${key}"] = value
} }
} }
state.HarmonyHubs = hubs state.HarmonyHubs = hubs
state.HarmonyActivities = activities state.HarmonyActivities = activities
} }
} }
//CHILD DEVICE METHODS //CHILD DEVICE METHODS
def discovery() { def discovery() {
def Params = [auth: state.HarmonyAccessToken] def Params = [auth: state.HarmonyAccessToken]
def url = "https://home.myharmony.com/cloudapi/activity/all?${toQueryString(Params)}" def url = "https://home.myharmony.com/cloudapi/activity/all?${toQueryString(Params)}"
try { try {
httpGet(uri: url, headers: ["Accept": "application/json"]) {response -> httpGet(uri: url, headers: ["Accept": "application/json"]) {response ->
@@ -385,11 +385,11 @@ def discovery() {
poll() poll()
} else { } else {
log.debug "Error: $response.status" log.debug "Error: $response.status"
} }
} }
} 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")
log.warn "Harmony Access token has expired" log.warn "Harmony Access token has expired"
} }
} catch (java.net.SocketTimeoutException e) { } catch (java.net.SocketTimeoutException e) {
@@ -397,12 +397,12 @@ def discovery() {
state.resethub = true state.resethub = true
} catch (e) { } catch (e) {
log.warn "Hostname in certificate didn't match. Please try again later." log.warn "Hostname in certificate didn't match. Please try again later."
} }
return null return null
} }
def addDevice() { def addDevice() {
log.trace "Adding Hubs" log.trace "Adding Hubs"
selectedhubs.each { dni -> selectedhubs.each { dni ->
def d = getChildDevice(dni) def d = getChildDevice(dni)
if(!d) { if(!d) {
@@ -413,8 +413,8 @@ def addDevice() {
} else { } else {
log.trace "found ${d.displayName} with id $dni already exists" log.trace "found ${d.displayName} with id $dni already exists"
} }
} }
log.trace "Adding Activities" log.trace "Adding Activities"
selectedactivities.each { dni -> selectedactivities.each { dni ->
def d = getChildDevice(dni) def d = getChildDevice(dni)
if(!d) { if(!d) {
@@ -425,7 +425,7 @@ def addDevice() {
} else { } else {
log.trace "found ${d.displayName} with id $dni already exists" log.trace "found ${d.displayName} with id $dni already exists"
} }
} }
} }
def activity(dni,mode) { def activity(dni,mode) {
@@ -433,26 +433,26 @@ def activity(dni,mode) {
def msg = "Command failed" def msg = "Command failed"
def url = '' def url = ''
if (dni == "all") { if (dni == "all") {
url = "https://home.myharmony.com/cloudapi/activity/off?${toQueryString(Params)}" url = "https://home.myharmony.com/cloudapi/activity/off?${toQueryString(Params)}"
} else { } else {
def aux = dni.split('-') def aux = dni.split('-')
def hubId = aux[1] def hubId = aux[1]
if (mode == "hub" || (aux.size() <= 2) || (aux[2] == "off")){ if (mode == "hub" || (aux.size() <= 2) || (aux[2] == "off")){
url = "https://home.myharmony.com/cloudapi/hub/${hubId}/activity/off?${toQueryString(Params)}" url = "https://home.myharmony.com/cloudapi/hub/${hubId}/activity/off?${toQueryString(Params)}"
} else { } else {
def activityId = aux[2] def activityId = aux[2]
url = "https://home.myharmony.com/cloudapi/hub/${hubId}/activity/${activityId}/${mode}?${toQueryString(Params)}" url = "https://home.myharmony.com/cloudapi/hub/${hubId}/activity/${activityId}/${mode}?${toQueryString(Params)}"
} }
} }
try { try {
httpPostJson(uri: url) { response -> httpPostJson(uri: url) { response ->
if (response.data.code == 200 || dni == "all") { if (response.data.code == 200 || dni == "all") {
msg = "Command sent succesfully" msg = "Command sent succesfully"
state.aux = 0 state.aux = 0
} else { } else {
msg = "Command failed. Error: $response.data.code" msg = "Command failed. Error: $response.data.code"
} }
} }
} catch (groovyx.net.http.HttpResponseException ex) { } catch (groovyx.net.http.HttpResponseException ex) {
log.error ex log.error ex
if (state.aux == 0) { if (state.aux == 0) {
@@ -460,7 +460,7 @@ def activity(dni,mode) {
activity(dni,mode) activity(dni,mode)
} else { } else {
msg = ex msg = ex
state.aux = 0 state.aux = 0
} }
} catch(Exception ex) { } catch(Exception ex) {
msg = ex msg = ex
@@ -473,10 +473,10 @@ 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 {
httpGet(uri: url, headers: ["Accept": "application/json"]) {response -> httpGet(uri: url, headers: ["Accept": "application/json"]) {response ->
def map = [:] def map = [:]
response.data.hubs.each { response.data.hubs.each {
if (it.value.message == "OK") { if (it.value.message == "OK") {
@@ -489,20 +489,20 @@ def poll() {
def currentActivity = getActivityName(it.value.response.data.currentAvActivity,it.key) def currentActivity = getActivityName(it.value.response.data.currentAvActivity,it.key)
hub.sendEvent(name: "currentActivity", value: currentActivity, descriptionText: "Current activity is ${currentActivity}", display: false) hub.sendEvent(name: "currentActivity", value: currentActivity, descriptionText: "Current activity is ${currentActivity}", display: false)
} }
} }
} else { } else {
log.trace it.value.message log.trace it.value.message
} }
} }
def activities = getChildDevices() def activities = getChildDevices()
def activitynotrunning = true def activitynotrunning = true
activities.each { activity -> activities.each { activity ->
def act = activity.deviceNetworkId.split('-') def act = activity.deviceNetworkId.split('-')
if (act.size() > 2) { if (act.size() > 2) {
def aux = map.find { it.key == act[1] } def aux = map.find { it.key == act[1] }
if (aux) { if (aux) {
def aux2 = aux.value.split(',') def aux2 = aux.value.split(',')
def childDevice = getChildDevice(activity.deviceNetworkId) def childDevice = getChildDevice(activity.deviceNetworkId)
if ((act[2] == aux2[0]) && (aux2[1] == "1" || aux2[1] == "2")) { if ((act[2] == aux2[0]) && (aux2[1] == "1" || aux2[1] == "2")) {
childDevice?.sendEvent(name: "switch", value: "on") childDevice?.sendEvent(name: "switch", value: "on")
if (aux2[1] == "1") if (aux2[1] == "1")
@@ -512,30 +512,30 @@ def poll() {
if (aux2[1] == "3") if (aux2[1] == "3")
runIn(5, "poll", [overwrite: true]) runIn(5, "poll", [overwrite: true])
} }
} }
} }
} }
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" return "Harmony Access token has expired"
} }
} catch(Exception e) { } catch(Exception e) {
log.trace e log.trace e
} }
} }
} }
def getActivityList() { def getActivityList() {
// GET ACTIVITY'S NAME // GET ACTIVITY'S NAME
if (state.HarmonyAccessToken) { if (state.HarmonyAccessToken) {
def Params = [auth: state.HarmonyAccessToken] def Params = [auth: state.HarmonyAccessToken]
def url = "https://home.myharmony.com/cloudapi/activity/all?${toQueryString(Params)}" def url = "https://home.myharmony.com/cloudapi/activity/all?${toQueryString(Params)}"
try { try {
httpGet(uri: url, headers: ["Accept": "application/json"]) {response -> httpGet(uri: url, headers: ["Accept": "application/json"]) {response ->
response.data.hubs.each { response.data.hubs.each {
def hub = getChildDevice("harmony-${it.key}") def hub = getChildDevice("harmony-${it.key}")
if (hub) { if (hub) {
@@ -548,10 +548,10 @@ def getActivityList() {
} }
activities += [id: "off", name: "Activity OFF", type: "0"] activities += [id: "off", name: "Activity OFF", type: "0"]
log.trace activities log.trace activities
} }
hub.sendEvent(name: "activities", value: new groovy.json.JsonBuilder(activities).toString(), descriptionText: "Activities are ${activities.collect { it.name }?.join(', ')}", display: false) hub.sendEvent(name: "activities", value: new groovy.json.JsonBuilder(activities).toString(), descriptionText: "Activities are ${activities.collect { it.name }?.join(', ')}", display: false)
} }
} }
} }
} catch (groovyx.net.http.HttpResponseException e) { } catch (groovyx.net.http.HttpResponseException e) {
log.trace e log.trace e
@@ -560,7 +560,7 @@ def getActivityList() {
} catch(Exception e) { } catch(Exception e) {
log.trace e log.trace e
} }
} }
return activity return activity
} }
@@ -568,16 +568,16 @@ def getActivityName(activity,hubId) {
// GET ACTIVITY'S NAME // GET ACTIVITY'S NAME
def actname = activity def actname = activity
if (state.HarmonyAccessToken) { if (state.HarmonyAccessToken) {
def Params = [auth: state.HarmonyAccessToken] def Params = [auth: state.HarmonyAccessToken]
def url = "https://home.myharmony.com/cloudapi/hub/${hubId}/activity/all?${toQueryString(Params)}" def url = "https://home.myharmony.com/cloudapi/hub/${hubId}/activity/all?${toQueryString(Params)}"
try { try {
httpGet(uri: url, headers: ["Accept": "application/json"]) {response -> httpGet(uri: url, headers: ["Accept": "application/json"]) {response ->
actname = response.data.data.activities[activity].name actname = response.data.data.activities[activity].name
} }
} catch(Exception e) { } catch(Exception e) {
log.trace e log.trace e
} }
} }
return actname return actname
} }
@@ -585,19 +585,19 @@ def getActivityId(activity,hubId) {
// GET ACTIVITY'S NAME // GET ACTIVITY'S NAME
def actid = activity def actid = activity
if (state.HarmonyAccessToken) { if (state.HarmonyAccessToken) {
def Params = [auth: state.HarmonyAccessToken] def Params = [auth: state.HarmonyAccessToken]
def url = "https://home.myharmony.com/cloudapi/hub/${hubId}/activity/all?${toQueryString(Params)}" def url = "https://home.myharmony.com/cloudapi/hub/${hubId}/activity/all?${toQueryString(Params)}"
try { try {
httpGet(uri: url, headers: ["Accept": "application/json"]) {response -> httpGet(uri: url, headers: ["Accept": "application/json"]) {response ->
response.data.data.activities.each { response.data.data.activities.each {
if (it.value.name == activity) if (it.value.name == activity)
actid = it.key actid = it.key
} }
} }
} catch(Exception e) { } catch(Exception e) {
log.trace e log.trace e
} }
} }
return actid return actid
} }
@@ -605,16 +605,16 @@ def getHubName(hubId) {
// GET HUB'S NAME // GET HUB'S NAME
def hubname = hubId def hubname = hubId
if (state.HarmonyAccessToken) { if (state.HarmonyAccessToken) {
def Params = [auth: state.HarmonyAccessToken] def Params = [auth: state.HarmonyAccessToken]
def url = "https://home.myharmony.com/cloudapi/hub/${hubId}/discover?${toQueryString(Params)}" def url = "https://home.myharmony.com/cloudapi/hub/${hubId}/discover?${toQueryString(Params)}"
try { try {
httpGet(uri: url, headers: ["Accept": "application/json"]) {response -> httpGet(uri: url, headers: ["Accept": "application/json"]) {response ->
hubname = response.data.data.name hubname = response.data.data.name
} }
} catch(Exception e) { } catch(Exception e) {
log.trace e log.trace e
} }
} }
return hubname return hubname
} }
@@ -625,8 +625,8 @@ def sendNotification(msg) {
def hookEventHandler() { def hookEventHandler() {
// log.debug "In hookEventHandler method." // log.debug "In hookEventHandler method."
log.debug "request = ${request}" log.debug "request = ${request}"
def json = request.JSON def json = request.JSON
def html = """{"code":200,"message":"OK"}""" def html = """{"code":200,"message":"OK"}"""
render contentType: 'application/json', data: html render contentType: 'application/json', data: html
@@ -660,12 +660,16 @@ def updateDevice() {
} else { } else {
def device = allDevices.find { it.id == params.id } def device = allDevices.find { it.id == params.id }
if (device) { if (device) {
if (arguments) { if (device.hasCommand("$command")) {
device."$command"(*arguments) if (arguments) {
} else { device."$command"(*arguments)
device."$command"() } else {
device."$command"()
}
render status: 204, data: "{}"
} else {
render status: 404, data: '{"msg": "Command not supported by this Device"}'
} }
render status: 204, data: "{}"
} else { } else {
render status: 404, data: '{"msg": "Device not found"}' render status: 404, data: '{"msg": "Device not found"}'
} }