2016-08-01 17:53:33 +02:00
|
|
|
#!/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
|
2017-03-04 19:34:37 +01:00
|
|
|
import time
|
2016-08-01 17:53:33 +02:00
|
|
|
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
|
2016-09-17 11:41:57 +02:00
|
|
|
verbose = False
|
2016-08-01 17:53:33 +02:00
|
|
|
|
|
|
|
# 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]
|
2017-03-04 19:34:37 +01:00
|
|
|
data.append((time.time(), end_counter + 1)) # ensure that counter increments
|
2016-08-01 17:53:33 +02:00
|
|
|
for (ts, counter) in data:
|
|
|
|
if counter > start_counter:
|
|
|
|
duration = ts - start_time
|
2016-10-30 18:45:07 +01:00
|
|
|
if duration >= min_pause:
|
2016-08-01 17:53:33 +02:00
|
|
|
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()
|
|
|
|
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()
|