-
Notifications
You must be signed in to change notification settings - Fork 0
/
meter.py
355 lines (323 loc) · 14.2 KB
/
meter.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
# METER Listener application by GuruX. Modified by Streamworks/Jesper Bonde Petersen
#
# --------------------------------------------------------------------------
# Gurux Ltd
#
#
#
# Filename: $HeadURL$
#
# Version: $Revision$,
# $Date$
# $Author$
#
# Copyright (c) Gurux Ltd
#
# ---------------------------------------------------------------------------
#
# DESCRIPTION
#
# This file is a part of Gurux Device Framework.
#
# Gurux Device Framework is Open Source software; you can redistribute it
# and/or modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; version 2 of the License.
# Gurux Device Framework is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU General Public License for more details.
#
# More information of Gurux products: http://www.gurux.org
#
# This code is licensed under the GNU General Public License v2.
# Full text may be retrieved at http://www.gnu.org/licenses/gpl-2.0.txt
# ---------------------------------------------------------------------------
import time
import sys
import ftplib
import pkg_resources
import os
import socket
from socket import *
from gurux_common.GXCommon import GXCommon
from gurux_common.IGXMediaListener import IGXMediaListener
from gurux_common.enums.TraceLevel import TraceLevel
from gurux_serial.GXSerial import GXSerial
from GXSettings import GXSettings
from gurux_dlms.enums.InterfaceType import InterfaceType
from gurux_dlms.GXDLMSTranslator import GXDLMSTranslator
from gurux_dlms.GXReplyData import GXReplyData
from gurux_dlms.GXByteBuffer import GXByteBuffer
SWver=130 # Streamworks software version
current_directory = os.getcwd()
print("current_directory " current_directory)
#OBIS information as received from the Meter
OBIS="" # general meter data
OBISmeterNow=0 # Current Meter reading: 1.1.1.8.0.255 Active energy A14 01 01 01 08 00 FF
OBISmeterBef=0 # Meter reading at start of a new cycle/day.
OBISmeterAcc=0 # Accumulated meter readings today: OBISmeterNow - OBISmeterBef
OBISpowerNow=0 # Actual power usage: 1.1.1.7.0.255 Actual power P14 01 01 01 07 00 FF
OBISpower1=0 # Actual power usage phase 1: 1.1.21.7.0.255 Actual power P14 of phase L1 01 01 15 07 00 FF
OBISpower2=0 # Actual power usage phase 2: 1.1.41.7.0.255 Actual power P14 of phase L2 01 01 29 07 00 FF
OBISpower3=0 # Actual power usage phase 3: 1.1.61.7.0.255 Actual power P14 of phase L3 01 01 3D 07 00 FF
OBISstring="OBISstring" # String with all interesting Meter Data
OBISupdated=False # Used to ensure that all OBIS data is updated before first run after reboot
POWERtoday=0 # Accumulated power usage (Actual Power P14) today
#UDP SETUP
UDPaddress='255.255.255.255'
UDPport="your preferred port number"
UDPtimeout=1
#Timings for updating CSV files
MinNow=None # Current minute
MinBef=None # Checking if a minute has passed since last update
DayNow=None # Current day
DayBef=time.strftime('%d') # Checking if day is over. Default is today so same csv is used after reboot.
# FTP information
FTPhost = 'host'
FTPdomain = 'user'
FTPpass = 'pass'
# General settings
METERpath="/home/pi/Gurux.DLMS.Python/Gurux.DLMS.Push.Listener.Example.python/" # current directory
# ---------------------------------------------------------------------------
# This example wait push notifications from the serial port or TCP/IP port.
# ---------------------------------------------------------------------------
#pylint: disable=no-self-argument
def var_write(var, value):
try:
t = open(METERpath str(var) '.txt', 'w')
t.write(str(value))
t.close()
except:
print(" var_write: File write problem ")
def var_read(var):
try:
t = open(METERpath str(var) '.txt', 'r')
return float(t.read())
except:
print(" var_read: File read problem ")
OBISmeterBef=var_read("OBISmeterBef") # Use stored file at reboot
print("OBISmeterBef loaded: " str(OBISmeterBef))
POWERtoday=var_read("POWERtoday") # Use stored file at reboot
print("POWERtoday loaded: " str(POWERtoday))
def Upload(filename): # Uploads the specified CSV file to the domain
fileup = METERpath str(filename)
# print(fileup)
try:
session = ftplib.FTP(str(FTPhost), str(FTPdomain), str(FTPpass))
session.encoding = "utf-8"
file = open(str(fileup), 'rb') # file to send
session.cwd('/directory/') # change directory
with open(fileup, "rb") as file:
# Command for Uploading the file "STOR filename"
session.storbinary(f"STOR {filename}", file)
print(time.strftime('%H:%M:%S') " Updating and uploading CSV: " str(fileup) )
# print(session.dir()) # if you want to see content at server
file.close() # close file and FTP
session.quit()
except:
print ("FTP upload failed ")
def UDPsubmit():
UDPencoded = str.encode(str(OBISstring) "," "METER" "," str(OBISmeterNow) "," "SWversion" "," str(SWver))
try:
UDPsocket = socket(AF_INET, SOCK_DGRAM)
UDPsocket.settimeout(.02)
# print("udp.timeout " str(udp.timeout))
#print ("IP: " str(udp.getsockname()[0]))
UDPsocket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
UDPsocket.setsockopt(SOL_SOCKET, SO_BROADCAST, 1)
UDPsocket.sendto(UDPencoded, (UDPaddress, UDPport))
print("UDP string submitted: " str(OBISstring))
except UDPsocket.timeout:
print("UDP timeout")
#log_error("UDP timeout")
except OSError:
print("OSError")
#log_error("OSError")
finally:
UDPsocket.close
def CSVupdate():
global MinBef, DayBef, POWERtoday,OBISmeterBef,OBISmeterAcc,OBISstring
POWERtoday=float(POWERtoday) float(OBISpowerNow)/360 # Accumulated power today. Updated every 10 sec (6*60=360)
var_write("POWERtoday", POWERtoday) # Update file to use after reboot
DayNow=time.strftime('%d')
if DayNow!=DayBef:
# Open the CSV and write the header instead of old content
print(time.strftime('%H:%M:%S') " Creating new CSV in " METERpath)
r = open(METERpath 'meter.csv', 'w')
r.write("Time" "," "METER today(Wh)" "," "Power today(W)" "," "Power Now(W)" "," "Phase1(W)" "," "Phase2(W)" "," "Phase3(W)" '\r\n')
r.close()
POWERtoday=0 # Reset accumulated power at midnight
OBISmeterBef=OBISmeterNow # Resetting the Meter counter
var_write("OBISmeterBef", OBISmeterBef) # Update file to use after reboot
print("OBISmeterBef " OBISmeterBef)
DayBef=DayNow
MinNow=time.strftime('%M')
if MinNow!=MinBef:
OBISmeterAcc=float(OBISmeterNow)-float(OBISmeterBef)
OBISstring=time.strftime('%H:%M:%S') "," str(OBISmeterAcc) "," str(POWERtoday) "," str(OBISpowerNow) "," str(OBISpower1) "," str(OBISpower2) "," str(OBISpower3) '\r\n'
r = open(METERpath 'meter.csv', 'a')
r.write(OBISstring)
Upload("meter.csv")
MinBef=MinNow
class sampleclient(IGXMediaListener):
def __init__(self, args):
try:
print("gurux_dlms version: " pkg_resources.get_distribution("gurux_dlms").version)
print("gurux_net version: " pkg_resources.get_distribution("gurux_net").version)
print("gurux_serial version: " pkg_resources.get_distribution("gurux_serial").version)
print("Streamworks software version " str(SWver))
except Exception:
#It's OK if this fails.
print("pkg_resources not found")
settings = GXSettings()
ret = settings.getParameters(args)
if ret != 0:
return
#There might be several notify messages in GBT.
self.notify = GXReplyData()
self.client = settings.client
self.translator = GXDLMSTranslator()
self.reply = GXByteBuffer()
settings.media.trace = settings.trace
print(settings.media)
#Start to listen events from the media.
settings.media.addListener(self)
#Set EOP for the media.
if settings.client.interfaceType == InterfaceType.HDLC:
settings.media.eop = 0x7e
try:
print("Press any key to close the application.")
#Open the connection.
settings.media.open()
#Wait input.
input()
print("Closing")
except (KeyboardInterrupt, SystemExit, Exception) as ex:
print(ex)
settings.media.close()
settings.media.removeListener(self)
def onError(self, sender, ex):
"""
Represents the method that will handle the error event of a Gurux
component.
sender : The source of the event.
ex : An Exception object that contains the event data.
"""
print("Error has occured. " str(ex))
@classmethod
def printData(cls, value, offset):
global OBIS, OBISmeterNow, OBISpowerNow, OBISpower1, OBISpower2, OBISpower3,OBISstring,OBISupdated
sb = ' ' * 2 * offset
if isinstance(value, list):
#print(sb "{")
offset = offset 1
#Print received data.
for it in value:
cls.printData(it, offset)
#print(it)
#print(sb "}")
#print ("OBISmeterNow=" str(OBISmeterNow))
#print ("OBISpowerNow=" str(OBISpowerNow))
#print ("OBISpower1=" str(OBISpower1))
#print ("OBISpower2=" str(OBISpower2))
#print ("OBISpower3=" str(OBISpower3))
#OBISstring=(OBISmeterNow "," OBISmeterAcc "," OBISpowerNow "," OBISpower1 "," OBISpower2 "," OBISpower3)
if OBISupdated==True: # Only run this after reboot when OBIS data is updated
UDPsubmit()
CSVupdate()
offset = offset - 1
elif isinstance(value, bytearray):
#Print value.
OBIS=str(GXCommon.toHex(value))
#print(sb str(OBIS))
else:
#Print value.
if OBIS=="01 01 01 08 00 FF ":
OBISmeterNow=float(str(value))*10 # Need to multiply for converting to Wh
OBISupdated=True
if OBIS=="01 01 01 07 00 FF ":
OBISpowerNow=str(value)
if OBIS=="01 01 15 07 00 FF ":
OBISpower1=str(value)
if OBIS=="01 01 29 07 00 FF ":
OBISpower2=str(value)
if OBIS=="01 01 3D 07 00 FF ":
OBISpower3=str(value)
#print(sb str(value))
def onReceived(self, sender, e):
"""Media component sends received data through this method.
sender : The source of the event.
e : Event arguments.
"""
#print("New data is received. " str(e))
#Data might come in fragments.
self.reply.set(e.data)
data = GXReplyData()
try:
if not self.client.getData(self.reply, data, self.notify):
#If all data is received.
if self.notify.complete:
if not self.notify.isMoreData():
#Show received data as XML.
try:
xml = self.translator.dataToXml(self.notify.data)
#print(xml)
#Print received data.
self.printData(self.notify.value, 0)
#Example is sending list of push messages in first parameter.
if isinstance(self.notify.value, list):
objects = self.client.parsePushObjects(self.notify.value[0])
#Remove first item because it's not needed anymore.
objects.pop(0)
Valueindex = 1
for obj, index in objects:
self.client.updateValue(obj, index, self.notify.value[Valueindex])
Valueindex = 1
#Print value
#print(str(obj.objectType) " " obj.logicalName " " str(index) ": " str(obj.getValues()[index - 1]))
#print("Server address:" str(self.notify.serverAddress) " Client Address:" str(self.notify.clientAddress))
except Exception:
self.reply.position = 0
xml = self.translator.messageToXml(self.reply)
#print(xml)
self.notify.clear()
self.reply.clear()
except Exception as ex:
#print(ex)
self.notify.clear()
self.reply.clear()
def onMediaStateChange(self, sender, e):
"""Media component sends notification, when its state changes.
sender : The source of the event.
e : Event arguments.
"""
print("Media state changed. " str(e))
def onTrace(self, sender, e):
"""Called when the Media is sending or receiving data.
sender : The source of the event.
e : Event arguments.
"""
print("trace:" str(e))
def onPropertyChanged(self, sender, e):
"""
Event is raised when a property is changed on a component.
sender : The source of the event.
e : Event arguments.
"""
print("Property {!r} has hanged.".format(str(e)))
if __name__ == '__main__':
sampleclient(sys.argv)
'''
INSTALL GURUX LIBRARY:
pip install gurux-common
pip install gurux-serial
pip install gurux-net
pip install gurux-dlms
COPY THIS PYTHON SCRIPT TO:
Gurux.DLMS.Python/Gurux.DLMS.Push.Listener.Example.python
MAKE THE APP AUTORUN AT BOOT
sudo nano /home/pi/.bashrc
INSERT AS LAST LINE:
python Gurux.DLMS.Python/Gurux.DLMS.Push.Listener.Example.python/meter.py -S '/dev/ttyS0:2400:8None1' -A "YourPersonalKeyA" -B "YourPersonalKeyB"
Ctrl X
'''