Skip to content

Commit 99a6e77

Browse files
committed
Add kWh to power input measurements
1 parent 9c4a97c commit 99a6e77

File tree

1 file changed

+129
-7
lines changed

1 file changed

+129
-7
lines changed

mycodo/inputs/power_monitor_rpi_2.py

+129-7
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# coding=utf-8
22
import copy
3+
import time
34
from collections import OrderedDict
4-
from datetime import datetime
5-
from math import sqrt
5+
from mycodo.utils.lockfile import LockFile
66

77
from mycodo.inputs.base_input import AbstractInput
88

@@ -19,6 +19,11 @@
1919
'unit': 'W',
2020
'name': f'CT{ct}'
2121
}
22+
measurements_dict[channel] = {
23+
'measurement': 'energy',
24+
'unit': 'kWh',
25+
'name': f'CT{ct} Total'
26+
}
2227
measurements_dict[channel + 1] = {
2328
'measurement': 'electrical_current',
2429
'unit': 'A',
@@ -29,7 +34,7 @@
2934
'unit': 'unitless',
3035
'name': f'CT{ct}'
3136
}
32-
channel += 3
37+
channel += 4
3338

3439
# Input information
3540
INPUT_INFORMATION = {
@@ -38,7 +43,7 @@
3843
'input_name': 'RPi 6-Channel Power Monitor (v0.4.0)',
3944
'input_name_short': 'RPi Power Monitor v0.4.0',
4045
'input_library': 'rpi-power-monitor',
41-
'measurements_name': 'AC Voltage, Power, Current, Power Factor',
46+
'measurements_name': 'AC Voltage, Power, Energy, Current, Power Factor',
4247
'measurements_dict': measurements_dict,
4348
'url_manufacturer': 'https://github.com/David00/rpi-power-monitor',
4449
'url_product_purchase': 'https://power-monitor.dalbrecht.tech/',
@@ -57,7 +62,45 @@
5762
('pip-pypi', 'rpi_power_monitor', 'git+https://github.com/David00/rpi-power-monitor.git@eeb3143fba6452de916407ae94a0a6e8834a7d67')
5863
],
5964

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+
6095
'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+
},
61104
{
62105
'id': 'grid_voltage',
63106
'type': 'float',
@@ -148,6 +191,7 @@ def __init__(self, input_dev, testing=False):
148191

149192
self.sensor = None
150193

194+
self.kwh_measure_period_sec = None
151195
self.grid_voltage = None
152196
self.transformer_voltage = None
153197
self.ac_frequency = None
@@ -160,6 +204,10 @@ def __init__(self, input_dev, testing=False):
160204
self.ct6_accuracy_calibration = None
161205
self.ac_accuracy_calibration = None
162206

207+
self.timer_kwh_measure = 0
208+
self.kwh_saved = None
209+
self.lock_file = '/var/lock/power_kwh.lock'
210+
163211
if not testing:
164212
self.setup_custom_options(
165213
INPUT_INFORMATION['custom_options'], input_dev)
@@ -171,6 +219,18 @@ def initialize(self):
171219
logger.setLevel(logging.DEBUG)
172220
ch.setLevel(logging.DEBUG)
173221

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+
174234
config = {
175235
'general': {
176236
'name': 'Power-Monitor'
@@ -270,6 +330,32 @@ def initialize(self):
270330

271331
self.sensor = RPiPowerMonitor(config=config)
272332

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+
273359
def get_measurement(self):
274360
self.return_dict = copy.deepcopy(measurements_dict)
275361

@@ -282,8 +368,44 @@ def get_measurement(self):
282368
for measure_channel in range(1, 7):
283369
if self.is_enabled(measure_channel - 1) and measure_channel in results:
284370
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)
288384

289385
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

Comments
 (0)