Advertisement
Guest User

P1 Meter read-out

a guest
May 30th, 2017
283
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 10.62 KB | None | 0 0
  1. #!/usr/bin/python3 -tt
  2. import configparser
  3. import os, serial, logging, re, json, datetime
  4.  
  5. import sys, time
  6. import httplib2
  7.  
  8. from daemon import Daemon
  9. from array import array
  10.  
  11. logging.basicConfig(filename='p1meter.log', level=logging.INFO)
  12. log = logging.getLogger(__name__)
  13.  
  14. # DSMR interesting codes
  15. list_of_interesting_codes = {
  16.     '1-0:1.8.1': 'Meter Reading electricity delivered to client (Tariff 1) in kWh',
  17.     '1-0:1.8.2': 'Meter Reading electricity delivered to client (Tariff 2) in kWh',
  18.     '1-0:2.8.1': 'Meter Reading electricity delivered by dient (Tariff 1) in kWh',
  19.     '1-0:2.8.2': 'Meter Reading electricity delivered by client (Tariff 2) in kWh',
  20.     '0-0:96.14.0': 'Tariff indicator electricity',
  21.     '1-0:1.7.0': 'Actual electricity power delivered (+P) in kW',
  22.     '1-0:2.7.0': 'Actual electricity power received (-P) in kW',
  23.     '0-0:17.0.0': 'The actual threshold electricity in kW',
  24.     '0-0:96.3.10': 'Switch position electricity',
  25.     '0-0:96.7.21': 'Number of power failures in any phase',
  26.     '0-0:96.7.9': 'Number of long power failures in any phase',
  27.     '1-0:32.32.0': 'Number of voltage sags in phase L1',
  28.     '1-0:52.32.0': 'Number of voltage sags in phase L2',
  29.     '1-0:72:32.0': 'Number of voltage sags in phase L3',
  30.     '1-0:32.36.0': 'Number of voltage swells in phase L1',
  31.     '1-0:52.36.0': 'Number of voltage swells in phase L2',
  32.     '1-0:72.36.0': 'Number of voltage swells in phase L3',
  33.     '1-0:31.7.0': 'Instantaneous current L1 in A',
  34.     '1-0:51.7.0': 'Instantaneous current L2 in A',
  35.     '1-0:71.7.0': 'Instantaneous current L3 in A',
  36.     '1-0:21.7.0': 'Instantaneous active power L1 (+P) in kW',
  37.     '1-0:41.7.0': 'Instantaneous active power L2 (+P) in kW',
  38.     '1-0:61.7.0': 'Instantaneous active power L3 (+P) in kW',
  39.     '1-0:22.7.0': 'Instantaneous active power L1 (-P) in kW',
  40.     '1-0:42.7.0': 'Instantaneous active power L2 (-P) in kW',
  41.     '1-0:62.7.0': 'Instantaneous active power L3 (-P) in kW'
  42. }
  43.  
  44. class MyDaemon(Daemon):
  45.     def run(self):
  46.         config = configparser.RawConfigParser()
  47.         config.read('/home/p1meter/p1meter/p1meter.conf')
  48.         baudrate = config.getint("meter", "baudrate") ## 115200
  49.         bytesize = config.getint("meter", "bytesize") ## 8
  50.         parity = serial.PARITY_NONE;
  51.         stopbits = config.getint("meter", "stopbits") ## 1
  52.         xonoff = config.getboolean("meter", "xonoff") ## false
  53.         timeout = config.getint("meter", "timeout") ## 10
  54.         port = config.get("meter", "port")
  55.  
  56.         lastLogAt = datetime.datetime.now()
  57.         usageArray = array('f')
  58.         returnArray = array('f')
  59.         p1 = {}
  60.  
  61.         log.debug('Open serial connect to {} with: {}'.format(port, ', '.join('{}={}'.format(key, value) for key, value in config.items())))
  62.  
  63.         try:
  64.             self.serial = serial.Serial()
  65.             self.serial.baudrate = baudrate
  66.             self.serial.bytesize = bytesize
  67.             self.serial.parity   = parity
  68.             self.serial.stopbits = stopbits
  69.             self.serial.xonxoff  = xonoff
  70.             self.serial.timeout  = timeout
  71.             self.serial.port = port
  72.            
  73.             self.serial.open()
  74.            
  75.         except (serial.SerialException, OSError) as e:
  76.             log.error(e)
  77.             sys.exit ("Fout bij het openen van poort %s. "  % self.serial.name)      
  78.             self.serial.close()
  79.         else:
  80.             self.serial.setRTS(False)
  81.             self.port = self.serial.name
  82.  
  83.         log.info('New serial connection opened to %s', self.port)
  84.  
  85.         while True:
  86.             try:
  87.                 readAt = datetime.datetime.now()                
  88.  
  89.                 if (readAt - lastLogAt).total_seconds() >= 60:
  90.                     e = {}
  91.                     e['read_at'] = p1['timestamp']
  92.                     e['usage_1'] = p1['kwh_low_usage']
  93.                     e['return_1'] = p1['kwh_low_return']
  94.                     e['usage_2'] = p1['kwh_high_usage']
  95.                     e['return_2'] = p1['kwh_high_return']
  96.                     e['usage'] = sum(usageArray) / len(usageArray)
  97.                     e['return'] = sum(returnArray) / len(returnArray)
  98.                     e['current_L1'] = p1['current_L1']
  99.                     e['current_L2'] = p1['current_L2']
  100.                     e['current_L3'] = p1['current_L3']
  101.                     e['usage_L1'] = p1['usage_L1']
  102.                     e['usage_L2'] = p1['usage_L2']
  103.                     e['usage_L3'] = p1['usage_L3']
  104.                     e['return_L1'] = p1['return_L1']
  105.                     e['return_L2'] = p1['return_L2']
  106.                     e['return_L3'] = p1['return_L3']
  107.                     e['usage_gas'] = p1['gas_total']
  108.                     e['date_gas'] = p1['gas_date']
  109.  
  110.                     http = httplib2.Http()
  111.                     header = {"Content-Type":"application/json","User-Agent":"Python3 Energy monitor"}
  112.                     response, send = http.request("https://url.of.my.own/postp1.php","POST",headers=header, body=json.dumps(e))
  113.                     str_content = send.decode('utf-8')
  114.                     if str_content != "OK":
  115.                         log.info(str_content)
  116.                     lastLogAt = readAt
  117.                     usageArray = []
  118.                     returnArray = []
  119.  
  120.                 datagram = b''
  121.                 lines_read = 0
  122.                 startFound = False
  123.                 endFound = False
  124.        
  125.                 log.debug('Start reading lines')
  126.        
  127.                 while not startFound or not endFound:
  128.                     try:
  129.                         line = self.serial.readline()
  130.                         log.debug('>> %s', line.decode('ascii').rstrip())
  131.                     except Exception as e:
  132.                         log.error(e)
  133.                         log.error('Read a total of %d lines', lines_read)
  134.                         break
  135.        
  136.                     lines_read += 1
  137.        
  138.                     if re.match(b'.*(?=/)', line):
  139.                         startFound = True
  140.                         endFound = False
  141.                         datagram = line.lstrip()
  142.                     elif re.match(b'(?=!)', line):
  143.                         endFound = True
  144.                         datagram = datagram + line
  145.                     else:
  146.                         datagram = datagram + line
  147.        
  148.                     # TODO: build in some protection for infinite loops
  149.  
  150.                 self._datagram = datagram
  151.  
  152.                 log.debug('Done reading one packet (containing %d lines)' % len(datagram.splitlines()))
  153.                 log.debug('Total lines read from serial port: %d', lines_read)
  154.                 log.debug('Constructing P1Packet from raw data')
  155.                
  156.                 p1 = {}
  157.                 p1['timestamp'] = '{:%Y%m%d%H%M%S}'.format(datetime.datetime.now())
  158.                 p1['header'] = self.get(b'^\s*(/.*)\r\n')
  159.                 p1['kwh_id'] = self.get(b'^0-0:96\.1\.1\(([^)]+)\)\r\n')
  160.                 p1['kwh_tariff'] = self.get_int(b'^0-0:96\.14\.0\(([0-9]+)\)\r\n')
  161.                 p1['kwh_low_usage'] = self.get_float(b'^1-0:1\.8\.1\(([0-9]+\.[0-9]+)\*kWh\)\r\n')
  162.                 p1['kwh_low_return'] = self.get_float(b'^1-0:2\.8\.1\(([0-9]+\.[0-9]+)\*kWh\)\r\n')
  163.                 p1['kwh_high_usage'] = self.get_float(b'^1-0:1\.8\.2\(([0-9]+\.[0-9]+)\*kWh\)\r\n')
  164.                 p1['kwh_high_return'] = self.get_float(b'^1-0:2\.8\.2\(([0-9]+\.[0-9]+)\*kWh\)\r\n')
  165.                 u = self.get_float(b'^1-0:1\.7\.0\(([0-9]+\.[0-9]+)\*kW\)\r\n')
  166.                 r = self.get_float(b'^1-0:2\.7\.0\(([0-9]+\.[0-9]+)\*kW\)\r\n')
  167.                 p1['kwh_usage'] = u
  168.                 p1['kwh_return'] = r
  169.                 p1['current_L1'] = self.get_int(b'^1-0:31\.7\.0\(([0-9]+)\*A\)\r\n')
  170.                 p1['current_L2'] = self.get_int(b'^1-0:51\.7\.0\(([0-9]+)\*A\)\r\n')
  171.                 p1['current_L3'] = self.get_int(b'^1-0:71\.7\.0\(([0-9]+)\*A\)\r\n')
  172.                 p1['usage_L1'] = self.get_float(b'^1-0:21\.7\.0\(([0-9]+\.[0-9]+)\*kW\)\r\n')
  173.                 p1['usage_L2'] = self.get_float(b'^1-0:41\.7\.0\(([0-9]+\.[0-9]+)\*kW\)\r\n')
  174.                 p1['usage_L3'] = self.get_float(b'^1-0:61\.7\.0\(([0-9]+\.[0-9]+)\*kW\)\r\n')
  175.                 p1['return_L1'] = self.get_float(b'^1-0:22\.7\.0\(([0-9]+\.[0-9]+)\*kW\)\r\n')
  176.                 p1['return_L2'] = self.get_float(b'^1-0:42\.7\.0\(([0-9]+\.[0-9]+)\*kW\)\r\n')
  177.                 p1['return_L3'] = self.get_float(b'^1-0:62\.7\.0\(([0-9]+\.[0-9]+)\*kW\)\r\n')
  178.                 p1['gas_id'] = self.get(b'^0-1:96\.1\.0\(([^)]+)\)\r\n')
  179.                 p1['gas_device'] = self.get_int(b'^0-1:24\.1\.0\((\d)+\)\r\n')
  180.                 p1['gas_total'] = self.get_float(b'^(?:0-1:24\.2\.1(?:\(\d+[SW]\))?)?\(([0-9]{5}\.[0-9]{3})(?:\*m3)?\)\r\n', 0)
  181.                 p1['gas_date'] = '20' + repr(self.get_int(b'^(?:0-1:24\.2\.1(?:\((\d+)[SW]\))?)?\([0-9]{5}\.[0-9]{3}(?:\*m3)?\)\r\n'))
  182.                 p1['msg_code'] = self.get(b'^0-0:96\.13\.1\((\d+)\)\r\n')
  183.                 p1['msg_text'] = self.get(b'^0-0:96\.13\.0\((.+)\)\r\n')
  184. # 0-1:24.2.1(170325200000W)(00494.157*m3)                
  185.                 usageArray.append(u)
  186.                 returnArray.append(r)
  187.  
  188.                 with open('/var/ramdisk1M/p1.json'.format(datetime.datetime.now()), 'w+') as outfile:
  189.                     json.dump(p1, outfile)
  190. #                    outfile.write('\n')
  191.  
  192.             except Exception as err:
  193.                 log.error(err)
  194.                 #log.error('Sleep for 60 seconds');
  195.                 #time.sleep(60)
  196.                 break
  197.  
  198.     def get_float(self, regex, default=None):
  199.         result = self.get(regex, None)
  200.         if not result:
  201.             return default
  202.         return float(self.get(regex, default))
  203.  
  204.     def get_int(self, regex, default=None):
  205.         result = self.get(regex, None)
  206.         if not result:
  207.             return default
  208.         return int(result)
  209.  
  210.     def get(self, regex, default=None):
  211.         results = re.search(regex, self._datagram, re.MULTILINE)
  212.         if not results:
  213.             return default
  214.         return results.group(1).decode('ascii')
  215.  
  216. if __name__ == "__main__":
  217.     daemon = MyDaemon('/home/p1meter/p1meter/p1meter.pid', '/dev/null', '/home/p1meter/p1meter/p1meter.out', '/home/p1meter/p1meter/p1meter.err')
  218.     if len(sys.argv) == 2:
  219.         if 'start' == sys.argv[1]:
  220.             daemon.start()
  221.         elif 'stop' == sys.argv[1]:
  222.             daemon.stop()
  223.         elif 'restart' == sys.argv[1]:
  224.             daemon.restart()
  225.         else:
  226.             print ("Unknown command")
  227.             sys.exit(2)
  228.         sys.exit(0)
  229.     else:
  230.         print ("usage: %s start|stop|restart" % sys.argv[0])
  231.         sys.exit(2)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement