Rainforest Zigbee Smart Meter support

Has anyone incorporated a Rainforest EAGLE™ Energy Access Gateway Link to Ethernet
with REST API or the USB Raven into OpenHAB. I’m looking to get one but I want to figure out what will be the easiest to implement to monitor my smart meter. documentation can be found HERE

I have one of these, the code below isn’t preity, as I wrote it a while back, but it’ll get you started. I can’t test it atm as PG&E just swapped out my SmartMeter for some reason and I need to pair the Eagle with the new device.

Here are my Items:

Number   HousePowerInstant    "SmartMeter Power [%.3f]" (GMonitorPower,GPersist)
Number   HouseEnergyDelivered "SmartMeter Energy Delivered [%.3f]" (GMonitorEnergy,GPersist)
Number   HouseEnergySent      "SmartMeter Energy Sent [%.3f]" (GMonitorEnergy,GPersist)
Number   HouseEnergyPrice     "SmartMeter Energy Price [%.3f]" (GMonitorEnergy,GPersist)
Number   HouseEnergyCost      "SmartMeter Energy Cost [%.3f]" (GMonitorEnergy,GPersist)

Here’s the Rule to drive it…

import org.openhab.core.library.types.*
import java.lang.Float
import java.lang.Long
import org.joda.time.DateTime

var DateTime lastTimestamp = null
var float lastDelivered
var float lastReceived

rule "Pull Data from Eagle"
  when
    Time cron "0 0-59 * * * ?"
  then
          var t = now
    val String EAGLE_MAC = "0xe1e100000e1e10"
    val String EAGLE_URL = "http://192.168.1.13/cgi-bin/cgi_manager"

    var String postData = String::format("<LocalCommand>
  <Name>get_usage_data</Name>
  <MacId>%s</MacId>
</LocalCommand>", EAGLE_MAC, EAGLE_MAC)

    var result = sendHttpPostRequest(EAGLE_URL, "application/x-www-form-urlencoded", postData)
    // logDebug("eagle", result)
       
    try {
      var long timestamp = Long::parseLong(transform("JSONPATH", "$.demand_timestamp", result))
      var DateTime currTimestamp = new DateTime(timestamp * 1000)
      var float price = Float::parseFloat(transform("JSONPATH", "$.price", result))
      var float currDemand = Float::parseFloat(transform("JSONPATH", "$.demand", result))
      var float currDelivered = Float::parseFloat(transform("JSONPATH", "$.summation_delivered", result))
      var float currReceived = Float::parseFloat(transform("JSONPATH", "$.summation_received", result))
  
      postUpdate(HousePowerInstant, currDemand * 1000)

      if (lastTimestamp != null && !lastTimestamp.equals(currTimestamp)) {
        var float used = currDelivered - lastDelivered
        var float sent = currReceived - lastReceived

        logDebug("eagle", String::format("Energy %s demand=%.3f received=%.3f delivered=%.3f", currTimestamp.toString,
          currDemand, used, sent))
        postUpdate(HouseEnergySent, sent * 1000)
        postUpdate(HouseEnergyDelivered, used * 1000)
        postUpdate(HouseEnergyCost, (used - sent) * 1000 * price)
      }

      postUpdate(HouseEnergyPrice, price)

      lastDelivered = currDelivered
      lastReceived = currReceived
      lastTimestamp = currTimestamp  
    } catch (NumberFormatException nfe) {
      logError("eagle", "Bad Data " + result.replaceAll("\n", " "))
    }
    var long x = now.getMillis - t.getMillis
    logInfo("eagle", "PERF Pull-Data-from-Eagle elapsed: " + String::valueOf(x) + "ms")
end
1 Like

I am looking at the rainforest products as well.
I assume this rule will only work with the Eagle due to its network connection? The Raven presumably can’t be accessed over usb using a modification of this script?
Cheers

The Raven exposes an XML API:

but it’d be easy enough to hand-translate. The Eagle exposes that API, as well as the one it’s UI uses (which is what I’m using here) which has JSON and XML.

Thanks Mark.
I’ll probably get the Eagle and use your code as a starting point.
Cheers!

I purchased the eagle end set it up one thing I had to do was disable the security to make it work.
In the web portal click the down arrow next to date.
at the bottom of that page click the gear.

uncheck the security box.

Hope this helps.

It would be nice if we could combine all this information under the Application Integration section of the wiki.

Feel free to add a page there, it’s open to community members to create/update.

done

1 Like

One thing I noticed is sometimes I miss the consumption update even with the cron set to every 30seconds.

What if openHAB had a listener for the updates posted from the eagle.

my eagle is on the way, looking forward to getting my hands on my data… :smile:

It’s possible I have the logic somewhat amiss. In general, you only see periodic updates to the values, based upon how often the SmartMeter itself publishes the changes.

In the middle, if you call the API, it really just returns the last known (absolute) values.

I use this as a backup for my Brultech setup, so I haven’t spent a ton of time tuning it.

I was mistaken it stores the consumed number till you pole. I noticed this phenomenon when my eagle was disconnected for a bit then when it did connect the consumed was a total of consumption from that last read and was much greater that the normal 1 min read.

I believe
var float used = currDelivered - lastDelivered
it just generating the demand for each reported read.
is there a way to have these totals for last hour and day?
like a cron that records current delivered at midnight then have it compare that number on every reported read.
for last hour though I’m not sure. I use mysql is there a way to read it for an hour back and compare with that.

EDIT I have the midnight read figured out… i think.
I added this rule. Now I just need to figure out how to read that and subtract current from it to make daily running total

rule "Set Data from Eagle at midnight"
  when
    Time is midnight
  then
    postUpdate(HouseEnergyDeliveredMidnight, lastDelivered)
end

found an interesting line in the persistence demo

Weather_Temperature.historicState(now.minusMinutes(2), "mysql")

one thing I am having a problem with is getting the float out of a number item. this seems to be workig but the last number is dissapearing. if the eagle reads 15896.321 but it’ll get written to the item as 15696.350.

var float day = currDelivered - (HouseEnergyDeliveredMidnight.state as DecimalType).floatValue()

Thank you for posting this. I’ve been playing with the cloud API (I’m a beginner) on and off during Dec/Jan. Question - I tried to use it tonight and I get an Error message “Bad Data <>”. Any thoughts/feedback on what I’m doing incorrectly? I changed the IP address and device ID to match my device. Help? Thanks.

With that message will be the source JSON data that contains the bad data.

This might be as simple as a value like "nan" (not a number) that the Eagle emits when it can’t contact the SmartMeter.

You’ll want to post the full log line.

I realized since I am not producing electricity at my home I could simplify the rule.

import org.openhab.core.library.types.*
import java.lang.Float
import java.lang.Long
import org.joda.time.DateTime

var DateTime lastTimestamp = null
var float lastDelivered

rule "Pull Data from Eagle"
  when
    Time cron "*/30 * * * * ?"
  then
          var t = now
    val String EAGLE_MAC = "0xd8d5b90000005aeb"
    val String EAGLE_URL = "http://192.168.0.109/cgi-bin/cgi_manager"

    var String postData = String::format("<LocalCommand>
  <Name>get_usage_data</Name>
  <MacId>%s</MacId>
</LocalCommand>", EAGLE_MAC, EAGLE_MAC)

    var result = sendHttpPostRequest(EAGLE_URL, "application/x-www-form-urlencoded", postData)
    //	logInfo("eagle", result)
       
    try {
      var long timestamp = Long::parseLong(transform("JSONPATH", "$.demand_timestamp", result))
      var DateTime currTimestamp = new DateTime(timestamp * 1000)
      var float currDemand = Float::parseFloat(transform("JSONPATH", "$.demand", result))
      var float currDelivered = Float::parseFloat(transform("JSONPATH", "$.summation_delivered", result))
  
      postUpdate(HousePowerInstant, currDemand * 1000)

      if (lastTimestamp != null && !lastTimestamp.equals(currTimestamp) && !currDelivered.equals(lastDelivered)) {
		var float day = currDelivered - (HouseEnergyDeliveredMidnight.state as DecimalType).floatValue()
        logInfo("eagle", String::format("Energy %s demand=%.3f delivered=%.3f", currTimestamp.toString, currDemand, currDelivered))
		postUpdate(HouseMeterRead, currDelivered)
		postUpdate(HouseEnergyDay, day * 100)
      }


      lastDelivered = currDelivered
      lastTimestamp = currTimestamp  
    } catch (NumberFormatException nfe) {
      logError("eagle", "Bad Data " + result.replaceAll("\n", " "))
    }
    var long x = now.getMillis - t.getMillis
    logInfo("eagle", "PERF Pull-Data-from-Eagle elapsed: " + String::valueOf(x) + "ms")
end
rule "Set Data from Eagle at midnight"
  when
    Time is midnight
  then
    postUpdate(HouseEnergyDeliveredMidnight, lastDelivered)
end
1 Like

Hi,
Full log line. Is this what you meant?
2016-03-02 20:25:46.269 [INFO ] [c.internal.ModelRepositoryImpl] - Refreshing model ‘kozo.rules.rules’
2016-03-02 20:26:00.410 [ERROR] [org.openhab.model.script.eagle] - Bad Data {}
2016-03-02 20:26:00.473 [INFO ] [org.openhab.model.script.eagle] - PERF Pull-Data-from-Eagle elapsed: 403ms
2016-03-02 20:27:00.282 [ERROR] [org.openhab.model.script.eagle] - Bad Data {}
2016-03-02 20:27:00.282 [INFO ] [org.openhab.model.script.eagle] - PERF Pull-Data-from-Eagle elapsed: 281ms
2016-03-02 20:28:00.266 [ERROR] [org.openhab.model.script.eagle] - Bad Data {}
2016-03-02 20:28:00.266 [INFO ] [org.openhab.model.script.eagle] - PERF Pull-Data-from-Eagle elapsed: 265ms
2016-03-02 20:29:00.256 [ERROR] [org.openhab.model.script.eagle] - Bad Data {}
2016-03-02 20:29:00.256 [INFO ] [org.openhab.model.script.eagle] - PERF Pull-Data-from-Eagle elapsed: 249ms

I’ve tried to run the URL (http:///cgi-bin/cgi_manager) in a browser and I get the { } response there as well. I also tried to run this URL in Postman to see if I could get a response but no difference (this is again on the eagle directly, not the cloud).

I have shut off security and I am also able to bring up the energy meter results with the standard URL:
http:///htdocs/emu/meter.html?mac_id=

Welcome any thoughts…Thank you for your support.

try adding the code

	logInfo("eagle", result)

after

 var result = sendHttpPostRequest(EAGLE_URL, "application/x-www-form-urlencoded", postData)

like mine to see what the eagle is returning.

here is an example of what mine returns.

2016-03-03 13:49:30.441 [INFO ] [org.openhab.model.script.eagle] - {"meter_status":"Connected",
"demand":"1.0870",
"demand_units":"kW",
"demand_timestamp":"1457012973",
"summation_received":"0.000",
"summation_delivered":"166566.550",
"summation_units":"kWh",
"price":"0.0940",
"price_units":"840",
"price_label":"Set by User",
"message_timestamp":"946684800",
"message_confirmed":"N",
"message_confirm_required":"N",
"message_id":"0",
"message_queue":"active",
"message_read":"Y",
"threshold_upper_demand":"12.749000",
"threshold_lower_demand":"-2.000000",
"fast_poll_frequency":"0x00",
"fast_poll_endtime":"0x00000000"}

2016-03-03 13:49:30.805 [INFO ] [org.openhab.model.script.eagle] - PERF Pull-Data-from-Eagle elapsed: 722ms
1 Like