water-counter/leakdetect.py

88 lines
2.7 KiB
Python
Executable File

#!/usr/bin/env python
# Script to detect leakages in water consumption by reading the timestamps
# and counter values from a database.
# The script assumes that during a period of some hours of a day should
# be absolute no water consumption.
# The script detects the longest pause of water consumption. If its duration
# is below a given threshold, then a warning message is printed.
import os
import csv
import rrdtool as rrd
from datetime import datetime
import time
from math import *
# Path to RRD with counter values
count_rrd = "{0}/water.rrd".format(os.path.dirname(os.path.abspath(__file__)))
# Alternative: Path to output of rrdtool fetch
count_fetch = "{0}/water.fetch.txt".format(os.path.dirname(os.path.abspath(__file__)))
# Length of the minimum duration without water consumption in seconds
min_pause = 3 * 60 * 60 # 3 hours
# Verbose output
verbose = False
# Read the rrd specified in count_rrd.
# Returns array of (timestamp, counter).
def read_rrd():
result = []
((start, stop, step), head, data) = rrd.fetch(count_rrd, 'LAST', '-s', 'now - 1 day', '-e', 'now')
t = start
for row in data:
if row[0]:
result.append((t, row[0]))
t += step
return result
# Read the text file specified in count_fetch.
# The text file is the output of command 'rrdtool fetch'.
# Returns array of (timestamp, counter).
def read_fetch_output():
result = []
with open(count_fetch, 'r') as f:
reader = csv.reader(f, delimiter=' ')
for row in reader:
if len(row) == 3:
value = int(row[0][:-1]), float(row[1])
if not isnan(value[1]):
result.append(value)
return result
# Detect all pauses which last longer than min_pause.
# Param data: array of (timestamp, counter)
# Returns array of dictionaries {start, duration}.
def detect_pauses(data):
result = []
(start_time, start_counter) = data[0]
(end_time, end_counter) = data[-1]
data.append((time.time(), end_counter + 1)) # ensure that counter increments
for (ts, counter) in data:
if counter > start_counter:
duration = ts - start_time
if duration >= min_pause:
result.append({'start' : start_time, 'duration' : duration})
start_time = ts
start_counter = counter
return result
# MAIN
def main():
#counter = read_fetch_output()
counter = read_rrd()
if len(counter) > 0:
pauses = detect_pauses(counter)
if verbose:
for p in pauses:
print("Pause starting at {0:%Y-%m-%d %H:%M:%S}: {1:.1f} hours"
.format(datetime.fromtimestamp(p['start']), p['duration']/3600.0))
if len(pauses) == 0:
print("Possible leak detected! There is no break of at least {0} hours."
.format(min_pause/3600.0))
if __name__ == '__main__':
main()