New user alert - Owl Intuition PV capture/logging

I am seeing snippets all over the internet saying it is possible to capture the packets from the OWL hub but no real details of how to set it all up and put it into practice to publish the stats on Emoncms.  

Has anyone managed it and willing to share their information?

Thanks

 

pb66's picture

Re: New user alert - Owl Intuition PV capture/logging

I have an owl lurking in the back of one of my cupboards somewhere, it was one of the systems I trialed on the route to this project.

I have collected some links of interest with an intention of doing something one day, in no particular order

Multicast & UDP API Information (OWL website)

micolous/intuitionProtocol notes for Owl Intuition 

OWL Energy Monitor @ 433Mhz

​Re: Owl Intuition Multicast not working on emoncms raspberry pi sd image (SOLVED)

The "network owl" seems to transmit UDP and multicast packets which is apparently quite easy to capture, the JeeLabs bit is interesting as I also have an Oregon weatherstation in another cupboard gathering dust.

Paul

j20ands10's picture

Re: New user alert - Owl Intuition PV capture/logging

Cheers Paul just in the process of building a clean raspbian SD card hope to run the python script on that if I can get my head around it.

sumnerboy's picture

Re: New user alert - Owl Intuition PV capture/logging

I used this script (https://github.com/cornetp/eagle-owl) to interface with my Owl CM160 a while back (no longer use it after moving to an EmonTx). I had it set up to monitor the values and publish to openHAB over the REST API which means it should be pretty easy to modify for publishing to EmonCMS in the required format.

j20ands10's picture

Re: New user alert - Owl Intuition PV capture/logging

spent a few hours looking at the python code last night got to the point now where it will not even compile; I guess this is why people move across to EmonTX etc rather than using 3rd party devices. not giving up but wish I had spotted Open Energy Monitor before buying the owl.

pb66's picture

Re: New user alert - Owl Intuition PV capture/logging

I just hooked mine up to try (no CT's etc) and could receive the owl multicast using

#!/usr/bin/env python

import socket
import struct

MCAST_GRP = '224.192.32.19'
MCAST_PORT = 22600

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('', MCAST_PORT))  # use MCAST_GRP instead of '' to listen only
                             # to MCAST_GRP, not all groups on MCAST_PORT
mreq = struct.pack("4sl", socket.inet_aton(MCAST_GRP), socket.INADDR_ANY)

sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)

while True:
  print sock.recv(1024)

Also had to add port 22600 to the ufw allowed list, but then I received xml packets that don't look too difficult to parse into a emoncms url or if you are using emonhub it would be quite easy to adapt a SocketInterfacer to a OwlInterfacer.

Paul

pb66's picture

Re: New user alert - Owl Intuition PV capture/logging

Just tried "xmltodict" python module for parsing, if replacing the "print sock.recv(1024)" line (above) with

data = xmltodict.parse(sock.recv(1024))
    if 'electricity' in data:
        if data['electricity']['@id'] == '123456789abc:
            print "rssi       : " + data['electricity']['signal']['@rssi']
            print "lqi        : " + data['electricity']['signal']['@lqi']
            print "batt level : " + data['electricity']['battery']['@level'][:-1]\
                  + " " + data['electricity']['battery']['@level'][-1]
            for ct in (0, 1, 2):
                if data['electricity']['chan'][ct]:
                    print "CT" + str(ct + 1) + " power  : " \
                          + data['electricity']['chan'][ct]['curr']['#text'] + " " \
                          + data['electricity']['chan'][ct]['curr']['@units']
                    print "CT" + str(ct + 1) + " energy : " \
                          + data['electricity']['chan'][ct]['day']['#text'] + " " \
                          + data['electricity']['chan'][ct]['day']['@units']

    elif 'solar' in data:
        if data['solar']['@id'] == '123456789abc':
            print "generating : " + data['solar']['current']['generating']['#text']\
                  + " " + data['solar']['current']['generating']['@units']
            print "generated  : " + data['solar']['day']['generated']['#text']\
                  + " " + data['solar']['day']['generated']['@units']
            print "exporting  : " + data['solar']['current']['exporting']['#text']\
                  + " " + data['solar']['current']['exporting']['@units']
            print "exported   : " + data['solar']['day']['exported']['#text']\
                  + " " + data['solar']['day']['exported']['@units']

and adding import xmltodict at the head of the script gives a print out of 

rssi       : -20
lqi        : 0
batt level : 100 %
CT1 power  : 0.00 w
CT1 energy : 0.00 wh
CT2 power  : 0.00 w
CT2 energy : 0.00 wh
CT3 power  : 0.00 w
CT3 energy : 0.00 wh
generating : 0.00 w
generated  : 0.00 wh
exporting  : 0.00 w
exported   : 0.00 wh

if the device id is matched (Ive used 123456789abc in the example above) 

The data comes in in 2 packets so ideally needs to be collated and then sent as 1 node id or sent as 2 individual nodes, or as 2 halves of the same node id maybe better.

This is just debug prints the final payload can be just the usual timestamped data array with a node id.

the xmltodict module can be installed using sudo pip install xmltodict if pip is installed

Paul 

j20ands10's picture

Re: New user alert - Owl Intuition PV capture/logging

this is the code i am using

from twisted.internet.protocol import DatagramProtocol
from lxml import objectify
from decimal import Decimal
import socket
import requests

sock = socket.socket(socket.AF_INET, # Internet
          socket.SOCK_DGRAM) # UDP

MCAST_ADDR = '224.192.32.19'
MCAST_PORT = 22600

class node(object):
# structure for storing data about channels
    def __init__(self, channel_id, current_w, daily_wh):
#  self.channel_id = channel_id
      self.current_w = Decimal(current_w)
#  self.daily_wh = Decimal(daily_wh)

def __str__(self):
  return '%s' % (
#   self.channel_id,
   self.current_w,
#   self.daily_wh
  )

class OwlMessage(object):
    def __init__(self, datagram):
  #print "datagram: %r" % (datagram,)
        self.root = objectify.fromstring(datagram)
 
  # there are also weather events -- we don't care about these
        assert (self.root.tag == 'electricity'), ('OwlMessage XML must have `electricity` root node (got %r).' % self.root.tag)
 
  # note that the MAC address is given by the message, not the packet.
  # this can be spoofed
        self.mac = self.root.attrib['id']
 
  # read signal information for the sensor's 433MHz link
        self.rssi = Decimal(self.root.signal[0].attrib['rssi'])
        self.lqi = Decimal(self.root.signal[0].attrib['lqi'])
 
  # read battery information from the sensor.
        self.battery = Decimal(self.root.battery[0].attrib['level'][:-1])
 
  # read sensors (channels)
    self.channels = {}
    for channel in self.root.chan:
        assert channel.attrib['id'] not in self.channels, 'Channel duplicate'
        assert channel.curr[0].attrib['units'] == 'w', 'Current units must be watts'
        assert channel.day[0].attrib['units'] == 'wh', 'Daily usage must be watthours'
  
   # we're good and done our tests, create a channel
        self.channels[channel.attrib['id']] = node(channel.attrib['id'], channel.curr[0].text, channel.day[0].text)

def __str__(self):
  return 'csv=%s' % (
   ','.join((str(x) for x in self.channels.itervalues()))
  )

class OwlIntuitionProtocol(DatagramProtocol):
    def __init__(self, iface=''):
      """
      Protocol for Owl Intution (Network Owl) multicast UDP.
 
      :param iface: Name of the interface to use to communicate with the Network Owl.  If not specified, uses the default network connection on the cost.
      :type iface: str
      """
      self.iface = iface

    def startProtocol(self):
      self.transport.joinGroup(MCAST_ADDR, self.iface)

    def datagramReceived(self, datagram, address):
      msg = OwlMessage(datagram)
      self.owlReceived(address, msg)

    def owlReceived(self, address, msg):
      print '%s' % (msg)
      curlmessage = '%s' % (msg)
      r = requests.post("http://emoncms.org/input/post.json?node=1&"+curlmessage+",""&apikey=xxxxxxxxxx")

if __name__ == '__main__':
    from twisted.internet import reactor
    from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument('-i', '--iface', dest='iface', default='', help='Network interface to use for getting data.')

options = parser.parse_args()

protocol = OwlIntuitionProtocol(iface=options.iface)
reactor.listenMulticast(MCAST_PORT, protocol, listenMultiple=True)
reactor.run()

but when it runs I gets an error on line 27 in <module> and 47 in owlmessage

 

pb66's picture

Re: New user alert - Owl Intuition PV capture/logging

When I started looking at the various scripts they all seemed a bit heavy for what seemed a simple task, so I wondered how little code it could be done with. considering the bulk of all the print statements I used that can be stripped away after dev I think this could be be small enough to include in an emonHub interfacer.

Looking at Line 47 it looks like there is an indentation issue (hard to be sure in a post) the "self.channels = {}" and the following 5 lines block should be indented once more so the "self.channels = {}" lines up with the "self.battery = Decimal(self.root.battery[0].attrib['level'][:-1])" line above it not the "def __init__(self, datagram):" further up.

Paul

G6EJD's picture

Re: New user alert - Owl Intuition PV capture/logging

I've got the Ardunio reading the network owl UDP datagrams:

/*
Network OWL UDP Reader using an Arduino with Ethernet shield attached.

The Arduino is assigned an arbitrary network address (192.168.0.177)and then monitors port 22600 used by the Network Owl to distribute data.
The Serial monitor is used to see the received data from the Wiznet Ethernet shield.

This is an example of the Network UDP Datagram broadcast on UDP port 22600 (typically 321 bytes of data)

<electricity id='443719000DDC'>
  <signal rssi='-56' lqi='30'/>
  <battery level='100%'/>
  <chan id='0'>
    <curr units='w'>273.00</curr><day units='wh'>2699.73</day>
  </chan>
  <chan id='1'>
    <curr units='w'>0.00</curr><day units='wh'>0.00</day>
  </chan>
  <chan id='2'>
    <curr units='w'>0.00</curr><day units='wh'>0.00</day>
  </chan>
</electricity>

This is an exmplae of the weather data packet:
<weather id='00A0C914C851' code='263'>
  <temperature>19.00</temperature>
  <text>Patchy light drizzle</text>
</weather>

*/
#include <SPI.h>       
#include <Ethernet.h>
#include <EthernetUdp.h>

// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192, 168, 0, 177);   // Give the Arduino an attribary IP address
unsigned int localPort = 22600;   // local port to listen on

// An EthernetUDP instance to let us send and receive packets over UDP
EthernetUDP Udp;

//char packetBuffer[UDP_TX_PACKET_MAX_SIZE]; //buffer to hold incoming packet,
#define buffer_size 260

char packetBuffer[buffer_size]; //buffer to hold incoming packet,
int start_point;
String UDP_Data;
boolean address_shown = false;

void setup() {
   // start the Ethernet and UDP:
   Serial.begin(115200);
   Ethernet.begin(mac,ip);
   Udp.begin(localPort);
   Serial.println("Receiving packets of data");
   //Serial.println(UDP_TX_PACKET_MAX_SIZE); / Diagnostic print option
}

void loop() {
   int packetSize = Udp.parsePacket();
   if(packetSize)
   {
     if (!address_shown) {
       Serial.print("Received data packet of size ");
       Serial.print(packetSize);
       Serial.print(" From ");
       IPAddress remote = Udp.remoteIP();
       for (int i =0; i < 4; i++) {
         Serial.print(remote[i], DEC);
         if (i < 3) {Serial.print(".");}
       }
       Serial.print(", port: ");
       Serial.println(Udp.remotePort());
       address_shown = true;
     }
     Udp.read(packetBuffer,buffer_size);
     //Serial.println("Received data:");
     UDP_Data = packetBuffer; // Convert char array to string for ease of manipulation
     //Serial.println(UDP_Data); // Diagnostic print. If required uncomment start of line
     // First look for Watts data
     start_point = UDP_Data.indexOf("<curr units=\'w\'>") + 16;
     Serial.print(UDP_Data.substring(start_point,UDP_Data.indexOf("</curr>",start_point)));
     Serial.print(" Watts  ");
     // Next look for accumlated Watts used today
     start_point = UDP_Data.indexOf("<day units=\'wh\'>") + 16;
     Serial.print(UDP_Data.substring(start_point,UDP_Data.indexOf("</day>",start_point)));
     Serial.print(" W-Hours or ");
     float KW_Hours = (UDP_Data.substring(start_point,UDP_Data.indexOf("</day>",start_point))).toFloat() / 1000;
     Serial.print(KW_Hours, 2);
     Serial.println(" KWHrs (accumulated power used today)");
     // Display associated weather data
     // UDP_Data = "<weather id='00A0C914C851' code='263'><temperature>19.00</temperature><text>Patchy light drizzle</text></weather>"; // test data
     if (UDP_Data.indexOf("<weather") >= 0) {
       Serial.println();
       Serial.print("Weather report: ");
       start_point = UDP_Data.indexOf("<temperature>") + 13;
       Serial.print(UDP_Data.substring(start_point,UDP_Data.indexOf("</temperature>",start_point)));
       Serial.print("'C  ");
       start_point = UDP_Data.indexOf("<text>") + 6;
       Serial.println(UDP_Data.substring(start_point,UDP_Data.indexOf("</text>",start_point)));
       Serial.println();
     }
  }
}

 

j20ands10's picture

Re: New user alert - Owl Intuition PV capture/logging

G6EJD,

All I am getting is "Receiving packets of data" any ideas?

John

G6EJD's picture

Re: New user alert - Owl Intuition PV capture/logging

What Ethernet card are you using - 5100, 5200 or the latest 5500, make sure you have the correct library, but generally only 5500 needs EthernetV2.0. Do the  examples in the Ethernet folder work e.g. NTP . I presume your network owl is connected and running OK. Have you changed any of the default UDP/Push settings in the network owl, you should find it set to port 22600 by default, it may be different.

I assume your network owl is on the same network. Your ethernet card will need t obe plugged into a router.

Using the WiFi card does work, but it's buffer size is very small, so you can only just receive power usage, that's all, but it works nonetheless.

Put some diagnostic print statements in the code, so you can see that it arrives at various places. e.g. Serial.println("Start of the main loop");

G6EJD's picture

Re: New user alert - Owl Intuition PV capture/logging

BTW works on IDE 1.6.5 but 1.06 would be a more stable choice.

j20ands10's picture

Re: New user alert - Owl Intuition PV capture/logging

its working forgot I had the owl base plugged into the router and the cable which came down to the living room was plugged into a 1gig switch. Both plugged into the router and all sorted, now the fun begins.

 

pb66's picture

Re: New user alert - Owl Intuition PV capture/logging

@John - Have you decided not to use the Pi now?

I expanded the stuff I posted above to send to emoncms and/or via emonhub (see attached)

Although I'm not able to spend much time on this now, I have noticed that the "udp push" interface can actually accept commands too, including setting the AC voltage and power factor (defaults to 230vac and pf1.0) so it is probally quite possible to use data from a emontx to adjust the owl settings on a regular interval eg 1min and make it considerably more accurate, something to look at another time :-)

Paul

j20ands10's picture

Re: New user alert - Owl Intuition PV capture/logging

Paul I was really struggling with the RPi and got side tracked onto arduino in a hope I could understand what was going on more; as the nights get darker i am sure I will get back to RPi.

Having had the hiccup with the router and switch thats what might have been causing my issue on the RPi. :)

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.