mirror of
https://github.com/mtan93/SmartThingsPublic.git
synced 2026-03-20 13:20:53 +00:00
Initial commit
This commit is contained in:
@@ -0,0 +1,481 @@
|
||||
/**
|
||||
* Copyright 2015 SmartThings
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Wattvision Manager
|
||||
*
|
||||
* Author: steve
|
||||
* Date: 2014-02-13
|
||||
*/
|
||||
|
||||
// Automatically generated. Make future change here.
|
||||
definition(
|
||||
name: "Wattvision Manager",
|
||||
namespace: "smartthings",
|
||||
author: "SmartThings",
|
||||
description: "Monitor your whole-house energy use by connecting to your Wattvision account",
|
||||
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Partner/wattvision.png",
|
||||
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Partner/wattvision%402x.png",
|
||||
oauth: [displayName: "Wattvision", displayLink: "https://www.wattvision.com/"]
|
||||
)
|
||||
|
||||
preferences {
|
||||
page(name: "rootPage")
|
||||
}
|
||||
|
||||
def rootPage() {
|
||||
def sensors = state.sensors
|
||||
def hrefState = sensors ? "complete" : ""
|
||||
def hrefDescription = ""
|
||||
sensors.each { sensorId, sensorName ->
|
||||
hrefDescription += "${sensorName}\n"
|
||||
}
|
||||
|
||||
dynamicPage(name: "rootPage", install: sensors ? true : false, uninstall: true) {
|
||||
section {
|
||||
href(url: loginURL(), title: "Connect Wattvision Sensors", style: "embedded", description: hrefDescription, state: hrefState)
|
||||
}
|
||||
section {
|
||||
href(url: "https://www.wattvision.com", title: "Learn More About Wattvision", style: "external", description: null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mappings {
|
||||
path("/access") {
|
||||
actions:
|
||||
[
|
||||
POST : "setApiAccess",
|
||||
DELETE: "revokeApiAccess"
|
||||
]
|
||||
}
|
||||
path("/devices") {
|
||||
actions:
|
||||
[
|
||||
GET: "listDevices"
|
||||
]
|
||||
}
|
||||
path("/device/:sensorId") {
|
||||
actions:
|
||||
[
|
||||
GET : "getDevice",
|
||||
PUT : "updateDevice",
|
||||
POST : "createDevice",
|
||||
DELETE: "deleteDevice"
|
||||
]
|
||||
}
|
||||
path("/${loginCallbackPath()}") {
|
||||
actions:
|
||||
[
|
||||
GET: "loginCallback"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
def installed() {
|
||||
log.debug "Installed with settings: ${settings}"
|
||||
|
||||
initialize()
|
||||
}
|
||||
|
||||
def updated() {
|
||||
log.debug "Updated with settings: ${settings}"
|
||||
|
||||
unsubscribe()
|
||||
unschedule()
|
||||
initialize()
|
||||
}
|
||||
|
||||
def initialize() {
|
||||
getDataFromWattvision()
|
||||
scheduleDataCollection()
|
||||
}
|
||||
|
||||
def getDataFromWattvision() {
|
||||
|
||||
log.trace "Getting data from Wattvision"
|
||||
|
||||
def children = getChildDevices()
|
||||
if (!children) {
|
||||
log.warn "No children. Not collecting data from Wattviwion"
|
||||
// currently only support one child
|
||||
return
|
||||
}
|
||||
|
||||
def endDate = new Date()
|
||||
def startDate
|
||||
|
||||
if (!state.lastUpdated) {
|
||||
// log.debug "no state.lastUpdated"
|
||||
startDate = new Date(hours: endDate.hours - 3)
|
||||
} else {
|
||||
// log.debug "parsing state.lastUpdated"
|
||||
startDate = new Date().parse(smartThingsDateFormat(), state.lastUpdated)
|
||||
}
|
||||
|
||||
state.lastUpdated = endDate.format(smartThingsDateFormat())
|
||||
|
||||
children.each { child ->
|
||||
getDataForChild(child, startDate, endDate)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
def getDataForChild(child, startDate, endDate) {
|
||||
if (!child) {
|
||||
return
|
||||
}
|
||||
|
||||
def wattvisionURL = wattvisionURL(child.deviceNetworkId, startDate, endDate)
|
||||
if (wattvisionURL) {
|
||||
httpGet(uri: wattvisionURL) { response ->
|
||||
def json = new org.json.JSONObject(response.data.toString())
|
||||
child.addWattvisionData(json)
|
||||
return "success"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def wattvisionURL(senorId, startDate, endDate) {
|
||||
|
||||
log.trace "getting wattvisionURL"
|
||||
|
||||
def wattvisionApiAccess = state.wattvisionApiAccess
|
||||
if (!wattvisionApiAccess.id || !wattvisionApiAccess.key) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (!endDate) {
|
||||
endDate = new Date()
|
||||
}
|
||||
if (!startDate) {
|
||||
startDate = new Date(hours: endDate.hours - 3)
|
||||
}
|
||||
|
||||
def diff = endDate.getTime() - startDate.getTime()
|
||||
if (diff > 259200000) { // 3 days in milliseconds
|
||||
// Wattvision only allows pulling 3 hours of data at a time
|
||||
startDate = new Date(hours: endDate.hours - 3)
|
||||
}
|
||||
|
||||
|
||||
def params = [
|
||||
"sensor_id" : senorId,
|
||||
"api_id" : wattvisionApiAccess.id,
|
||||
"api_key" : wattvisionApiAccess.key,
|
||||
"type" : wattvisionDataType ?: "rate",
|
||||
"start_time": startDate.format(wattvisionDateFormat()),
|
||||
"end_time" : endDate.format(wattvisionDateFormat())
|
||||
]
|
||||
|
||||
def parameterString = params.collect { key, value -> "${key.encodeAsURL()}=${value.encodeAsURL()}" }.join("&")
|
||||
def accessURL = wattvisionApiAccess.url ?: "https://www.wattvision.com/api/v0.2/elec"
|
||||
def url = "${accessURL}?${parameterString}"
|
||||
|
||||
// log.debug "wattvisionURL: ${url}"
|
||||
return url
|
||||
}
|
||||
|
||||
def getData() {
|
||||
state.lastUpdated = new Date().format(smartThingsDateFormat())
|
||||
}
|
||||
|
||||
public smartThingsDateFormat() { "yyyy-MM-dd'T'HH:mm:ss.SSSZ" }
|
||||
|
||||
public wattvisionDateFormat() { "yyyy-MM-dd'T'HH:mm:ss" }
|
||||
|
||||
def childMarshaller(child) {
|
||||
return [
|
||||
name : child.name,
|
||||
label : child.label,
|
||||
sensor_id: child.deviceNetworkId,
|
||||
location : child.location.name
|
||||
]
|
||||
}
|
||||
|
||||
// ========================================================
|
||||
// ENDPOINTS
|
||||
// ========================================================
|
||||
|
||||
def listDevices() {
|
||||
getChildDevices().collect { childMarshaller(it) }
|
||||
}
|
||||
|
||||
def getDevice() {
|
||||
|
||||
log.trace "Getting device"
|
||||
|
||||
def child = getChildDevice(params.sensorId)
|
||||
|
||||
if (!child) {
|
||||
httpError(404, "Device not found")
|
||||
}
|
||||
|
||||
return childMarshaller(child)
|
||||
}
|
||||
|
||||
def updateDevice() {
|
||||
|
||||
log.trace "Updating Device with data from Wattvision"
|
||||
|
||||
def body = request.JSON
|
||||
|
||||
def child = getChildDevice(params.sensorId)
|
||||
|
||||
if (!child) {
|
||||
httpError(404, "Device not found")
|
||||
}
|
||||
|
||||
child.addWattvisionData(body)
|
||||
|
||||
render([status: 204, data: " "])
|
||||
}
|
||||
|
||||
def createDevice() {
|
||||
|
||||
log.trace "Creating Wattvision device"
|
||||
|
||||
if (getChildDevice(params.sensorId)) {
|
||||
httpError(403, "Device already exists")
|
||||
}
|
||||
|
||||
def child = addChildDevice("smartthings", "Wattvision", params.sensorId, null, [name: "Wattvision", label: request.JSON.label])
|
||||
|
||||
child.setGraphUrl(getGraphUrl(params.sensorId));
|
||||
|
||||
getDataForChild(child, null, null)
|
||||
|
||||
return childMarshaller(child)
|
||||
}
|
||||
|
||||
def deleteDevice() {
|
||||
|
||||
log.trace "Deleting Wattvision device"
|
||||
|
||||
deleteChildDevice(params.sensorId)
|
||||
render([status: 204, data: " "])
|
||||
}
|
||||
|
||||
def setApiAccess() {
|
||||
|
||||
log.trace "Granting access to Wattvision API"
|
||||
|
||||
def body = request.JSON
|
||||
|
||||
state.wattvisionApiAccess = [
|
||||
url: body.url,
|
||||
id : body.id,
|
||||
key: body.key
|
||||
]
|
||||
|
||||
scheduleDataCollection()
|
||||
|
||||
render([status: 204, data: " "])
|
||||
}
|
||||
|
||||
def scheduleDataCollection() {
|
||||
schedule("* /1 * * * ?", "getDataFromWattvision") // every 1 minute
|
||||
}
|
||||
|
||||
def revokeApiAccess() {
|
||||
|
||||
log.trace "Revoking access to Wattvision API"
|
||||
|
||||
state.wattvisionApiAccess = [:]
|
||||
render([status: 204, data: " "])
|
||||
}
|
||||
|
||||
public getGraphUrl(sensorId) {
|
||||
|
||||
log.trace "Collecting URL for Wattvision graph"
|
||||
|
||||
def apiId = state.wattvisionApiAccess.id
|
||||
def apiKey = state.wattvisionApiAccess.key
|
||||
|
||||
// TODO: allow the changing of type?
|
||||
"http://www.wattvision.com/partners/smartthings/charts?s=${sensorId}&api_id=${apiId}&api_key=${apiKey}&type=w"
|
||||
}
|
||||
|
||||
// ========================================================
|
||||
// SmartThings initiated setup
|
||||
// ========================================================
|
||||
|
||||
/* Debug info for Steve / Andrew
|
||||
|
||||
this page: /partners/smartthings/whatswv
|
||||
- linked from within smartthings, will tell you how to get a wattvision sensor, etc.
|
||||
- pass the debug flag (?debug=1) to show this text.
|
||||
|
||||
login page: /partners/smartthings/login?callback_url=CALLBACKURL
|
||||
- open this page, which will require login.
|
||||
- once login is complete, we call you back at callback_url with:
|
||||
<callback_url>?id=<wattvision_api_id>&key=<wattvision_api_key>
|
||||
question: will you know which user this is on your end?
|
||||
|
||||
sensor json: /partners/smartthings/sensor_list?api_id=...&api_key=...
|
||||
- returns a list of sensors and their associated house names, as a json object
|
||||
- example return value with one sensor id 2, associated with house 'Test's House'
|
||||
- content type is application/json
|
||||
- {"2": "Test's House"}
|
||||
|
||||
*/
|
||||
|
||||
def loginCallback() {
|
||||
log.trace "loginCallback"
|
||||
|
||||
state.wattvisionApiAccess = [
|
||||
id : params.id,
|
||||
key: params.key
|
||||
]
|
||||
|
||||
getSensorJSON(params.id, params.key)
|
||||
|
||||
connectionSuccessful("Wattvision", "https://s3.amazonaws.com/smartapp-icons/Partner/wattvision@2x.png")
|
||||
}
|
||||
|
||||
private getSensorJSON(id, key) {
|
||||
log.trace "getSensorJSON"
|
||||
|
||||
def sensorUrl = "${wattvisionBaseURL()}/partners/smartthings/sensor_list?api_id=${id}&api_key=${key}"
|
||||
|
||||
httpGet(uri: sensorUrl) { response ->
|
||||
|
||||
def json = new org.json.JSONObject(response.data)
|
||||
|
||||
state.sensors = json
|
||||
|
||||
json.each { sensorId, sensorName ->
|
||||
createChild(sensorId, sensorName)
|
||||
}
|
||||
|
||||
return "success"
|
||||
}
|
||||
}
|
||||
|
||||
def createChild(sensorId, sensorName) {
|
||||
log.trace "creating Wattvision Child"
|
||||
|
||||
def child = getChildDevice(sensorId)
|
||||
|
||||
if (child) {
|
||||
log.warn "Device already exists"
|
||||
} else {
|
||||
child = addChildDevice("smartthings", "Wattvision", sensorId, null, [name: "Wattvision", label: sensorName])
|
||||
}
|
||||
|
||||
child.setGraphUrl(getGraphUrl(sensorId));
|
||||
|
||||
getDataForChild(child, null, null)
|
||||
|
||||
scheduleDataCollection()
|
||||
|
||||
return childMarshaller(child)
|
||||
}
|
||||
|
||||
// ========================================================
|
||||
// URL HELPERS
|
||||
// ========================================================
|
||||
|
||||
private loginURL() { "${wattvisionBaseURL()}${loginPath()}" }
|
||||
|
||||
private wattvisionBaseURL() { "https://www.wattvision.com" }
|
||||
|
||||
private loginPath() { "/partners/smartthings/login?callback_url=${loginCallbackURL().encodeAsURL()}" }
|
||||
|
||||
private loginCallbackURL() {
|
||||
if (!atomicState.accessToken) { createAccessToken() }
|
||||
buildActionUrl(loginCallbackPath())
|
||||
}
|
||||
private loginCallbackPath() { "login/callback" }
|
||||
|
||||
// ========================================================
|
||||
// Access Token
|
||||
// ========================================================
|
||||
|
||||
private getMyAccessToken() { return atomicState.accessToken ?: createAccessToken() }
|
||||
|
||||
// ========================================================
|
||||
// CONNECTED HTML
|
||||
// ========================================================
|
||||
|
||||
def connectionSuccessful(deviceName, iconSrc) {
|
||||
def html = """
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=640">
|
||||
<title>Withings Connection</title>
|
||||
<style type="text/css">
|
||||
@font-face {
|
||||
font-family: 'Swiss 721 W01 Thin';
|
||||
src: url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-thin-webfont.eot');
|
||||
src: url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-thin-webfont.eot?#iefix') format('embedded-opentype'),
|
||||
url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-thin-webfont.woff') format('woff'),
|
||||
url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-thin-webfont.ttf') format('truetype'),
|
||||
url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-thin-webfont.svg#swis721_th_btthin') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Swiss 721 W01 Light';
|
||||
src: url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-light-webfont.eot');
|
||||
src: url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-light-webfont.eot?#iefix') format('embedded-opentype'),
|
||||
url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-light-webfont.woff') format('woff'),
|
||||
url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-light-webfont.ttf') format('truetype'),
|
||||
url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-light-webfont.svg#swis721_lt_btlight') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
.container {
|
||||
width: 560px;
|
||||
padding: 40px;
|
||||
/*background: #eee;*/
|
||||
text-align: center;
|
||||
}
|
||||
img {
|
||||
vertical-align: middle;
|
||||
}
|
||||
img:nth-child(2) {
|
||||
margin: 0 30px;
|
||||
}
|
||||
p {
|
||||
font-size: 2.2em;
|
||||
font-family: 'Swiss 721 W01 Thin';
|
||||
text-align: center;
|
||||
color: #666666;
|
||||
padding: 0 40px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
/*
|
||||
p:last-child {
|
||||
margin-top: 0px;
|
||||
}
|
||||
*/
|
||||
span {
|
||||
font-family: 'Swiss 721 W01 Light';
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<img src="${iconSrc}" alt="${deviceName} icon" />
|
||||
<img src="https://s3.amazonaws.com/smartapp-icons/Partner/support/connected-device-icn%402x.png" alt="connected device icon" />
|
||||
<img src="https://s3.amazonaws.com/smartapp-icons/Partner/support/st-logo%402x.png" alt="SmartThings logo" />
|
||||
<p>Your ${deviceName} is now connected to SmartThings!</p>
|
||||
<p>Click 'Done' to finish setup.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
render contentType: 'text/html', data: html
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user