diff --git a/HTML/NodeList.html b/HTML/NodeList.html new file mode 100644 index 0000000..17630bf --- /dev/null +++ b/HTML/NodeList.html @@ -0,0 +1,41 @@ + + +
+
+
+
+ +
+ +
+ +
+

<%= node.vanityname %>

+
+ +
+

Flow Rate

+

<%= round(channels["flowmonitor.gpm_flow"].value * 100) / 100 %> GPM

+
+ +
+

Run Status

+

<%= channels["flowmonitor.run_status"].value %>

+
+
diff --git a/README.md b/README.md index 313ac08..f5e4e1a 100644 --- a/README.md +++ b/README.md @@ -1 +1,22 @@ -# flow-monitor \ No newline at end of file +# flow-monitor + +## To set up sqlite database +``` +sqlite3 flow-monitor.db + +CREATE TABLE flow_data ( + id integer PRIMARY KEY, + gal_totalizer_value float, + bbl_totalizer_value float, + last_measured_timestamp integer +); + +INSERT INTO flow_data (id, gal_totalizer_value, bbl_totalizer_value, last_measured_timestamp) VALUES (1, 0.0, 0.0, 0); +``` + +## To reset the sqlite database +``` +sqlite3 flow-monitor.db + +UPDATE flow_data SET gal_totalizer_value=0.0, bbl_totalizer_value=0.0, last_measured_timestamp=0 WHERE id=1; +``` diff --git a/config.txt b/config.txt new file mode 100644 index 0000000..eec9092 --- /dev/null +++ b/config.txt @@ -0,0 +1,10 @@ +{ + +"driverFileName":"flow-monitor.py", +"deviceName":"flowmonitor", +"driverId":"0140", +"releaseVersion":"1", +"files": { + "file1":"flow-monitor.py"} + +} diff --git a/flow-monitor.db b/flow-monitor.db new file mode 100644 index 0000000..6e32248 Binary files /dev/null and b/flow-monitor.db differ diff --git a/flow-monitor.py b/flow-monitor.py new file mode 100644 index 0000000..90f428c --- /dev/null +++ b/flow-monitor.py @@ -0,0 +1,170 @@ +"""Driver for connecting Flow Monitor to Meshify.""" +import threading +from device_base import deviceBase +import time +from datetime import datetime +import sqlite3 + + +class Channel: + """Meshify channel structure.""" + + def __init__(self, meshify_name, senddelta_value, senddelta_time): + """Initialize the channel with variables.""" + self.meshify_name = meshify_name + self.senddelta_time = senddelta_time + self.senddelta_value = senddelta_value + + self.last_sent_value = None + self.last_sent_timestamp = 0 + + def check_if_send_needed(self, value, timestamp): + """Check to see if the value needs to be pushed.""" + if self.last_sent_value is None or self.last_sent_timestamp == 0: + return True + + if abs(value - self.last_sent_value) > self.senddelta_value: + return True + + if (timestamp - self.last_sent_timestamp) > self.senddelta_time: + return True + + return False + + def update(self, last_sent_value, last_sent_timestamp): + """Update values after a push.""" + self.last_sent_value = last_sent_value + self.last_sent_timestamp = last_sent_timestamp + + +def scale(raw_val, raw_min, raw_max, eu_min, eu_max): + """Scale a raw value.""" + m = (eu_max - eu_min) / (raw_max - raw_min) + b = eu_max - (m * raw_max) + return m * raw_val + b + + +def is_today(tstamp): + """Check if a given timestamp belongs to the current date.""" + midnight_today = datetime.today().replace(hour=0, minute=0, second=0, microsecond=0) + midnight_ts = (midnight_today - datetime(1970, 1, 1)).total_seconds() + return tstamp >= midnight_ts + + +class start(threading.Thread, deviceBase): + """Start class required for driver.""" + + def __init__(self, name=None, number=None, mac=None, Q=None, mcu=None, + companyId=None, offset=None, mqtt=None, Nodes=None): + """Initalize the driver.""" + threading.Thread.__init__(self) + deviceBase.__init__(self, name=name, number=number, mac=mac, Q=Q, + mcu=mcu, companyId=companyId, offset=offset, + mqtt=mqtt, Nodes=Nodes) + + self.daemon = True + self.version = "1" + self.finished = threading.Event() + threading.Thread.start(self) + + # this is a required function for all drivers + # its goal is to upload some piece of data + # about your device so it can be seen on the web + def register(self): + """Register the driver.""" + self.channels["status"]["last_value"] = "" + + + def run(self): + """Run the driver.""" + + # Configuration Parameters + total_time_store_delta = 60 # seconds + flow_time_store_delta = 60 # seconds + + raw_min = 3.89 + raw_max = 19.54 + + gpm_min = 0.0 + gpm_max = 600.0 + + gal_per_bbl = 42.0 + + galtotal_ch = Channel('gal_total', 100.0, total_time_store_delta) + bbltotal_ch = Channel('bbl_total', galtotal_ch.senddelta_value/gal_per_bbl, total_time_store_delta) + + gpmflow_ch = Channel('gpm_flow', 10.0, flow_time_store_delta) + bpdflow_ch = Channel('bpd_flow', gpmflow_ch.senddelta_value/gal_per_bbl, total_time_store_delta) + + runstatus_ch = Channel('run_status', 0.5, 600) + + last_measured_timestamp = time.time() + conn = sqlite3.connect('/root/python_firmware/drivers/flow-monitor.db') + c = conn.cursor() + c.execute('SELECT * FROM flow_data WHERE id = 1') + stored_data = c.fetchone() + gal_totalizer_value = stored_data[1] + bbl_totalizer_value = stored_data[2] + last_measured_timestamp = stored_data[3] + if not is_today(last_measured_timestamp): + gal_totalizer_value = 0 + bbl_totalizer_value = 0 + last_measured_timestamp = time.time() + + wait_loops = 0 + while wait_loops < 15: + print("Waiting to start driver in {} seconds".format(15 - wait_loops)) + wait_loops += 1 + time.sleep(1) + + while True: + try: + mcu_status = self.mcu.getDict() + print(mcu_status) + cloop_val = float(mcu_status['cloop']) + + din1_val = 1 if mcu_status['din1'] == 'On' else 0 + gpm_val = scale(cloop_val, raw_min, raw_max, gpm_min, gpm_max) + if gpm_val < 0: + gpm_val = 0 + + bpd_val = (gpm_val / gal_per_bbl) * 60.0 * 24.0 + + now = time.time() + time_diff = now - last_measured_timestamp + gal_flow_delta = (time_diff / 60.0) * gpm_val + bbl_flow_delta = (time_diff / 60.0) * (1.0 / 60.0) * (1.0 / 24.0) * bpd_val + + gal_totalizer_value += gal_flow_delta + bbl_totalizer_value += bbl_flow_delta + last_measured_timestamp = now + + c.execute('UPDATE flow_data SET gal_totalizer_value=?, bbl_totalizer_value=?, last_measured_timestamp=?', + (gal_totalizer_value, bbl_totalizer_value, last_measured_timestamp)) + conn.commit() + + print('gpm: {}, bpd: {}, gal: {}, bbl:{}'.format(gpm_val, bpd_val, gal_totalizer_value, bbl_totalizer_value)) + + if galtotal_ch.check_if_send_needed(gal_totalizer_value, now): + self.sendtodb(galtotal_ch.meshify_name, gal_totalizer_value, 0) + galtotal_ch.update(gal_totalizer_value, now) + + if bbltotal_ch.check_if_send_needed(bbl_totalizer_value, now): + self.sendtodb(bbltotal_ch.meshify_name, bbl_totalizer_value, 0) + bbltotal_ch.update(bbl_totalizer_value, now) + + if gpmflow_ch.check_if_send_needed(gpm_val, now): + self.sendtodb(gpmflow_ch.meshify_name, gpm_val, 0) + gpmflow_ch.update(gpm_val, now) + + if bpdflow_ch.check_if_send_needed(bpd_val, now): + self.sendtodb(bpdflow_ch.meshify_name, bpd_val, 0) + bpdflow_ch.update(bpd_val, now) + + if runstatus_ch.check_if_send_needed(din1_val, now): + self.sendtodb(runstatus_ch.meshify_name, din1_val, 0) + runstatus_ch.update(din1_val, now) + + except Exception as e: + print("problem in the driver: {}".format(e)) + time.sleep(5) diff --git a/test_totalizer.py b/test_totalizer.py new file mode 100644 index 0000000..917500f --- /dev/null +++ b/test_totalizer.py @@ -0,0 +1,150 @@ +"""Driver for testing Flow Monitor to Meshify.""" +import time +import sqlite3 +from datetime import datetime + + +class Channel: + """Meshify channel structure.""" + + def __init__(self, meshify_name, senddelta_value, senddelta_time): + """Initialize the channel with variables.""" + self.meshify_name = meshify_name + self.senddelta_time = senddelta_time + self.senddelta_value = senddelta_value + + self.last_sent_value = None + self.last_sent_timestamp = 0 + + def check_if_send_needed(self, value, timestamp): + """Check to see if the value needs to be pushed.""" + if self.last_sent_value is None or self.last_sent_timestamp == 0: + return True + + if abs(value - self.last_sent_value) > self.senddelta_value: + return True + + if (timestamp - self.last_sent_timestamp) > self.senddelta_time: + return True + + return False + + def update(self, last_sent_value, last_sent_timestamp): + """Update values after a push.""" + self.last_sent_value = last_sent_value + self.last_sent_timestamp = last_sent_timestamp + + +def scale(raw_val, raw_min, raw_max, eu_min, eu_max): + """Scale a raw value.""" + m = (eu_max - eu_min) / (raw_max - raw_min) + b = eu_max - (m * raw_max) + return m * raw_val + b + + +def is_today(tstamp): + """Check if a given timestamp belongs to the current date.""" + midnight_today = datetime.today().replace(hour=0, minute=0, second=0, microsecond=0) + midnight_ts = (midnight_today - datetime(1970, 1, 1)).total_seconds() + return tstamp >= midnight_ts + + +def loop(): + """Run the driver.""" + # Configuration Parameters + total_time_store_delta = 600 # seconds + flow_time_store_delta = 600 # seconds + + raw_min = 3.89 + raw_max = 19.54 + + gpm_min = 0.0 + gpm_max = 600.0 + + gal_per_bbl = 42.0 + + galtotal_ch = Channel('gal_total', 100.0, total_time_store_delta) + bbltotal_ch = Channel('bbl_total', galtotal_ch.senddelta_value/gal_per_bbl, total_time_store_delta) + + gpmflow_ch = Channel('gpm_flow', 10.0, flow_time_store_delta) + bpdflow_ch = Channel('bpd_flow', gpmflow_ch.senddelta_value/gal_per_bbl, total_time_store_delta) + + runstatus_ch = Channel('run_status', 0.5, 600) + + last_measured_timestamp = time.time() + conn = sqlite3.connect('flow-monitor.db') + c = conn.cursor() + + conn.commit() + c.execute('SELECT * FROM flow_data WHERE id = 1') + stored_data = c.fetchone() + gal_totalizer_value = stored_data[1] + bbl_totalizer_value = stored_data[2] + last_measured_timestamp = stored_data[3] + if not is_today(last_measured_timestamp): + gal_totalizer_value = 0 + bbl_totalizer_value = 0 + last_measured_timestamp = time.time() + + wait_loops = 0 + while wait_loops < 15: + print("Waiting to start driver in {} seconds".format(15 - wait_loops)) + wait_loops += 1 + time.sleep(1) + + while True: + try: + mcu_status = {} + mcu_status['cloop'] = '12.0' + mcu_status['din1'] = 'Off' + print(mcu_status) + cloop_val = float(mcu_status['cloop']) + + din1_val = 1 if mcu_status['din1'] == 'On' else 0 + gpm_val = scale(cloop_val, raw_min, raw_max, gpm_min, gpm_max) + if gpm_val < 0: + gpm_val = 0 + + bpd_val = (gpm_val / gal_per_bbl) * 60.0 * 24.0 + + now = time.time() + time_diff = now - last_measured_timestamp + gal_flow_delta = (time_diff / 60.0) * gpm_val + bbl_flow_delta = (time_diff / 60.0) * (1.0 / 60.0) * (1.0 / 24.0) * bpd_val + + gal_totalizer_value += gal_flow_delta + bbl_totalizer_value += bbl_flow_delta + last_measured_timestamp = now + + c.execute('UPDATE flow_data SET gal_totalizer_value=?, bbl_totalizer_value=?, last_measured_timestamp=?', + (gal_totalizer_value, bbl_totalizer_value, last_measured_timestamp)) + conn.commit() + + print('gpm: {}, bpd: {}, gal: {}, bbl:{}'.format(gpm_val, bpd_val, gal_totalizer_value, bbl_totalizer_value)) + + # if galtotal_ch.check_if_send_needed(gal_totalizer_value, now): + # self.sendtodb(galtotal_ch.meshify_name, gal_totalizer_value, 0) + # galtotal_ch.update(gal_totalizer_value, now) + # + # if bbltotal_ch.check_if_send_needed(bbl_totalizer_value, now): + # self.sendtodb(bbltotal_ch.meshify_name, bbl_totalizer_value, 0) + # bbltotal_ch.update(bbl_totalizer_value, now) + # + # if gpmflow_ch.check_if_send_needed(gpm_val, now): + # self.sendtodb(gpmflow_ch.meshify_name, gpm_val, 0) + # gpmflow_ch.update(gpm_val, now) + # + # if bpdflow_ch.check_if_send_needed(bpd_val, now): + # self.sendtodb(bpdflow_ch.meshify_name, bpd_val, 0) + # bpdflow_ch.update(bpd_val, now) + # + # if runstatus_ch.check_if_send_needed(din1_val, now): + # self.sendtodb(runstatus_ch.meshify_name, din1_val, 0) + # runstatus_ch.update(din1_val, now) + + except Exception as e: + print("problem in the driver: {}".format(e)) + time.sleep(5) + + +loop()