Subversion Repositories Python_P1_Script

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 raymond 1
#!python3
2
# P1 Datalogger
3
# $Revision: 9 $
4
# $Author: raymond $
5
# $Date: 2015-09-06 13:19:50 +0200 (zo, 06 sep 2015) $
6
# Copyright (c) 2015 R. V. Slegers
7
#
8
# Although there is a explicit copyright on this sourcecode, anyone may use it freely under a 
9
# "Creative Commons Naamsvermelding-NietCommercieel-GeenAfgeleideWerken 3.0 Nederland" licentie.
10
# Please check http://creativecommons.org/licenses/by-nc-nd/3.0/nl/ for details
11
#
12
# This software is provided as is and comes with absolutely no warranty.
13
# The author is not responsible or liable (direct or indirect) to anyone for the use or misuse of this software.
14
# Any person using this software does so entirely at his/her own risk. 
15
# That person bears sole responsibility and liability for any claims or actions, legal or civil, arising from such use.
16
# If you believe this software is in breach of anyone's copyright you will inform the author immediately so the offending material 
17
# can be removed upon receipt of proof of copyright for that material.
18
#
19
# Dependend on Python 3.x and Python 3.x packages: PySerial 2.5
20
#
21
 
22
progname='P1.py'
23
version = "v0.73b"
24
import_db = True
25
import sys
26
import serial
27
import datetime
28
import csv
29
import os
30
import locale
31
import socket
32
import http.client
33
import urllib.parse
34
import argparse
35
import configparser
36
import glob
37
 
38
socket.setdefaulttimeout(30)
39
MySQL_loaded = True
40
try:
41
    import mysql.connector
42
except ImportError:
43
    MySQL_loaded=False
44
from time import sleep
45
 
46
#####################################################################
47
# pvoutput.org system parameters
48
#####################################################################
49
pvo_url = 'http://pvoutput.org/service/r2/addstatus.jsp'
50
#####################################################################
51
 
52
class P1_ChannelData:
53
    def __init__(self, id=0, type_id=0, type_desc='', equipment_id='', timestamp='0000-00-00 00:00:00', meterreading=0.0, unit='', valveposition=0):
54
        self.id = id
55
        self.type_id = type_id
56
        self.type_desc = type_desc
57
        self.equipment_id = equipment_id
58
        self.timestamp = timestamp
59
        self.meterreading = meterreading
60
        self.unit = unit
61
        self.valveposition = valveposition
62
 
63
def scan_serial():
64
# scan for available ports. return a list of tuples (num, name)
65
    """ Lists serial port names
66
 
67
        :raises EnvironmentError:
68
            On unsupported or unknown platforms
69
        :returns:
70
            A list of the serial ports available on the system
71
    """
72
    if sys.platform.startswith('win'):
73
        ports = ['COM%s' % (i + 1) for i in range(256)]
74
    elif sys.platform.startswith('linux') or sys.platform.startswith('cygwin'):
75
        # this excludes your current terminal "/dev/tty"
76
        ports = glob.glob('/dev/tty[A-Za-z]*')
77
    elif sys.platform.startswith('darwin'):
78
        ports = glob.glob('/dev/tty.*')
79
    else:
80
        raise EnvironmentError('Unsupported platform')
81
 
82
    port_exceptions = ['/dev/ttyprintk','dev/ttyprintk']
83
    for port in port_exceptions:
84
        if port in ports:
85
            ports.remove(port)
86
 
87
    available = []
88
    for port in ports:
89
        try:
90
            s = serial.Serial(port)
91
            s.close()
92
            available.append(port)
93
        except (OSError, serial.SerialException):
94
            pass
95
    if not available:
96
            available.append('Helaas geen seriele poorten gevonden')
97
    return available
98
 
99
################
100
#Error display #
101
################
102
def show_error():
103
    ft = sys.exc_info()[0]
104
    fv = sys.exc_info()[1]
105
    print("Fout type: %s" % ft )
106
    print("Fout waarde: %s" % fv )
107
    return
108
 
109
################
110
#Scherm output #
111
################
112
def print_p1_telegram():
113
    print ("---------------------------------------------------------------------------------------")
114
    print ("P1 telegram ontvangen op: %s" % p1_timestamp)
115
    if p1_meter_supplier == "KMP":
116
        print ("Meter fabrikant: Kamstrup")
117
    elif p1_meter_supplier == "ISk":
118
        print ("Meter fabrikant: IskraEmeco")
119
    elif p1_meter_supplier == "XMX":
120
        print ("Meter fabrikant: Xemex")
121
    elif p1_meter_supplier == "KFM":
122
        print ("Meter fabrikant: Kaifa")
123
    else:
124
        print ("Meter fabrikant: Niet herkend")
125
    print ("Meter informatie: %s" % p1_header )
126
    print (" 0. 2. 8 - DSMR versie: %s" % p1_dsmr_version )
127
    print ("96. 1. 1 - Meternummer Elektriciteit: %s" % p1_equipment_id )
128
    print (" 1. 8. 1 - Meterstand Elektriciteit levering (T1/Laagtarief): %0.3f %s" % (p1_meterreading_in_1,p1_unitmeterreading_in_1) )
129
    print (" 1. 8. 2 - Meterstand Elektriciteit levering (T2/Normaaltarief): %0.3f %s" % (p1_meterreading_in_2,p1_unitmeterreading_in_2) )
130
    print (" 2. 8. 1 - Meterstand Elektriciteit teruglevering (T1/Laagtarief): %0.3f %s" % (p1_meterreading_out_1,p1_unitmeterreading_out_1) )
131
    print (" 2. 8. 2 - Meterstand Elektriciteit teruglevering (T2/Normaaltarief): %0.3f %s" % (p1_meterreading_out_2,p1_unitmeterreading_out_2) )
132
    print ("96.14. 0 - Actueel tarief Elektriciteit: %d" % p1_current_tariff )
133
    print (" 1. 7. 0 - Actueel vermogen Electriciteit levering (+P): %0.3f %s" % (p1_current_power_in,p1_unit_current_power_in) )
134
    print (" 2. 7. 0 - Actueel vermogen Electriciteit teruglevering (-P): %0.3f %s" % (p1_current_power_out,p1_unit_current_power_out) )
135
    print ("17. 0. 0 - Actuele doorlaatwaarde Elektriciteit: %0.3f %s" % (p1_current_threshold,p1_unit_current_threshold) )
136
    print ("96. 3.10 - Actuele schakelaarpositie Elektriciteit: %s" % p1_current_switch_position )
137
    print ("96. 7.21 - Aantal onderbrekingen Elektriciteit: %s" % p1_powerfailures )
138
    print ("96. 7. 9 - Aantal lange onderbrekingen Elektriciteit: %s" % p1_long_powerfailures )
139
    print ("99.97. 0 - Lange onderbrekingen Elektriciteit logboek: %s" % p1_long_powerfailures_log )
140
    print ("32.32. 0 - Aantal korte spanningsdalingen Elektriciteit in fase 1: %s" % p1_voltage_sags_l1 )
141
    print ("52.32. 0 - Aantal korte spanningsdalingen Elektriciteit in fase 2: %s" % p1_voltage_sags_l2 )
142
    print ("72.32. 0 - Aantal korte spanningsdalingen Elektriciteit in fase 3: %s" % p1_voltage_sags_l3 )
143
    print ("32.36. 0 - Aantal korte spanningsstijgingen Elektriciteit in fase 1: %s" % p1_voltage_swells_l1 )
144
    print ("52.36. 0 - Aantal korte spanningsstijgingen Elektriciteit in fase 2: %s" % p1_voltage_swells_l2 )
145
    print ("72.36. 0 - Aantal korte spanningsstijgingen Elektriciteit in fase 3: %s" % p1_voltage_swells_l3 )       
146
    print ("31. 7. 0 - Instantane stroom Elektriciteit in fase 1: %0.3f %s" % (p1_instantaneous_current_l1,p1_unit_instantaneous_current_l1) )  
147
    print ("51. 7. 0 - Instantane stroom Elektriciteit in fase 2: %0.3f %s" % (p1_instantaneous_current_l2,p1_unit_instantaneous_current_l2) )  
148
    print ("71. 7. 0 - Instantane stroom Elektriciteit in fase 3: %0.3f %s" % (p1_instantaneous_current_l3,p1_unit_instantaneous_current_l3) )     
149
    print ("32. 7. 0 - Spanning Elektriciteit in fase 1: %0.3f %s" % (p1_voltage_l1,p1_unit_voltage_l1) )  
150
    print ("52. 7. 0 - Spanning Elektriciteit in fase 2: %0.3f %s" % (p1_voltage_l2,p1_unit_voltage_l2) )  
151
    print ("72. 7. 0 - Spanning Elektriciteit in fase 3: %0.3f %s" % (p1_voltage_l3,p1_unit_voltage_l3) )  
152
    print ("21. 7. 0 - Instantaan vermogen Elektriciteit levering (+P) in fase 1: %0.3f %s" % (p1_instantaneous_active_power_in_l1,p1_unit_instantaneous_active_power_in_l1) )  
153
    print ("41. 7. 0 - Instantaan vermogen Elektriciteit levering (+P) in fase 2: %0.3f %s" % (p1_instantaneous_active_power_in_l2,p1_unit_instantaneous_active_power_in_l2) )  
154
    print ("61. 7. 0 - Instantaan vermogen Elektriciteit levering (+P) in fase 3: %0.3f %s" % (p1_instantaneous_active_power_in_l3,p1_unit_instantaneous_active_power_in_l3) )   
155
    print ("22. 7. 0 - Instantaan vermogen Elektriciteit teruglevering (-P) in fase 1: %0.3f %s" % (p1_instantaneous_active_power_out_l1,p1_unit_instantaneous_active_power_out_l1) )  
156
    print ("42. 7. 0 - Instantaan vermogen Elektriciteit teruglevering (-P) in fase 2: %0.3f %s" % (p1_instantaneous_active_power_out_l2,p1_unit_instantaneous_active_power_out_l2) )  
157
    print ("62. 7. 0 - Instantaan vermogen Elektriciteit teruglevering (-P) in fase 3: %0.3f %s" % (p1_instantaneous_active_power_out_l3,p1_unit_instantaneous_active_power_out_l3) )   
158
    print ("96.13. 1 - Bericht code: %s" % p1_message_code )
159
    print ("96.13. 0 - Bericht tekst: %s" % p1_message_text )
160
    channellist = [p1_channel_1, p1_channel_2, p1_channel_3, p1_channel_4]
161
    for channel in channellist:
162
        if channel.id != 0:
163
            print ("MBus Meterkanaal: %s" % channel.id )
164
            print ("24. 1. 0 - Productsoort: %s (%s)" % (channel.type_id, channel.type_desc) )
165
            print ("91. 1. 0 - Meternummer %s: %s" % (channel.type_desc, channel.equipment_id) )
166
            if p1_dsmr_version != "40":
167
                print ("24. 3. 0 - Tijdstip meterstand %s levering: %s" % (channel.type_desc, channel.timestamp) )
168
                print ("24. 3. 0 - Meterstand %s levering: %0.3f %s" % (channel.type_desc, channel.meterreading, channel.unit) )
169
            else:
170
                print ("24. 2. 1 - Tijdstip meterstand %s levering: %s" % (channel.type_desc, channel.timestamp) )
171
                print ("24. 2. 1 - Meterstand %s levering: %0.3f %s" % (channel.type_desc, channel.meterreading, channel.unit) )            
172
            print ("24. 4. 0 - Actuele kleppositie %s: %s" % (channel.type_desc,channel.valveposition) )
173
    print ("Einde P1 telegram" )
174
    return
175
################
176
#Csv output #
177
################
178
def csv_p1_telegram():
179
#New filename every day
180
    csv_filename=datetime.datetime.strftime(datetime.datetime.today(), "P1_"+"%Y-%m-%d_"+str(log_interval*10)+"s.csv" )
181
    try:
182
#If csv_file exists: open it
183
        csv_file=open(csv_filename, 'rt')
184
        csv_file.close()
185
        csv_file=open(csv_filename, 'at', newline='', encoding="utf-8")
186
        writer = csv.writer(csv_file, dialect='excel', delimiter=';', quoting=csv.QUOTE_NONNUMERIC)
187
    except IOError:
188
#Otherwise: create it
189
        csv_file=open(csv_filename, 'wt', newline='', encoding="utf-8")
190
        writer = csv.writer(csv_file, dialect='excel', delimiter=';', quoting=csv.QUOTE_NONNUMERIC)
191
#Write csv-header
192
        writer.writerow([
193
         'p1_timestamp', 
194
         'p1_meter_supplier', 
195
         'p1_header',
196
         'p1_dsmr_version',
197
         'p1_equipment_id', 
198
         'p1_meterreading_in_1', 
199
         'p1_unitmeterreading_in_1', 
200
         'p1_meterreading_in_2', 
201
         'p1_unitmeterreading_in_2',
202
         'p1_meterreading_out_1',
203
         'p1_unitmeterreading_out_1',
204
         'p1_meterreading_out_2',
205
         'p1_unitmeterreading_out_2',
206
         'p1_current_tariff',
207
         'p1_current_power_in',
208
         'p1_unit_current_power_in',
209
         'p1_current_power_out',
210
         'p1_unit_current_power_out',
211
         'p1_current_threshold',
212
         'p1_unit_current_threshold',
213
         'p1_current_switch_position',
214
         'p1_powerfailures',
215
         'p1_long_powerfailures',
216
         'p1_long_powerfailures_log',
217
         'p1_voltage_sags_l1',
218
         'p1_voltage_sags_l2',
219
         'p1_voltage_sags_l3',
220
         'p1_voltage_swells_l1',
221
         'p1_voltage_swells_l2',
222
         'p1_voltage_swells_l3',
223
         'p1_instantaneous_current_l1',
224
         'p1_unit_instantaneous_current_l1',
225
         'p1_instantaneous_current_l2',
226
         'p1_unit_instantaneous_current_l2',
227
         'p1_instantaneous_current_l3',
228
         'p1_unit_instantaneous_current_l3',
229
         'p1_voltage_l1',
230
         'p1_unit_voltage_l1',
231
         'p1_voltage_l2',
232
         'p1_unit_voltage_l2',
233
         'p1_voltage_l3',
234
         'p1_unit_voltage_l3',             
235
         'p1_instantaneous_active_power_in_l1',
236
         'p1_unit_instantaneous_active_power_in_l1',
237
         'p1_instantaneous_active_power_in_l2',
238
         'p1_unit_instantaneous_active_power_in_l2',
239
         'p1_instantaneous_active_power_in_l3',
240
         'p1_unit_instantaneous_active_power_in_l3',
241
         'p1_instantaneous_active_power_out_l1',
242
         'p1_unit_instantaneous_active_power_out_l1',
243
         'p1_instantaneous_active_power_out_l2',
244
         'p1_unit_instantaneous_active_power_out_l2',
245
         'p1_instantaneous_active_power_out_l3',
246
         'p1_unit_instantaneous_active_power_out_l3',
247
         'p1_message_code',
248
         'p1_message_text',
249
         'p1_channel_1_id',
250
         'p1_channel_1_type_id', 
251
         'p1_channel_1_type_desc',
252
         'p1_channel_1_equipment_id',
253
         'p1_channel_1_timestamp',
254
         'p1_channel_1_meterreading', 
255
         'p1_channel_1_unit',
256
         'p1_channel_1_valveposition',
257
         'p1_channel_2_id',
258
         'p1_channel_2_type_id', 
259
         'p1_channel_2_type_desc',
260
         'p1_channel_2_equipment_id',
261
         'p1_channel_2_timestamp',
262
         'p1_channel_2_meterreading', 
263
         'p1_channel_2_unit',
264
         'p1_channel_2_valveposition',
265
         'p1_channel_3_id',
266
         'p1_channel_3_type_id', 
267
         'p1_channel_3_type_desc',
268
         'p1_channel_3_equipment_id',
269
         'p1_channel_3_timestamp',
270
         'p1_channel_3_meterreading', 
271
         'p1_channel_3_unit',
272
         'p1_channel_3_valveposition',
273
         'p1_channel_4_id',
274
         'p1_channel_4_type_id', 
275
         'p1_channel_4_type_desc',
276
         'p1_channel_4_equipment_id',
277
         'p1_channel_4_timestamp',
278
         'p1_channel_4_meterreading', 
279
         'p1_channel_4_unit',
280
         'p1_channel_4_valveposition' ])
281
 
282
    print ("P1 telegram in %s gelogd op: %s" % (csv_filename, p1_timestamp) )
283
    writer.writerow([
284
         p1_timestamp, 
285
         p1_meter_supplier, 
286
         p1_header, 
287
         p1_dsmr_version,    
288
         p1_equipment_id,
289
         p1_meterreading_in_1, p1_unitmeterreading_in_1, 
290
         p1_meterreading_in_2, p1_unitmeterreading_in_2,
291
         p1_meterreading_out_1,p1_unitmeterreading_out_1,
292
         p1_meterreading_out_2,p1_unitmeterreading_out_2,
293
         p1_current_tariff,
294
         p1_current_power_in,p1_unit_current_power_in,
295
         p1_current_power_out,p1_unit_current_power_out,
296
         p1_current_threshold,p1_unit_current_threshold,
297
         p1_current_switch_position,
298
         p1_powerfailures,
299
         p1_long_powerfailures,
300
         p1_long_powerfailures_log,
301
         p1_voltage_sags_l1,
302
         p1_voltage_sags_l2,
303
         p1_voltage_sags_l3,
304
         p1_voltage_swells_l1,
305
         p1_voltage_swells_l2,
306
         p1_voltage_swells_l3,
307
         p1_instantaneous_current_l1, p1_unit_instantaneous_current_l1,
308
         p1_instantaneous_current_l2, p1_unit_instantaneous_current_l2,
309
         p1_instantaneous_current_l3, p1_unit_instantaneous_current_l3,
310
         p1_voltage_l1, p1_unit_voltage_l1,
311
         p1_voltage_l2, p1_unit_voltage_l2,
312
         p1_voltage_l3, p1_unit_voltage_l3,   
313
         p1_instantaneous_active_power_in_l1, p1_unit_instantaneous_active_power_in_l1,
314
         p1_instantaneous_active_power_in_l2, p1_unit_instantaneous_active_power_in_l2,
315
         p1_instantaneous_active_power_in_l3, p1_unit_instantaneous_active_power_in_l3,
316
         p1_instantaneous_active_power_out_l1, p1_unit_instantaneous_active_power_out_l1,
317
         p1_instantaneous_active_power_out_l2, p1_unit_instantaneous_active_power_out_l2,
318
         p1_instantaneous_active_power_out_l3, p1_unit_instantaneous_active_power_out_l3,
319
         p1_message_code,
320
         p1_message_text,
321
         p1_channel_1.id,
322
         p1_channel_1.type_id, 
323
         p1_channel_1.equipment_id,
324
         p1_channel_1.timestamp,
325
         p1_channel_1.meterreading, p1_channel_1.unit,
326
         p1_channel_1.valveposition,
327
         p1_channel_2.id,
328
         p1_channel_2.type_id, 
329
         p1_channel_2.equipment_id,
330
         p1_channel_2.timestamp,
331
         p1_channel_2.meterreading, p1_channel_2.unit,
332
         p1_channel_2.valveposition,
333
         p1_channel_3.id,
334
         p1_channel_3.type_id, 
335
         p1_channel_3.equipment_id,
336
         p1_channel_3.timestamp,
337
         p1_channel_3.meterreading, p1_channel_3.unit,
338
         p1_channel_3.valveposition,
339
         p1_channel_4.id,
340
         p1_channel_4.type_id, 
341
         p1_channel_4.equipment_id,
342
         p1_channel_4.timestamp,
343
         p1_channel_4.meterreading, p1_channel_4.unit,
344
         p1_channel_4.valveposition ])
345
    csv_file.close()
346
 
347
    return        
348
 
349
################
350
#DB output     #
351
################
352
ID = ''
353
def db_p1_telegram():
354
    query = "insert into p1_log values (\'" + \
355
         ID + "\',\'" + \
356
	 p1_timestamp + "\',\'" + \
357
         str(p1_meterreading_in_1) + "\',\'" + \
358
         str(p1_meterreading_in_2) + "\',\'" + \
359
         str(p1_meterreading_out_1) + "\',\'" +\
360
         str(p1_meterreading_out_2) + "\',\'" + \
361
         str(p1_current_tariff) + "\',\'" + \
362
         str(p1_current_power_in) + "\',\'" + \
363
         str(p1_current_power_out) + "\',\'" + \
364
         str(p1_channel_1.id) + "\',\'" + \
365
         str(p1_channel_1.type_id) + "\',\'" +  \
366
         p1_channel_1.timestamp + "\',\'" + \
367
         str(p1_channel_1.meterreading) + "\',\'" + \
368
         p1_channel_1.unit + "\',\'" + \
369
         str(p1_channel_2.id) + "\',\'" + \
370
         str(p1_channel_2.type_id) + "\',\'" +  \
371
         p1_channel_2.timestamp + "\',\'" + \
372
         str(p1_channel_2.meterreading) + "\',\'" + \
373
         p1_channel_2.unit + "\',\'" + \
374
         str(p1_channel_3.id) + "\',\'" + \
375
         str(p1_channel_3.type_id) + "\',\'" + \
376
         p1_channel_3.timestamp + "\',\'" + \
377
         str(p1_channel_3.meterreading) + "\',\'" + \
378
         p1_channel_3.unit + "\',\'" + \
379
         str(p1_channel_4.id) + "\',\'" + \
380
         str(p1_channel_4.type_id) + "\',\'" + \
381
         p1_channel_4.timestamp + "\',\'" + \
382
         str(p1_channel_4.meterreading) + "\',\'" + \
383
         p1_channel_4.unit  + "\')"
384
#    print(query)
385
    try:
386
        db = mysql.connector.connect(user=p1_mysql_user, password=p1_mysql_passwd, host=p1_mysql_host, database=p1_mysql_db, get_warnings=True)
387
        c = db.cursor()
388
        c.execute (query)
389
        db.commit()
390
        print ("P1 telegram in database %s / %s gelogd op: %s" % (p1_mysql_host, p1_mysql_db, p1_timestamp) )
391
        c.close()
392
        db.close()
393
    except:
394
        show_error()
395
        print ("Fout bij het openen van / schrijven naar database %s / %s. P1 Telegram wordt gelogd in csv-bestand."  % (p1_mysql_host, p1_mysql_db))      
396
        csv_p1_telegram()
397
    return    
398
 
399
def db_p2_telegram():
400
    query = "insert into p2_log values (\'" + \
401
         ID + "\',\'" + \
402
         p1_timestamp + "\',\'" + \
403
         p1_meter_supplier + "\',\'" + \
404
         p1_header + "\',\'" + \
405
         p1_dsmr_version + "\',\'" + \
406
         p1_equipment_id + "\',\'" + \
407
         str(p1_meterreading_in_1) + "\',\'" + \
408
         p1_unitmeterreading_in_1 + "\',\'" + \
409
         str(p1_meterreading_in_2) + "\',\'" + \
410
         p1_unitmeterreading_in_2 + "\',\'" + \
411
         str(p1_meterreading_out_1) + "\',\'" +\
412
         p1_unitmeterreading_out_1 + "\',\'" + \
413
         str(p1_meterreading_out_2) + "\',\'" + \
414
         p1_unitmeterreading_out_2 + "\',\'" + \
415
         str(p1_current_tariff) + "\',\'" + \
416
         str(p1_current_power_in) + "\',\'" + \
417
         p1_unit_current_power_in + "\',\'" + \
418
         str(p1_current_power_out) + "\',\'" + \
419
         p1_unit_current_power_out + "\',\'" + \
420
         str(p1_current_threshold) + "\',\'" + \
421
         p1_unit_current_threshold + "\',\'" + \
422
         str(p1_current_switch_position) + "\',\'" + \
423
         str(p1_powerfailures) + "\',\'" + \
424
         str(p1_long_powerfailures) + "\',\'" + \
425
         p1_long_powerfailures_log + "\',\'" + \
426
         str(p1_voltage_sags_l1)  + "\',\'" + \
427
         str(p1_voltage_sags_l2) + "\',\'" + \
428
         str(p1_voltage_sags_l3) + "\',\'" + \
429
         str(p1_voltage_swells_l1) + "\',\'" + \
430
         str(p1_voltage_swells_l2) + "\',\'" + \
431
         str(p1_voltage_swells_l3) + "\',\'" + \
432
         str(p1_instantaneous_current_l1)  + "\',\'" + \
433
         p1_unit_instantaneous_current_l1 + "\',\'" + \
434
         str(p1_instantaneous_current_l2)  + "\',\'" + \
435
         p1_unit_instantaneous_current_l2 + "\',\'" + \
436
         str(p1_instantaneous_current_l3)  + "\',\'" + \
437
         p1_unit_instantaneous_current_l3 + "\',\'" + \
438
         str(p1_voltage_l1) + "\',\'" + \
439
         p1_unit_voltage_l1 + "\',\'" + \
440
         str(p1_voltage_l2) + "\',\'" + \
441
         p1_unit_voltage_l2 + "\',\'" + \
442
         str(p1_voltage_l3) + "\',\'" + \
443
         p1_unit_voltage_l3 + "\',\'" + \
444
         str(p1_instantaneous_active_power_in_l1)  + "\',\'" + \
445
         p1_unit_instantaneous_active_power_in_l1 + "\',\'" + \
446
         str(p1_instantaneous_active_power_in_l2)  + "\',\'" + \
447
         p1_unit_instantaneous_active_power_in_l2 + "\',\'" + \
448
         str(p1_instantaneous_active_power_in_l3)  + "\',\'" + \
449
         p1_unit_instantaneous_active_power_in_l3 + "\',\'" + \
450
         str(p1_instantaneous_active_power_out_l1)  + "\',\'" + \
451
         p1_unit_instantaneous_active_power_out_l1 + "\',\'" + \
452
         str(p1_instantaneous_active_power_out_l2)  + "\',\'" + \
453
         p1_unit_instantaneous_active_power_out_l2 + "\',\'" + \
454
         str(p1_instantaneous_active_power_out_l3)  + "\',\'" + \
455
         p1_unit_instantaneous_active_power_out_l3 + "\',\'" + \
456
         p1_message_code + "\',\'" + \
457
         p1_message_text + "\',\'" + \
458
         str(p1_channel_1.id) + "\',\'" + \
459
         str(p1_channel_1.type_id) + "\',\'" +  \
460
         p1_channel_1.type_desc + "\',\'" + \
461
         str(p1_channel_1.equipment_id) + "\',\'" + \
462
         p1_channel_1.timestamp + "\',\'" + \
463
         str(p1_channel_1.meterreading) + "\',\'" + \
464
         p1_channel_1.unit + "\',\'" + \
465
         str(p1_channel_1.valveposition) + "\',\'" + \
466
         str(p1_channel_2.id) + "\',\'" + \
467
         str(p1_channel_2.type_id) + "\',\'" +  \
468
         p1_channel_2.type_desc + "\',\'" + \
469
         str(p1_channel_2.equipment_id) + "\',\'" + \
470
         p1_channel_2.timestamp + "\',\'" + \
471
         str(p1_channel_2.meterreading) + "\',\'" + \
472
         p1_channel_2.unit + "\',\'" + \
473
         str(p1_channel_2.valveposition) + "\',\'" + \
474
         str(p1_channel_3.id) + "\',\'" + \
475
         str(p1_channel_3.type_id) + "\',\'" + \
476
         p1_channel_3.type_desc + "\',\'" + \
477
         str(p1_channel_3.equipment_id) + "\',\'" + \
478
         p1_channel_3.timestamp + "\',\'" + \
479
         str(p1_channel_3.meterreading) + "\',\'" + \
480
         p1_channel_3.unit + "\',\'" + \
481
         str(p1_channel_3.valveposition) + "\',\'" + \
482
         str(p1_channel_4.id) + "\',\'" + \
483
         str(p1_channel_4.type_id) + "\',\'" + \
484
         p1_channel_4.type_desc + "\',\'" + \
485
         str(p1_channel_4.equipment_id) + "\',\'" + \
486
         p1_channel_4.timestamp + "\',\'" + \
487
         str(p1_channel_4.meterreading) + "\',\'" + \
488
         p1_channel_4.unit + "\',\'" + \
489
         str(p1_channel_4.valveposition)  + "\')"
490
#    print(query)
491
    try:
492
        db = mysql.connector.connect(user=p1_mysql_user, password=p1_mysql_passwd, host=p1_mysql_host, database=p1_mysql_db)
493
        c = db.cursor()
494
        c.execute (query)
495
        db.commit()
496
        print ("P2 telegram in database %s / %s gelogd op: %s" % (p1_mysql_host, p1_mysql_db, p1_timestamp) )
497
        c.close()
498
        db.close()
499
    except:
500
        show_error()
501
        print ("Fout bij het openen van / schrijven naar database %s / %s."  % (p1_mysql_host, p1_mysql_db))      
502
    return    
503
 
504
def db_p3_telegram():
505
    query = "insert into p3_log values (\'" + \
506
         ID + "\',\'" + \
507
         p1_timestamp + "\',\'" + \
508
         p1_meter_supplier + "\',\'" + \
509
         p1_header + "\',\'" + \
510
         p1_dsmr_version + "\',\'" + \
511
         p1_equipment_id + "\',\'" + \
512
         str(p1_meterreading_in_1) + "\',\'" + \
513
         p1_unitmeterreading_in_1 + "\',\'" + \
514
         str(p1_meterreading_in_2) + "\',\'" + \
515
         p1_unitmeterreading_in_2 + "\',\'" + \
516
         str(p1_meterreading_out_1) + "\',\'" +\
517
         p1_unitmeterreading_out_1 + "\',\'" + \
518
         str(p1_meterreading_out_2) + "\',\'" + \
519
         p1_unitmeterreading_out_2 + "\',\'" + \
520
         str(p1_current_tariff) + "\',\'" + \
521
         str(p1_current_power_in) + "\',\'" + \
522
         p1_unit_current_power_in + "\',\'" + \
523
         str(p1_current_power_out) + "\',\'" + \
524
         p1_unit_current_power_out + "\',\'" + \
525
         str(p1_current_threshold) + "\',\'" + \
526
         p1_unit_current_threshold + "\',\'" + \
527
         str(p1_current_switch_position) + "\',\'" + \
528
         str(p1_powerfailures) + "\',\'" + \
529
         str(p1_long_powerfailures) + "\',\'" + \
530
         p1_long_powerfailures_log + "\',\'" + \
531
         str(p1_voltage_sags_l1)  + "\',\'" + \
532
         str(p1_voltage_sags_l2) + "\',\'" + \
533
         str(p1_voltage_sags_l3) + "\',\'" + \
534
         str(p1_voltage_swells_l1) + "\',\'" + \
535
         str(p1_voltage_swells_l2) + "\',\'" + \
536
         str(p1_voltage_swells_l3) + "\',\'" + \
537
         str(p1_instantaneous_current_l1)  + "\',\'" + \
538
         p1_unit_instantaneous_current_l1 + "\',\'" + \
539
         str(p1_instantaneous_current_l2)  + "\',\'" + \
540
         p1_unit_instantaneous_current_l2 + "\',\'" + \
541
         str(p1_instantaneous_current_l3)  + "\',\'" + \
542
         p1_unit_instantaneous_current_l3 + "\',\'" + \
543
         str(p1_voltage_l1) + "\',\'" + \
544
         p1_unit_voltage_l1 + "\',\'" + \
545
         str(p1_voltage_l2) + "\',\'" + \
546
         p1_unit_voltage_l2 + "\',\'" + \
547
         str(p1_voltage_l3) + "\',\'" + \
548
         p1_unit_voltage_l3 + "\',\'" + \
549
         str(p1_instantaneous_active_power_in_l1)  + "\',\'" + \
550
         p1_unit_instantaneous_active_power_in_l1 + "\',\'" + \
551
         str(p1_instantaneous_active_power_in_l2)  + "\',\'" + \
552
         p1_unit_instantaneous_active_power_in_l2 + "\',\'" + \
553
         str(p1_instantaneous_active_power_in_l3)  + "\',\'" + \
554
         p1_unit_instantaneous_active_power_in_l3 + "\',\'" + \
555
         str(p1_instantaneous_active_power_out_l1)  + "\',\'" + \
556
         p1_unit_instantaneous_active_power_out_l1 + "\',\'" + \
557
         str(p1_instantaneous_active_power_out_l2)  + "\',\'" + \
558
         p1_unit_instantaneous_active_power_out_l2 + "\',\'" + \
559
         str(p1_instantaneous_active_power_out_l3)  + "\',\'" + \
560
         p1_unit_instantaneous_active_power_out_l3 + "\',\'" + \
561
         p1_message_code + "\',\'" + \
562
         p1_message_text + "\',\'" + \
563
         str(p1_channel_1.id) + "\',\'" + \
564
         str(p1_channel_1.type_id) + "\',\'" +  \
565
         p1_channel_1.type_desc + "\',\'" + \
566
         str(p1_channel_1.equipment_id) + "\',\'" + \
567
         p1_channel_1.timestamp + "\',\'" + \
568
         str(p1_channel_1.meterreading) + "\',\'" + \
569
         p1_channel_1.unit + "\',\'" + \
570
         str(p1_channel_1.valveposition) + "\',\'" + \
571
         str(p1_channel_2.id) + "\',\'" + \
572
         str(p1_channel_2.type_id) + "\',\'" +  \
573
         p1_channel_2.type_desc + "\',\'" + \
574
         str(p1_channel_2.equipment_id) + "\',\'" + \
575
         p1_channel_2.timestamp + "\',\'" + \
576
         str(p1_channel_2.meterreading) + "\',\'" + \
577
         p1_channel_2.unit + "\',\'" + \
578
         str(p1_channel_2.valveposition) + "\',\'" + \
579
         str(p1_channel_3.id) + "\',\'" + \
580
         str(p1_channel_3.type_id) + "\',\'" + \
581
         p1_channel_3.type_desc + "\',\'" + \
582
         str(p1_channel_3.equipment_id) + "\',\'" + \
583
         p1_channel_3.timestamp + "\',\'" + \
584
         str(p1_channel_3.meterreading) + "\',\'" + \
585
         p1_channel_3.unit + "\',\'" + \
586
         str(p1_channel_3.valveposition) + "\',\'" + \
587
         str(p1_channel_4.id) + "\',\'" + \
588
         str(p1_channel_4.type_id) + "\',\'" + \
589
         p1_channel_4.type_desc + "\',\'" + \
590
         str(p1_channel_4.equipment_id) + "\',\'" + \
591
         p1_channel_4.timestamp + "\',\'" + \
592
         str(p1_channel_4.meterreading) + "\',\'" + \
593
         p1_channel_4.unit + "\',\'" + \
594
         str(p1_channel_4.valveposition)  + "\')"
595
#    print(query)
596
    try:
597
        db = mysql.connector.connect(user=p1_mysql_user, password=p1_mysql_passwd, host=p1_mysql_host, database=p1_mysql_db)
598
        c = db.cursor()
599
        c.execute (query)
600
        db.commit()
601
        print ("P3 telegram in database %s / %s gelogd op: %s" % (p1_mysql_host, p1_mysql_db, p1_timestamp) )
602
        c.close()
603
        db.close()
604
    except:
605
        show_error()
606
        print ("Fout bij het openen van / schrijven naar database %s / %s."  % (p1_mysql_host, p1_mysql_db))      
607
    return    
608
 
609
######################
610
#PVOutput.org output #
611
######################
612
def pvo_p1_telegram():
613
    global pvo_prev_date
614
    global p1_prev_meterreading_out_1, p1_prev_meterreading_out_2
615
    global p1_prev_meterreading_in_1, p1_prev_meterreading_in_2    
616
    if pvo_url[0:7] != "http://":
617
        print("Invalid PVOutput.org URL to post to, must be of form http://host/service: %s" % pvo_url)
618
        sys.exit(1)
619
    url = pvo_url[7:].split('/')
620
    pvo_host = url[0]
621
    pvo_service = '/' + '/'.join(url[1:])
622
#
623
# d Date
624
# t Time
625
# v1 energy generation (Wh) => P1 Export
626
# v2 power generation (W) => P1 Export
627
# v3 energy consumption (Wh) => P1 Import
628
# v4 power consumption (W) => P1 Import
629
# v5 temperature (c)
630
# v6 voltage (V)
631
# c1 cumulative flag: if set to '1' lifetime values are to be passed
632
# n  net flag: if set to '1' net import/export values are to be passed
633
#
634
    pvo_date=str(datetime.datetime.strftime(datetime.datetime.strptime(p1_timestamp, "%Y-%m-%d %H:%M:%S" ), "%Y%m%d" ))
635
    pvo_time=str(datetime.datetime.strftime(datetime.datetime.strptime(p1_timestamp, "%Y-%m-%d %H:%M:%S" ), "%H:%M" ))
636
 
637
# Initialize pvo volumes when a new day has started
638
    if pvo_prev_date != pvo_date:
639
        print ("PVOutput volumes worden gereset, vorige datum: %s, huidige datum: %s" % (pvo_prev_date, pvo_date) )
640
        p1_prev_meterreading_out_1 = p1_meterreading_out_1
641
        p1_prev_meterreading_out_2 = p1_meterreading_out_2
642
        p1_prev_meterreading_in_1 = p1_meterreading_in_1
643
        p1_prev_meterreading_in_2 = p1_meterreading_in_2
644
        pvo_prev_date = pvo_date
645
 
646
    pvo_volume_out=round((p1_meterreading_out_1+p1_meterreading_out_2-p1_prev_meterreading_out_1-p1_prev_meterreading_out_2) * 1000)
647
    pvo_volume_in=round((p1_meterreading_in_1+p1_meterreading_in_2-p1_prev_meterreading_in_1-p1_prev_meterreading_in_2) *1000)
648
 
649
    pvo_power_out=round(p1_current_power_out * 1000)
650
    pvo_power_in=round(p1_current_power_in * 1000)
651
 
652
    print("PVOutput volume out (v1): %s"% pvo_volume_out)
653
    print("MR1 out: %s"% p1_meterreading_out_1)
654
    print("MR2 out: %s"% p1_meterreading_out_2)
655
    print("Prev MR1 out: %s"% p1_prev_meterreading_out_1)
656
    print("Prev MR2 out: %s"% p1_prev_meterreading_out_2)
657
 
658
    print("PVOutput volume in (v3): %s"% pvo_volume_in)
659
    print("MR1 in: %s"% p1_meterreading_in_1)
660
    print("MR2 in: %s"% p1_meterreading_in_2)
661
    print("Prev MR1 in: %s"% p1_prev_meterreading_in_1)
662
    print("Prev MR2 in: %s"% p1_prev_meterreading_in_2)
663
 
664
 
665
    pvo_cumulative = 0 # volumes are reset once a day
666
    pvo_net = 0 #Typically not correct but the best you can get with this PVOutput interface as net=1 discards the volume data
667
    params = urllib.parse.urlencode({ 'd' : pvo_date,
668
                         't' : pvo_time,
669
# This is how it should be                         
670
#                         'v1' : pvo_volume_out,
671
#                         'v2' : pvo_power_out,
672
#                         'v3' : pvo_volume_in,
673
#                         'v4' : pvo_power_in,
674
# Patch wrong behaviour of PVOutput in case of using net values because PVOutput obly uses power values in this case
675
                         'v1' : pvo_volume_out,
676
                         'v2' : pvo_power_out,
677
                         'v3' : pvo_volume_in,
678
                         'v4' : pvo_power_in,                         
679
                         'c1' : pvo_cumulative,
680
                         'n'  : pvo_net })
681
    headers = {"Content-type": "application/x-www-form-urlencoded",
682
               "Accept": "text/plain",
683
               "X-Pvoutput-SystemId" : pvo_systemid,
684
               "X-Pvoutput-Apikey" : pvo_apikey}
685
    print("Verbinden met %s" % pvo_host)
686
    try:
687
        conn = http.client.HTTPConnection(pvo_host)
688
#        print("Sending data: %s" % params)
689
        try:
690
            conn.request("POST", pvo_service, params, headers)
691
            response = conn.getresponse()
692
            if response.status != 200:
693
                print ("Fout bij het schrijven naar %s / %s. Response: %s %s %s" % (pvo_host, pvo_systemid, response.status, response.reason, response.read()))
694
            else: 
695
                print ("Delta P1 telegram in %s / %s gelogd op: %s. Response: %s %s" % (pvo_host, pvo_systemid, p1_timestamp,response.status, response.reason) )
696
        except:
697
            show_error()
698
            print ("Fout bij het schrijven naar %s / %s."  % (pvo_host, pvo_systemid))      
699
    except:
700
        show_error()
701
        print ("Fout bij het verbinden met %s / %s."  % (pvo_host, pvo_systemid))      
702
 
703
#################################################################
704
# Start of procedures to add other metering data to p1_telegram #
705
#################################################################
706
 
707
#################################################################
708
# PV Inverter Data                                              #
709
#################################################################
710
def get_pv_data(channelA,p1_channelA,channelB,p1_channelB):
711
    query = "select pv_timestamp, pv_equipmentmodel, pv_equipmentid, pv_cumvolume, pv_cumvolumeunit, pv_intvolume, pv_intvolumeunit, pv_power, pv_powerunit from pv_log order by pv_timestamp desc"
712
#    print(query)
713
    try:
714
        db = mysql.connector.connect(user=p1_mysql_user, password=p1_mysql_passwd, host=p1_mysql_host, database=p1_mysql_db)
715
        c = db.cursor()
716
        c.execute(query)
717
        pv_timestamp, pv_equipmentmodel, pv_equipmentid, pv_cumvolume, pv_cumvolumeunit, pv_intvolume, pv_intvolumeunit, pv_power, pv_powerunit = c.fetchone()
718
        p1_channelA.id = channelA
719
        p1_channelA.type_id = 1
720
        p1_channelA.type_desc = "E-Production volume"
721
        p1_channelA.equipment_id = pv_equipmentid
722
        p1_channelA.timestamp = str(datetime.datetime.strftime(pv_timestamp, "%Y-%m-%d %H:%M:%S" ))
723
        p1_channelA.meterreading = pv_cumvolume
724
        p1_channelA.unit = pv_cumvolumeunit
725
        p1_channelA.valveposition = 1
726
        print ("PV volume %s toegevoegd aan P1 telegram - kanaal %s" % (pv_timestamp, channelA ) )
727
        if channelB != 0:
728
            p1_channelB.id = channelB
729
            p1_channelB.type_id = 1
730
            p1_channelB.type_desc = "E-Production power"
731
            p1_channelB.equipment_id = pv_equipmentid
732
            p1_channelB.timestamp = str(datetime.datetime.strftime(pv_timestamp, "%Y-%m-%d %H:%M:%S" ))
733
            p1_channelB.meterreading = pv_power
734
            p1_channelB.unit = pv_powerunit
735
            p1_channelB.valveposition = 1
736
            print ("PV vermogen %s toegevoegd aan P1 telegram - kanaal %s" % (pv_timestamp, channelB ) )
737
        #c.close()
738
        db.close()
739
    except:
740
        show_error()
741
        print ("Fout bij het openen / lezen van database %s / %s. PV telegram niet opgehaald."  % (p1_mysql_host, p1_mysql_db))      
742
 
743
    return
744
#################################################################
745
# Heat Data                                                     #
746
#################################################################
747
def get_heat_data(channelA,p1_channelA,channelB,p1_channelB):
748
    query = "select heat_timestamp, heat_equipment_id, heat_meterreading_energy, heat_unitmeterreading_energy, heat_meterreading_volume, heat_unitmeterreading_volume from heat_log order by heat_timestamp desc"
749
#    print(query)
750
    try:
751
        db = mysql.connector.connect(user=p1_mysql_user, password=p1_mysql_passwd, host=p1_mysql_host, database=p1_mysql_db)
752
        c = db.cursor()
753
        c.execute(query)
754
        heat_timestamp, heat_equipment_id, heat_meterreading_energy, heat_unitmeterreading_energy, heat_meterreading_volume, heat_unitmeterreading_volume = c.fetchone()
755
        p1_channelA.id = channelA
756
        p1_channelA.type_id = 5
757
        p1_channelA.type_desc = "Heat energy"
758
        p1_channelA.equipment_id = heat_equipment_id
759
        p1_channelA.timestamp = str(datetime.datetime.strftime(heat_timestamp, "%Y-%m-%d %H:%M:%S" ))
760
        p1_channelA.meterreading = heat_meterreading_energy
761
        p1_channelA.unit = heat_unitmeterreading_energy
762
        p1_channelA.valveposition = 1
763
        print ("Warmte energie %s toegevoegd aan P1 telegram - kanaal %s" % (heat_timestamp, channelA ) )
764
        if channelB != 0:
765
            p1_channelB.id = channelB
766
            p1_channelB.type_id = 5
767
            p1_channelB.type_desc = "Heat flow"
768
            p1_channelB.equipment_id = heat_equipment_id
769
            p1_channelB.timestamp = str(datetime.datetime.strftime(heat_timestamp, "%Y-%m-%d %H:%M:%S" ))
770
            p1_channelB.meterreading = heat_meterreading_volume
771
            p1_channelB.unit = heat_unitmeterreading_volume
772
            p1_channelB.valveposition = 1
773
            print ("Warmte flow %s toegevoegd aan P1 telegram - kanaal %s" % (heat_timestamp, channelB ) )
774
        #c.close()
775
        db.close()
776
    except:
777
        show_error()
778
        print ("Fout bij het openen / lezen van database %s / %s. Heat telegram niet opgehaald."  % (p1_mysql_host, p1_mysql_db))      
779
    return
780
#################################################################
781
# S0 Pulse Counter Data                                         #
782
#################################################################
783
def get_s0_data(id,meter,channel,p1_channel,type_id,type_desc):
784
# Use the total s0 volume to improve performance. In the S0 Datalogger, make sure it is not reset!!
785
    query = "select s0_timestamp, s0_id, s0_m" + meter + "_volume_total, s0_m" + meter + "_volume_total_unit from s0_log where s0_id = '" + id + "' order by s0_timestamp desc limit 1"
786
#    print(query)
787
    try:
788
        db = mysql.connector.connect(user=p1_mysql_user, password=p1_mysql_passwd, host=p1_mysql_host, database=p1_mysql_db)
789
        c = db.cursor()
790
        c.execute(query)
791
        s0_timestamp, s0_id, s0_volume_total, s0_volume_total_unit = c.fetchone()
792
        p1_channel.id = channel
793
        p1_channel.type_id = type_id
794
        p1_channel.type_desc = type_desc
795
        p1_channel.equipment_id = s0_id  + "-" + meter
796
        p1_channel.timestamp = str(datetime.datetime.strftime(s0_timestamp, "%Y-%m-%d %H:%M:%S" ))
797
        p1_channel.meterreading = s0_volume_total
798
        p1_channel.unit = s0_volume_total_unit
799
        p1_channel.valveposition = "1"
800
        print ("S0 %s %s toegevoegd aan P1 telegram - kanaal %s" % (type_desc, s0_timestamp,channel))
801
        #c.close()
802
        db.close()
803
    except:
804
        show_error()
805
        print ("Fout bij het openen / lezen van database %s / %s. S0 telegram niet opgehaald."  % (p1_mysql_host, p1_mysql_db))
806
    return
807
#################################################################
808
# Electricity sub-meter                                         #
809
#################################################################
810
def get_power_data(channel,p1_channel,type_id,type_desc):
811
    query = "select power_timestamp, power_equipment_id, power_meterreading_1_tot, power_unitmeterreading_1_tot from power_log order by power_timestamp desc"
812
#    print(query)
813
    try:
814
        db = mysql.connector.connect(user=p1_mysql_user, password=p1_mysql_passwd, host=p1_mysql_host, database=p1_mysql_db)
815
        c = db.cursor()
816
        c.execute(query)
817
        power_timestamp, power_equipment_id, power_meterreading_1_tot, power_unitmeterreading_1_tot = c.fetchone()
818
        p1_channel.id = channel
819
        p1_channel.type_id = type_id
820
        p1_channel.type_desc = type_desc
821
        p1_channel.equipment_id = row.power_equipment_id
822
        p1_channel.timestamp = str(row.power_timestamp)
823
        p1_channel.meterreading = power_meterreading_1_tot
824
        p1_channel.unit = power_unitmeterreading_1_tot
825
        p1_channel.valveposition = "1"
826
        print ("Elektra %s %s toegevoegd aan P1 telegram - kanaal %s" % (type_desc, power_timestamp,channel) )
827
        #c.close()
828
        db.close()
829
    except:
830
        show_error()
831
        print ("Fout bij het openen / lezen van database %s / %s. Iskra telegram niet opgehaald."  % (p1_mysql_host, p1_mysql_db))      
832
 
833
    return
834
#################################################################
835
# End of procedures to add other metering data to p1_telegram   #
836
#################################################################    
837
 
838
################################################################################################################################################
839
#Main program
840
################################################################################################################################################
841
print("%s %s" % (progname, version))
842
comport=-1
843
win_os = (os.name == 'nt')
844
if win_os:
845
    print("Windows Mode")
846
else:
847
    print("Non-Windows Mode")
848
print("Python version %s.%s.%s" % sys.version_info[:3])
849
print ("Control-C to abort")
850
 
851
################################################################################################################################################
852
#Commandline arguments parsing
853
################################################################################################################################################    
854
parser = argparse.ArgumentParser(prog=progname, description='P1 Datalogger - www.smartmeterdashboard.nl', epilog="Copyright (c) 2011/2012/2013/2014 J. van der Linde. Although there is a explicit copyright on this sourcecode, anyone may use it freely under a 'Creative Commons Naamsvermelding-NietCommercieel-GeenAfgeleideWerken 3.0 Nederland' license.")
855
#parser.add_argument("-c", "--comport", help="COM-port identifier", type=int)
856
#parser.add_argument("-l", "--loginterval", help="Log frequency in 10 second-units, default=1", default=1, type=int)
857
#parser.add_argument("-o", "--output", help="Output mode, default='screen'", default='screen', choices=['screen', 'csv', 'db'])
858
#parser.add_argument("-pvo", "--pvoutput", help="Output to PVOutput ==EXPERIMENTAL==, default='N'", default='N', choices=['Y', 'N'])
859
#parser.add_argument("-pvoapi", "--pvoutputapikey", help="PVOutput.org API key")
860
#parser.add_argument("-pvosys", "--pvoutputsystemid", help="PVOutput.org system id", type=int)
861
#parser.add_argument("-s", "--server", help="Database server, default='localhost'", default='localhost')
862
#parser.add_argument("-u", "--user", help="Database user, default='root'", default='root')
863
#parser.add_argument("-p", "--password", help="Database user password, default='password'", default='password')
864
#parser.add_argument("-d", "--database", help="Database name, default=p1'", default='p1')
865
parser.add_argument("-v", "--version", help="DSMR COM-port setting version, default=3'", choices=['2','3','4'], default='3')
866
args = parser.parse_args()
867
 
868
################################################################
869
# Script arguments parsing
870
mydir = os.path.dirname(os.path.abspath(__file__))
871
config = configparser.RawConfigParser()
872
#config.read([mydir + 'P1.cfg'])
873
config.read(['/usr/local/P1.cfg'])
874
 
875
cfgloginterval  = config.getint('cfgGeneral', 'loginterval')
876
cfgoutput       = config.get('cfgGeneral', 'output')
877
 
878
cfgserver       = config.get('cfgDatabase', 'server')
879
cfguser         = config.get('cfgDatabase', 'user')
880
cfgpassword     = config.get('cfgDatabase', 'password')
881
cfgdatabase     = config.get('cfgDatabase', 'database')
882
cfgtable        = config.get('cfgDatabase', 'table')
883
cfgtable2       = config.get('cfgDatabase', 'table2')
884
cfgtable3       = config.get('cfgDatabase', 'table3')
885
 
886
cfgcomport      = config.get('cfgCOM', 'comport')
887
cfgtcphost      = config.get('cfgNET', 'tcphost')
888
cfgtcpport      = config.getint('cfgNET', 'tcpport')
889
 
890
cfgpvo          = config.get('cfgPVO', 'pvo')
891
cfgpvoapi       = config.get('cfgPVO', 'pvoapi')
892
cfgpvosys       = config.getint('cfgPVO', 'pvosys')
893
# End parsing
894
################################################################
895
 
896
if cfgcomport == None:
897
    parser.print_help()
898
    print ("\r")
899
    print("%s: error: The following arguments are required: -c/--comport." % progname)
900
    print("Allowed values for argument -c/--comport:") 
901
    #scanserial returns win_os serial ports and non win_os USB serial ports
902
    for n,s in scan_serial():
903
        port=n+1
904
        print ("%d --> %s" % (port,s) )
905
    print ("Program aborted.")
906
    sys.exit()
907
 
908
comport = int(cfgcomport)
909
pvo_output = (cfgpvo == "Y")
910
log_interval = cfgloginterval
911
if pvo_output and (cfgpvoapi == None or cfgpvosys == None):
912
    parser.print_help()
913
    print ("\r")
914
    print("%s: error: If -pvo/--pvoutput is 'Y', the following arguments are required: -pvoapi/--pvoutputapikey and -pvosys/--pvoutputsystemid." % progname)
915
    print ("Program aborted.")
916
    sys.exit()
917
 
918
if pvo_output and log_interval < 6:
919
    log_interval = 6
920
    print("%s: warning: If -pvo/--pvoutput is 'Y', log interval should be 6 or higher. Log interval 6 used instead." % progname)
921
output_mode = cfgoutput
922
dsmr_version = args.version
923
pvo_apikey = cfgpvoapi
924
pvo_systemid = cfgpvosys
925
pvo_prev_date = ""
926
 
927
#Show startup arguments
928
print ("\r")
929
print ("Startup parameters:")
930
print ("Output mode           : %s" % output_mode)
931
print ("PVOutput.org logging  : %s" % pvo_output)
932
if pvo_output:
933
    print ("PVOutput.org API key  : %s" % pvo_apikey)
934
    print ("PVOutput.org system ID: %s" % pvo_systemid)
935
print ("Log interval          : %s (once every %s seconds)" % (log_interval, log_interval * 10))
936
print ("DSMR COM-port setting : %s" % dsmr_version)
937
if (output_mode == "db" or import_db) and MySQL_loaded:
938
    p1_mysql_host=cfgserver
939
    p1_mysql_user=cfguser
940
    p1_mysql_passwd=cfgpassword
941
    p1_mysql_db=cfgdatabase   
942
    print ("Database credentials used:")
943
    print ("- Server  : %s" % p1_mysql_host)
944
    print ("- User    : %s" % p1_mysql_user)
945
    print ("- Password: %s" % p1_mysql_passwd)
946
    print ("- Database: %s" % p1_mysql_db)
947
if (output_mode == "db" or import_db) and not MySQL_loaded:
948
   print("%s: warning: MySQL Connector/Python not found. Output mode 'db' not allowed. Output mode 'csv' used instead." % progname)
949
   output_mode = "csv"
950
   import_db = False   
951
#################################################################################################################################################
952
 
953
#Set COM port config
954
if comport != 0:
955
    ser = serial.Serial()
956
    if dsmr_version == '2' or dsmr_version == '3':
957
        ser.baudrate = 9600
958
        ser.bytesize=serial.SEVENBITS
959
        ser.parity=serial.PARITY_EVEN
960
        ser.stopbits=serial.STOPBITS_ONE
961
        ser.xonxoff=1
962
    if dsmr_version == '4':
963
        ser.baudrate = 115200
964
        ser.bytesize=serial.EIGHTBITS
965
        ser.parity=serial.PARITY_NONE
966
        ser.stopbits=serial.STOPBITS_ONE
967
        ser.xonxoff=1
968
    ser.rtscts=0
969
    ser.timeout=20
970
    if win_os:
971
        ser.port=comport-1
972
        print ("COM-port              : %d (%s)" % (comport, ser.name) )   
973
    else:
974
        ser.port="/dev/ttyUSB"+str(comport-1)
975
        port="/dev/ttyUSB"+str(comport-1)  # Linux Style for /dev/ttyUSB0, /dev/ttyUSB1, etc...
976
        print ("COM-port              : %d (%s)" % (comport, port) )
977
else:
978
    print ("Inputfile assigned    : 'p1test.log'")
979
 
980
#Open COM port
981
if comport != 0:
982
    try:
983
        ser.open()
984
    except:
985
        if win_os:
986
            sys.exit ("Error opening %s. Program aborted."  % ser.name)
987
        else:
988
            sys.exit ("Error opening %s. Program aborted."  %  port) 
989
else:
990
    try:
991
        ser = open("p1test.log", "rt")   
992
    except:
993
        sys.exit ("Error opening 'p1test.log'. Program aborted.")      
994
 
995
 
996
#Initialize
997
p1_telegram=False
998
p1_meter_supplier=""
999
p1_timestamp=""
1000
p1_dsmr_version="30"
1001
p1_current_threshold=0
1002
p1_unit_current_threshold=""
1003
p1_current_switch_position=1
1004
p1_powerfailures=0
1005
p1_long_powerfailures=0
1006
p1_long_powerfailures_log=""
1007
p1_voltage_sags_l1=0
1008
p1_voltage_sags_l2=0
1009
p1_voltage_sags_l3=0
1010
p1_voltage_swells_l1=0
1011
p1_voltage_swells_l2=0
1012
p1_voltage_swells_l3=0
1013
p1_instantaneous_current_l1=0
1014
p1_unit_instantaneous_current_l1=""
1015
p1_instantaneous_current_l2=0
1016
p1_unit_instantaneous_current_l2=""
1017
p1_instantaneous_current_l3=0
1018
p1_unit_instantaneous_current_l3=""
1019
p1_instantaneous_active_power_in_l1=0
1020
p1_unit_instantaneous_active_power_in_l1=""
1021
p1_instantaneous_active_power_in_l2=0
1022
p1_unit_instantaneous_active_power_in_l2=""
1023
p1_instantaneous_active_power_in_l3=0
1024
p1_unit_instantaneous_active_power_in_l3=""
1025
p1_instantaneous_active_power_out_l1=0
1026
p1_unit_instantaneous_active_power_out_l1=""
1027
p1_instantaneous_active_power_out_l2=0
1028
p1_unit_instantaneous_active_power_out_l2=""
1029
p1_instantaneous_active_power_out_l3=0
1030
p1_unit_instantaneous_active_power_out_l3=""
1031
p1_voltage_l1=0
1032
p1_unit_voltage_l1=""
1033
p1_voltage_l2=0
1034
p1_unit_voltage_l2=""
1035
p1_voltage_l3=0
1036
p1_unit_voltage_l3=""
1037
p1_prev_meterreading_out_1 = 0
1038
p1_prev_meterreading_out_2 = 0
1039
p1_prev_meterreading_in_1 = 0
1040
p1_prev_meterreading_in_2 = 0
1041
pvo_volume_initialize = False
1042
pvo_prev_date=""
1043
 
1044
p1_teller=0
1045
 
1046
while 1:
1047
    p1_line=''
1048
#Read 1 line
1049
    try:
1050
        p1_raw = ser.readline()
1051
    except:
1052
        if comport != 0:
1053
            if win_os:
1054
                sys.exit ("Error reading %s. Program aborted."  % ser.name)
1055
            else:
1056
               sys.exit ("Error reading %s. Program aborted."  %  port) 
1057
            ser.close()
1058
        else:
1059
            sys.exit ("Error reading 'p1test.log'. Program aborted.")                  
1060
            ser.close()
1061
    if comport == 0 and len(p1_raw) == 0:
1062
            ser.close()  
1063
            sys.exit ("Finished reading 'p1test.log'. Program ended.")                  
1064
    p1_str=p1_raw
1065
    if comport != 0:
1066
        p1_str=str(p1_raw, "utf-8")
1067
    p1_line=p1_str.strip()
1068
 
1069
#Inspect 1st character
1070
    if p1_line[0:1] == "/":
1071
#Start of new P1 telegram
1072
        p1_telegram=True
1073
        p1_teller=p1_teller+1
1074
#P1 Timestamp to cover DSMR 3 and before       
1075
        p1_timestamp=datetime.datetime.strftime(datetime.datetime.today(), "%Y-%m-%d %H:%M:%S" )
1076
#Initialize P1 channeldata
1077
        p1_channel_1=P1_ChannelData()
1078
        p1_channel_2=P1_ChannelData()
1079
        p1_channel_3=P1_ChannelData()
1080
        p1_channel_4=P1_ChannelData()
1081
 
1082
#Only proceed if P1 telegram start is recognized.        
1083
    if p1_telegram:
1084
        if p1_line[0:1] == "/":
1085
#Header information 
1086
#eg. /KMP5 KA6U001511209910 (Kamstrup Enexis)
1087
#eg. /ISk5\2ME382-1003 (InkraEmeco Liander)
1088
#eg. /XMX5XMXABCE000018914 (Landis&Gyr Stedin, Xemex communicatiemodule)
1089
#eg. /KFM5KAIFA-METER (Kaifa)
1090
            p1_meter_supplier=p1_line[1:4]
1091
            p1_header=p1_line
1092
 
1093
        elif p1_line[4:9] == "1.0.0":
1094
#P1 Timestamp (DSMR 4)
1095
#eg. 0-0:1.0.0(101209113020W)
1096
            if p1_line[10:23] != "000101010000W":
1097
#Check if meter clock is running
1098
                p1_timestamp="20"+p1_line[10:12]+"-"+p1_line[12:14]+"-"+p1_line[14:16]+" "+p1_line[16:18]+":"+p1_line[18:20]+":"+p1_line[20:22]
1099
            else:
1100
                print ("%s: warning: invalid P1-telegram date/time value '%s', system date/time used instead: '%s'" % (progname, p1_line[10:23], p1_timestamp) )
1101
 
1102
        elif p1_line[4:9] == "0.2.8":
1103
#DSMR Version (DSMR V4)
1104
#eg. 1-3:0.2.8(40)
1105
            p1_lastpos=len(p1_line)-1
1106
            p1_dsmr_version=p1_line[10:p1_lastpos]
1107
 
1108
        elif p1_line[4:10] == "96.1.1":
1109
#####
1110
#Channel 0 = E
1111
#####
1112
#Equipment identifier (Electricity)
1113
#eg. 0-0:96.1.1(204B413655303031353131323039393130)
1114
            p1_lastpos=len(p1_line)-1
1115
            p1_equipment_id=p1_line[11:p1_lastpos]
1116
 
1117
        elif p1_line[4:9] == "1.8.1":
1118
#Meter Reading electricity delivered to client (normal tariff)
1119
#eg. 1-0:1.8.1(00721.000*kWh) (DSMR 3)
1120
#eg. 1-0:1.8.1(000038.851*kWh) (DSMR 4)
1121
#        p1_meterreading_in_1=float(p1_line[10:19])
1122
#        p1_unitmeterreading_in_1=p1_line[20:23]
1123
            p1_lastpos=len(p1_line)-1
1124
            p1_num_start = p1_line.find("(") +1
1125
            p1_num_end = p1_line.find("*")
1126
            p1_meterreading_in_1=float(p1_line[p1_num_start:p1_num_end])        
1127
            p1_unitmeterreading_in_1=p1_line[p1_num_end+1:p1_lastpos]
1128
        elif p1_line[4:9] == "1.8.2":
1129
#Meter Reading electricity delivered to client (low tariff)
1130
#eg. 1-0:1.8.2(00392.000*kWh)
1131
#        p1_meterreading_in_2=float(p1_line[10:19])
1132
#        p1_unitmeterreading_in_2=p1_line[20:23]
1133
            p1_lastpos=len(p1_line)-1
1134
            p1_num_start = p1_line.find("(") +1
1135
            p1_num_end = p1_line.find("*")
1136
            p1_meterreading_in_2=float(p1_line[p1_num_start:p1_num_end])        
1137
            p1_unitmeterreading_in_2=p1_line[p1_num_end+1:p1_lastpos]
1138
        elif p1_line[4:9] == "2.8.1":
1139
#Meter Reading electricity delivered by client (normal tariff)
1140
#eg. 1-0:2.8.1(00000.000*kWh)
1141
#        p1_meterreading_out_1=float(p1_line[10:19])
1142
#        p1_unitmeterreading_out_1=p1_line[20:23]
1143
            p1_lastpos=len(p1_line)-1
1144
            p1_num_start = p1_line.find("(") +1
1145
            p1_num_end = p1_line.find("*")
1146
            p1_meterreading_out_1=float(p1_line[p1_num_start:p1_num_end])        
1147
            p1_unitmeterreading_out_1=p1_line[p1_num_end+1:p1_lastpos]
1148
 
1149
        elif p1_line[4:9] == "2.8.2":
1150
#Meter Reading electricity delivered by client (low tariff)
1151
#eg. 1-0:2.8.2(00000.000*kWh)
1152
#        p1_meterreading_out_2=float(p1_line[10:19])
1153
#        p1_unitmeterreading_out_2=p1_line[20:23]
1154
            p1_lastpos=len(p1_line)-1
1155
            p1_num_start = p1_line.find("(") +1
1156
            p1_num_end = p1_line.find("*")
1157
            p1_meterreading_out_2=float(p1_line[p1_num_start:p1_num_end])        
1158
            p1_unitmeterreading_out_2=p1_line[p1_num_end+1:p1_lastpos]
1159
 
1160
        elif p1_line[4:11] == "96.14.0":
1161
#Tariff indicator electricity
1162
#eg. 0-0:96.14.0(0001)
1163
#alternative 0-0:96.14.0(1)
1164
            p1_lastpos=len(p1_line)-1
1165
            p1_current_tariff=int(p1_line[12:p1_lastpos])
1166
 
1167
        elif p1_line[4:9] == "1.7.0":
1168
#Actual electricity power delivered to client (+P)
1169
#eg. 1-0:1.7.0(0000.91*kW)
1170
#        p1_current_power_in=float(p1_line[10:17])
1171
#        p1_unit_current_power_in=p1_line[18:20]
1172
            p1_lastpos=len(p1_line)-1
1173
            p1_num_start = p1_line.find("(") +1
1174
            p1_num_end = p1_line.find("*")
1175
            p1_current_power_in=float(p1_line[p1_num_start:p1_num_end])        
1176
            p1_unit_current_power_in=p1_line[p1_num_end+1:p1_lastpos]
1177
 
1178
        elif p1_line[4:9] == "2.7.0":
1179
#Actual electricity power delivered by client (-P)
1180
#1-0:2.7.0(0000.00*kW)
1181
#        p1_current_power_out=float(p1_line[10:17])
1182
#        p1_unit_current_power_out=p1_line[18:20]
1183
            p1_lastpos=len(p1_line)-1
1184
            p1_num_start = p1_line.find("(") +1
1185
            p1_num_end = p1_line.find("*")
1186
            p1_current_power_out=float(p1_line[p1_num_start:p1_num_end])        
1187
            p1_unit_current_power_out=p1_line[p1_num_end+1:p1_lastpos]
1188
 
1189
        elif p1_line[4:10] == "17.0.0":
1190
#Actual threshold Electricity
1191
#Companion standard, eg Kamstrup, Xemex
1192
#eg. 0-0:17.0.0(999*A)
1193
#Iskraemeco
1194
#eg. 0-0:17.0.0(0999.00*kW)
1195
            p1_lastpos=len(p1_line)-1
1196
            p1_num_start = p1_line.find("(") +1
1197
            p1_num_end = p1_line.find("*")
1198
            p1_current_threshold=float(p1_line[p1_num_start:p1_num_end])        
1199
            p1_unit_current_threshold=p1_line[p1_num_end+1:p1_lastpos]
1200
 
1201
        elif p1_line[4:11] == "96.3.10":
1202
#Actual switch position Electricity (in/out/enabled).
1203
#eg. 0-0:96.3.10(1), default to 1
1204
            p1_value=p1_line[12:13]
1205
            if not isinstance(p1_value, int):
1206
               p1_value=1
1207
            p1_current_switch_position=int(p1_value)
1208
        elif p1_line[4:11] == "96.7.21":
1209
#Number of powerfailures in any phase (DSMR4)
1210
#eg. 0-0:96.7.21(00004)
1211
            p1_lastpos=len(p1_line)-1
1212
            p1_num_start = p1_line.find("(") +1
1213
            p1_powerfailures=int(float(p1_line[p1_num_start:p1_lastpos]))
1214
 
1215
        elif p1_line[4:10] == "96.7.9":
1216
#Number of long powerfailures in any phase (DSMR4)
1217
#eg. 0-0:96.7.9(00002)
1218
            p1_lastpos=len(p1_line)-1
1219
            p1_num_start = p1_line.find("(") +1
1220
            p1_long_powerfailures=int(float(p1_line[p1_num_start:p1_lastpos]))
1221
 
1222
        elif p1_line[4:11] == "99.97.0":
1223
#Powerfailure eventlog (DSMR4)
1224
#eg. 1-0:99:97.0(2)(0:96.7.19)(101208152415W)(0000000240*s)(101208151004W)(00000000301*s)
1225
#    1-0:99.97.0(0)(0-0:96.7.19)
1226
            p1_lastpos=len(p1_line)
1227
            p1_log_start= p1_line.find("0:96.7.19") +10
1228
            p1_long_powerfailures_log=p1_line[p1_log_start:p1_lastpos]
1229
 
1230
        elif p1_line[4:11] == "32.32.0":
1231
#Number of Voltage sags L1 (DSMR4)
1232
#eg. 1-0:32.32.0(00002)
1233
            p1_lastpos=len(p1_line)-1
1234
            p1_num_start = p1_line.find("(") +1
1235
            p1_voltage_sags_l1=int(float(p1_line[p1_num_start:p1_lastpos]))
1236
 
1237
        elif p1_line[4:11] == "52.32.0":
1238
#Number of Voltage sags L2 (DSMR4)
1239
#eg. 1-0:52.32.0(00002)
1240
            p1_lastpos=len(p1_line)-1
1241
            p1_num_start = p1_line.find("(") +1
1242
            p1_voltage_sags_l2=int(float(p1_line[p1_num_start:p1_lastpos]))
1243
 
1244
        elif p1_line[4:11] == "72.32.0":
1245
#Number of Voltage sags L3 (DSMR4)
1246
#eg. 1-0:72.32.0(00002)
1247
            p1_lastpos=len(p1_line)-1
1248
            p1_num_start = p1_line.find("(") +1
1249
            p1_voltage_sags_l3=int(float(p1_line[p1_num_start:p1_lastpos]))
1250
 
1251
        elif p1_line[4:11] == "32.36.0":
1252
#Number of Voltage swells L1 (DSMR4)
1253
#eg. 1-0:32.36.0(00002)
1254
            p1_lastpos=len(p1_line)-1
1255
            p1_num_start = p1_line.find("(") +1
1256
            p1_voltage_swells_l1=int(float(p1_line[p1_num_start:p1_lastpos]))
1257
 
1258
        elif p1_line[4:11] == "52.36.0":
1259
#Number of Voltage swells L2 (DSMR4)
1260
#eg. 1-0:52.36.0(00002)
1261
            p1_lastpos=len(p1_line)-1
1262
            p1_num_start = p1_line.find("(") +1
1263
            p1_voltage_swells_l2=int(float(p1_line[p1_num_start:p1_lastpos]))
1264
 
1265
        elif p1_line[4:11] == "72.36.0":
1266
#Number of Voltage swells L3 (DSMR4)
1267
#eg. 1-0:72.36.0(00002)
1268
            p1_lastpos=len(p1_line)-1
1269
            p1_num_start = p1_line.find("(") +1
1270
            p1_voltage_swells_l3=int(float(p1_line[p1_num_start:p1_lastpos]))
1271
 
1272
 
1273
        elif p1_line[4:10] == "31.7.0":
1274
#Instantaneous current L1 in A (DSMR4)
1275
#eg. 1-0:31.7.0.255(001*A)
1276
            p1_lastpos=len(p1_line)-1
1277
            p1_num_start = p1_line.find("(") +1
1278
            p1_num_end = p1_line.find("*")
1279
            p1_instantaneous_current_l1=int(float(p1_line[p1_num_start:p1_num_end]))
1280
            p1_unit_instantaneous_current_l1=p1_line[p1_num_end+1:p1_lastpos]
1281
 
1282
        elif p1_line[4:10] == "51.7.0":
1283
#Instantaneous current L2 in A (DSMR4)
1284
#eg. 1-0:51.7.0.255(002*A)
1285
            p1_lastpos=len(p1_line)-1
1286
            p1_num_start = p1_line.find("(") +1
1287
            p1_num_end = p1_line.find("*")
1288
            p1_instantaneous_current_l2=int(float(p1_line[p1_num_start:p1_num_end]))
1289
            p1_unit_instantaneous_current_l2=p1_line[p1_num_end+1:p1_lastpos]
1290
 
1291
 
1292
        elif p1_line[4:10] == "71.7.0":
1293
#Instantaneous current L3 in A (DSMR4)
1294
#eg. 1-0:71.7.0.255(003*A)
1295
            p1_lastpos=len(p1_line)-1
1296
            p1_num_start = p1_line.find("(") +1
1297
            p1_num_end = p1_line.find("*")
1298
            p1_instantaneous_current_l3=int(float(p1_line[p1_num_start:p1_num_end]))
1299
            p1_unit_instantaneous_current_l3=p1_line[p1_num_end+1:p1_lastpos]
1300
 
1301
        elif p1_line[4:10] == "21.7.0":
1302
#Instantaneous active power L1 (+P) in W (DSMR4)          
1303
#eg 1-0:21.7.0.255(01.111*kW)
1304
            p1_lastpos=len(p1_line)-1
1305
            p1_num_start = p1_line.find("(") +1
1306
            p1_num_end = p1_line.find("*")
1307
            p1_instantaneous_active_power_in_l1=int(float(p1_line[p1_num_start:p1_num_end]))
1308
            p1_unit_instantaneous_active_power_in_l1=p1_line[p1_num_end+1:p1_lastpos]
1309
 
1310
        elif p1_line[4:10] == "41.7.0":
1311
#Instantaneous active power L2 (+P) in W (DSMR4)           
1312
#eg 1-0:41.7.0.255(02.222*kW)
1313
            p1_lastpos=len(p1_line)-1
1314
            p1_num_start = p1_line.find("(") +1
1315
            p1_num_end = p1_line.find("*")
1316
            p1_instantaneous_active_power_in_l2=int(float(p1_line[p1_num_start:p1_num_end]))
1317
            p1_unit_instantaneous_active_power_in_l2=p1_line[p1_num_end+1:p1_lastpos]            
1318
 
1319
        elif p1_line[4:10] == "61.7.0":
1320
#Instantaneous active power L3 (+P) in W (DSMR4)           
1321
#eg 1-0:61.7.0.255(03.333*kW)
1322
            p1_lastpos=len(p1_line)-1
1323
            p1_num_start = p1_line.find("(") +1
1324
            p1_num_end = p1_line.find("*")
1325
            p1_instantaneous_active_power_in_l3=int(float(p1_line[p1_num_start:p1_num_end]))
1326
            p1_unit_instantaneous_active_power_in_l3=p1_line[p1_num_end+1:p1_lastpos]
1327
 
1328
        elif p1_line[4:10] == "22.7.0":
1329
#Instantaneous active power L1 (+P) in W  (DSMR4)          
1330
#eg 1-0:22.7.0.255(04.444*kW)
1331
            p1_lastpos=len(p1_line)-1
1332
            p1_num_start = p1_line.find("(") +1
1333
            p1_num_end = p1_line.find("*")
1334
            p1_instantaneous_active_power_out_l1=int(float(p1_line[p1_num_start:p1_num_end]))
1335
            p1_unit_instantaneous_active_power_out_l1=p1_line[p1_num_end+1:p1_lastpos]
1336
 
1337
        elif p1_line[4:10] == "42.7.0":
1338
#Instantaneous active power L2 (+P) in W  (DSMR4)          
1339
#eg 1-0:42.7.0.255(05.555*kW)
1340
            p1_lastpos=len(p1_line)-1
1341
            p1_num_start = p1_line.find("(") +1
1342
            p1_num_end = p1_line.find("*")
1343
            p1_instantaneous_active_power_out_l2=int(float(p1_line[p1_num_start:p1_num_end]))
1344
            p1_unit_instantaneous_active_power_out_l2=p1_line[p1_num_end+1:p1_lastpos]            
1345
 
1346
        elif p1_line[4:10] == "62.7.0":
1347
#Instantaneous active power L3 (+P) in W (DSMR4)           
1348
#eg 1-0:62.7.0.255(06.666*kW)
1349
            p1_lastpos=len(p1_line)-1
1350
            p1_num_start = p1_line.find("(") +1
1351
            p1_num_end = p1_line.find("*")
1352
            p1_instantaneous_active_power_out_l3=int(float(p1_line[p1_num_start:p1_num_end]))
1353
            p1_unit_instantaneous_active_power_out_l3=p1_line[p1_num_end+1:p1_lastpos]    
1354
 
1355
        elif p1_line[4:10] == "32.7.0":
1356
#Voltage level L1 in V (DSMR4)            
1357
#1-0:32.7.0(00234*V) 
1358
            p1_lastpos=len(p1_line)-1
1359
            p1_num_start = p1_line.find("(") +1
1360
            p1_num_end = p1_line.find("*")
1361
            p1_voltage_l1=int(float(p1_line[p1_num_start:p1_num_end]))
1362
            p1_unit_voltage_l1=p1_line[p1_num_end+1:p1_lastpos]   
1363
 
1364
        elif p1_line[4:10] == "52.7.0":            
1365
#Voltage level L2 in V (DSMR4)            
1366
#1-0:52.7.0(00234*V) 
1367
            p1_lastpos=len(p1_line)-1
1368
            p1_num_start = p1_line.find("(") +1
1369
            p1_num_end = p1_line.find("*")
1370
            p1_voltage_l2=int(float(p1_line[p1_num_start:p1_num_end]))
1371
            p1_unit_voltage_l2=p1_line[p1_num_end+1:p1_lastpos]    
1372
 
1373
        elif p1_line[4:10] == "72.7.0":            
1374
#Voltage level L3 in V (DSMR4)            
1375
#1-0:72.7.0(00234*V) 
1376
            p1_lastpos=len(p1_line)-1
1377
            p1_num_start = p1_line.find("(") +1
1378
            p1_num_end = p1_line.find("*")
1379
            p1_voltage_l3=int(float(p1_line[p1_num_start:p1_num_end]))
1380
            p1_unit_voltage_l3=p1_line[p1_num_end+1:p1_lastpos]  
1381
 
1382
        elif p1_line[4:11] == "96.13.1":
1383
#Text message code: numeric 8 digits
1384
#eg. 0-0:96.13.1()
1385
            p1_lastpos=len(p1_line)-1
1386
#        p1_message_code=p1_line[12:p1_lastpos]
1387
            p1_message_code=bytes.fromhex(p1_line[12:p1_lastpos]).decode('utf-8')
1388
        elif p1_line[4:11] == "96.13.0":
1389
    #Text message max 1024 characters.
1390
    #eg. 0-0:96.13.0()
1391
            p1_lastpos=len(p1_line)-1
1392
            p1_message_text=bytes.fromhex(p1_line[12:p1_lastpos]).decode('utf-8')
1393
#        p1_line[12:p1_lastpos]
1394
#####
1395
#Channels 1/2/3/4: MBus connected meters
1396
#####
1397
        elif p1_line[4:10] == "24.1.0":
1398
#Device-Type
1399
#eg. 0-1:24.1.0(3)
1400
#or 0-1:24.1.0(03) 3=Gas;5=Heat;6=Cooling
1401
#or 0-1:24.1.0(03) 3/7=Gas;5=Heat;6=Cooling (Standard OBIS: 1-Electricity / 4-HeatCostAllocation / 5-Cooling / 6-Heat / 7-Gas / 8-ColdWater / 9-HotWater)
1402
 
1403
            p1_channel=int(p1_line[2:3])
1404
            p1_lastpos=len(p1_line)-1
1405
            p1_value=int(p1_line[11:p1_lastpos])
1406
            if p1_value in [3,7]:
1407
                 p1_value2="Gas"
1408
            elif p1_value == 4:
1409
                 p1_value2="HeatCost"                 
1410
            elif p1_value == 5:
1411
                 p1_value2="Heat"
1412
            elif p1_value == 6:
1413
                 p1_value2="Cold"
1414
            elif p1_value == 8:
1415
                 p1_value2="Cold water"
1416
            elif p1_value == 9:
1417
                 p1_value2="Hot water"
1418
 
1419
            else:
1420
                 p1_value2="Unknown"
1421
#self, id=None, type_id=None, type_desc=None, equipment_id=None, timestamp=None, meterreading=None, unit=None, valveposition=None
1422
            if p1_channel==1:
1423
                p1_channel_1.id=p1_channel
1424
                p1_channel_1.type_id = p1_value
1425
                p1_channel_1.type_desc= p1_value2
1426
            elif p1_channel==2:
1427
                p1_channel_2.id=p1_channel
1428
                p1_channel_2.type_id = p1_value
1429
                p1_channel_2.type_desc= p1_value2
1430
            elif p1_channel==3:
1431
                p1_channel_3.id=p1_channel
1432
                p1_channel_3.type_id = p1_value
1433
                p1_channel_3.type_desc= p1_value2
1434
            elif p1_channel==4:
1435
                p1_channel_4.id=p1_channel
1436
                p1_channel_4.type_id = p1_value
1437
                p1_channel_4.type_desc= p1_value2
1438
 
1439
 
1440
        elif p1_line[4:10] == "96.1.0":
1441
#Equipment identifier
1442
#eg. 0-1:96.1.0(3238303039303031303434303132303130)
1443
            p1_channel=int(p1_line[2:3])
1444
            p1_lastpos=len(p1_line)-1
1445
            p1_value=p1_line[11:p1_lastpos]
1446
#self, id=None, type_id=None, type_desc=None, equipment_id=None, timestamp=None, meterreading=None, unit=None, valveposition=None
1447
            if p1_channel==1:
1448
                p1_channel_1.equipment_id=p1_value
1449
            elif p1_channel==2:
1450
                p1_channel_2.equipment_id=p1_value
1451
            elif p1_channel==3:
1452
                p1_channel_3.equipment_id=p1_value
1453
            elif p1_channel==4:
1454
                p1_channel_4.equipment_id=p1_value
1455
 
1456
        elif p1_line[4:10] == "24.3.0":
1457
#Last hourly value delivered to client (DSMR < V4)
1458
#eg. Kamstrup/Iskraemeco:
1459
#0-1:24.3.0(110403140000)(000008)(60)(1)(0-1:24.2.1)(m3)
1460
#(00437.631)
1461
#eg. Companion Standard:
1462
#0-1:24.3.0(110403140000)(000008)(60)(1)(0-1:24.2.1)(m3)(00437.631)
1463
            p1_channel=int(p1_line[2:3])
1464
            p1_channel_timestamp="20"+p1_line[11:13]+"-"+p1_line[13:15]+"-"+p1_line[15:17]+" "+p1_line[17:19]+":"+p1_line[19:21]+":"+p1_line[21:23]
1465
            p1_lastpos=len(p1_line)-1
1466
#Value is in next line
1467
            p1_unit=p1_line[p1_lastpos-2:p1_lastpos]
1468
            p1_raw = ser.readline()
1469
#        p1_str=str(p1_raw, "utf-8")
1470
            p1_str=p1_raw
1471
            if comport != 0:
1472
                p1_str=str(p1_raw, "utf-8")
1473
            p1_line=p1_str.strip()
1474
#self, id=None, type_id=None, type_desc=None, equipment_id=None, timestamp= None, meterreading=None, unit=None, valveposition=None
1475
            if p1_channel==1:
1476
                p1_channel_1.timestamp=p1_channel_timestamp
1477
                p1_channel_1.meterreading=float(p1_line[1:10])
1478
                p1_channel_1.unit=p1_unit
1479
            elif p1_channel==2:
1480
                p1_channel_2.timestamp=p1_channel_timestamp
1481
                p1_channel_2.meterreading=float(p1_line[1:10])
1482
                p1_channel_2.unit=p1_unit
1483
            elif p1_channel==3:
1484
                p1_channel_3.timestamp=p1_channel_timestamp
1485
                p1_channel_3.meterreading=float(p1_line[1:10])
1486
                p1_channel_3.unit=p1_unit
1487
            elif p1_channel==4:
1488
                p1_channel_4.timestamp=p1_channel_timestamp
1489
                p1_channel_4.meterreading=float(p1_line[1:10])
1490
                p1_channel_4.unit=p1_unit
1491
 
1492
        elif p1_line[4:10] == "24.2.1":
1493
#Last hourly value delivered to client (DSMR v4)
1494
#eg. 0-1:24.2.1(101209110000W)(12785.123*m3)
1495
            p1_channel=int(p1_line[2:3])
1496
            p1_channel_timestamp="20"+p1_line[11:13]+"-"+p1_line[13:15]+"-"+p1_line[15:17]+" "+p1_line[17:19]+":"+p1_line[19:21]+":"+p1_line[21:23]
1497
            p1_lastpos=len(p1_line)-1
1498
            p1_line=p1_line[25:p1_lastpos]
1499
            p1_lastpos=len(p1_line)
1500
            p1_num_start = p1_line.find("(") +1
1501
            p1_num_end = p1_line.find("*")
1502
            p1_value=float(p1_line[p1_num_start:p1_num_end])        
1503
            p1_unit=p1_line[p1_num_end+1:p1_lastpos]
1504
#self, id=None, type_id=None, type_desc=None, equipment_id=None, timestamp= None, meterreading=None, unit=None, valveposition=None
1505
            if p1_channel==1:
1506
                p1_channel_1.timestamp=p1_channel_timestamp
1507
                p1_channel_1.meterreading=p1_value
1508
                p1_channel_1.unit=p1_unit
1509
            elif p1_channel==2:
1510
                p1_channel_2.timestamp=p1_channel_timestamp
1511
                p1_channel_2.meterreading=p1_value
1512
                p1_channel_2.unit=p1_unit
1513
            elif p1_channel==3:
1514
                p1_channel_3.timestamp=p1_channel_timestamp
1515
                p1_channel_3.meterreading=p1_value
1516
                p1_channel_3.unit=p1_unit
1517
            elif p1_channel==4:
1518
                p1_channel_4.timestamp=p1_channel_timestamp
1519
                p1_channel_4.meterreading=p1_value
1520
                p1_channel_4.unit=p1_unit
1521
 
1522
        elif p1_line[4:10] == "24.4.0":
1523
#Valve position (on/off/released)
1524
#eg. 0-1:24.4.0()
1525
#eg. 0-1:24.4.0(1)
1526
#Valveposition defaults to '1'(=Open) if invalid value
1527
            p1_channel=int(p1_line[2:3])
1528
            p1_lastpos=len(p1_line)-1
1529
            p1_value=p1_line[12:p1_lastpos].strip()
1530
            if not isinstance(p1_value, int):
1531
               p1_value=1
1532
            if p1_channel==1:
1533
                p1_channel_1.valveposition=p1_value
1534
            elif p1_channel==2:
1535
                p1_channel_2.valveposition=p1_value
1536
            elif p1_channel==3:
1537
                p1_channel_3.valveposition=p1_value
1538
            elif p1_channel==4:
1539
                p1_channel_4.valveposition=p1_value
1540
 
1541
        elif p1_line[0:1] == "" or p1_line[0:1] == " ":
1542
#Empty line
1543
            p1_value=""
1544
 
1545
        elif p1_line[0:1] == "!":
1546
#in DSMR 4 telegrams there might be a checksum following the "!".
1547
#eg. !141B
1548
#CRC16 value calculated over the preceding characters in the data message (from “/” to “!” using the polynomial: x16+x15+x2+1).
1549
#the checksum is discarded
1550
 
1551
#End of P1 telegram
1552
#Output if a complete telegram and matching log_interval
1553
             if p1_teller == log_interval:
1554
################################################################
1555
#Start of functionality to add other meterdata to p1-telegram  #
1556
################################################################
1557
#Comment out / remove when not applicable                      #
1558
################################################################
1559
                if import_db:
1560
######################HEAT: Mandatory 1st ChannelID, 1st ChannelDataElement, optional 2nd ChannelID, 2nd ChannelDataElement
1561
                   get_heat_data(1,p1_channel_1,2,p1_channel_2)
1562
######################POWER SUB METERING: ChannelID, ChannelDataElement, TypeID, TypeDescription
1563
#                   get_power_data(#,p1_channel_#,1,"E-Production volume")
1564
######################S0 SUB METERING: S0-ID, S0-Register, ChannelID, ChannelDataElement, TypeID, TypeDescription
1565
#                   get_s0_data('25325','1',3,p1_channel_3,1,"E-Production volume")
1566
######################PV INVERTER: Mandatory 1st ChannelID, 1st ChannelDataElement, optional 2nd ChannelID, 2nd ChannelDataElement
1567
#                   get_pv_data(1,p1_channel_1,2,p1_channel_2)
1568
################################################################
1569
#End of functionality to add other meterdata to p1-telegram    #
1570
################################################################
1571
#Output to screen
1572
                if output_mode=="screen": print_p1_telegram()
1573
#Output to csv_file
1574
                if output_mode=="csv": csv_p1_telegram()
1575
#Output to database
1576
                if output_mode=="db":
1577
                    db_p1_telegram()
1578
                    db_p2_telegram()
1579
                    db_p3_telegram()
1580
#Output to PVOutput.org
1581
                if pvo_output: pvo_p1_telegram()
1582
################################################################
1583
                p1_teller=0
1584
                p1_telegram=False
1585
#to facilitate testing, when reading p1test.log always wait 10 seconds before proceeding to next telegram to simulate actual meter behaviour           
1586
             if comport == 0: sleep(10)
1587
        else:
1588
#Always dump unrecognized data in identified telegram to screen
1589
            print ("Error interpreting P1-telegram, unrecognized data encountered: '%s'" % p1_line )
1590
#    elif p1_line != '':
1591
#Always dump unrecognized data in identified telegram to screen    
1592
#        print ("Fout bij analyseren P1 data, nog geen compleet P1-telegram ontvangen: '%s'" % p1_line )
1593
 
1594
#Close port and show status
1595
try:
1596
    ser.close()
1597
except:
1598
    if win_os:
1599
        sys.exit ("Error closing %s. Program aborted."  % ser.name)
1600
    else:
1601
        sys.exit ("Error closing %s. Program aborted."  %  port)  
1602
 
1603
 
1604
 
1605
 
1606
 
1607
 
1608
 
1609
 
1610
 
1611