This script is special in fashion that it does not just count pulses, it actually calculates your current electric power usage in watts, and pretty accurately too - at least for usual electric meters which blink once per Wh.
It uses new gpiozero library which is a lot better protection against false triggers/cable noise than standard wiringPi. This should help you if you did get discrepancy in pulse counts/pulses counting twice, etc.
It uses interrupts so it should never miss pulses unlike polling, and won't use CPU unnecessarily.
Installation instructions: please make sure you've done them before complaining that script doesn't work
- install gpiozero by sudo apt-get install python-gpiozero
- if you haven't already, create a "Dummy hardware" in Domoticz
- on Dummy hardware click "Create Virtual Sensors"
- create a sensor with any name you like and type "Electric: Instant + Counter"
- create a sensor type "Gas"
- go to "Devices" menu. Write down the "Idx" column for each counters you just created. Edit the script to put these numbers in GAS_IDX= and ELEC_IDX= at the script beginning
- Set GAS_GPIO= and ELEC_GPIO= at script beginning to actual GPIO numbers your sensors connected to
Code: Select all
#!/usr/bin/env python
import time
import json
import urllib2
import threading
import logging
import logging.handlers
import os
from gpiozero import DigitalInputDevice
GET_URL = 'http://127.0.0.1:8080/json.htm?type=devices&rid=%d'
SET_URL = 'http://127.0.0.1:8080/json.htm?type=command¶m=udevice&idx=%d&svalue=%d'
GAS_DELTA = 0
GAS_IDX =
GAS_GPIO =
GAS_COUNTER_LOCK = threading.Lock()
ELEC_DELTA = 0
ELEC_IDX =
ELEC_GPIO =
ELEC_COUNTER_LOCK = threading.Lock()
ELEC_LAST_TIME = 0
ELEC_POST_TIME = 0
def gas_intr():
global GAS_DELTA
with GAS_COUNTER_LOCK:
GAS_DELTA += 10
logging.debug( 'Gas counter tick: %d' % GAS_DELTA )
def elec_intr():
tme = time.time()
global ELEC_DELTA
global ELEC_LAST_TIME
global ELEC_POST_TIME
with ELEC_COUNTER_LOCK:
ELEC_LAST_TIME = tme
if ELEC_POST_TIME == 0:
ELEC_POST_TIME = ELEC_LAST_TIME
else:
ELEC_DELTA += 1
logging.debug( 'Electricity counter tick: %d' % ELEC_DELTA )
def main():
global GAS_DELTA
global GAS_COUNTER
global ELEC_DELTA
global ELEC_LAST_TIME
global ELEC_POST_TIME
global ELEC_COUNTER
syslog = logging.handlers.SysLogHandler(address='/dev/log', facility='local1')
syslog.setFormatter(logging.Formatter('local_sensors.py: %(levelname)s %(message)s'))
logging.getLogger().addHandler(syslog)
logging.getLogger().setLevel(logging.INFO)
while True:
try:
res = json.load(urllib2.urlopen(GET_URL % GAS_IDX))
if res['status'] != 'OK':
raise Exception('Domoticz json error')
break
except Exception as e:
logging.warning( e )
time.sleep(30.0)
GAS_COUNTER = int(float(res['result'][0]['Data']) * 1000)
# GAS_COUNTER = <Your initial count here * 1000, don't forget to remove after Domoticz updated!>
logging.info( 'Current gas counter is: %d' % GAS_COUNTER )
while True:
try:
res = json.load(urllib2.urlopen(GET_URL % ELEC_IDX))
if res['status'] != 'OK':
raise Exception('Domoticz json error')
break
except Exception as e:
logging.warning( e )
time.sleep(30.0)
ELEC_COUNTER = int(float(res['result'][0]['Data'][:-4]) * 1000)
# ELEC_COUNTER = <Your initial count here * 1000, don't forget to remove after Domoticz updated!>
logging.info( 'Current electricity counter is: %d' % ELEC_COUNTER )
elecSensor = DigitalInputDevice(ELEC_GPIO, pull_up=True)
elecSensor.when_deactivated = elec_intr
gasSensor = DigitalInputDevice(GAS_GPIO, pull_up=True)
gasSensor.when_activated = gas_intr
os.nice(-20)
logging.info('Polling loop starting')
while True:
time.sleep(60)
GAS_DELTA_POST = GAS_DELTA
if GAS_DELTA_POST != 0:
GAS_COUNTER += GAS_DELTA_POST
with GAS_COUNTER_LOCK:
GAS_DELTA -= GAS_DELTA_POST
try:
res = json.load(urllib2.urlopen(SET_URL % (GAS_IDX, GAS_COUNTER)))
if res['status'] != 'OK':
raise Exception('Domoticz json error')
logging.info( 'Gas counter %d' % GAS_COUNTER )
except Exception as e:
logging.warning( e )
with ELEC_COUNTER_LOCK:
if ELEC_LAST_TIME > ELEC_POST_TIME:
ELEC_LOAD = ELEC_DELTA * 3600 / ( ELEC_LAST_TIME - ELEC_POST_TIME )
else:
ELEC_LOAD = 0
ELEC_COUNTER += ELEC_DELTA
ELEC_DELTA = 0
ELEC_POST_TIME = ELEC_LAST_TIME
if ELEC_LOAD != 0:
try:
res = json.load(urllib2.urlopen((SET_URL+';%d') % (ELEC_IDX, int(ELEC_LOAD), ELEC_COUNTER)))
if res['status'] != 'OK':
raise Exception('Domoticz json error')
logging.info('Elec load %.2f counter %d' % (ELEC_LOAD, ELEC_COUNTER) )
except Exception as e:
logging.warning( e )
if __name__=="__main__":
main()
I recommend you run script on root user since it will try to raise its own priority to prevent losing pulses if CPU is busy by some other process. If you don't want to run as root, you can remove os.nice line.
Script uses syslog on local1 facility, so you may change this for dedicated file or raise logging level to DEBUG if you want to debug when pulses are counters.
The hardware I've used:
Optical sensor module with photodiode (I recommend using photodiode and not photoresistor since diode is better for quick pulses) - http://www.ebay.co.uk/itm/371350785169? ... EBIDX%3AIT. Note that it signals by pulling line *low* (so its Vcc output when there is *no light detected*, and drops to zero when light detected). Please make sure you tune it properly, this is critical! First rotate potentiometer slowly to find where it just switches to "light detected" always, even when meter is not blinking and output stays steady low, note the screw position. Then rotate potentiometer until it *always no light detected" and it stops seeing the pulses, note the screw position. Then set screw roughly *in the middle between these two extremes*. This will give you most reliable pulse detection even if meter blinks very quickly. This sensor outputs at same voltage as Vcc, so make sure you power it from 3.3V pin, don't use 5V power since this will damage your GPIO!.
For gas meter I detected a magnet which was located *after* the last digit disk. I've used this reed switch - http://uk.farnell.com/hamlin/miti-3v1-6 ... 7-00001003. If you want to use other switch, note that you better choose one which is sensitive enough (lower "AT activation" number, the better). You simply connect this to connect GPIO to ground, as with usual switch.
Note that I strongly recommend to use around 2K pullup resistors just around start of your cable between each GPIO signal line and 3.3V. This will greatly reduce interference and possibility of false ticks. But I also recommend to use shielded cable between Pi and sensor - for example Cat7 Ethernet is a good and cheap choice (e.g. you could just solder RJ45 sockets at sensor and to 40Pin cable near Pi and use store-bought Cat7 patch cables to connect things neatly).
This gives very cheap but very functional setup to keep track of your utility cost even if you don't have smart meters.