1
1
# coding=utf-8
2
2
import copy
3
+ import time
3
4
from collections import OrderedDict
4
- from datetime import datetime
5
- from math import sqrt
5
+ from mycodo .utils .lockfile import LockFile
6
6
7
7
from mycodo .inputs .base_input import AbstractInput
8
8
19
19
'unit' : 'W' ,
20
20
'name' : f'CT{ ct } '
21
21
}
22
+ measurements_dict [channel ] = {
23
+ 'measurement' : 'energy' ,
24
+ 'unit' : 'kWh' ,
25
+ 'name' : f'CT{ ct } Total'
26
+ }
22
27
measurements_dict [channel + 1 ] = {
23
28
'measurement' : 'electrical_current' ,
24
29
'unit' : 'A' ,
29
34
'unit' : 'unitless' ,
30
35
'name' : f'CT{ ct } '
31
36
}
32
- channel += 3
37
+ channel += 4
33
38
34
39
# Input information
35
40
INPUT_INFORMATION = {
38
43
'input_name' : 'RPi 6-Channel Power Monitor (v0.4.0)' ,
39
44
'input_name_short' : 'RPi Power Monitor v0.4.0' ,
40
45
'input_library' : 'rpi-power-monitor' ,
41
- 'measurements_name' : 'AC Voltage, Power, Current, Power Factor' ,
46
+ 'measurements_name' : 'AC Voltage, Power, Energy, Current, Power Factor' ,
42
47
'measurements_dict' : measurements_dict ,
43
48
'url_manufacturer' : 'https://github.com/David00/rpi-power-monitor' ,
44
49
'url_product_purchase' : 'https://power-monitor.dalbrecht.tech/' ,
57
62
('pip-pypi' , 'rpi_power_monitor' , 'git+https://github.com/David00/rpi-power-monitor.git@eeb3143fba6452de916407ae94a0a6e8834a7d67' )
58
63
],
59
64
65
+ 'custom_commands' : [
66
+ {
67
+ 'type' : 'message' ,
68
+ 'default_value' : """Clear the running kWh totals."""
69
+ },
70
+ {
71
+ 'id' : 'channel_to_clear' ,
72
+ 'type' : 'select' ,
73
+ 'default_value' : '1' ,
74
+ 'options_select' : [
75
+ ('all' , 'All Channels' ),
76
+ ('1' , 'Channel 1' ),
77
+ ('2' , 'Channel 2' ),
78
+ ('3' , 'Channel 3' ),
79
+ ('4' , 'Channel 4' ),
80
+ ('5' , 'Channel 5' ),
81
+ ('6' , 'Channel 6' )
82
+
83
+ ],
84
+ 'name' : 'Channel to Clear' ,
85
+ 'phrase' : 'The channel(s) to clear the kWh total and start back at 0.'
86
+ },
87
+ {
88
+ 'id' : 'clear_channel' ,
89
+ 'type' : 'button' ,
90
+ 'wait_for_return' : True ,
91
+ 'name' : 'Clear kWh Total'
92
+ }
93
+ ],
94
+
60
95
'custom_options' : [
96
+ {
97
+ 'id' : 'kwh_measure_period_sec' ,
98
+ 'type' : 'integer' ,
99
+ 'default_value' : 5 ,
100
+ 'required' : True ,
101
+ 'name' : 'Period (Seconds) for kWh Measuring' ,
102
+ 'phrase' : 'How often to acquire measurements to calculate kWh'
103
+ },
61
104
{
62
105
'id' : 'grid_voltage' ,
63
106
'type' : 'float' ,
@@ -148,6 +191,7 @@ def __init__(self, input_dev, testing=False):
148
191
149
192
self .sensor = None
150
193
194
+ self .kwh_measure_period_sec = None
151
195
self .grid_voltage = None
152
196
self .transformer_voltage = None
153
197
self .ac_frequency = None
@@ -160,6 +204,10 @@ def __init__(self, input_dev, testing=False):
160
204
self .ct6_accuracy_calibration = None
161
205
self .ac_accuracy_calibration = None
162
206
207
+ self .timer_kwh_measure = 0
208
+ self .kwh_saved = None
209
+ self .lock_file = '/var/lock/power_kwh.lock'
210
+
163
211
if not testing :
164
212
self .setup_custom_options (
165
213
INPUT_INFORMATION ['custom_options' ], input_dev )
@@ -171,6 +219,18 @@ def initialize(self):
171
219
logger .setLevel (logging .DEBUG )
172
220
ch .setLevel (logging .DEBUG )
173
221
222
+ self .kwh_saved = self .get_custom_option ("kwh_saved" )
223
+
224
+ if self .kwh_saved is None :
225
+ self .kwh_saved = {
226
+ 1 : 0 ,
227
+ 2 : 0 ,
228
+ 3 : 0 ,
229
+ 4 : 0 ,
230
+ 5 : 0 ,
231
+ 6 : 0
232
+ }
233
+
174
234
config = {
175
235
'general' : {
176
236
'name' : 'Power-Monitor'
@@ -270,6 +330,32 @@ def initialize(self):
270
330
271
331
self .sensor = RPiPowerMonitor (config = config )
272
332
333
+ def listener (self ):
334
+ while self .running :
335
+ if time .time () - self .timer_kwh_measure >= self .kwh_measure_period_sec :
336
+ while time .time () - self .timer_kwh_measure >= self .kwh_measure_period_sec :
337
+ self .timer_kwh_measure += self .kwh_measure_period_sec
338
+
339
+ results = self .sensor .get_power_measurements (duration = 0.5 )
340
+
341
+ per_hour_fraction = self .kwh_measure_period_sec / 3600
342
+
343
+ lf = LockFile ()
344
+ if lf .lock_acquire (self .lock_file , timeout = 10 ):
345
+ try :
346
+ chan = 1
347
+ for measure_channel in range (1 , 7 ):
348
+ if self .is_enabled (measure_channel - 1 ) and measure_channel in results :
349
+ kilo_watts = results [measure_channel ]['power' ] / 1000
350
+ kwh_calculated = kilo_watts * per_hour_fraction
351
+ self .kwh_saved [measure_channel ] += kwh_calculated
352
+ chan += 4
353
+ except :
354
+ self .logger .exception ("acquiring measurements" )
355
+ finally :
356
+ lf .lock_release (self .lock_file )
357
+ time .sleep (0.01 )
358
+
273
359
def get_measurement (self ):
274
360
self .return_dict = copy .deepcopy (measurements_dict )
275
361
@@ -282,8 +368,44 @@ def get_measurement(self):
282
368
for measure_channel in range (1 , 7 ):
283
369
if self .is_enabled (measure_channel - 1 ) and measure_channel in results :
284
370
self .value_set (chan , results [measure_channel ]['power' ])
285
- self .value_set (chan + 1 , results [measure_channel ]['current' ])
286
- self .value_set (chan + 2 , results [measure_channel ]['pf' ])
287
- chan += 3
371
+ self .value_set (chan + 2 , results [measure_channel ]['current' ])
372
+ self .value_set (chan + 3 , results [measure_channel ]['pf' ])
373
+ chan += 4
374
+
375
+ # Save kWh calculations to DB
376
+ lf = LockFile ()
377
+ if lf .lock_acquire (self .lock_file , timeout = 10 ):
378
+ try :
379
+ self .set_custom_option ("kwh_saved" , self .kwh_saved )
380
+ except :
381
+ self .logger .exception ("acquiring measurements" )
382
+ finally :
383
+ lf .lock_release (self .lock_file )
288
384
289
385
return self .return_dict
386
+
387
+ def clear_channel (self , args_dict ):
388
+ if 'channel_to_clear' not in args_dict or not args_dict ['channel_to_clear' ]:
389
+ self .logger .error ("channel_to_clear is required" )
390
+ return
391
+ try :
392
+ lf = LockFile ()
393
+ if lf .lock_acquire (self .lock_file , timeout = 10 ):
394
+ try :
395
+ if args_dict ['channel_to_clear' ] == 'all' :
396
+ self .kwh_saved [1 ] = 0
397
+ self .kwh_saved [2 ] = 0
398
+ self .kwh_saved [3 ] = 0
399
+ self .kwh_saved [4 ] = 0
400
+ self .kwh_saved [5 ] = 0
401
+ self .kwh_saved [6 ] = 0
402
+ else :
403
+ self .kwh_saved [int (args_dict ['channel_to_clear' ])] = 0
404
+
405
+ self .set_custom_option ("kwh_saved" , self .kwh_saved )
406
+ except :
407
+ self .logger .exception (f"Clearing kWH for channel: { args_dict ['channel_to_clear' ]} " )
408
+ finally :
409
+ lf .lock_release (self .lock_file )
410
+ except Exception as err :
411
+ self .logger .error (f"Error clearing kWh for channel { args_dict ['channel_to_clear' ]} : { err } " )
0 commit comments