Rainforest Zigbee Smart Meter support

rainforest
energy
zigbee
Tags: #<Tag:0x00007f51df44af88> #<Tag:0x00007f51df44acb8> #<Tag:0x00007f51df44ab50>

(David Luther) #41

Here ya go.

First it builds the query replace MacId with the id of your EAGLE

<LocalCommand>   <Name>get_usage_data</Name>   <MacId>0xd8d5b90000005aeb</MacId> </LocalCommand>

Then it will POST to the EAGLE http://192.168.0.109/cgi-bin/cgi_manager replace the ip with yours.
The JSON data is parsed and seperated.

The first branch takes the power usage and converts kW to W and sends it to graphs and out to MQTT
also converting current usage to cost.

The delivered sum doesn’t update as often as the usage so it gets checked and only passed if it is different than the last message with the rbe and then sent out to MQTT

The last branch takes out the data and converts it back to JSON to make the raw data available on mqtt.

[{"id":"ead95b90.b7dc28","type":"inject","z":"1f5ff597.a9c6da","name":"10sec Loop","topic":"","payload":"","payloadType":"str","repeat":"10","crontab":"","once":false,"x":238.88888549804688,"y":197.77777004241943,"wires":[["8d43225d.18a7d"]]},{"id":"8d43225d.18a7d","type":"change","z":"1f5ff597.a9c6da","name":"EAGLE Request","rules":[{"t":"set","p":"payload","pt":"msg","to":"<LocalCommand>   <Name>get_usage_data</Name>   <MacId>0xd8d5b90000005aeb</MacId> </LocalCommand>","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":248.88888549804688,"y":237.77777004241943,"wires":[["4408e4f7.42df2c"]]},{"id":"4408e4f7.42df2c","type":"http request","z":"1f5ff597.a9c6da","name":"EAGLE POST","method":"POST","ret":"obj","url":"http://192.168.0.109/cgi-bin/cgi_manager","tls":"","x":248.88888549804688,"y":277.77777004241943,"wires":[["ab65ea2f.aed7b8","f455e57f.ec02a8","2b82e58d.0cc42a","2ecba0de.c2a92"]]},{"id":"6a7bc971.502e88","type":"rbe","z":"1f5ff597.a9c6da","name":"","func":"rbe","gap":"","start":"","inout":"out","x":658.8888854980469,"y":297.77777004241943,"wires":[["e04d4b53.15dc98"]]},{"id":"ab65ea2f.aed7b8","type":"change","z":"1f5ff597.a9c6da","name":"Delivered","rules":[{"t":"set","p":"payload","pt":"msg","to":"msg.payload.summation_delivered","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":488.8888854980469,"y":297.77777004241943,"wires":[["6a7bc971.502e88"]]},{"id":"e3d21794.5c7298","type":"mqtt out","z":"1f5ff597.a9c6da","name":"Lexor_MQTT","topic":"","qos":"2","retain":"true","broker":"fee1fac2.261a88","x":1172.2222900390625,"y":296.66665267944336,"wires":[]},{"id":"3cb8b99.2976846","type":"ui_chart","z":"1f5ff597.a9c6da","name":"","group":"dfc4c73a.069fe8","order":0,"width":"0","height":"0","label":"1hr","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","ymin":"","ymax":"","removeOlder":1,"removeOlderPoints":"","removeOlderUnit":"3600","cutout":0,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"x":827.77783203125,"y":122.22222137451172,"wires":[[],[]]},{"id":"74d9ddcb.082af4","type":"ui_chart","z":"1f5ff597.a9c6da","name":"","group":"dfc4c73a.069fe8","order":0,"width":0,"height":0,"label":"5min","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","ymin":"","ymax":"","removeOlder":"5","removeOlderPoints":"","removeOlderUnit":"60","cutout":0,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"x":826.6666831970215,"y":165.55555152893066,"wires":[[],[]]},{"id":"f455e57f.ec02a8","type":"function","z":"1f5ff597.a9c6da","name":"Multiply1000","func":"var demandkw = Number(msg.payload.demand);\nvar price = 949.4;\nvar demandw = { payload: Math.round(1000 * demandkw)};\nvar L = Math.round(price * demandkw);\nvar priceh = { payload: (L/10000) };\nreturn [demandw, priceh];","outputs":"2","noerr":0,"x":498.8888854980469,"y":237.77777004241943,"wires":[["74d9ddcb.082af4","3cb8b99.2976846","62741850.d8fb08","7466596b.be66c8"],["109d42e2.f12a9d"]]},{"id":"109d42e2.f12a9d","type":"ui_text","z":"1f5ff597.a9c6da","group":"dfc4c73a.069fe8","order":0,"width":0,"height":0,"name":"","label":"Power Cost","format":"{{msg.payload| number:3}}$/h","layout":"col-center","x":832.2222213745117,"y":258.88889503479004,"wires":[]},{"id":"2b82e58d.0cc42a","type":"json","z":"1f5ff597.a9c6da","name":"","x":479.99999237060547,"y":348.88887214660645,"wires":[["eed1b386.2814f"]]},{"id":"62741850.d8fb08","type":"ui_gauge","z":"1f5ff597.a9c6da","name":"","group":"dfc4c73a.069fe8","order":0,"width":0,"height":0,"gtype":"gage","title":"Gauge","label":"W","format":"{{value}}","min":"1000","max":"14000","colors":["#00b500","#e6e600","#ca3838"],"seg1":"","seg2":"","x":827.7777938842773,"y":80.00000762939453,"wires":[]},{"id":"eed1b386.2814f","type":"change","z":"1f5ff597.a9c6da","name":"","rules":[{"t":"set","p":"topic","pt":"msg","to":"lexor/eagle/json","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":831.1111145019531,"y":364.44443130493164,"wires":[["e3d21794.5c7298"]]},{"id":"e04d4b53.15dc98","type":"change","z":"1f5ff597.a9c6da","name":"","rules":[{"t":"set","p":"topic","pt":"msg","to":"openHAB/out/HouseMeterRead/state","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":832.2222213745117,"y":320.0000190734863,"wires":[["e3d21794.5c7298"]]},{"id":"7466596b.be66c8","type":"change","z":"1f5ff597.a9c6da","name":"","rules":[{"t":"set","p":"topic","pt":"msg","to":"openHAB/out/HousePowerInstant/state","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":845.5555648803711,"y":214.44443893432617,"wires":[["e3d21794.5c7298"]]},{"id":"fee1fac2.261a88","type":"mqtt-broker","z":"","broker":"mqtt.YOUR.BROKER","port":"1883","clientid":"nodered","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"willTopic":"","willQos":"0","willPayload":"","birthTopic":"","birthQos":"0","birthPayload":""},{"id":"dfc4c73a.069fe8","type":"ui_group","z":"","name":"Main","tab":"36c72615.fbcdea","order":1,"disp":true,"width":"6"},{"id":"36c72615.fbcdea","type":"ui_tab","z":"","name":"Home","icon":"fa-home","order":"1"}]

(Jon) #42

Hi Everyone,

I am just starting out trying to figure out how to get my Rainforest Eagle-200 setup with OpenHab.

This thread has been dormant for over 2 years. Can anyone tell me if the code will work for the Eagle-200, rather than the original Eagle?

If not, any other threads you could point me to in order to set this up with their newer 200 product?

Thanks,

Jon


(David Luther) #43

Your probably better off starting a new thread. When you do, post some screens shots of the setup screens. It does say there is a rest interface so it might be possible.

After looking at the actual device I see it is a replacement not a different device to the Eagle. definitely would belong here. so others can benefit from the work to make the data available to openhab.


(David Luther) #44

This would be a good start.


(Thomas Hentschel) #45

I got a initial shot at this working. Needs a bit of clean-up, I’ll post it over the weekend.


(Thomas Hentschel) #46

(Steve M.) #47

I just tried the new binding with my legacy Rainforest Eagle. Although the binding finds my device, it will not initialize after entering the install code in the paper UI. This leads me to believe the API is different.

I guess it’s time to buy a new one!


(Thomas Hentschel) #48

Yep, sorry, they are different animals when it comes to the API. I don’t have the old style Eagle to test, so can’t really support it in that binding.

Cheers,
-Th


(jamac) #49

Hi. I’m trying to get this binding to work with my legacy Rainforest Eagle 100 under OH2 on an Openhabian RPI 3B+.

I’m using @LeXLuther422’s original code with the improvements suggested by @swamiller. Data seems to be coming from the Eagle but I keep getting bad data errors. I’ve posted my log and code below.

Does anyone have any thoughts on what’s going wrong?

log

2019-01-09 01:08:06.298 [INFO ] [eclipse.smarthome.model.script.eagle] - {"meter_status":"Connected",

"demand":"0.9800",

"demand_units":"kW",

"demand_timestamp":"1546996080",

"summation_received":"6286.000",

"summation_delivered":"17101.000",

"summation_units":"kWh",

"price":"0.2400",

"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":"14.570000",

"threshold_lower_demand":"-5.300000",

"fast_poll_frequency":"0x00",

"fast_poll_endtime":"0x00000000"}

2019-01-09 01:08:06.308 [ERROR] [eclipse.smarthome.model.script.eagle] - Bad Data {"meter_status":"Connected", "demand":"0.9800", "demand_units":"kW", "demand_timestamp":"1546996080", "summation_received":"6286.000", "summation_delivered":"17101.000", "summation_units":"kWh", "price":"0.2400", "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":"14.570000", "threshold_lower_demand":"-5.300000", "fast_poll_frequency":"0x00", "fast_poll_endtime":"0x00000000"} 

2019-01-09 01:08:06.314 [INFO ] [eclipse.smarthome.model.script.eagle] - PERF Pull-Data-from-Eagle elapsed: 1191ms

Rule

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 = "0xD8D5B900000011ab"
    val String EAGLE_URL = "http://192.168.1.22/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.toString, 3000)
    logInfo("eagle", result)
    try {
        var long timestamp = Long::parseLong(transform("JSONPATH", "$.demand_timestamp", result.toString))
        var DateTime currTimestamp = new DateTime(timestamp * 1000)
        var float price = Float::parseFloat(transform("JSONPATH", "$.price", result.toString))
        var float currDemand = Float::parseFloat(transform("JSONPATH", "$.demand", result.toString))
        var float currDelivered = Float::parseFloat(transform("JSONPATH", "$.summation_delivered", result.toString))
        var float currReceived = Float::parseFloat(transform("JSONPATH", "$.summation_received", result.toString))
        postUpdate(HousePowerInstant, currDemand * 1000)
    
        if (lastTimestamp !== null && !lastTimestamp.equals(currTimestamp)) {
            var float used = currDelivered - lastDelivered
            var float sent = currReceived - lastReceived
            logInfo("eagle ", String::format("Energy %s demand=%.3f received=%.3f delivered=%.3f", currTimestamp.toString,
              currDemand, used, sent)) 
            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

(John Schmitz) #50

You do have the JSONPATH service loaded, right? I’d like to see this work because I too have an Eagle 100 which I’d like to get connected. If you do have that loaded, then my debug suggestion would be to shorten that long try{} to something much smaller and see if you can get a single data point such as the demand_timestamp. Use logInfo to push it to the log file. If that works, move on to the others. If it doesn’t, try to debug just that one.


(jamac) #51

Yep. That was the problem! It looks like it’s running ok now.


(Dave Baldwin) #52

Has anyone had issues with frequent requests (every 1 minute) knocking down the EAGLE?
After a while, I am experiencing ‘stale’ data - i.e. demand values that don’t change - and then when I go to the EAGLE’s own site, instead of a green ‘Connected’, I am always redirected to the Settings page, where I see a red “Rejoining CH xx” indicator. It keeps cycling through the channels and never rejoins.

If I reboot the EAGLE, it seems to rejoin and start getting real data again.

I saw some discussion over on a Home Assistant forum that says some others have experienced this. The solution for some seemed to be to configure the EAGLE to push its data stream to a locally-configured web server, and that have web server parse the EAGLE’s HTTP POST requests and push the data into OH (or HA) over MQTT. That’s possibly an option for me but a heavy lift - I’m not confident I could reasonably quickly figure out how to set up a Python web server to list and parse the incoming stuff.

Another weird item is that I have looked at Rainforest’s API documentation, and tried several running other commands via HTTP POST request, but in general I just blank responses back, which confuses me…

My rule for pulling data out of the EAGLE is below. Any suggestions on what I might be doing wrong to knock it down?

Thanks in advance!

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 * * * ?" or
    Item ManualDataPull changed to ON
then
    // turn the manual pull off immediately
    postUpdate(ManualDataPull, OFF)
    logInfo("eagle", "Starting data pull from Eagle")

    var t = now
    val String EAGLE_MAC = "0xd8d5b9xxxxxetc"
    val String EAGLE_URL = "http://192.168.x.x/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.toString, 5000)
    logInfo("eagle", result)
           
    try {
        var String currStatus = transform("JSONPATH", "$.meter_status", result)
        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)
        postUpdate(LastEagleTimestamp, new DateTimeType(currTimestamp.toCalendar(null)))
        postUpdate(EagleStatus, currStatus)
    
        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

(John Schmitz) #53

Do you have the Eagle 200 or 100? I had some issues with the Eagle 200 crashing. Eventually Rainforest updated the firmware and it seems to work better. Make sure you get the latest firmware. But I do not poll every minute. I think I use 10 or 15 minutes which is good enough for me.

Try your POST requests using curl first. Then you can try them in a rule. Which requests are not working?