SmartThings and Eagle by Rainforest (Energy Monitoring) Connection?


(John) #1

Does the Eagle 200 Energy Gateway by Rainforest Automation talk to the SmartThings hub? I can’t seem to find any answers while searching so my guess is no…

Thanks in advance,

John

https://rainforestautomation.com/rfa-z114-eagle-200/


ComED Smart Meters?
FAQ: Can I integrate my Zigbee Smart Meter With SmartThings?
(Andreas A.) #2

It is not supported out of the box at this moment. However, there are several possibilities to get this to work using custom code:

  1. Creating a custom device type handler - the Eagle has a Rest API you can query: https://rainforestautomation.com/wp-content/uploads/2015/07/EAGLE_REST_API-1.1.pdf
  2. Uploading the Eagle data to Wattvision (one of the could API supported by the Eagle) and the using the SmartThings Wattvision DTH to pull the data back

Since the Rest API is not exactly fun to work with I am currently using the 2nd approach and it is working well for me.


(John) #3

I already use Wattvision… I didn’t realize you could pull from it. I’ll go that route then. I appreciate the quick and thorough follow up.


#4

I’m new to SmartThings but I’ve been using Wattvision (through RainForest Eagle).
SmartThings Wattvision DTH isn’t pulling any data. copy/paste the Wattvision url from iDE results in a blank page.
Any thoughts ? Thx.


(Andreas A.) #5

Did you install the Wattvision Manager as well?


#6

Not sure what I’m missing.
Here’s what I did: Under MarketPlace->smartapps on my phone, I chose Wattvision under energy management. Pressed Wattvision sensors button, signed into Wattvision through Wattvision manager. I get a message saying Wattvision is installed and is activated. I checked on IDE website and confirmed device type is ‘Wattvision’.
Refreshed thing on home screen, no smartapp listed under thing-settings.

Hopefully you can help. thx


(Andreas A.) #7

Not sure - works for me (though I tweaked the DTH for my personal preferences a little). Since this is an official integration from ST you might want to reach out to support to have them help you.


#8

An update - finally got Wattvision working! Wasn’t sure where the break in the chain was - re configured all devices involved -PGE data stream/Rainforest gateway/cloud setup/and Wattvision website settings.

The SmartApp is no longer officially supported.

Today: more Wattvision problems: need to look into java security errors-- I can get graph url and wattage on graph to display, but doesn’t export the data value.
Pls chime in if anybody has ideas.


(Andreas A.) #9

Their SSL certificate expired yesterday. You can change the URL to use http instead of https until they resolve that.


(Suresh Vilayanur) #10

andreas…can you point me to the latest custom DTH for watt
vision? thanks!!


(Andreas A.) #11

Here’s what I am currently using:

Wattvision Manager:

/**
 *  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)
	}
	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())
            if (json.data.length() > 0) {
				child.addWattvisionData(json)
               	state.lastUpdated = endDate.format(smartThingsDateFormat())
            }
            else {
            	log.trace "No data received - not updating 'state.lastUpdated'"
            }
			return "success"
		}
	}
}

def wattvisionURL(senorId, startDate, endDate, wattvisionDataType = null) {

	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 > 10800000) { // 3 hours 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", // rate: power in W (once every 6-7s), consumption: energy in Wh (once every 5min)
		"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() {
	def seconds = new Random(now()).nextInt(60)
	schedule("${seconds} * * * * ?", "getDataFromWattvision") // every 1 minute (with random offset)
}

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?
	"https://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 sensors = [:]

        response.data.each { sensorId, sensorName ->
        	sensors[sensorId] = sensorName
			createChild(sensorId, sensorName)
        }
        
        state.sensors = sensors

		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
}

Wattvision device:

/**
 *  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
 *
 *  Author: steve
 *  Date: 2014-02-13
 */
metadata {

	definition(name: "Wattvision", namespace: "smartthings", author: "Steve Vlaminck") {
		capability "Power Meter"
		capability "Refresh"
		capability "Sensor"
		attribute "powerContent", "string"
	}

	simulator {
		// define status and reply messages here
	}

	tiles {

		valueTile("power", "device.power", canChangeIcon: true) {
			state "power", label: '${currentValue}W',
                backgroundColors:[
                    [value: "-3000", 	color: "#90d2a7"],
                    [value: "0", 		color: "#1e9cbb"],
                    [value: "6000", 	color: "#bc2323"]
                ]
		}

		htmlTile(name: "powerContent", attribute: "powerContent", type: "HTML", whitelist: ["www.wattvision.com"] , url: '${currentValue}', width: 3, height: 2)

		standardTile("refresh", "device.power", inactiveLabel: false, decoration: "flat") {
			state "default", label: '', action: "refresh.refresh", icon: "st.secondary.refresh"
		}

		main "power"
		details(["powerContent", "power", "refresh"])

	}
}

def refresh() {
	parent.getDataFromWattvision()
	setGraphUrl(parent.getGraphUrl(device.deviceNetworkId))
}

// parse events into attributes
def parse(String description) {
	log.debug "Parsing '${description}'"
}

public setGraphUrl(graphUrl) {

	log.trace "setting url for Wattvision graph"

	sendEvent([
		date           : new Date(),
		value          : graphUrl,
		name           : "powerContent",
		displayed      : false,
		isStateChange  : true,
		description    : "Graph updated",
		descriptionText: "Graph updated"
	])
}

public addWattvisionData(json) {
	def data = parseJson(json.data.toString())
	log.trace "New data from Wattvision: ${data}"
	def units = json.units ?: "W"
    if (units == "watts") {
    	units = "W"
    }
    def powerValues = []
	if (data.size() > 0) {
		data.each {
			sendPowerEvent(it.t, it.v, units)
            powerValues << it.v
		}
        // send the average power as the current value
        sendPowerEvent(data[-1].t, (powerValues.sum()/powerValues.size()).toFloat().round(0).toInteger(), units, true)
	}

}

private sendPowerEvent(time, value, units, isLatest = false) {
	def wattvisionDateFormat = parent.wattvisionDateFormat()

	def eventData = [
		date           : new Date().parse(wattvisionDateFormat, time),
		value          : value,
		name           : "power",
		displayed      : isLatest,
		isStateChange  : isLatest,
        unit		   : units
	]

	if (isLatest) {
	    log.debug "Updating power reading: ${value}${units}"
    }
	sendEvent(eventData)

}

def parseJson(String s) {
	new groovy.json.JsonSlurper().parseText(s)
}

(Suresh Vilayanur) #12

Thanks so much, I installed the device handler and then installed a new device called wattvision. I think I am missing a step as I keep getting errors. Should I add this as a smartapp? If you could give me some brief steps it would be much aprpeciated!!!


(MacTechGenius) #13

I have mine working via watvision:


(Andreas A.) #14

There are two things - the Wattvision Manager which has to be installed and published as a SmartApp and the Wattvision device which has to be installed as a DTH. After that you use the SmartApp form within the ST app to add your device.

At least that’s how it worked way back then for me - haven’t gone through a new installation in a while…


(Suresh Vilayanur) #15

Thanks so much for clarifying. I think the code has been deprecated and is not working for me, The installation of the smartapp just hangs it seems…


(Suresh Vilayanur) #16

Thanks for the help @ahndee.

Ok so here is the deal. The code is accepted by ST and all gets installed fine. However when you try to install the smartapp it times out and I could not get it to install to configure it. It shows as a published app, but I get the spinning wheel when i click it…So it seems that they have deprecated the code and it can no longer function on the platfrom. I contacted suport and they could not get it to work, and I guess since this isnt supported officially there is a limit to what they will troubleshoot, which is fair.
If one of you braniacs cracks this nut and gets it working again that would be great! Till then I guess I just have to wait!!!

Thanks


(Suresh Vilayanur) #17

Yes I got this working…what we are trying to do here is integrate it into the ST app…


(MacTechGenius) #18

Yup, I have mine working using watvsion on the smart things app


(Suresh Vilayanur) #19

You are lucky then. If you look at my previous post I am unable to get it to work. Did u use the same configs?


(MacTechGenius) #20

I had the device handler installed when it was available in the SmartThings market place. Does your data show up on your watvision account?