From 8ed2c1510cc7790b2bb8e7b0748e9d3721b8de6a Mon Sep 17 00:00:00 2001 From: Nico Melone Date: Wed, 5 Oct 2022 12:30:53 -0500 Subject: [PATCH] added meshify converted drivers --- .DS_Store | Bin 8196 -> 10244 bytes Thingsboard Gateway/installtbg.sh | 50 + meshifyDrivers/.DS_Store | Bin 0 -> 6148 bytes meshifyDrivers/mainHP/config.txt | 13 + meshifyDrivers/mainHP/device_base.py | 200 ++++ meshifyDrivers/mainHP/main.py | 1399 ++++++++++++++++++++++ meshifyDrivers/mainHP/mainMeshify.py | 133 ++ meshifyDrivers/mainHP/meshifyData.py | 112 ++ meshifyDrivers/mainHPRPI/config.txt | 14 + meshifyDrivers/mainHPRPI/device_base.py | 200 ++++ meshifyDrivers/mainHPRPI/main.py | 1399 ++++++++++++++++++++++ meshifyDrivers/mainHPRPI/mainMeshify.py | 133 ++ meshifyDrivers/mainHPRPI/meshifyData.py | 112 ++ meshifyDrivers/tankalarms/Channel.py | 299 +++++ meshifyDrivers/tankalarms/Tags.py | 24 + meshifyDrivers/tankalarms/config.txt | 14 + meshifyDrivers/tankalarms/device_base.py | 200 ++++ meshifyDrivers/tankalarms/file_logger.py | 18 + meshifyDrivers/tankalarms/persistence.py | 21 + meshifyDrivers/tankalarms/tankalarms.py | 197 +++ meshifyDrivers/tankalarms/utilities.py | 62 + 21 files changed, 4600 insertions(+) create mode 100644 Thingsboard Gateway/installtbg.sh create mode 100644 meshifyDrivers/.DS_Store create mode 100644 meshifyDrivers/mainHP/config.txt create mode 100644 meshifyDrivers/mainHP/device_base.py create mode 100644 meshifyDrivers/mainHP/main.py create mode 100644 meshifyDrivers/mainHP/mainMeshify.py create mode 100644 meshifyDrivers/mainHP/meshifyData.py create mode 100644 meshifyDrivers/mainHPRPI/config.txt create mode 100644 meshifyDrivers/mainHPRPI/device_base.py create mode 100644 meshifyDrivers/mainHPRPI/main.py create mode 100644 meshifyDrivers/mainHPRPI/mainMeshify.py create mode 100644 meshifyDrivers/mainHPRPI/meshifyData.py create mode 100644 meshifyDrivers/tankalarms/Channel.py create mode 100644 meshifyDrivers/tankalarms/Tags.py create mode 100644 meshifyDrivers/tankalarms/config.txt create mode 100644 meshifyDrivers/tankalarms/device_base.py create mode 100644 meshifyDrivers/tankalarms/file_logger.py create mode 100644 meshifyDrivers/tankalarms/persistence.py create mode 100644 meshifyDrivers/tankalarms/tankalarms.py create mode 100644 meshifyDrivers/tankalarms/utilities.py diff --git a/.DS_Store b/.DS_Store index 34bf634d30420228840b74b43a3a544d2f74b096..7a5db0b25aa1b1077ae3d3b4f4fa0b26c8ec957a 100644 GIT binary patch literal 10244 zcmeHM%Wl&^6unLhbrl{hr3)mGEU|z^RFb%)M6gJcAXJG3A{CW*D8xx4 z3M&@Cjs+X|1=g$(5^HwwBYXgy8M~?DIEm9rv`913_>S#+Jd@)ycYG&AB;_}2DI$f4 zWK?EnPT>$z*nL*}O3b@*1y%x{s7e*o4XRVQ&s$s&3=h=>HT{uZDoFuPj$;(hA-aBv#cPA<5T5%K%2nPBYV7Yr7*?O6pwB3`x zmsbp{su`9B4>qZmQ5XL3g4RG(&8ZriWf{z62B0-6VJhFn#z2EKs=>Nv+t?(q16_Ou z#^1$Ayw-M_-VncNO+$BD$>c97HZghPTw%4v` z5A=GwJafKanXQU$)@=^ZYjwChUenE*)y`X+X3gQ)l6ym*mZ!@z7j|}3HJwmWnWf!? zvXe?>vI%7=d1H5XTAsgr?dJXRbE9EeZx|B@NM7Kg)9!iq_!g~^Kh#)Hm8w>w*+oUb zG^}FLBS(Jsem~lAMorRsWg}ez(l491_mmZ&E4;nHkO35XFMiY+-j{>jE1jO9zarUaZJ~dgoB;MB zupfMLVJP;Oz6!)ptGPn8{tl&J7_UAw_yVsj4|E&H8@9+o^sTRN@o_YClqy2Ws=IKlL=)} z%^V=?Mf|ybZAFBHs4ODVueXmsez(6q^GpsStyRhv7j24=^m{h zlJ-Tz*w26xXM2>G@9MdLsH{WHCbfEF;zVKmCmgko)wGJ`JDWXhWQ;(TU&9gH1Ml-J z-lxvjCj(KWi3n34ydLr|wNJj)X$l`Yy!`_YJSb$61w8zd>DIUQ zxt9e#nfko;C(U55kja6Oiy{~h3uj5({!GUkb!}9HToUrZqtEkL3$t){q=RDhSEv}&a h^FISZc6dTQFGc=8VB1sV|J{5zu)pKqJ3Zd{|6i63K-2&L delta 164 zcmZn(XmOBWU|?W$DortDU;r^WfEYvza8E20o2aMAD7rCVH$S83WF7&j$uR=Dn@4aU diff --git a/Thingsboard Gateway/installtbg.sh b/Thingsboard Gateway/installtbg.sh new file mode 100644 index 0000000..7b72dcc --- /dev/null +++ b/Thingsboard Gateway/installtbg.sh @@ -0,0 +1,50 @@ +apt update +apt upgrade +#download thingsboard-gateway +wget https://github.com/thingsboard/thingsboard-gateway/releases/latest/download/python3-thingsboard-gateway.deb +apt install ./python3-thingsboard-gateway.deb -y +systemctl status thingsboard-gateway +#stop thingsboard-gateway +systemctl stop thingsboard-gateway + +#download ethernetip extension +curl -H 'Authorization: token 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' \ + -H 'Accept: application/vnd.github.v3.raw' \ + -O \ + -L https://github.com/Henry-Pump/ThingsBoard/raw/main/Thingsboard%20Gateway/ethernetip_converter.py + +curl -H 'Authorization: token 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' \ + -H 'Accept: application/vnd.github.v3.raw' \ + -O \ + -L https://github.com/Henry-Pump/ThingsBoard/raw/main/Thingsboard%20Gateway/ethernetip_connector.py + +curl -H 'Authorization: token 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' \ + -H 'Accept: application/vnd.github.v3.raw' \ + -O \ + -L https://github.com/Henry-Pump/ThingsBoard/raw/main/Thingsboard%20Gateway/ethernetip.json + +curl -H 'Authorization: token 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' \ + -H 'Accept: application/vnd.github.v3.raw' \ + -O \ + -L https://github.com/Henry-Pump/ThingsBoard/raw/main/Thingsboard%20Gateway/tb_gateway.yaml?token=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 + +wget https://hp-thingsboard.s3.amazonaws.com/ethernetip_connector.py +wget https://hp-thingsboard.s3.amazonaws.com/ethernetip_converter.py +wget https://hp-thingsboard.s3.amazonaws.com/ethernetip.json +wget https://hp-thingsboard.s3.amazonaws.com/tb_gateway.yaml + + +#move to proper location +mkdir /var/lib/thingsboard_gateway/extensions/ethernetip +mv ethernetip_converter.py /var/lib/thingsboard_gateway/extensions/ethernetip +mv ethernetip_connector.py /var/lib/thingsboard_gateway/extensions/ethernetip + +mv tb_gateway.yaml /etc/thingsboard-gateway/config +mv ethernetip.json /etc/thingsboard-gateway/config + +#install pycomm3 for thingsboard +sudo pip3 install --target=/usr/lib/python3/dist-packages pycomm3 + + +systemctl start thingsboard-gateway +rm installtbg.sh python3-thingsboard-gateway.deb \ No newline at end of file diff --git a/meshifyDrivers/.DS_Store b/meshifyDrivers/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..ff2a3e1f52a81bc0b078f41447e9535e056b1598 GIT binary patch literal 6148 zcmeHK&2AGh5FWRoby|UfL?pOb;u=AgN&XT~0X(RyM4ApKby0JH#rqAG0cVdD^GSGplv zcIE~m(QCYbqqG?DL`uHl?I%0~o`L_30rlVO#kXLIosH$Sx*p-;Rp`v*NnsLNY>$1Px;dcZ`+iep1%_R zv!^5dJt&c10q59q)F~dPIOK9<=~9=kqUzocE!YEa#w7D%Vq-oh6P^`ixBHE3Z8xs% z?9yGjPfwyVIgiS?oE80e`bLdj;$57q`f>6q8_$QWThF8@<4laFIv`8O=<@b;CQ>=? z%b7?E9UB<~ZPMnjb!V|ScyQ2RL9e&$utg9&>~+|q?&IaMN!xetKRF(r^IXUeI(;Bm z5VTykxPp(^aUQPzi#i?NM^nM2IK>54i?0^-co_yPi*)}o!9K<_;2F5m45<5op(+e5 zRtELefsK9wAU1GX3-(nxQ!&h9Xt6Sg78GJa5ltwuPYhzhah_Sf&|+oKgafgU4`O#F z_Ju<9?zleF?m$9=zV!@v2G$waHp7;x|IdH_{$CIBBhP?m;7Tz-8iQ!i$0OO=x^XyF wYZ>Z0R3*x /etc/resolv.conf") + + + def sendtodbLoc(self, ch, channel, value, timestamp, deviceName, mac): + + + #this will add your derived nodes the master nodes list, allowing them to receive sets!! + localNodesName = deviceName + "_" + str(ch) + "99" + + if not self.localNodes.has_key(localNodesName): + self.localNodes[localNodesName] = True + self.nodes[localNodesName] = self + + #make the techname + lst = textwrap.wrap(str(mac), width=2) + tech = "" + for i in range(len(lst)): + tech += lst[i].lower() + ":" + + + chName2 = '_[' + tech + + if int(ch) < 10: + ch = "0" + str(int(ch)) + + if len(ch) > 2: + ch = ch[:-2] + + dname = deviceName + chName2 + str(ch) + ":98]!" + + if int(timestamp) == 0: + timestamp = self.getTime() + + topic = 'meshify/db/%s/%s/%s/%s' % (self.company, mac, dname, channel) + print topic + msg = """[ { "value":"%s", "timestamp":"%s" } ]""" % (str(value), str(timestamp)) + print msg + self.q.put([topic, msg, 0]) + + def sendtodbDevJSON(self, ch, channel, value, timestamp, deviceName): + + if int(ch) < 10: + ch = "0" + str(int(ch)) + dname = deviceName + self.chName2 + str(ch) + ":99]!" + if int(timestamp) == 0: + timestamp = self.getTime() + + topic = 'meshify/db/%s/%s/%s/%s' % (self.company, self.mac, dname, channel) + print topic + msg = """[ { "value":%s, "timestamp":"%s" } ]""" % (str(value), str(timestamp)) + print msg + self.q.put([topic, msg, 0]) + + def sendtodbLora(self, ch, channel, value, timestamp, deviceName): + + if ":" not in ch: + ch = ch[0:2] + ":" + ch[2:4] + + #this will add your derived nodes the master nodes list, allowing them to receive sets!! + localNodesName = deviceName + "_" + str(ch).replace(':', "") + + if not self.localNodes.has_key(localNodesName): + self.localNodes[localNodesName] = True + self.nodes[localNodesName] = self + + + + dname = deviceName + self.chName2 + str(ch) + "]!" + + + + if int(timestamp) == 0: + timestamp = self.getTime() + + topic = 'meshify/db/%s/%s/%s/%s' % (self.company, self.mac, dname, channel) + print topic + msg = """[ { "value":"%s", "timestamp":"%s" } ]""" % (str(value), str(timestamp)) + print msg + self.q.put([topic, msg, 0]) + + def sendtodbDev(self, ch, channel, value, timestamp, deviceName): + + + #this will add your derived nodes the master nodes list, allowing them to receive sets!! + localNodesName = deviceName + "_" + str(ch) + "99" + + if not self.localNodes.has_key(localNodesName): + self.localNodes[localNodesName] = True + self.nodes[localNodesName] = self + + if int(ch) < 10: + ch = "0" + str(int(ch)) + + dname = deviceName + self.chName2 + str(ch) + ":99]!" + + + + if int(timestamp) == 0: + timestamp = self.getTime() + + topic = 'meshify/db/%s/%s/%s/%s' % (self.company, self.mac, dname, channel) + print topic + msg = """[ { "value":"%s", "timestamp":"%s" } ]""" % (str(value), str(timestamp)) + print msg + self.q.put([topic, msg, 0]) + + def sendToTB(self, payload): + topic = 'v1/devices/me/telemetry' + print(topic, payload) + self.q.put([topic, payload, 0]) + + def sendtodbCH(self, ch, channel, value, timestamp): + + + if int(ch) < 10: + ch = "0" + str(ch) + + dname = self.chName + str(ch) + ":99]!" + + + + if int(timestamp) == 0: + timestamp = self.getTime() + + topic = 'meshify/db/%s/%s/%s/%s' % (self.company, self.mac, dname, channel) + print topic + msg = """[ { "value":"%s", "timestamp":"%s" } ]""" % (str(value), str(timestamp)) + print msg + self.q.put([topic, msg, 0]) + + def sendtodb(self, channel, value, timestamp): + + if int(timestamp) == 0: + timestamp = self.getTime() + if timestamp < 1400499858: + return + else: + timestamp = str(int(timestamp) + int(self.offset)) + + topic = 'meshify/db/%s/%s/%s/%s' % (self.company, self.mac, self.deviceName, channel) + print topic + msg = """[ { "value":"%s", "timestamp":"%s" } ]""" % (str(value), str(timestamp)) + print msg + self.q.put([topic, msg, 0]) + + def sendtodbJSON(self, channel, value, timestamp): + + if int(timestamp) == 0: + timestamp = self.getTime() + if timestamp < 1400499858: + return + else: + timestamp = str(int(timestamp) + int(self.offset)) + + topic = 'meshify/db/%s/%s/%s/%s' % (self.company, self.mac, self.deviceName, channel) + print topic + msg = """[ { "value":%s, "timestamp":"%s" } ]""" % (str(value), str(timestamp)) + print msg + self.q.put([topic, msg, 0]) + def getTime(self): + return str(int(time.time() + int(self.offset))) + + + + diff --git a/meshifyDrivers/mainHP/main.py b/meshifyDrivers/mainHP/main.py new file mode 100644 index 0000000..1fe2e9a --- /dev/null +++ b/meshifyDrivers/mainHP/main.py @@ -0,0 +1,1399 @@ +import sys +import string +import socket +import fcntl +import struct +import client as paho +import os +import re +import time +import ssl +try: + import json +except: + import simplejson as json +import thread +import threading +import random +import base64 + +from meshifyData import meshifyData +# from devices import mainMistaway, m1, apg #, gds #cam, +#from devices import gdsMT as gds +import Queue +import pickle +import urllib2 +import urllib +try: + from sqlQueue import myqueue as SQLQ +except: + pass +try: + import SkyWaveDataAPI + SAT = True +except: + SAT = False + +try: + import schedule + sced = True +except: + sced = False + +unitName = "mainMeshify" + +broker = "hp.henrypump.cloud" + +root = os.getcwd() + "/" +# non secure +port = 1883 + +LORA = True +try: + import lora_main +except: + LORA = False + + +# secure port +#port = 1883 + +# driver for a virtual device called "main", it can never send data up to the website, but it hold methods that +# can be called easier than others because it has no unquie name assosicated with it +class main(): + + def __init__(self, name, number, mac, q, mcu, company, offset, mqtt, nodes, topics): + self.topics = topics + self.nodes = nodes + self.offset = offset + self.company = company + self.name = name + self.number = number + self.q = q + self.mqtt = mqtt + # + '_[' + mac + ':' + number[0:2] + ':' + number[2:] + ']!' + self.deviceName = name + print('device name is:') + print(self.deviceName) + mac2 = mac.replace(":", "") + self.mac = mac2.upper() + self.mcu = mcu + self.count = 0 + self.dst = "" + # queue for sets to the mesh network will handeled through a queue in this main driver + self.meshQ = Queue.Queue() + version = "13" # 6 - mistification # 5 - updated for SAT data and generic sets. 4 - devices changed to drivers for dia + + # self.sendtodb("version", version, 0) + thread.start_new_thread(self.registerThread, ()) + + #pickle.dump( version, open( "coreVersion.p", "wb" ) ) + + def sendtodb(self, channel, value, timestamp): + + csplit = re.split(r"(.*?)_\[(.*?)\]", self.deviceName) + nodeTypeName = csplit[1] + uniqueID = csplit[2] + company = "194" + topic = "v1/devices/me/telemetry" + print(topic) + + msg = """[{"%s":"%s"}]""" % (channel, str(value)) + print(msg) + self.q.put([topic, msg, 0]) + + def getTime(self): + return str(int(time.time() + int(self.offset))) + + def main_mcuupdate(self, name, value): + success = self.mcu.firmUpdate(value) + + if success == True: + val = "update to " + value + " was a success" + elif success == False: + print("you need to reboot and pray you didn't brick the MCU") + val = "update to " + value + " failed" + else: + val = "update to " + value + " failed because " + str(success) + print("you need to reboot and pray you didn't brick the MCU") + + # reseting the MCU also resets the Cellular modem, so we need to redo our mqtt connection once the modem comes back to life + time.sleep(180) + try: + self.mqtt.reconnect() + except Exception as e: + print(str(e)) + os.system("/root/reboot") + self.sendtodb(name, val, 0) + + def normalThread(self): + time.sleep(10) + os.system("/root/normalStart.sh") + + def debugThread(self): + time.sleep(10) + os.system("/root/debugStart.sh") + + def rebootThread(self): + time.sleep(10) + os.system("/root/reboot") + + def main_normal(self, name, value): + + if int(value) == 1: + + thread.start_new_thread(self.normalThread, ()) + return True + else: + return False + + def main_debug(self, name, value): + + if int(value) == 1: + + thread.start_new_thread(self.debugThread, ()) + return True + else: + return False + + def main_reboot(self, name, value): + if bool(value) == True: + # reset the modem + # self.mcu.resetModem() + # time.sleep(2) + + # resest the MCU + # try: + # os.system("/root/mcu2reset") + # except: + # pass + thread.start_new_thread(self.rebootThread, ()) + return True + + # os.system("/root/reboot") + + def main_SAT(self, name, value): + print("SAT is SET in MAIN") + st = "date -s @" + str(int(float(value))) + os.system(st) + + return True + + def registerThread(self): + while True: + time.sleep(3600 * 24) + + try: + os.system("/usr/bin/ntpdate pool.ntp.org") + os.system("/usr/sbin/ntpdate pool.ntp.org") + except: + pass + + try: + for name, driver in self.nodes.iteritems(): + + try: + driver.offset = self.offset + driver.company = self.company + + except Exception as e: + print(e) + except: + pass + + def main_register(self, name, value): + # try and sync the time + + try: + #setData = """$$$%s""" % ("main.register", "On", 1) + setData = {} + setData['name'] = 'main.register' + setData['value'] = 'On' + setData['id'] = 1 + print(setData) + self.meshQ.put(setData) + except: + pass + + try: + meshData = meshifyData.meshifyData(self.mac) + self.offset, self.dst, self.company = meshData.getdata() + csplit = re.split(r"(.*?)_\[(.*?)\]", self.deviceName) + nodeTypeName = csplit[1] + uniqueID = csplit[2] + company = "194" + + print(("meshify/sets/" + str(self.company) + "/" + self.mac + "/#")) + self.mqtt.subscribe(("v1/devices/me/requests/+"), 0) + self.mqtt.append("v1/devices/me/telemetry") + + except Exception as e: + print(e) + for name, driver in self.nodes.iteritems(): + + try: + driver.offset = self.offset + driver.company = self.company + driver.register() + + except Exception as e: + print(e) + + return True + + # this is where I need to put the function to have all of my devices check in + + +class meshifyMain(): + + def __init__(self): + + # add google nameserver + os.system("/bin/echo nameserver 8.8.8.8 > /etc/resolv.conf") + # make dictionary for xbee sets + + # check and see if the drivers folder is there: + # if not os.path.exists("drivers"): + # #make the drivers dir + # os.makedirs("drivers") + self.MCU_ON = False + # marker or wether or not the device is a fly + self.FLY = False + self.reconnecting = False + self.mqttQ = Queue.Queue() + # get MAC address, if this breaks, you are [no longer] screwed + try: + mac = self.getHwAddr('eth0') + print("success getting mac") + except: + print("error getting mac") + try: + from uuid import getnode as get_mac + mac = get_mac() + mac = hex(mac).replace('0x', '') + n = 2 + mac = [mac[i:i+n] for i in range(0, len(mac), n)] + newMac = "" + for i in mac: + newMac = newMac + i + ":" + + mac = newMac[:-1] + except: + mac = "12:34:56:78:91:23" + mac = str(mac) + self.OK2Send = True + self.zigMac = mac + self.deviceUrlList = [] + self.deviceVersions = {} + try: + # todo: change this to sd card if present on some devices + self.SQLQ = SQLQ.SqliteQueue(root + "sqlQueue/q2") + except: + pass + + # varible for if the device is connected to the satellites. + self.SAT_COM = False + + # if schedule module is there, then set up the global + try: + if sced == True: + self.schedule = schedule.schedule( + self.sets, self.mqttQ, self.getTime) + else: + self.schedule = False + except Exception as e: + print("####################") + print(e) + + # try and sync the time + try: + os.system("/usr/bin/ntpdate pool.ntp.org") + os.system("/usr/sbin/ntpdate pool.ntp.org") + except: + pass + + # dictionary of all the nodes attched + self.nodes = {} + + mac = mac.replace(":", "") + mac = mac.upper() + print("here is the mac address") + print(mac) + + # get your time offset, DST value and company id from meshify + try: + meshData = meshifyData.meshifyData(mac) + self.offset, self.dst, self.companyId = meshData.getdata() + except: + print("didn't work on api to get meshify data") + + self.mac = mac + + # start the debug thread: + thread.start_new_thread(self.debugThread, ()) + + # set up placeholder for self.mqtt + try: + clientData = {} + with open("mqtt.json", "r") as creds: + clientData = json.load(creds) + except: + clientData = {"clientId": mac, "username": "admin", "password": "columbus"} + with open("mqtt.json", "w+") as creds: + json.dump(clientData, creds) + self.mqtt = paho.Client(client_id=clientData["clientId"], clean_session=True) + + # change to false for mqtt.meshify.com + # self.mqtt.tls_insecure_set(True) + #self.mqtt.tls_set(root + "ca.crt", certfile=root + "client.crt", keyfile=root + "client.key", cert_reqs=ssl.CERT_NONE) + self.mqtt.username_pw_set(clientData["username"], clientData["password"]) + + print("now I'm here") + # Set up the last will and testiment to tell the system that I'm disconneted + lc = int(self.getTime()) + 30 + + self.deviceName = unitName + \ + '_[' + self.zigMac + ':' + "00" + ':' + "00" + ']!' + self.deviceNameMain = "mainMeshify" + \ + '_[' + self.zigMac + ':' + "00" + ':' + "00" + ']!' + + + + + # tell mqtt what to do on connect + self.mqtt.on_connect = self.on_connect + + # tell mqtt which function to call when a message is received + self.mqtt.on_message = self.on_message + self.mqtt.on_disconnect = self.on_disconnect + + # make conneciton to the MCU (only on M1 V5 right now): + + try: + #from mcu import mcu_main + self.MCU_ON = True + except: + self.MCU_ON = False + + try: + if self.MCU_ON == True: + print("mcu is on") + try: + print("mcu loading") + self.mcu = mcu_main.mcu_main() + + # added these for ifttt actions + self.mcu.mac = self.mac + self.mcu.on_message = self.on_message + self.mcu.FLY = self.FLY + + # adding this global for turning all data coming out of the device off + self.mcu.cutAllDataOff = False + + print("mcu loaded") + # now see if we are a fly + flyTries = 0 + self.mcu.xbeeCom(0) + self.mcu.xbee.write("+++") + time.sleep(1) + self.mcu.xbee.write("ATFR" + chr(13)) + time.sleep(1) + while flyTries < 2: + #self.mcu.send_mcu("""{\"SET\": [{\"XBEECOM\": \"0\"}]}\n""") + flyTries += 1 + self.mcu.xbee.read() + self.mcu.xbee.write("+++") + time.sleep(2) + self.mcu.xbee.write("ATDD" + chr(13)) + time.sleep(3) + response = self.mcu.xbee.read() + if "777" in response or LORA: + print("I'm a fly") + # self.mcu.spiOn(0) + self.FLY = True + self.mcu.FLY = self.FLY + if LORA: + #self.mcu.xbee = lora_main.lora() + #self.mcu.xbee.fileTransfer = False + pass + # set the xbee to talk to the coordinator + # this needs to be working for network switching + #self.mcu.xbee.atCommandSet("DH", "0") + #self.mcu.xbee.atCommandSet("DL", "FFFF") + thread.start_new_thread(self.xbeeGetThread, ()) + break + else: + self.FLY = False + except Exception as e: + print(e) + self.mcu.FLY = self.FLY + self.FLY = False + else: + print("MCU NOT ON") + self.mcu = None + + except Exception as e: + print(e) + + # while True: + # time.sleep(3) + # print self.mcu.getDict() + + # turn off connected LED's + if self.MCU_ON: + self.mcu.FLY = self.FLY + self.mcu.ledControl(2, 0, 0) + self.mcu.ledControl(3, 0, 0) + + self.topics = [] + + self.topics.append("v1/device/me/telemetry") + + # wait a few seconds to connect + time.sleep(5) + + ##################################################### + # this is the loading of the devices, if it fails, we still need main to work + # order of operations: + # 1. From API + # 2. From Text file + # 3. From previous loads pickle file + # 4. Nothing, it will only run main (not mainMeshify, just main wich can do reboot and register) + + # the json data should come back from an API, for now it will come from in the root/python_firmware deviceList.txt + + try: + try: + json_data = open('deviceList.txt') + data = json.load(json_data) + data = meshData.checkConfig() + if len(data) < 1: + raise ValueError('too short') + except: + json_data = open('deviceList.txt') + data = json.load(json_data) + # build a list of urls for your device driver buckets + for i in data: + print(i) + # check and see if we are running the dia, if so then kill the mcu thread running the xbee so the dia can have full access to the xbee + if i == "dia" or i == "spider": + self.mcu.xbee.runXbee = False + self.deviceUrlList.append(data[i]) + print(i) + print(data[i]) + + pickle.dump(self.deviceUrlList, open("deviceUrls.p", "wb")) + except Exception as e: + print("####################") + print(e) + # if we can't get it from the web, look for the pickled version + try: + self.deviceUrlList = pickle.load(open("deviceUrls.p", "rb")) + except: + print("couldn't load devices from pickle") + + # try and load your versions dictionary, if this can't load, then load all drivers for the first time + try: + self.deviceVersions = pickle.load( + open(root + "deviceVersions.p", "rb")) + except: + print("couldn't load devices Versions from pickle") + + self.deviceList = {} + + if self.FLY == False: + for i in self.deviceUrlList: + try: + print(i) + # json.load(urllib2.urlopen("http://" + i + "config.txt")) + data = json.load(urllib.urlopen(i + "config.txt")) + #data = urllib2.urlopen(("http://" + i + "config.txt")) + #data = data.read() + print(data) + #data = json.load(data) + + # download the files + print("trying to download the files") + try: + if int(data["releaseVersion"]) > int(self.deviceVersions[(data["deviceName"] + "_" + data["driverId"])]): + print("new version found in repo", data["releaseVersion"]) + for x in data["files"]: + print((i + data["files"][x])) + urllib.urlretrieve( + (i + data["files"][x]), ("./drivers/" + (data["files"][x]))) + else: + print("we have the latest version for: " + (data["deviceName"] + "_" + data["driverId"]) + " of " + str(self.deviceVersions[(data["deviceName"] + "_" + data["driverId"])])) + except: + print("probably didn't have any files to start with, loading all files") + for x in data["files"]: + print((i + data["files"][x])) + urllib.urlretrieve( + (i + data["files"][x]), ("./drivers/" + (data["files"][x]))) + + dList = [data["driverFileName"].replace( + ".py", ""), data["deviceName"], data["driverId"], data["releaseVersion"]] + self.deviceList[(data["deviceName"] + + "_" + data["driverId"])] = dList + print(self.deviceList) + except Exception as e: + print(e) + continue + + # if our device list is still empty, try and grab the saved one + if len(self.deviceList) < 1: + # get the old device list + try: + dl = pickle.load(open("deviceList.p", "rb")) + self.deviceList = dl + print(self.deviceList) + except Exception as e: + print(e) + self.deviceList = {} + print("couldn't load deviceList from pickle") + else: + pickle.dump(self.deviceList, open("deviceList.p", "wb")) + + try: + self.main = main('main', '', self.zigMac, self.mqttQ, self.mcu, + self.companyId, self.offset, self.mqtt, self.nodes, self.topics) + except Exception as e: + print(e) + + self.nodes["main"] = self.main + + # filename, node name, 4 digit identifier, version number (must be an integer) + + # gds deviceList + #deviceList = [ ["mainMistaway", "mainMistaway", "0000", "1"], ["gdsMT", "gdsc", "0005", "1"] ] + + # gate Device List + #deviceList = [ ["mainMistaway", "mainMistaway", "0000", "1"], ["gate", "gate", "0006", "1"] ] + + for device in self.deviceList: + print("trying to load: " + device) + + try: + device = self.deviceList[device] + + # subscribe to the device first, if it breaks, this makes it possible to fix all of this type at once + self.topics.append("v1/devices/me/attributes") + self.topics.append("v1/devices/me/rpc/request/+") + topic = str(("v1/devices/me/attributes")) + print("######", topic) + self.mqtt.subscribe(topic, 0) + topic = str(("v1/devices/me/rpc/request/+")) + print("######", topic) + self.mqtt.subscribe(topic, 0) + + # import the file from the devices folder + imported_module = __import__("drivers." + str(device[0])) + # import the code from the device driver + fileImport = getattr(imported_module, str(device[0])) + # import the driver class from the file + funct = getattr(fileImport, "start") + # start this instance and add it to the devices dictionary + self.nodes[(str(device[1]) + "_" + str(device[2]))] = funct(name=str(device[1]), number=str(device[2]), mac=self.zigMac, + Q=self.mqttQ, mcu=self.mcu, companyId=self.companyId, offset=self.offset, mqtt=self.mqtt, Nodes=self.nodes) + + # add name and version to a dictionary for pickling + self.deviceVersions[(str(device[1]) + "_" + str(device[2])) + ] = self.nodes[(str(device[1]) + "_" + str(device[2]))].version + pickle.dump(self.deviceVersions, open( + root + "deviceVersions.p", "wb")) + + except Exception as e: + print(e) + lc = self.getTime() + value = "Failed Loading: " + \ + str(device[2]) + " on startup with error: " + str(e) + msg = """[ { "value":"%s", "msgId":"%s" } ]""" % ((value), "1") + topic = "meshify/errors/" + self.mqttQ.put([topic, str(msg), 2]) + print(e) + + if self.FLY == False: + print("made it to here, not a fly") + # start logging data!!! + self.reconnecting = True + thread.start_new_thread(self.sqlPoolThread, ()) + + # connect to broker + #thread.start_new_thread(self.connect_thread, ()) + while True: + try: + self.connect_to_broker() + self.reconnecting = False + try: + os.system("/usr/bin/ntpdate pool.ntp.org") + os.system("/usr/sbin/ntpdate pool.ntp.org") + os.system("ntpdate pool.ntp.org") + except: + pass + break + except: + print("didn't work this time") + time.sleep(30) + + # tell the MQTT client to run forever!! + print("made it here") + while True: + print("##### here is the connection status #### ", str(self.mqtt._state)) + try: + if not self.FLY: + print(self.mqtt.loop()) + if self.MCU_ON: + if self.mcu.cutAllDataOff: + print("All Data is Being cut off, no outbound alloud") + time.sleep(5) + continue + + if not self.mqttQ.empty(): # or self.SQLQ.getLen() > 0: + if str(self.mqtt._state) == "1" or self.FLY == True: + + try: + print(self.SQLQ.getLen()) + if self.SQLQ.getLen() > 0: + loopCount = 0 + while self.SQLQ.getLen() > 0: + loopCount += 1 + if loopCount > 20: + loopCount = 0 + if not self.FLY: + print(self.mqtt.loop()) + val = self.SQLQ.popleft() + if not self.FLY: + resp = self.mqtt.publish( + val[0], val[1], val[2]) + + else: + if val[0].split("/")[1] == "db": + xeebVal = json.dumps( + json.loads(val[1])[0]["value"]) + if xeebVal.startswith('"') and xeebVal.endswith('"'): + xeebVal = xeebVal[1:-1] + upld = val[0].split( + "/")[3] + "/" + val[0].split("/")[4] + "/" + val[0].split("/")[5] + "/" + xeebVal + self.xbeeSend(upld) + time.sleep(.5) + else: + respId = (''.join(random.choice( + '0123456789ABCDEF') for i in range(4))) + self.xbeeSend( + "%%" + val[0] + "%%" + json.dumps(json.loads(val[1])[0]["value"]) + "%%") + + except Exception as e: + print(e) + print("no SQL Queue on this device") + + try: + val = self.mqttQ.get(block=False, timeout=1) + # print "Outputting: ", val + if not self.FLY: + resp = self.mqtt.publish( + val[0], val[1], val[2]) + elif self.mcu.xbee.fileTransfer == True: + # if we are sending a file, put this data point back in the Q + self.mqttQ.put(val) + time.sleep(3) + continue + else: + if val[0].split("/")[1] == "db": + xeebVal = json.dumps( + json.loads(val[1])[0]["value"]) + if xeebVal.startswith('"') and xeebVal.endswith('"'): + xeebVal = xeebVal[1:-1] + upld = val[0].split( + "/")[3] + "/" + val[0].split("/")[4] + "/" + val[0].split("/")[5] + "/" + xeebVal + self.xbeeSend(upld) + else: + self.xbeeSend( + "%%" + val[0] + "%%" + json.dumps(json.loads(val[1])[0]["value"]) + "%%") + time.sleep(2) + # print "####### here is the response" + # make a dictionary of response codes xbee + # print resp + except: + print("Q had an error") + time.sleep(1) + loopCount = 0 + while self.mqttQ.qsize() > 20: + loopCount += 1 + if loopCount > 20: + loopCount = 0 + if not self.FLY: + print(self.mqtt.loop()) + try: + val = self.mqttQ.get(block=False, timeout=1) + # print "Outputting: ", val + + if not self.FLY: + resp = self.mqtt.publish( + val[0], val[1], val[2]) + elif self.mcu.xbee.fileTransfer == True: + # if we are sending a file, put this data point back in the Q + self.mqttQ.put(val) + time.sleep(3) + continue + else: + if val[0].split("/")[1] == "db": + xeebVal = json.dumps( + json.loads(val[1])[0]["value"]) + if xeebVal.startswith('"') and xeebVal.endswith('"'): + xeebVal = xeebVal[1:-1] + upld = val[0].split( + "/")[3] + "/" + val[0].split("/")[4] + "/" + val[0].split("/")[5] + "/" + xeebVal + self.xbeeSend(upld) + + else: + self.xbeeSend( + "%%" + val[0] + "%%" + json.dumps(json.loads(val[1])[0]["value"]) + "%%") + time.sleep(2) + + except: + print("Q had an error") + time.sleep(1) + + except Exception as e: + print(e) + time.sleep(10) + if self.reconnecting == False: + self.recon() + time.sleep(.5) + + def connect_thread(self): + while True: + try: + print("####### connecting to the broker ########") + self.connect_to_broker() + + except Exception as e: + print((str(e))) + print("didn't work to connect, restarting....") + os.system('/root/reboot') + + def connect_to_broker(self): + + if self.companyId == "1": + meshData = meshifyData.meshifyData(self.mac) + self.offset, self.dst, self.companyId = meshData.getdata() + + self.mqtt.connect(broker, port, keepalive=120) + self.topic_sub() + + # tell the MQTT client to run forever!! + # self.mqtt.loop_forever() + + def on_disconnect(self, mosq, userdata, rc): + print("################ DISCONECCTED #################") + + # turn off connected LED + if self.MCU_ON: + self.mcu.ledControl(2, 0, 0) + + self.recon() + + def connect_check(self): + # this funtion checks to see how many times its tried to reconnect in the 3 minutes that it goes to sleep + # if it wakes up and you are on the same attempt, then its froze and needs to reboot + local_count = self.count + time.sleep(180) + if local_count == self.count: + # Dont reboot if connected to SAT + if self.SAT_COM == True: + pass + else: + os.system('/root/reboot') + + def sqlPoolThread(self): + + # while spooling the data I am going to attempt a handshake with the SkyWave satellites + # handshake data will include my mac address + # the web will return my current UTC time, which by the time I get it, it will be off by a few seconds, but whos counting ;) + # if it connects, it will set a variable called SAT_COM to True, by way of the main device + print("starting up sat connection") + + # start a Queue for the sending up of data to the SATS + satQ = Queue.Queue() + if SAT == True and self.FLY == False: + thread.start_new_thread( + SkyWaveDataAPI.skywave, (self.sets, self.mac, self.stateData, satQ, self.mcu)) + + # wait 1 minute to see if we connect to the SATs + time.sleep(60) + try: + while self.reconnecting == True: + val = self.mqttQ.get() + try: + if self.SQLQ.getLen() > 5000: + # delete oldest value + trash = self.SQLQ.popleft() + + except: + pass + if self.SAT_COM == True: + print("sending up data to sats") + satQ.put(val) + else: + print("storing up data for later 3g connection") + try: + self.SQLQ.append(val) + except: + pass + except: + pass + + def recon(self): + self.reconnecting = True + thread.start_new_thread(self.sqlPoolThread, ()) + count = 0 + while True: + + if count > 2000: + if self.SAT_COM == True: + count = 0 + else: + break + + count += 1 + print(count) + try: + self.count = count + thread.start_new_thread(self.connect_check, ()) + self.mqtt.reconnect() + self.count = 0 + self.reconnecting = False + break + except: + print("couldn't reconnect, retrying in 30 seconds") + os.system("/bin/echo nameserver 8.8.8.8 > /etc/resolv.conf") + os.system("/sbin/ifup 3g") + time.sleep(30) + + if count > 2000 and not self.FLY: + # don't reboot if connected to SAT + print("rebooting now") + os.system('/root/reboot') + + def on_connect(self, mosq, userdata, rc): + + # turn connected LED + if self.MCU_ON: + self.mcu.ledControl(2, 1, 0) + self.mcu.ledControl(3, 0, 0) + else: + print("on_connect() MCU_OFF = False!!!!!") + + # stop using sat data + self.SAT_COM = False + + # let the watchdog know we are not on SAT anymore + os.system('/bin/echo False > /root/SAT') + + # wait a few seconds for the connection to be made solid + time.sleep(4) + + # set the channel connected to true + lc = self.getTime() + + csplit = re.split(r"(.*?)_\[(.*?)\]", self.deviceName) + nodeTypeName = csplit[1] + uniqueID = csplit[2] + company = "194" + + topic = 'meshify/db/%s/%s/%s/%s/%s' % (company, + "_", nodeTypeName, uniqueID, "connected") + msg = """[{"value":"%s"}]""" % ("true") + self.mqttQ.put([topic, msg, 2]) + + # set the network to 3g + lc = self.getTime() + topic = 'meshify/db/%s/%s/%s/%s/%s' % (company, + "_", nodeTypeName, uniqueID, "net") + msg = """[{"value":"%s"}]""" % ("3g") + self.mqttQ.put([topic, msg, 2]) + + print((" ############### Connection returned " + + str(rc) + " ###############")) + self.topic_sub() + + def topic_sub(self): + for topic in self.topics: + print(topic) + self.mqtt.subscribe(topic, 0) + + def sets(self, msg, sch=False): + entireMsg = msg + # [{"user":"demo@meshify.com","mac":"000CE373293D","company":"188","payload":{"name":"wipom_[00:0c:e3:73:29:3d:00:21]!.do2","value":"1","expires":"1389369695"},"msgId":4478}] + #{"driver.function": "value", "msgId": 1234} + print("I got the set") + print(msg) + try: + data = json.loads(msg) + keys = data.keys() + msgId = 0 + for x in keys: + if x != "msgId": + name = x + if x == "msgId": + msgId = data["msgId"] + value = data[name] + + print(name, value) + + #name = 'tankalarms.water_spt' + # grab the 4 digit unique device code from the end of the MAC address + + n = name.split('.') + channel = n[1] + n = n[0] + + try: + # if the first part of the zigbee mac address is legit, meaning it comes from digi + # then I need to treat this like a mesh node and send it to the dia + # This is only for the SPIDER's Dia Nodes + # the best check is to see if the addresses are derived from the same address + if name.split('.')[0].split("_")[1].replace("[", "").replace(":", "").replace("]!", "")[:-10].upper() == "0013A2": + print("found an xbee") + #setData = """$$$%s""" % (name, value, msgId) + setData = {} + setData['name'] = name + setData['value'] = value + setData['id'] = msgId + print(setData) + self.main.meshQ.put(setData) + return + except: + print("couldn't determine if there was an xbee") + + if n == "main": + if channel == "SAT": + print("got ping from sat cloud app, this mean we have a connection") + try: + if float(value) > 1422910000: + print("SAT CONNECTED!!!") + self.SAT_COM = True + # turn connected LED to amber + if self.MCU_ON: + self.mcu.ledControl(3, 1, 0) + self.mcu.ledControl(2, 0, 0) + # send connected = True + lc = self.getTime() + topic = 'meshify/db/%s/%s/%s/%s' % ( + self.companyId, self.mac, self.deviceName, "connected") + msg = """[ { "value":"%s", "timestamp":"%s" } ]""" % ( + "True", str(lc)) + self.mqttQ.put([topic, msg, 2]) + + # set the network to sat + lc = self.getTime() + topic = 'meshify/db/%s/%s/%s/%s' % ( + self.companyId, self.mac, self.deviceName, "net") + msg = """[ { "value":"%s", "timestamp":"%s" } ]""" % ( + "sat", str(lc)) + self.mqttQ.put([topic, msg, 2]) + + # let the watchdog know if we are on SAT + os.system('/bin/echo True > /root/SAT') + except: + print("didn't send the right date") + nodeName = "main" + nodeNumber = "" + + else: + #m = n + # m = m.split('_') + #m = re.split(r"(.*?)_\[(.*?)\]", m) + # nodeName = m[0] + nodeName = n + #n = n.replace(']!', '') + #n = n[-5:] + #nodeNumber = n.replace(':', '') + nodeNumber = "_" + "0199" #nodeNumber + except: + print("not valid JSON") + return + + # check and see if you are setting the scheduler + if channel.startswith("sch-"): + print("data now being sent to the scheduler") + try: + if self.schedule != False: + self.schedule.message(channel, json.loads(entireMsg)) + return + except: + print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") + print("BAD JSON") + return + + # this grabs the class for the driver for the node: #this should be called className + funcName = nodeName + nodeNumber + # this grabs the method for that channel inside of the driver for that node: + funcChan = nodeName + '_' + channel + print(funcName) + # try: + # # if nodes[funcName] != undefined + # #channelCallback = getattr(nodes[funcName], funcChan) + # #success = channelCallback(channel, value) + # func = getattr(self, funcName) + # except: + # print 'no Set callback found for channel: ' + funcName + # else: + try: + # classFunc = getattr(func, funcChan) #func(value) + #success = classFunc(channel, value) + + # first try a specific callback set, the fundtion will look like: deviceName_0000 + try: + print("trying to find callback for:") + print(funcChan) + channelCallback = getattr(self.nodes[funcName], funcChan) + success = channelCallback(channel, value) + except: + print("looking for genericSet") + # now try a generic one, that looks like self.genericSet(self, channel, value, UnitNumber) Unit number is the second to last 2 digits of the tech name + channelCallback = getattr(self.nodes[funcName], "genericSet") + try: + success = channelCallback( + channel, value, nodeNumber[1:3], nodeNumber[1:]) + except: + success = channelCallback(channel, value, nodeNumber[1:3]) + + #csplit = re.split(r"(.*?)_\[(.*?)\]", self.deviceNameMain) + #nodeTypeName = csplit[1] + #uniqueID = csplit[2] + #company = "194" + + if success == True: + if int(msgId) == 0: + return 0 + if sch == False: + msg = json.dumps({"value": True}) + topic = "v1/devices/me/rpc/response/" + msgId + self.mqttQ.put([topic, str(msg), 2]) + + else: + return 0 + + else: + if sch == False: + msg = json.dumps({"value": False}) + topic = "v1/devices/me/rpc/response/" + msgId + self.mqttQ.put([topic, str(msg), 2]) + + else: + return 1 + except: + if int(msgId) == 0: + return 2 + if sch == False: + msg = json.dumps({"value": False}) + topic = "v1/devices/me/rpc/response/" + msgId + self.mqttQ.put([topic, str(msg), 2]) + print('no Set callback found for channel: ' + funcName) + + else: + return 2 + + # function to be called when a message is received + + def handle_message(self, topic, payload, qos): + try: + + csplit = re.split(r"(.*?)_\[(.*?)\]", self.deviceNameMain) + nodeTypeName = csplit[1] + uniqueID = csplit[2] + company = "194" + + print(("Message received on topic "+topic+" with QoS "+str(qos))) + topics = topic.split("/") + if topics[1] == "files" and topics[4] == "write": + self.OK2Send = False + path = topics[5] + path = path.replace("$", "/") + print(path) + with open(path, 'wb') as fd: + fd.write(payload) + fd.close() + print("file written") + # update the channel mainMistaway_files, a dummy channel for keeping track of file transactions + lc = self.getTime() + topic = 'meshify/db/%s/%s/%s/%s/%s' % ( + company, "_", nodeTypeName, uniqueID, "files") + msg = """[ { "value":"%s" } ]""" % ( + (str(self.mac) + " File Written: " + path)) + self.mqttQ.put([topic, msg, 2]) + elif topics[1] == "files" and topics[4] == "get": + + self.OK2Send = False + path = topics[5] + mqttpath = path + path = path.replace("$", "/") + print(path) + f = open(path, 'rb') + byteArray = f.read() + #byteArray = bytes(byteArray) + topic = 'meshify/get/%s/%s/%s/%s' % ( + company, "_", uniqueID, mqttpath) + msg = byteArray + self.mqtt.publish(topic, msg, 0) + f.close() + print("message sent on topic: ", topic) + + # update the channel mainMistaway_files, a dummy channel for keeping track of file transactions + lc = self.getTime() + topic = 'meshify/db/%s/%s/%s/%s/%s' % ( + company, "_", nodeTypeName, uniqueID, "files") + msg = """[ { "value":"%s" } ]""" % ( + (str(self.mac) + " File Sent: " + path)) + self.mqttQ.put([topic, msg, 2]) + elif topics[1] == "files" and topics[4] == "delete" and payload == "delete": + path = topics[5] + path = path.replace("$", "/") + val = "Success Deleting " + try: + os.remove(path) + except OSError as e: # if failed, report it back to the user ## + val = "Error Deleting " + print(("Error: %s - %s." % (e.filename, e.strerror))) + + # update the channel mainMistaway_files, a dummy channel for keeping track of file transactions + lc = self.getTime() + topic = 'meshify/db/%s/%s/%s/%s/%s' % ( + company, "_", nodeTypeName, uniqueID, "files") + msg = """[ { "value":"%s" } ]""" % ((val + path)) + self.mqttQ.put([topic, msg, 2]) + elif topics[1] == "sets": + self.sets(payload) + self.OK2Send = True + except Exception as e: + print(e) + self.OK2Send = True + print("error understanding the mqtt message") + + def on_message(self, mosq, obj, msg): + print("!!!!!!! ON MESSAGE !!!!!!!") + print(msg.topic) + print(msg.payload) + try: + if "rpc" in msg.topic: + payload = {} + payload["msgId"] = msg.topic.split("/")[-1] + jpayload = json.loads(msg.payload) + payload[jpayload["method"]] = jpayload["params"]["value"] + msg.payload = json.dumps(payload) + self.sets(msg.payload) + except Exception as e: + print(e) + print("HELP HELP HELP") + """ + try: + print(msg) + + csplit = re.split(r"(.*?)_\[(.*?)\]", self.deviceNameMain) + nodeTypeName = csplit[1] + uniqueID = csplit[2] + company = "194" + + m1ID = uniqueID[:18] + "00:30" + rigpumpID = uniqueID[:18] + "01:99" + plevelID = uniqueID[:18] + "01:30" + flowID = uniqueID[:18] + "01:40" + subID = uniqueID[:18] + "02:10" + liteID = uniqueID[:18] + "02:30" + ippID = uniqueID[:18] + "00:90" + + # if the message has either a device name or the mac address of this unit then send to handle_message if not send to dia + # if the name isn't hex, then its an ascii group set + try: + num = int(msg.topic.split("/")[2], 16) + is_hex = True + except: + is_hex = False + + try: + # if the device is in the xbeeQ lookup dictionary, then pass it there + devName = json.loads(msg.payload)[ + 0]["payload"]["name"].split(".")[0] + if devName in self.mcu.xbees: + # this is an xbee node running the dia + xbeeSet = self.mcu.xbees[devName] + xbeeSet.put([msg.topic, msg.payload]) + return + except Exception as e: + print(e) + print("error parsing set for xbee") + # case 1, the message is meant for me or I'm a fly + # case 2, the company ID is a number, not a word, try the set to the xbee again i guess?? + # case 3, the compnay id was actually a group, send it to both the + if msg.topic.split("/")[3] == uniqueID.upper() or msg.topic.split("/")[3] == m1ID.upper() or msg.topic.split("/")[3] == rigpumpID.upper() or msg.topic.split("/")[3] == plevelID.upper() or msg.topic.split("/")[3] == flowID.upper() or msg.topic.split("/")[3] == subID.upper() or msg.topic.split("/")[3] == liteID.upper() or msg.topic.split("/")[3] == ippID.upper() or self.FLY == True: + thread.start_new_thread( + self.handle_message, (msg.topic, msg.payload, msg.qos)) + elif is_hex: + xbeeSet = self.mcu.xbees[msg.topic.split("/")[3]] + xbeeSet.put([msg.topic, msg.payload]) + else: + # this is a group, so set both + thread.start_new_thread( + self.handle_message, (msg.topic, msg.payload, msg.qos)) + xbeeSet = self.mcu.xbees[msg.topic.split("/")[3]] + xbeeSet.put([msg.topic, msg.payload]) + except: + pass""" + + # this retrieves the MAC address from the ethernet card + + def getHwAddr(self, ifname): + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + info = fcntl.ioctl(s.fileno(), 0x8927, + struct.pack('256s', ifname[:15])) + return ''.join(['%02x:' % ord(char) for char in info[18:24]])[:-1] + + def stateData(self): + return self.SAT_COM, self.reconnecting + + def getTime(self): + return str(int(time.time() + int(self.offset))) + # Here is the spot for all of your channel set callbacks + # Callbacks are to be writen with the following nameing scheme: + # deviceName_DeviceId_ChannelName + # the function will take in the new value it is getting set to + # for any action to take place on that channel set you must define a callback + # with the name defined by the nameing scheme above. + + def debugThread(self): + + # this thread is reading the system output of the core as its runs and publishes it to + # the topic: meshify/debug/macaddress + + # TODO Use iMist2 rset tracing module + + try: + print('Number of arguments:', len(sys.argv), 'arguments.') + if str(sys.argv[1]).lower().strip() == "debug=true" or str(sys.argv[1]).lower().strip() == "debug = true" or str(sys.argv[1]).lower().strip() == "true": + try: + if len(str(sys.argv[2]).lower().strip()) > 2: + fileLocation = str(sys.argv[2]).lower().strip() + else: + fileLocation = "/tmp/main.log" + except: + fileLocation = "/tmp/main.log" + + file = open(fileLocation, 'r+') + while 1: + where = file.tell() + line = file.readline() + if not line: + file.seek(where) + file.truncate(0) + time.sleep(1) + file.seek(0) + else: + topic = "meshify/debug/" + self.mac + msg = filter( + lambda x: x in string.printable, str(line)) + self.mqttQ.put([topic, msg, 0]) + except Exception as e: + print("debug error") + print(e) + + def xbeeSend(self, data): + + respId = (''.join(random.choice('0123456789ABCDEF') for i in range(4))) + data = data + respId + "$" + count = 0 + + while True and self.mcu.xbee.fileTransfer == False: + if count > 5: + print("failed getting response after 3 tries") + if self.xbeeConnected == True: + self.xbeeConnected = False + # broadcast to coordinator + #self.mcu.xbee.atCommandSet("DH", "0") + #self.mcu.xbee.atCommandSet("DL", "FFFF") + # turn off connected LED + if self.MCU_ON: + self.mcu.ledControl(2, 0, 0) + self.mcu.ledControl(1, 0, 0) + # let the watchdog know we are not connected to the SPIDER + os.system('/bin/echo False > /root/XBEE') + return False + count += 1 + try: + self.mcu.xbee.write(base64.b64encode(data.encode('utf-8'))) + except Exception as e: + print("error writing xbee to gateway") + print(e) + inner_count = 0 + while True and self.mcu.xbee.fileTransfer == False: + inner_count += 1 + time.sleep(.5) + if respId in self.xbeeResponseList: + print("id found!!!") + if self.xbeeConnected == False: + self.xbeeConnected = True + # turn connected LED + if self.MCU_ON: + self.mcu.ledControl(2, 1, 0) + self.mcu.ledControl(1, 1, 0) + # let the watchdog know we are connected to the SPIDER + os.system('/bin/echo True > /root/XBEE') + return True + elif inner_count > 12: + print("no response found") + break + + def xbeeGetThread(self): + # build a list of last 20 responses ie: if id in listofIds then OK + # when the ID's start populating, turn connected on!! LED + self.xbeeResponseList = [] + self.xbeeConnected = False + data = "" + while True: + if self.mcu.xbee.fileTransfer == True: + time.sleep(5) + continue + else: + try: + newData = self.mcu.xbee.read() + if newData != "": + data += newData + print(data) + if "$$" in data: + list_of_sets = data.split("$$") + if len(list_of_sets[len(list_of_sets) - 1]) < 1: + data = "" + del list_of_sets[-1] + else: + data = list_of_sets[len(list_of_sets) - 1] + del list_of_sets[-1] + for item in list_of_sets: + if len(item) == 4: + print("new response id", item) + self.xbeeResponseList.append(item) + if len(self.xbeeResponseList) > 20: + self.xbeeResponseList.pop(0) + continue + print("we have a complete message") + topic = item.split("%%")[1] + payload = item.split("%%")[2] + self.handle_message(topic, payload, 1) + time.sleep(2) + else: + time.sleep(.5) + continue + + except Exception as e: + print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") + print("xbee read error") + print(e) + time.sleep(1) + + +def startMain(): + try: + test = meshifyMain() + except: + pass + + +startMain() diff --git a/meshifyDrivers/mainHP/mainMeshify.py b/meshifyDrivers/mainHP/mainMeshify.py new file mode 100644 index 0000000..6844278 --- /dev/null +++ b/meshifyDrivers/mainHP/mainMeshify.py @@ -0,0 +1,133 @@ +import time +import os +try: + import json +except: + import simplejson as json +import thread +import threading +import re + + + + + +class start(threading.Thread): + + def __init__(self, name=None, number=None, mac=None, Q=None, mcu=None, companyId=None, offset=None, mqtt=None, Nodes=None): + threading.Thread.__init__(self) + self.daemon = True + self.offset = offset + self.company = companyId + self.name = name + self.number = number + self.q = Q + self.deviceName = name + '_[' + mac + ':' + number[0:2] + ':' + number[2:] + ']!' + print 'device name is:' + print self.deviceName + mac2 = mac.replace(":", "") + self.mac = mac2.upper() + self.version = "17" #hp device management + 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): + #self.mainMistaway_hb('hb', 'On') + self.sendtodb("connected", "true", 0) + + def stop (self): + self.finished.set() + self.join() + + def sendtodb(self, channel, value, timestamp): + if int(timestamp) == 0: + timestamp = self.getTime() + if timestamp < 1400499858: + return + csplit = re.split(r"(.*?)_\[(.*?)\]", self.deviceName) + nodeTypeName = csplit[1] + uniqueID = csplit[2] + company = "194" + + + try: + topic = 'meshify/db/%s/%s/%s/%s/%s' % (company, "_", nodeTypeName, uniqueID, channel) + print topic + if channel == "files": + #for the file structure I had to take off the " " around the value + msg = """[{"value":%s}]""" % (str(value)) + else: + msg = """[{"value":"%s"}]""" % (str(value)) + print msg + self.q.put([topic, msg, 0]) + except: + print "didn't work to send up MQTT data" + + def run(self): + #on startup send the version number + self.sendtodb("version", str(self.version), 0) + while True: + try: + self.mainMeshify_hb('hb', 'On') + self.sendtodb("connected", "true", 0) + time.sleep(3600 * 4) + except Exception, e: + print e + + def mainMeshify_files(self, name, value): + name = 'files' + + + + + + dict = {} + for dirname, dirnames, filenames in os.walk(str(value)): + # print path to all subdirectories first. + + + print "##########################################" + print "new directory: " + dirname + print "##########################################" + # print path to all filenames. + tempDictParent = {} + for filename in filenames: + tempDict = {} + filepath = os.path.join(dirname, filename) + try: + fileMem = os.stat(filepath).st_size + fileDate = os.stat(filepath).st_mtime + except: + fileMem = "" + fileDate = "" + print filepath, fileMem, fileDate + tempDict["mem"] = fileMem + tempDict["date"] = fileDate + tempDictParent[filename] = tempDict + + dict[dirname] = tempDictParent + + + # Advanced usage: + # editing the 'dirnames' list will stop os.walk() from recursing into there. + if '.git' in dirnames: + # don't go into any .git directories. + dirnames.remove('.git') + + value = json.dumps(dict) + self.sendtodb(name, value, 0) + return True + + + def mainMeshify_hb(self, name, value): + self.sendtodb(name, value, 0) + + + def getTime(self): + return str(int(time.time() + int(self.offset))) + diff --git a/meshifyDrivers/mainHP/meshifyData.py b/meshifyDrivers/mainHP/meshifyData.py new file mode 100644 index 0000000..8749714 --- /dev/null +++ b/meshifyDrivers/mainHP/meshifyData.py @@ -0,0 +1,112 @@ + + + +import urllib +try: + import json +except: + import simplejson as json +import pickle + +MAC = "00409D53168A" + +class meshifyData(): + + def __init__(self, MAC): + self.mac = MAC[0:6] + "FF-FF" + MAC[6:] + print "here is the mac: " + self.mac + #set the defaults + self.param_dict = {} + + def checkConfig(self): + + url = "https://f5rrbd3r45.execute-api.us-east-1.amazonaws.com/device_config?mac=" + self.mac + + try: + f = urllib.urlopen(url) + except: + print "Error opening url for remote config" + #return the defaults + return None + + try: + s = f.read() + #print s + if len(s) < 5: + return None + s = s[1:-1].replace("'", '"') + print s + data = json.loads(s) + #if we get there then replace the deviceList.txt + with open('/root/python_firmware/deviceList.txt', 'w') as myfile: + json.dump(data, myfile, indent=4) + return data + except Exception as e: + print e + #return the defaults + return None + + def checkAPI(self): + + + offset = -21600 + dst = False + companyId = "1" + + + url = "https://machines.meshify.com/api/gateway?macaddressForTimezone=" + self.mac + + try: + f = urllib.urlopen(url) + except: + print "Error opening url" + #return the defaults + return offset, dst, companyId + + try: + s = f.read() + print s + data = json.loads(s) + offset = int(data["offset"]) + dst = bool(int(data["dst"])) + print bool(int("0")) + companyId = data["companyId"] + return offset, dst, companyId + except Exception,e: + print e + #return the defaults + return -21600, False, "1" + + def getdata(self): + #if the API fails and the company ID of 1 is returned then you need to + #check and see if you have pickled anything. + #if it doesn't fail, and it gives you something other than 1 + #then you need to repickle the object + self.offset, self.dst, self.companyId = self.checkAPI() + if self.companyId == "1": + try: + self.param_dict = pickle.load( open( "params.p", "rb" ) ) + except: + print self.offset, self.dst, self.companyId + return self.offset, self.dst, self.companyId + try: + self.offset = self.param_dict["offset"] + self.dst = self.param_dict["dst"] + self.companyId = self.param_dict["companyId"] + except: + return -21600, False, "1" + + return self.offset, self.dst, self.companyId + + + else: + self.param_dict["offset"] = self.offset + self.param_dict["dst"] = self.dst + self.param_dict["companyId"] = self.companyId + pickle.dump( self.param_dict, open( "params.p", "wb" ) ) + print self.param_dict + print self.offset, self.dst, self.companyId + return self.offset, self.dst, self.companyId + + + diff --git a/meshifyDrivers/mainHPRPI/config.txt b/meshifyDrivers/mainHPRPI/config.txt new file mode 100644 index 0000000..0b7e563 --- /dev/null +++ b/meshifyDrivers/mainHPRPI/config.txt @@ -0,0 +1,14 @@ +{ + +"driverFileName":"mainMeshify.py", +"deviceName":"mainMeshify", +"driverId":"0000", +"releaseVersion":"16", +"files": { + "file1":"mainMeshify.py", + "file2":"main.py", + "file3":"device_base.py", + "file4":"meshifyData.py" + } + +} \ No newline at end of file diff --git a/meshifyDrivers/mainHPRPI/device_base.py b/meshifyDrivers/mainHPRPI/device_base.py new file mode 100644 index 0000000..9745b1a --- /dev/null +++ b/meshifyDrivers/mainHPRPI/device_base.py @@ -0,0 +1,200 @@ +import types +import traceback +import binascii +import threading +import time +import thread +import os +import struct +import sys +import textwrap + +class deviceBase(): + + def __init__(self, name=None, number=None, mac=None, Q=None, mcu=None, companyId=None, offset=None, mqtt=None, Nodes=None): + self.offset = offset + self.company = companyId + self.name = name + self.number = number + self.q = Q + self.deviceName = name + '_[' + mac + ':' + number[0:2] + ':' + number[2:] + ']!' + self.chName = "M1" + '_[' + mac + ':' + self.chName2 = '_[' + mac + ':' + print 'device name is:' + print self.deviceName + mac2 = mac.replace(":", "") + self.mac = mac2.upper() + self.address = 1 + self.debug = True + self.mcu = mcu + self.firstRun = True + self.mqtt = mqtt + self.nodes = Nodes + #local dictionary of derived nodes ex: localNodes[tank_0199] = self + self.localNodes = {} + os.system("chmod 777 /root/reboot") + os.system("echo nameserver 8.8.8.8 > /etc/resolv.conf") + + + def sendtodbLoc(self, ch, channel, value, timestamp, deviceName, mac): + + + #this will add your derived nodes the master nodes list, allowing them to receive sets!! + localNodesName = deviceName + "_" + str(ch) + "99" + + if not self.localNodes.has_key(localNodesName): + self.localNodes[localNodesName] = True + self.nodes[localNodesName] = self + + #make the techname + lst = textwrap.wrap(str(mac), width=2) + tech = "" + for i in range(len(lst)): + tech += lst[i].lower() + ":" + + + chName2 = '_[' + tech + + if int(ch) < 10: + ch = "0" + str(int(ch)) + + if len(ch) > 2: + ch = ch[:-2] + + dname = deviceName + chName2 + str(ch) + ":98]!" + + if int(timestamp) == 0: + timestamp = self.getTime() + + topic = 'meshify/db/%s/%s/%s/%s' % (self.company, mac, dname, channel) + print topic + msg = """[ { "value":"%s", "timestamp":"%s" } ]""" % (str(value), str(timestamp)) + print msg + self.q.put([topic, msg, 0]) + + def sendtodbDevJSON(self, ch, channel, value, timestamp, deviceName): + + if int(ch) < 10: + ch = "0" + str(int(ch)) + dname = deviceName + self.chName2 + str(ch) + ":99]!" + if int(timestamp) == 0: + timestamp = self.getTime() + + topic = 'meshify/db/%s/%s/%s/%s' % (self.company, self.mac, dname, channel) + print topic + msg = """[ { "value":%s, "timestamp":"%s" } ]""" % (str(value), str(timestamp)) + print msg + self.q.put([topic, msg, 0]) + + def sendtodbLora(self, ch, channel, value, timestamp, deviceName): + + if ":" not in ch: + ch = ch[0:2] + ":" + ch[2:4] + + #this will add your derived nodes the master nodes list, allowing them to receive sets!! + localNodesName = deviceName + "_" + str(ch).replace(':', "") + + if not self.localNodes.has_key(localNodesName): + self.localNodes[localNodesName] = True + self.nodes[localNodesName] = self + + + + dname = deviceName + self.chName2 + str(ch) + "]!" + + + + if int(timestamp) == 0: + timestamp = self.getTime() + + topic = 'meshify/db/%s/%s/%s/%s' % (self.company, self.mac, dname, channel) + print topic + msg = """[ { "value":"%s", "timestamp":"%s" } ]""" % (str(value), str(timestamp)) + print msg + self.q.put([topic, msg, 0]) + + def sendtodbDev(self, ch, channel, value, timestamp, deviceName): + + + #this will add your derived nodes the master nodes list, allowing them to receive sets!! + localNodesName = deviceName + "_" + str(ch) + "99" + + if not self.localNodes.has_key(localNodesName): + self.localNodes[localNodesName] = True + self.nodes[localNodesName] = self + + if int(ch) < 10: + ch = "0" + str(int(ch)) + + dname = deviceName + self.chName2 + str(ch) + ":99]!" + + + + if int(timestamp) == 0: + timestamp = self.getTime() + + topic = 'meshify/db/%s/%s/%s/%s' % (self.company, self.mac, dname, channel) + print topic + msg = """[ { "value":"%s", "timestamp":"%s" } ]""" % (str(value), str(timestamp)) + print msg + self.q.put([topic, msg, 0]) + + def sendToTB(self, payload): + topic = 'v1/devices/me/telemetry' + print(topic, payload) + self.q.put([topic, payload, 0]) + + def sendtodbCH(self, ch, channel, value, timestamp): + + + if int(ch) < 10: + ch = "0" + str(ch) + + dname = self.chName + str(ch) + ":99]!" + + + + if int(timestamp) == 0: + timestamp = self.getTime() + + topic = 'meshify/db/%s/%s/%s/%s' % (self.company, self.mac, dname, channel) + print topic + msg = """[ { "value":"%s", "timestamp":"%s" } ]""" % (str(value), str(timestamp)) + print msg + self.q.put([topic, msg, 0]) + + def sendtodb(self, channel, value, timestamp): + + if int(timestamp) == 0: + timestamp = self.getTime() + if timestamp < 1400499858: + return + else: + timestamp = str(int(timestamp) + int(self.offset)) + + topic = 'meshify/db/%s/%s/%s/%s' % (self.company, self.mac, self.deviceName, channel) + print topic + msg = """[ { "value":"%s", "timestamp":"%s" } ]""" % (str(value), str(timestamp)) + print msg + self.q.put([topic, msg, 0]) + + def sendtodbJSON(self, channel, value, timestamp): + + if int(timestamp) == 0: + timestamp = self.getTime() + if timestamp < 1400499858: + return + else: + timestamp = str(int(timestamp) + int(self.offset)) + + topic = 'meshify/db/%s/%s/%s/%s' % (self.company, self.mac, self.deviceName, channel) + print topic + msg = """[ { "value":%s, "timestamp":"%s" } ]""" % (str(value), str(timestamp)) + print msg + self.q.put([topic, msg, 0]) + def getTime(self): + return str(int(time.time() + int(self.offset))) + + + + diff --git a/meshifyDrivers/mainHPRPI/main.py b/meshifyDrivers/mainHPRPI/main.py new file mode 100644 index 0000000..9e1da06 --- /dev/null +++ b/meshifyDrivers/mainHPRPI/main.py @@ -0,0 +1,1399 @@ +import sys +import string +import socket +import fcntl +import struct +import client as paho +import os +import re +import time +import ssl +try: + import json +except: + import simplejson as json +import thread +import threading +import random +import base64 + +from meshifyData import meshifyData +# from devices import mainMistaway, m1, apg #, gds #cam, +#from devices import gdsMT as gds +import Queue +import pickle +import urllib2 +import urllib +try: + from sqlQueue import myqueue as SQLQ +except: + pass +try: + import SkyWaveDataAPI + SAT = True +except: + SAT = False + +try: + import schedule + sced = True +except: + sced = False + +unitName = "mainMeshify" + +broker = "hp.henrypump.cloud" + +root = os.getcwd() + "/" +# non secure +port = 1883 + +LORA = True +try: + import lora_main +except: + LORA = False + + +# secure port +#port = 1883 + +# driver for a virtual device called "main", it can never send data up to the website, but it hold methods that +# can be called easier than others because it has no unquie name assosicated with it +class main(): + + def __init__(self, name, number, mac, q, mcu, company, offset, mqtt, nodes, topics): + self.topics = topics + self.nodes = nodes + self.offset = offset + self.company = company + self.name = name + self.number = number + self.q = q + self.mqtt = mqtt + # + '_[' + mac + ':' + number[0:2] + ':' + number[2:] + ']!' + self.deviceName = name + print('device name is:') + print(self.deviceName) + mac2 = mac.replace(":", "") + self.mac = mac2.upper() + self.mcu = mcu + self.count = 0 + self.dst = "" + # queue for sets to the mesh network will handeled through a queue in this main driver + self.meshQ = Queue.Queue() + version = "13" # 6 - mistification # 5 - updated for SAT data and generic sets. 4 - devices changed to drivers for dia + + # self.sendtodb("version", version, 0) + thread.start_new_thread(self.registerThread, ()) + + #pickle.dump( version, open( "coreVersion.p", "wb" ) ) + + def sendtodb(self, channel, value, timestamp): + + csplit = re.split(r"(.*?)_\[(.*?)\]", self.deviceName) + nodeTypeName = csplit[1] + uniqueID = csplit[2] + company = "194" + topic = "v1/devices/me/telemetry" + print(topic) + + msg = """[{"%s":"%s"}]""" % (channel, str(value)) + print(msg) + self.q.put([topic, msg, 0]) + + def getTime(self): + return str(int(time.time() + int(self.offset))) + + def main_mcuupdate(self, name, value): + success = self.mcu.firmUpdate(value) + + if success == True: + val = "update to " + value + " was a success" + elif success == False: + print("you need to reboot and pray you didn't brick the MCU") + val = "update to " + value + " failed" + else: + val = "update to " + value + " failed because " + str(success) + print("you need to reboot and pray you didn't brick the MCU") + + # reseting the MCU also resets the Cellular modem, so we need to redo our mqtt connection once the modem comes back to life + time.sleep(180) + try: + self.mqtt.reconnect() + except Exception as e: + print(str(e)) + os.system("/root/reboot") + self.sendtodb(name, val, 0) + + def normalThread(self): + time.sleep(10) + os.system("/root/normalStart.sh") + + def debugThread(self): + time.sleep(10) + os.system("/root/debugStart.sh") + + def rebootThread(self): + time.sleep(10) + os.system("/root/reboot") + + def main_normal(self, name, value): + + if int(value) == 1: + + thread.start_new_thread(self.normalThread, ()) + return True + else: + return False + + def main_debug(self, name, value): + + if int(value) == 1: + + thread.start_new_thread(self.debugThread, ()) + return True + else: + return False + + def main_reboot(self, name, value): + if bool(value) == True: + # reset the modem + # self.mcu.resetModem() + # time.sleep(2) + + # resest the MCU + # try: + # os.system("/root/mcu2reset") + # except: + # pass + thread.start_new_thread(self.rebootThread, ()) + return True + + # os.system("/root/reboot") + + def main_SAT(self, name, value): + print("SAT is SET in MAIN") + st = "date -s @" + str(int(float(value))) + os.system(st) + + return True + + def registerThread(self): + while True: + time.sleep(3600 * 24) + + try: + os.system("/usr/bin/ntpdate pool.ntp.org") + os.system("/usr/sbin/ntpdate pool.ntp.org") + except: + pass + + try: + for name, driver in self.nodes.iteritems(): + + try: + driver.offset = self.offset + driver.company = self.company + + except Exception as e: + print(e) + except: + pass + + def main_register(self, name, value): + # try and sync the time + + try: + #setData = """$$$%s""" % ("main.register", "On", 1) + setData = {} + setData['name'] = 'main.register' + setData['value'] = 'On' + setData['id'] = 1 + print(setData) + self.meshQ.put(setData) + except: + pass + + try: + meshData = meshifyData.meshifyData(self.mac) + self.offset, self.dst, self.company = meshData.getdata() + csplit = re.split(r"(.*?)_\[(.*?)\]", self.deviceName) + nodeTypeName = csplit[1] + uniqueID = csplit[2] + company = "194" + + print(("meshify/sets/" + str(self.company) + "/" + self.mac + "/#")) + self.mqtt.subscribe(("v1/devices/me/requests/+"), 0) + self.mqtt.append("v1/devices/me/telemetry") + + except Exception as e: + print(e) + for name, driver in self.nodes.iteritems(): + + try: + driver.offset = self.offset + driver.company = self.company + driver.register() + + except Exception as e: + print(e) + + return True + + # this is where I need to put the function to have all of my devices check in + + +class meshifyMain(): + + def __init__(self): + + # add google nameserver + os.system("/bin/echo nameserver 8.8.8.8 > /etc/resolv.conf") + # make dictionary for xbee sets + + # check and see if the drivers folder is there: + # if not os.path.exists("drivers"): + # #make the drivers dir + # os.makedirs("drivers") + self.MCU_ON = False + # marker or wether or not the device is a fly + self.FLY = False + self.reconnecting = False + self.mqttQ = Queue.Queue() + # get MAC address, if this breaks, you are [no longer] screwed + try: + mac = self.getHwAddr('eth0') + print("success getting mac") + except: + print("error getting mac") + try: + from uuid import getnode as get_mac + mac = get_mac() + mac = hex(mac).replace('0x', '') + n = 2 + mac = [mac[i:i+n] for i in range(0, len(mac), n)] + newMac = "" + for i in mac: + newMac = newMac + i + ":" + + mac = newMac[:-1] + except: + mac = "12:34:56:78:91:23" + mac = str(mac) + self.OK2Send = True + self.zigMac = mac + self.deviceUrlList = [] + self.deviceVersions = {} + try: + # todo: change this to sd card if present on some devices + self.SQLQ = SQLQ.SqliteQueue(root + "sqlQueue/q2") + except: + pass + + # varible for if the device is connected to the satellites. + self.SAT_COM = False + + # if schedule module is there, then set up the global + try: + if sced == True: + self.schedule = schedule.schedule( + self.sets, self.mqttQ, self.getTime) + else: + self.schedule = False + except Exception as e: + print("####################") + print(e) + + # try and sync the time + try: + os.system("/usr/bin/ntpdate pool.ntp.org") + os.system("/usr/sbin/ntpdate pool.ntp.org") + except: + pass + + # dictionary of all the nodes attched + self.nodes = {} + + mac = mac.replace(":", "") + mac = mac.upper() + print("here is the mac address") + print(mac) + + # get your time offset, DST value and company id from meshify + try: + meshData = meshifyData.meshifyData(mac) + self.offset, self.dst, self.companyId = meshData.getdata() + except: + print("didn't work on api to get meshify data") + + self.mac = mac + + # start the debug thread: + thread.start_new_thread(self.debugThread, ()) + + # set up placeholder for self.mqtt + try: + clientData = {} + with open("mqtt.json", "r") as creds: + clientData = json.load(creds) + except: + clientData = {"clientId": mac, "username": "admin", "password": "columbus"} + with open("mqtt.json", "w+") as creds: + json.dump(clientData, creds) + self.mqtt = paho.Client(client_id=clientData["clientId"], clean_session=True) + + # change to false for mqtt.meshify.com + # self.mqtt.tls_insecure_set(True) + #self.mqtt.tls_set(root + "ca.crt", certfile=root + "client.crt", keyfile=root + "client.key", cert_reqs=ssl.CERT_NONE) + self.mqtt.username_pw_set(clientData["username"], clientData["password"]) + + print("now I'm here") + # Set up the last will and testiment to tell the system that I'm disconneted + lc = int(self.getTime()) + 30 + + self.deviceName = unitName + \ + '_[' + self.zigMac + ':' + "00" + ':' + "00" + ']!' + self.deviceNameMain = "mainMeshify" + \ + '_[' + self.zigMac + ':' + "00" + ':' + "00" + ']!' + + + + + # tell mqtt what to do on connect + self.mqtt.on_connect = self.on_connect + + # tell mqtt which function to call when a message is received + self.mqtt.on_message = self.on_message + self.mqtt.on_disconnect = self.on_disconnect + + # make conneciton to the MCU (only on M1 V5 right now): + + try: + #from mcu import mcu_main + self.MCU_ON = False + except: + self.MCU_ON = False + + try: + if self.MCU_ON == True: + print("mcu is on") + try: + print("mcu loading") + self.mcu = mcu_main.mcu_main() + + # added these for ifttt actions + self.mcu.mac = self.mac + self.mcu.on_message = self.on_message + self.mcu.FLY = self.FLY + + # adding this global for turning all data coming out of the device off + self.mcu.cutAllDataOff = False + + print("mcu loaded") + # now see if we are a fly + flyTries = 0 + self.mcu.xbeeCom(0) + self.mcu.xbee.write("+++") + time.sleep(1) + self.mcu.xbee.write("ATFR" + chr(13)) + time.sleep(1) + while flyTries < 2: + #self.mcu.send_mcu("""{\"SET\": [{\"XBEECOM\": \"0\"}]}\n""") + flyTries += 1 + self.mcu.xbee.read() + self.mcu.xbee.write("+++") + time.sleep(2) + self.mcu.xbee.write("ATDD" + chr(13)) + time.sleep(3) + response = self.mcu.xbee.read() + if "777" in response or LORA: + print("I'm a fly") + # self.mcu.spiOn(0) + self.FLY = True + self.mcu.FLY = self.FLY + if LORA: + #self.mcu.xbee = lora_main.lora() + #self.mcu.xbee.fileTransfer = False + pass + # set the xbee to talk to the coordinator + # this needs to be working for network switching + #self.mcu.xbee.atCommandSet("DH", "0") + #self.mcu.xbee.atCommandSet("DL", "FFFF") + thread.start_new_thread(self.xbeeGetThread, ()) + break + else: + self.FLY = False + except Exception as e: + print(e) + self.mcu.FLY = self.FLY + self.FLY = False + else: + print("MCU NOT ON") + self.mcu = None + + except Exception as e: + print(e) + + # while True: + # time.sleep(3) + # print self.mcu.getDict() + + # turn off connected LED's + if self.MCU_ON: + self.mcu.FLY = self.FLY + self.mcu.ledControl(2, 0, 0) + self.mcu.ledControl(3, 0, 0) + + self.topics = [] + + self.topics.append("v1/device/me/telemetry") + + # wait a few seconds to connect + time.sleep(5) + + ##################################################### + # this is the loading of the devices, if it fails, we still need main to work + # order of operations: + # 1. From API + # 2. From Text file + # 3. From previous loads pickle file + # 4. Nothing, it will only run main (not mainMeshify, just main wich can do reboot and register) + + # the json data should come back from an API, for now it will come from in the root/python_firmware deviceList.txt + + try: + try: + json_data = open('deviceList.txt') + data = json.load(json_data) + data = meshData.checkConfig() + if len(data) < 1: + raise ValueError('too short') + except: + json_data = open('deviceList.txt') + data = json.load(json_data) + # build a list of urls for your device driver buckets + for i in data: + print(i) + # check and see if we are running the dia, if so then kill the mcu thread running the xbee so the dia can have full access to the xbee + if i == "dia" or i == "spider": + self.mcu.xbee.runXbee = False + self.deviceUrlList.append(data[i]) + print(i) + print(data[i]) + + pickle.dump(self.deviceUrlList, open("deviceUrls.p", "wb")) + except Exception as e: + print("####################") + print(e) + # if we can't get it from the web, look for the pickled version + try: + self.deviceUrlList = pickle.load(open("deviceUrls.p", "rb")) + except: + print("couldn't load devices from pickle") + + # try and load your versions dictionary, if this can't load, then load all drivers for the first time + try: + self.deviceVersions = pickle.load( + open(root + "deviceVersions.p", "rb")) + except: + print("couldn't load devices Versions from pickle") + + self.deviceList = {} + + if self.FLY == False: + for i in self.deviceUrlList: + try: + print(i) + # json.load(urllib2.urlopen("http://" + i + "config.txt")) + data = json.load(urllib.urlopen(i + "config.txt")) + #data = urllib2.urlopen(("http://" + i + "config.txt")) + #data = data.read() + print(data) + #data = json.load(data) + + # download the files + print("trying to download the files") + try: + if int(data["releaseVersion"]) > int(self.deviceVersions[(data["deviceName"] + "_" + data["driverId"])]): + print("new version found in repo", data["releaseVersion"]) + for x in data["files"]: + print((i + data["files"][x])) + urllib.urlretrieve( + (i + data["files"][x]), ("./drivers/" + (data["files"][x]))) + else: + print("we have the latest version for: " + (data["deviceName"] + "_" + data["driverId"]) + " of " + str(self.deviceVersions[(data["deviceName"] + "_" + data["driverId"])])) + except: + print("probably didn't have any files to start with, loading all files") + for x in data["files"]: + print((i + data["files"][x])) + urllib.urlretrieve( + (i + data["files"][x]), ("./drivers/" + (data["files"][x]))) + + dList = [data["driverFileName"].replace( + ".py", ""), data["deviceName"], data["driverId"], data["releaseVersion"]] + self.deviceList[(data["deviceName"] + + "_" + data["driverId"])] = dList + print(self.deviceList) + except Exception as e: + print(e) + continue + + # if our device list is still empty, try and grab the saved one + if len(self.deviceList) < 1: + # get the old device list + try: + dl = pickle.load(open("deviceList.p", "rb")) + self.deviceList = dl + print(self.deviceList) + except Exception as e: + print(e) + self.deviceList = {} + print("couldn't load deviceList from pickle") + else: + pickle.dump(self.deviceList, open("deviceList.p", "wb")) + + try: + self.main = main('main', '', self.zigMac, self.mqttQ, self.mcu, + self.companyId, self.offset, self.mqtt, self.nodes, self.topics) + except Exception as e: + print(e) + + self.nodes["main"] = self.main + + # filename, node name, 4 digit identifier, version number (must be an integer) + + # gds deviceList + #deviceList = [ ["mainMistaway", "mainMistaway", "0000", "1"], ["gdsMT", "gdsc", "0005", "1"] ] + + # gate Device List + #deviceList = [ ["mainMistaway", "mainMistaway", "0000", "1"], ["gate", "gate", "0006", "1"] ] + + for device in self.deviceList: + print("trying to load: " + device) + + try: + device = self.deviceList[device] + + # subscribe to the device first, if it breaks, this makes it possible to fix all of this type at once + self.topics.append("v1/devices/me/attributes") + self.topics.append("v1/devices/me/rpc/request/+") + topic = str(("v1/devices/me/attributes")) + print("######", topic) + self.mqtt.subscribe(topic, 0) + topic = str(("v1/devices/me/rpc/request/+")) + print("######", topic) + self.mqtt.subscribe(topic, 0) + + # import the file from the devices folder + imported_module = __import__("drivers." + str(device[0])) + # import the code from the device driver + fileImport = getattr(imported_module, str(device[0])) + # import the driver class from the file + funct = getattr(fileImport, "start") + # start this instance and add it to the devices dictionary + self.nodes[(str(device[1]) + "_" + str(device[2]))] = funct(name=str(device[1]), number=str(device[2]), mac=self.zigMac, + Q=self.mqttQ, mcu=self.mcu, companyId=self.companyId, offset=self.offset, mqtt=self.mqtt, Nodes=self.nodes) + + # add name and version to a dictionary for pickling + self.deviceVersions[(str(device[1]) + "_" + str(device[2])) + ] = self.nodes[(str(device[1]) + "_" + str(device[2]))].version + pickle.dump(self.deviceVersions, open( + root + "deviceVersions.p", "wb")) + + except Exception as e: + print(e) + lc = self.getTime() + value = "Failed Loading: " + \ + str(device[2]) + " on startup with error: " + str(e) + msg = """[ { "value":"%s", "msgId":"%s" } ]""" % ((value), "1") + topic = "meshify/errors/" + self.mqttQ.put([topic, str(msg), 2]) + print(e) + + if self.FLY == False: + print("made it to here, not a fly") + # start logging data!!! + self.reconnecting = True + thread.start_new_thread(self.sqlPoolThread, ()) + + # connect to broker + #thread.start_new_thread(self.connect_thread, ()) + while True: + try: + self.connect_to_broker() + self.reconnecting = False + try: + os.system("/usr/bin/ntpdate pool.ntp.org") + os.system("/usr/sbin/ntpdate pool.ntp.org") + os.system("ntpdate pool.ntp.org") + except: + pass + break + except: + print("didn't work this time") + time.sleep(30) + + # tell the MQTT client to run forever!! + print("made it here") + while True: + print("##### here is the connection status #### ", str(self.mqtt._state)) + try: + if not self.FLY: + print(self.mqtt.loop()) + if self.MCU_ON: + if self.mcu.cutAllDataOff: + print("All Data is Being cut off, no outbound alloud") + time.sleep(5) + continue + + if not self.mqttQ.empty(): # or self.SQLQ.getLen() > 0: + if str(self.mqtt._state) == "1" or self.FLY == True: + + try: + print(self.SQLQ.getLen()) + if self.SQLQ.getLen() > 0: + loopCount = 0 + while self.SQLQ.getLen() > 0: + loopCount += 1 + if loopCount > 20: + loopCount = 0 + if not self.FLY: + print(self.mqtt.loop()) + val = self.SQLQ.popleft() + if not self.FLY: + resp = self.mqtt.publish( + val[0], val[1], val[2]) + + else: + if val[0].split("/")[1] == "db": + xeebVal = json.dumps( + json.loads(val[1])[0]["value"]) + if xeebVal.startswith('"') and xeebVal.endswith('"'): + xeebVal = xeebVal[1:-1] + upld = val[0].split( + "/")[3] + "/" + val[0].split("/")[4] + "/" + val[0].split("/")[5] + "/" + xeebVal + self.xbeeSend(upld) + time.sleep(.5) + else: + respId = (''.join(random.choice( + '0123456789ABCDEF') for i in range(4))) + self.xbeeSend( + "%%" + val[0] + "%%" + json.dumps(json.loads(val[1])[0]["value"]) + "%%") + + except Exception as e: + print(e) + print("no SQL Queue on this device") + + try: + val = self.mqttQ.get(block=False, timeout=1) + # print "Outputting: ", val + if not self.FLY: + resp = self.mqtt.publish( + val[0], val[1], val[2]) + elif self.mcu.xbee.fileTransfer == True: + # if we are sending a file, put this data point back in the Q + self.mqttQ.put(val) + time.sleep(3) + continue + else: + if val[0].split("/")[1] == "db": + xeebVal = json.dumps( + json.loads(val[1])[0]["value"]) + if xeebVal.startswith('"') and xeebVal.endswith('"'): + xeebVal = xeebVal[1:-1] + upld = val[0].split( + "/")[3] + "/" + val[0].split("/")[4] + "/" + val[0].split("/")[5] + "/" + xeebVal + self.xbeeSend(upld) + else: + self.xbeeSend( + "%%" + val[0] + "%%" + json.dumps(json.loads(val[1])[0]["value"]) + "%%") + time.sleep(2) + # print "####### here is the response" + # make a dictionary of response codes xbee + # print resp + except: + print("Q had an error") + time.sleep(1) + loopCount = 0 + while self.mqttQ.qsize() > 20: + loopCount += 1 + if loopCount > 20: + loopCount = 0 + if not self.FLY: + print(self.mqtt.loop()) + try: + val = self.mqttQ.get(block=False, timeout=1) + # print "Outputting: ", val + + if not self.FLY: + resp = self.mqtt.publish( + val[0], val[1], val[2]) + elif self.mcu.xbee.fileTransfer == True: + # if we are sending a file, put this data point back in the Q + self.mqttQ.put(val) + time.sleep(3) + continue + else: + if val[0].split("/")[1] == "db": + xeebVal = json.dumps( + json.loads(val[1])[0]["value"]) + if xeebVal.startswith('"') and xeebVal.endswith('"'): + xeebVal = xeebVal[1:-1] + upld = val[0].split( + "/")[3] + "/" + val[0].split("/")[4] + "/" + val[0].split("/")[5] + "/" + xeebVal + self.xbeeSend(upld) + + else: + self.xbeeSend( + "%%" + val[0] + "%%" + json.dumps(json.loads(val[1])[0]["value"]) + "%%") + time.sleep(2) + + except: + print("Q had an error") + time.sleep(1) + + except Exception as e: + print(e) + time.sleep(10) + if self.reconnecting == False: + self.recon() + time.sleep(.5) + + def connect_thread(self): + while True: + try: + print("####### connecting to the broker ########") + self.connect_to_broker() + + except Exception as e: + print((str(e))) + print("didn't work to connect, restarting....") + os.system('/root/reboot') + + def connect_to_broker(self): + + if self.companyId == "1": + meshData = meshifyData.meshifyData(self.mac) + self.offset, self.dst, self.companyId = meshData.getdata() + + self.mqtt.connect(broker, port, keepalive=120) + self.topic_sub() + + # tell the MQTT client to run forever!! + # self.mqtt.loop_forever() + + def on_disconnect(self, mosq, userdata, rc): + print("################ DISCONECCTED #################") + + # turn off connected LED + if self.MCU_ON: + self.mcu.ledControl(2, 0, 0) + + self.recon() + + def connect_check(self): + # this funtion checks to see how many times its tried to reconnect in the 3 minutes that it goes to sleep + # if it wakes up and you are on the same attempt, then its froze and needs to reboot + local_count = self.count + time.sleep(180) + if local_count == self.count: + # Dont reboot if connected to SAT + if self.SAT_COM == True: + pass + else: + os.system('/root/reboot') + + def sqlPoolThread(self): + + # while spooling the data I am going to attempt a handshake with the SkyWave satellites + # handshake data will include my mac address + # the web will return my current UTC time, which by the time I get it, it will be off by a few seconds, but whos counting ;) + # if it connects, it will set a variable called SAT_COM to True, by way of the main device + print("starting up sat connection") + + # start a Queue for the sending up of data to the SATS + satQ = Queue.Queue() + if SAT == True and self.FLY == False: + thread.start_new_thread( + SkyWaveDataAPI.skywave, (self.sets, self.mac, self.stateData, satQ, self.mcu)) + + # wait 1 minute to see if we connect to the SATs + time.sleep(60) + try: + while self.reconnecting == True: + val = self.mqttQ.get() + try: + if self.SQLQ.getLen() > 5000: + # delete oldest value + trash = self.SQLQ.popleft() + + except: + pass + if self.SAT_COM == True: + print("sending up data to sats") + satQ.put(val) + else: + print("storing up data for later 3g connection") + try: + self.SQLQ.append(val) + except: + pass + except: + pass + + def recon(self): + self.reconnecting = True + thread.start_new_thread(self.sqlPoolThread, ()) + count = 0 + while True: + + if count > 2000: + if self.SAT_COM == True: + count = 0 + else: + break + + count += 1 + print(count) + try: + self.count = count + thread.start_new_thread(self.connect_check, ()) + self.mqtt.reconnect() + self.count = 0 + self.reconnecting = False + break + except: + print("couldn't reconnect, retrying in 30 seconds") + os.system("/bin/echo nameserver 8.8.8.8 > /etc/resolv.conf") + os.system("/sbin/ifup 3g") + time.sleep(30) + + if count > 2000 and not self.FLY: + # don't reboot if connected to SAT + print("rebooting now") + os.system('/root/reboot') + + def on_connect(self, mosq, userdata, rc): + + # turn connected LED + if self.MCU_ON: + self.mcu.ledControl(2, 1, 0) + self.mcu.ledControl(3, 0, 0) + else: + print("on_connect() MCU_OFF = False!!!!!") + + # stop using sat data + self.SAT_COM = False + + # let the watchdog know we are not on SAT anymore + os.system('/bin/echo False > /root/SAT') + + # wait a few seconds for the connection to be made solid + time.sleep(4) + + # set the channel connected to true + lc = self.getTime() + + csplit = re.split(r"(.*?)_\[(.*?)\]", self.deviceName) + nodeTypeName = csplit[1] + uniqueID = csplit[2] + company = "194" + + topic = 'meshify/db/%s/%s/%s/%s/%s' % (company, + "_", nodeTypeName, uniqueID, "connected") + msg = """[{"value":"%s"}]""" % ("true") + self.mqttQ.put([topic, msg, 2]) + + # set the network to 3g + lc = self.getTime() + topic = 'meshify/db/%s/%s/%s/%s/%s' % (company, + "_", nodeTypeName, uniqueID, "net") + msg = """[{"value":"%s"}]""" % ("3g") + self.mqttQ.put([topic, msg, 2]) + + print((" ############### Connection returned " + + str(rc) + " ###############")) + self.topic_sub() + + def topic_sub(self): + for topic in self.topics: + print(topic) + self.mqtt.subscribe(topic, 0) + + def sets(self, msg, sch=False): + entireMsg = msg + # [{"user":"demo@meshify.com","mac":"000CE373293D","company":"188","payload":{"name":"wipom_[00:0c:e3:73:29:3d:00:21]!.do2","value":"1","expires":"1389369695"},"msgId":4478}] + #{"driver.function": "value", "msgId": 1234} + print("I got the set") + print(msg) + try: + data = json.loads(msg) + keys = data.keys() + msgId = 0 + for x in keys: + if x != "msgId": + name = x + if x == "msgId": + msgId = data["msgId"] + value = data[name] + + print(name, value) + + #name = 'tankalarms.water_spt' + # grab the 4 digit unique device code from the end of the MAC address + + n = name.split('.') + channel = n[1] + n = n[0] + + try: + # if the first part of the zigbee mac address is legit, meaning it comes from digi + # then I need to treat this like a mesh node and send it to the dia + # This is only for the SPIDER's Dia Nodes + # the best check is to see if the addresses are derived from the same address + if name.split('.')[0].split("_")[1].replace("[", "").replace(":", "").replace("]!", "")[:-10].upper() == "0013A2": + print("found an xbee") + #setData = """$$$%s""" % (name, value, msgId) + setData = {} + setData['name'] = name + setData['value'] = value + setData['id'] = msgId + print(setData) + self.main.meshQ.put(setData) + return + except: + print("couldn't determine if there was an xbee") + + if n == "main": + if channel == "SAT": + print("got ping from sat cloud app, this mean we have a connection") + try: + if float(value) > 1422910000: + print("SAT CONNECTED!!!") + self.SAT_COM = True + # turn connected LED to amber + if self.MCU_ON: + self.mcu.ledControl(3, 1, 0) + self.mcu.ledControl(2, 0, 0) + # send connected = True + lc = self.getTime() + topic = 'meshify/db/%s/%s/%s/%s' % ( + self.companyId, self.mac, self.deviceName, "connected") + msg = """[ { "value":"%s", "timestamp":"%s" } ]""" % ( + "True", str(lc)) + self.mqttQ.put([topic, msg, 2]) + + # set the network to sat + lc = self.getTime() + topic = 'meshify/db/%s/%s/%s/%s' % ( + self.companyId, self.mac, self.deviceName, "net") + msg = """[ { "value":"%s", "timestamp":"%s" } ]""" % ( + "sat", str(lc)) + self.mqttQ.put([topic, msg, 2]) + + # let the watchdog know if we are on SAT + os.system('/bin/echo True > /root/SAT') + except: + print("didn't send the right date") + nodeName = "main" + nodeNumber = "" + + else: + #m = n + # m = m.split('_') + #m = re.split(r"(.*?)_\[(.*?)\]", m) + # nodeName = m[0] + nodeName = n + #n = n.replace(']!', '') + #n = n[-5:] + #nodeNumber = n.replace(':', '') + nodeNumber = "_" + "0199" #nodeNumber + except: + print("not valid JSON") + return + + # check and see if you are setting the scheduler + if channel.startswith("sch-"): + print("data now being sent to the scheduler") + try: + if self.schedule != False: + self.schedule.message(channel, json.loads(entireMsg)) + return + except: + print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") + print("BAD JSON") + return + + # this grabs the class for the driver for the node: #this should be called className + funcName = nodeName + nodeNumber + # this grabs the method for that channel inside of the driver for that node: + funcChan = nodeName + '_' + channel + print(funcName) + # try: + # # if nodes[funcName] != undefined + # #channelCallback = getattr(nodes[funcName], funcChan) + # #success = channelCallback(channel, value) + # func = getattr(self, funcName) + # except: + # print 'no Set callback found for channel: ' + funcName + # else: + try: + # classFunc = getattr(func, funcChan) #func(value) + #success = classFunc(channel, value) + + # first try a specific callback set, the fundtion will look like: deviceName_0000 + try: + print("trying to find callback for:") + print(funcChan) + channelCallback = getattr(self.nodes[funcName], funcChan) + success = channelCallback(channel, value) + except: + print("looking for genericSet") + # now try a generic one, that looks like self.genericSet(self, channel, value, UnitNumber) Unit number is the second to last 2 digits of the tech name + channelCallback = getattr(self.nodes[funcName], "genericSet") + try: + success = channelCallback( + channel, value, nodeNumber[1:3], nodeNumber[1:]) + except: + success = channelCallback(channel, value, nodeNumber[1:3]) + + #csplit = re.split(r"(.*?)_\[(.*?)\]", self.deviceNameMain) + #nodeTypeName = csplit[1] + #uniqueID = csplit[2] + #company = "194" + + if success == True: + if int(msgId) == 0: + return 0 + if sch == False: + msg = json.dumps({"value": True}) + topic = "v1/devices/me/rpc/response/" + msgId + self.mqttQ.put([topic, str(msg), 2]) + + else: + return 0 + + else: + if sch == False: + msg = json.dumps({"value": False}) + topic = "v1/devices/me/rpc/response/" + msgId + self.mqttQ.put([topic, str(msg), 2]) + + else: + return 1 + except: + if int(msgId) == 0: + return 2 + if sch == False: + msg = json.dumps({"value": False}) + topic = "v1/devices/me/rpc/response/" + msgId + self.mqttQ.put([topic, str(msg), 2]) + print('no Set callback found for channel: ' + funcName) + + else: + return 2 + + # function to be called when a message is received + + def handle_message(self, topic, payload, qos): + try: + + csplit = re.split(r"(.*?)_\[(.*?)\]", self.deviceNameMain) + nodeTypeName = csplit[1] + uniqueID = csplit[2] + company = "194" + + print(("Message received on topic "+topic+" with QoS "+str(qos))) + topics = topic.split("/") + if topics[1] == "files" and topics[4] == "write": + self.OK2Send = False + path = topics[5] + path = path.replace("$", "/") + print(path) + with open(path, 'wb') as fd: + fd.write(payload) + fd.close() + print("file written") + # update the channel mainMistaway_files, a dummy channel for keeping track of file transactions + lc = self.getTime() + topic = 'meshify/db/%s/%s/%s/%s/%s' % ( + company, "_", nodeTypeName, uniqueID, "files") + msg = """[ { "value":"%s" } ]""" % ( + (str(self.mac) + " File Written: " + path)) + self.mqttQ.put([topic, msg, 2]) + elif topics[1] == "files" and topics[4] == "get": + + self.OK2Send = False + path = topics[5] + mqttpath = path + path = path.replace("$", "/") + print(path) + f = open(path, 'rb') + byteArray = f.read() + #byteArray = bytes(byteArray) + topic = 'meshify/get/%s/%s/%s/%s' % ( + company, "_", uniqueID, mqttpath) + msg = byteArray + self.mqtt.publish(topic, msg, 0) + f.close() + print("message sent on topic: ", topic) + + # update the channel mainMistaway_files, a dummy channel for keeping track of file transactions + lc = self.getTime() + topic = 'meshify/db/%s/%s/%s/%s/%s' % ( + company, "_", nodeTypeName, uniqueID, "files") + msg = """[ { "value":"%s" } ]""" % ( + (str(self.mac) + " File Sent: " + path)) + self.mqttQ.put([topic, msg, 2]) + elif topics[1] == "files" and topics[4] == "delete" and payload == "delete": + path = topics[5] + path = path.replace("$", "/") + val = "Success Deleting " + try: + os.remove(path) + except OSError as e: # if failed, report it back to the user ## + val = "Error Deleting " + print(("Error: %s - %s." % (e.filename, e.strerror))) + + # update the channel mainMistaway_files, a dummy channel for keeping track of file transactions + lc = self.getTime() + topic = 'meshify/db/%s/%s/%s/%s/%s' % ( + company, "_", nodeTypeName, uniqueID, "files") + msg = """[ { "value":"%s" } ]""" % ((val + path)) + self.mqttQ.put([topic, msg, 2]) + elif topics[1] == "sets": + self.sets(payload) + self.OK2Send = True + except Exception as e: + print(e) + self.OK2Send = True + print("error understanding the mqtt message") + + def on_message(self, mosq, obj, msg): + print("!!!!!!! ON MESSAGE !!!!!!!") + print(msg.topic) + print(msg.payload) + try: + if "rpc" in msg.topic: + payload = {} + payload["msgId"] = msg.topic.split("/")[-1] + jpayload = json.loads(msg.payload) + payload[jpayload["method"]] = jpayload["params"]["value"] + msg.payload = json.dumps(payload) + self.sets(msg.payload) + except Exception as e: + print(e) + print("HELP HELP HELP") + """ + try: + print(msg) + + csplit = re.split(r"(.*?)_\[(.*?)\]", self.deviceNameMain) + nodeTypeName = csplit[1] + uniqueID = csplit[2] + company = "194" + + m1ID = uniqueID[:18] + "00:30" + rigpumpID = uniqueID[:18] + "01:99" + plevelID = uniqueID[:18] + "01:30" + flowID = uniqueID[:18] + "01:40" + subID = uniqueID[:18] + "02:10" + liteID = uniqueID[:18] + "02:30" + ippID = uniqueID[:18] + "00:90" + + # if the message has either a device name or the mac address of this unit then send to handle_message if not send to dia + # if the name isn't hex, then its an ascii group set + try: + num = int(msg.topic.split("/")[2], 16) + is_hex = True + except: + is_hex = False + + try: + # if the device is in the xbeeQ lookup dictionary, then pass it there + devName = json.loads(msg.payload)[ + 0]["payload"]["name"].split(".")[0] + if devName in self.mcu.xbees: + # this is an xbee node running the dia + xbeeSet = self.mcu.xbees[devName] + xbeeSet.put([msg.topic, msg.payload]) + return + except Exception as e: + print(e) + print("error parsing set for xbee") + # case 1, the message is meant for me or I'm a fly + # case 2, the company ID is a number, not a word, try the set to the xbee again i guess?? + # case 3, the compnay id was actually a group, send it to both the + if msg.topic.split("/")[3] == uniqueID.upper() or msg.topic.split("/")[3] == m1ID.upper() or msg.topic.split("/")[3] == rigpumpID.upper() or msg.topic.split("/")[3] == plevelID.upper() or msg.topic.split("/")[3] == flowID.upper() or msg.topic.split("/")[3] == subID.upper() or msg.topic.split("/")[3] == liteID.upper() or msg.topic.split("/")[3] == ippID.upper() or self.FLY == True: + thread.start_new_thread( + self.handle_message, (msg.topic, msg.payload, msg.qos)) + elif is_hex: + xbeeSet = self.mcu.xbees[msg.topic.split("/")[3]] + xbeeSet.put([msg.topic, msg.payload]) + else: + # this is a group, so set both + thread.start_new_thread( + self.handle_message, (msg.topic, msg.payload, msg.qos)) + xbeeSet = self.mcu.xbees[msg.topic.split("/")[3]] + xbeeSet.put([msg.topic, msg.payload]) + except: + pass""" + + # this retrieves the MAC address from the ethernet card + + def getHwAddr(self, ifname): + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + info = fcntl.ioctl(s.fileno(), 0x8927, + struct.pack('256s', ifname[:15])) + return ''.join(['%02x:' % ord(char) for char in info[18:24]])[:-1] + + def stateData(self): + return self.SAT_COM, self.reconnecting + + def getTime(self): + return str(int(time.time() + int(self.offset))) + # Here is the spot for all of your channel set callbacks + # Callbacks are to be writen with the following nameing scheme: + # deviceName_DeviceId_ChannelName + # the function will take in the new value it is getting set to + # for any action to take place on that channel set you must define a callback + # with the name defined by the nameing scheme above. + + def debugThread(self): + + # this thread is reading the system output of the core as its runs and publishes it to + # the topic: meshify/debug/macaddress + + # TODO Use iMist2 rset tracing module + + try: + print('Number of arguments:', len(sys.argv), 'arguments.') + if str(sys.argv[1]).lower().strip() == "debug=true" or str(sys.argv[1]).lower().strip() == "debug = true" or str(sys.argv[1]).lower().strip() == "true": + try: + if len(str(sys.argv[2]).lower().strip()) > 2: + fileLocation = str(sys.argv[2]).lower().strip() + else: + fileLocation = "/tmp/main.log" + except: + fileLocation = "/tmp/main.log" + + file = open(fileLocation, 'r+') + while 1: + where = file.tell() + line = file.readline() + if not line: + file.seek(where) + file.truncate(0) + time.sleep(1) + file.seek(0) + else: + topic = "meshify/debug/" + self.mac + msg = filter( + lambda x: x in string.printable, str(line)) + self.mqttQ.put([topic, msg, 0]) + except Exception as e: + print("debug error") + print(e) + + def xbeeSend(self, data): + + respId = (''.join(random.choice('0123456789ABCDEF') for i in range(4))) + data = data + respId + "$" + count = 0 + + while True and self.mcu.xbee.fileTransfer == False: + if count > 5: + print("failed getting response after 3 tries") + if self.xbeeConnected == True: + self.xbeeConnected = False + # broadcast to coordinator + #self.mcu.xbee.atCommandSet("DH", "0") + #self.mcu.xbee.atCommandSet("DL", "FFFF") + # turn off connected LED + if self.MCU_ON: + self.mcu.ledControl(2, 0, 0) + self.mcu.ledControl(1, 0, 0) + # let the watchdog know we are not connected to the SPIDER + os.system('/bin/echo False > /root/XBEE') + return False + count += 1 + try: + self.mcu.xbee.write(base64.b64encode(data.encode('utf-8'))) + except Exception as e: + print("error writing xbee to gateway") + print(e) + inner_count = 0 + while True and self.mcu.xbee.fileTransfer == False: + inner_count += 1 + time.sleep(.5) + if respId in self.xbeeResponseList: + print("id found!!!") + if self.xbeeConnected == False: + self.xbeeConnected = True + # turn connected LED + if self.MCU_ON: + self.mcu.ledControl(2, 1, 0) + self.mcu.ledControl(1, 1, 0) + # let the watchdog know we are connected to the SPIDER + os.system('/bin/echo True > /root/XBEE') + return True + elif inner_count > 12: + print("no response found") + break + + def xbeeGetThread(self): + # build a list of last 20 responses ie: if id in listofIds then OK + # when the ID's start populating, turn connected on!! LED + self.xbeeResponseList = [] + self.xbeeConnected = False + data = "" + while True: + if self.mcu.xbee.fileTransfer == True: + time.sleep(5) + continue + else: + try: + newData = self.mcu.xbee.read() + if newData != "": + data += newData + print(data) + if "$$" in data: + list_of_sets = data.split("$$") + if len(list_of_sets[len(list_of_sets) - 1]) < 1: + data = "" + del list_of_sets[-1] + else: + data = list_of_sets[len(list_of_sets) - 1] + del list_of_sets[-1] + for item in list_of_sets: + if len(item) == 4: + print("new response id", item) + self.xbeeResponseList.append(item) + if len(self.xbeeResponseList) > 20: + self.xbeeResponseList.pop(0) + continue + print("we have a complete message") + topic = item.split("%%")[1] + payload = item.split("%%")[2] + self.handle_message(topic, payload, 1) + time.sleep(2) + else: + time.sleep(.5) + continue + + except Exception as e: + print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") + print("xbee read error") + print(e) + time.sleep(1) + + +def startMain(): + try: + test = meshifyMain() + except: + pass + + +startMain() diff --git a/meshifyDrivers/mainHPRPI/mainMeshify.py b/meshifyDrivers/mainHPRPI/mainMeshify.py new file mode 100644 index 0000000..6844278 --- /dev/null +++ b/meshifyDrivers/mainHPRPI/mainMeshify.py @@ -0,0 +1,133 @@ +import time +import os +try: + import json +except: + import simplejson as json +import thread +import threading +import re + + + + + +class start(threading.Thread): + + def __init__(self, name=None, number=None, mac=None, Q=None, mcu=None, companyId=None, offset=None, mqtt=None, Nodes=None): + threading.Thread.__init__(self) + self.daemon = True + self.offset = offset + self.company = companyId + self.name = name + self.number = number + self.q = Q + self.deviceName = name + '_[' + mac + ':' + number[0:2] + ':' + number[2:] + ']!' + print 'device name is:' + print self.deviceName + mac2 = mac.replace(":", "") + self.mac = mac2.upper() + self.version = "17" #hp device management + 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): + #self.mainMistaway_hb('hb', 'On') + self.sendtodb("connected", "true", 0) + + def stop (self): + self.finished.set() + self.join() + + def sendtodb(self, channel, value, timestamp): + if int(timestamp) == 0: + timestamp = self.getTime() + if timestamp < 1400499858: + return + csplit = re.split(r"(.*?)_\[(.*?)\]", self.deviceName) + nodeTypeName = csplit[1] + uniqueID = csplit[2] + company = "194" + + + try: + topic = 'meshify/db/%s/%s/%s/%s/%s' % (company, "_", nodeTypeName, uniqueID, channel) + print topic + if channel == "files": + #for the file structure I had to take off the " " around the value + msg = """[{"value":%s}]""" % (str(value)) + else: + msg = """[{"value":"%s"}]""" % (str(value)) + print msg + self.q.put([topic, msg, 0]) + except: + print "didn't work to send up MQTT data" + + def run(self): + #on startup send the version number + self.sendtodb("version", str(self.version), 0) + while True: + try: + self.mainMeshify_hb('hb', 'On') + self.sendtodb("connected", "true", 0) + time.sleep(3600 * 4) + except Exception, e: + print e + + def mainMeshify_files(self, name, value): + name = 'files' + + + + + + dict = {} + for dirname, dirnames, filenames in os.walk(str(value)): + # print path to all subdirectories first. + + + print "##########################################" + print "new directory: " + dirname + print "##########################################" + # print path to all filenames. + tempDictParent = {} + for filename in filenames: + tempDict = {} + filepath = os.path.join(dirname, filename) + try: + fileMem = os.stat(filepath).st_size + fileDate = os.stat(filepath).st_mtime + except: + fileMem = "" + fileDate = "" + print filepath, fileMem, fileDate + tempDict["mem"] = fileMem + tempDict["date"] = fileDate + tempDictParent[filename] = tempDict + + dict[dirname] = tempDictParent + + + # Advanced usage: + # editing the 'dirnames' list will stop os.walk() from recursing into there. + if '.git' in dirnames: + # don't go into any .git directories. + dirnames.remove('.git') + + value = json.dumps(dict) + self.sendtodb(name, value, 0) + return True + + + def mainMeshify_hb(self, name, value): + self.sendtodb(name, value, 0) + + + def getTime(self): + return str(int(time.time() + int(self.offset))) + diff --git a/meshifyDrivers/mainHPRPI/meshifyData.py b/meshifyDrivers/mainHPRPI/meshifyData.py new file mode 100644 index 0000000..8749714 --- /dev/null +++ b/meshifyDrivers/mainHPRPI/meshifyData.py @@ -0,0 +1,112 @@ + + + +import urllib +try: + import json +except: + import simplejson as json +import pickle + +MAC = "00409D53168A" + +class meshifyData(): + + def __init__(self, MAC): + self.mac = MAC[0:6] + "FF-FF" + MAC[6:] + print "here is the mac: " + self.mac + #set the defaults + self.param_dict = {} + + def checkConfig(self): + + url = "https://f5rrbd3r45.execute-api.us-east-1.amazonaws.com/device_config?mac=" + self.mac + + try: + f = urllib.urlopen(url) + except: + print "Error opening url for remote config" + #return the defaults + return None + + try: + s = f.read() + #print s + if len(s) < 5: + return None + s = s[1:-1].replace("'", '"') + print s + data = json.loads(s) + #if we get there then replace the deviceList.txt + with open('/root/python_firmware/deviceList.txt', 'w') as myfile: + json.dump(data, myfile, indent=4) + return data + except Exception as e: + print e + #return the defaults + return None + + def checkAPI(self): + + + offset = -21600 + dst = False + companyId = "1" + + + url = "https://machines.meshify.com/api/gateway?macaddressForTimezone=" + self.mac + + try: + f = urllib.urlopen(url) + except: + print "Error opening url" + #return the defaults + return offset, dst, companyId + + try: + s = f.read() + print s + data = json.loads(s) + offset = int(data["offset"]) + dst = bool(int(data["dst"])) + print bool(int("0")) + companyId = data["companyId"] + return offset, dst, companyId + except Exception,e: + print e + #return the defaults + return -21600, False, "1" + + def getdata(self): + #if the API fails and the company ID of 1 is returned then you need to + #check and see if you have pickled anything. + #if it doesn't fail, and it gives you something other than 1 + #then you need to repickle the object + self.offset, self.dst, self.companyId = self.checkAPI() + if self.companyId == "1": + try: + self.param_dict = pickle.load( open( "params.p", "rb" ) ) + except: + print self.offset, self.dst, self.companyId + return self.offset, self.dst, self.companyId + try: + self.offset = self.param_dict["offset"] + self.dst = self.param_dict["dst"] + self.companyId = self.param_dict["companyId"] + except: + return -21600, False, "1" + + return self.offset, self.dst, self.companyId + + + else: + self.param_dict["offset"] = self.offset + self.param_dict["dst"] = self.dst + self.param_dict["companyId"] = self.companyId + pickle.dump( self.param_dict, open( "params.p", "wb" ) ) + print self.param_dict + print self.offset, self.dst, self.companyId + return self.offset, self.dst, self.companyId + + + diff --git a/meshifyDrivers/tankalarms/Channel.py b/meshifyDrivers/tankalarms/Channel.py new file mode 100644 index 0000000..353ead7 --- /dev/null +++ b/meshifyDrivers/tankalarms/Channel.py @@ -0,0 +1,299 @@ +"""Define Meshify channel class.""" +import time +from pycomm.ab_comm.clx import Driver as ClxDriver +from pycomm.cip.cip_base import CommError, DataError +from file_logger import filelogger as log +import minimalmodbus + +minimalmodbus.BAUDRATE = 9600 +minimalmodbus.STOPBITS = 1 + + +TAG_DATAERROR_SLEEPTIME = 5 + +def binarray(intval): + """Split an integer into its bits.""" + bin_string = '{0:08b}'.format(intval) + bin_arr = [i for i in bin_string] + bin_arr.reverse() + return bin_arr + + +def read_tag(addr, tag, plc_type="CLX"): + """Read a tag from the PLC.""" + direct = plc_type == "Micro800" + clx = ClxDriver() + try: + if clx.open(addr, direct_connection=direct): + try: + val = clx.read_tag(tag) + clx.close() + return val + except DataError as err: + clx.close() + time.sleep(TAG_DATAERROR_SLEEPTIME) + log.error("Data Error during readTag({}, {}): {}".format(addr, tag, err)) + except CommError: + # err = c.get_status() + + log.error("Could not connect during readTag({}, {})".format(addr, tag)) + except AttributeError as err: + clx.close() + log.error("AttributeError during readTag({}, {}): \n{}".format(addr, tag, err)) + clx.close() + return False + + +def read_array(addr, tag, start, end, plc_type="CLX"): + """Read an array from the PLC.""" + direct = plc_type == "Micro800" + clx = ClxDriver() + if clx.open(addr, direct_connection=direct): + arr_vals = [] + try: + for i in range(start, end): + tag_w_index = tag + "[{}]".format(i) + val = clx.read_tag(tag_w_index) + arr_vals.append(round(val[0], 4)) + if arr_vals: + clx.close() + return arr_vals + else: + log.error("No length for {}".format(addr)) + clx.close() + return False + except Exception: + log.error("Error during readArray({}, {}, {}, {})".format(addr, tag, start, end)) + err = clx.get_status() + clx.close() + log.error(err) + clx.close() + + +def write_tag(addr, tag, val, plc_type="CLX"): + """Write a tag value to the PLC.""" + direct = plc_type == "Micro800" + clx = ClxDriver() + try: + if clx.open(addr, direct_connection=direct): + try: + initial_val = clx.read_tag(tag) + write_status = clx.write_tag(tag, val, initial_val[1]) + clx.close() + return write_status + except DataError as err: + clx_err = clx.get_status() + clx.close() + log.error("--\nDataError during writeTag({}, {}, {}, plc_type={}) -- {}\n{}\n".format(addr, tag, val, plc_type, err, clx_err)) + + except CommError as err: + clx_err = clx.get_status() + log.error("--\nCommError during write_tag({}, {}, {}, plc_type={})\n{}\n--".format(addr, tag, val, plc_type, err)) + clx.close() + return False + + +class Channel(object): + """Holds the configuration for a Meshify channel.""" + + def __init__(self, mesh_name, data_type, chg_threshold, guarantee_sec, map_=False, write_enabled=False): + """Initialize the channel.""" + self.mesh_name = mesh_name + self.data_type = data_type + self.last_value = None + self.value = None + self.last_send_time = 0 + self.chg_threshold = chg_threshold + self.guarantee_sec = guarantee_sec + self.map_ = map_ + self.write_enabled = write_enabled + + def __str__(self): + """Create a string for the channel.""" + return "{}\nvalue: {}, last_send_time: {}".format(self.mesh_name, self.value, self.last_send_time) + + def check(self, new_value, force_send=False): + """Check to see if the new_value needs to be stored.""" + send_needed = False + send_reason = "" + if self.data_type == 'BOOL' or self.data_type == 'STRING': + if self.last_send_time == 0: + send_needed = True + send_reason = "no send time" + elif self.value is None: + send_needed = True + send_reason = "no value" + elif self.value != new_value: + if self.map_: + if not self.value == self.map_[new_value]: + send_needed = True + send_reason = "value change" + else: + send_needed = True + send_reason = "value change" + elif (time.time() - self.last_send_time) > self.guarantee_sec: + send_needed = True + send_reason = "guarantee sec" + elif force_send: + send_needed = True + send_reason = "forced" + else: + if self.last_send_time == 0: + send_needed = True + send_reason = "no send time" + elif self.value is None: + send_needed = True + send_reason = "no value" + elif abs(self.value - new_value) > self.chg_threshold: + send_needed = True + send_reason = "change threshold" + elif (time.time() - self.last_send_time) > self.guarantee_sec: + send_needed = True + send_reason = "guarantee sec" + elif force_send: + send_needed = True + send_reason = "forced" + if send_needed: + self.last_value = self.value + if self.map_: + try: + self.value = self.map_[new_value] + except KeyError: + log.error("Cannot find a map value for {} in {} for {}".format(new_value, self.map_, self.mesh_name)) + self.value = new_value + else: + self.value = new_value + self.last_send_time = time.time() + log.info("Sending {} for {} - {}".format(self.value, self.mesh_name, send_reason)) + return send_needed + + def read(self): + """Read the value.""" + pass + +def identity(sent): + """Return exactly what was sent to it.""" + return sent + + +class ModbusChannel(Channel): + """Modbus channel object.""" + + def __init__(self, mesh_name, register_number, data_type, chg_threshold, guarantee_sec, channel_size=1, map_=False, write_enabled=False, transform_fn=identity): + """Initialize the channel.""" + super(ModbusChannel, self).__init__(mesh_name, data_type, chg_threshold, guarantee_sec, map_, write_enabled) + self.mesh_name = mesh_name + self.register_number = register_number + self.channel_size = channel_size + self.data_type = data_type + self.last_value = None + self.value = None + self.last_send_time = 0 + self.chg_threshold = chg_threshold + self.guarantee_sec = guarantee_sec + self.map_ = map_ + self.write_enabled = write_enabled + self.transform_fn = transform_fn + + def read(self, mbsvalue): + """Return the transformed read value.""" + return self.transform_fn(mbsvalue) + + +class PLCChannel(Channel): + """PLC Channel Object.""" + + def __init__(self, ip, mesh_name, plc_tag, data_type, chg_threshold, guarantee_sec, map_=False, write_enabled=False, plc_type='CLX'): + """Initialize the channel.""" + super(PLCChannel, self).__init__(mesh_name, data_type, chg_threshold, guarantee_sec, map_, write_enabled) + self.plc_ip = ip + self.mesh_name = mesh_name + self.plc_tag = plc_tag + self.data_type = data_type + self.last_value = None + self.value = None + self.last_send_time = 0 + self.chg_threshold = chg_threshold + self.guarantee_sec = guarantee_sec + self.map_ = map_ + self.write_enabled = write_enabled + self.plc_type = plc_type + + def read(self): + """Read the value.""" + plc_value = None + if self.plc_tag and self.plc_ip: + read_value = read_tag(self.plc_ip, self.plc_tag, plc_type=self.plc_type) + if read_value: + plc_value = read_value[0] + + return plc_value + + +class BoolArrayChannels(Channel): + """Hold the configuration for a set of boolean array channels.""" + + def __init__(self, ip, mesh_name, plc_tag, data_type, chg_threshold, guarantee_sec, map_=False, write_enabled=False): + """Initialize the channel.""" + super(BoolArrayChannels, self).__init__(mesh_name, data_type, chg_threshold, guarantee_sec, map_, write_enabled) + self.plc_ip = ip + self.mesh_name = mesh_name + self.plc_tag = plc_tag + self.data_type = data_type + self.last_value = None + self.value = None + self.last_send_time = 0 + self.chg_threshold = chg_threshold + self.guarantee_sec = guarantee_sec + self.map_ = map_ + self.write_enabled = write_enabled + + def compare_values(self, new_val_dict): + """Compare new values to old values to see if the values need storing.""" + send = False + for idx in new_val_dict: + try: + if new_val_dict[idx] != self.last_value[idx]: + send = True + except KeyError: + log.error("Key Error in self.compare_values for index {}".format(idx)) + send = True + return send + + def read(self, force_send=False): + """Read the value and check to see if needs to be stored.""" + send_needed = False + send_reason = "" + if self.plc_tag: + val = read_tag(self.plc_ip, self.plc_tag) + if val: + bool_arr = binarray(val[0]) + new_val = {} + for idx in self.map_: + try: + new_val[self.map_[idx]] = bool_arr[idx] + except KeyError: + log.error("Not able to get value for index {}".format(idx)) + + if self.last_send_time == 0: + send_needed = True + send_reason = "no send time" + elif self.value is None: + send_needed = True + send_reason = "no value" + elif self.compare_values(new_val): + send_needed = True + send_reason = "value change" + elif (time.time() - self.last_send_time) > self.guarantee_sec: + send_needed = True + send_reason = "guarantee sec" + elif force_send: + send_needed = True + send_reason = "forced" + + if send_needed: + self.value = new_val + self.last_value = self.value + self.last_send_time = time.time() + log.info("Sending {} for {} - {}".format(self.value, self.mesh_name, send_reason)) + return send_needed diff --git a/meshifyDrivers/tankalarms/Tags.py b/meshifyDrivers/tankalarms/Tags.py new file mode 100644 index 0000000..6499d12 --- /dev/null +++ b/meshifyDrivers/tankalarms/Tags.py @@ -0,0 +1,24 @@ +from Channel import PLCChannel, ModbusChannel +from tankalarms import PLC_IP_ADDRESS + +tags = [ + PLCChannel(PLC_IP_ADDRESS, "water_level","WaterTxLevel","REAL", 0.5, 3600, plc_type="Micro800"), + PLCChannel(PLC_IP_ADDRESS, "water_batt_volts","WaterTxBatVolts","REAL", 5, 3600, plc_type="Micro800"), + PLCChannel(PLC_IP_ADDRESS, "water_rssi","WaterTxRSSI","REAL", 5, 3600, plc_type="Micro800"), + PLCChannel(PLC_IP_ADDRESS, "water_hihi_spt","WaterTx_HHSP","REAL", 5, 3600, plc_type="Micro800"), + PLCChannel(PLC_IP_ADDRESS, "water_hi_spt","WaterTx_HSP ","REAL", 5, 3600, plc_type="Micro800"), + PLCChannel(PLC_IP_ADDRESS, "water_hihi_alm","WaterTx_HHAlrm","BOOL", 0, 3600, plc_type="Micro800"), + PLCChannel(PLC_IP_ADDRESS, "water_hi_alm","WaterTx_HAlrm","BOOL", 0, 3600, plc_type="Micro800"), + PLCChannel(PLC_IP_ADDRESS, "water_batt_lolo_alm","WaterTxBatVolts_LLAlrm","BOOL", 0, 3600, plc_type="Micro800"), + PLCChannel(PLC_IP_ADDRESS, "water_comm_fail_alm","WaterTx_RF_CommFail","BOOL", 0, 3600, plc_type="Micro800"), + PLCChannel(PLC_IP_ADDRESS, "oil_level","OilTxLevel","REAL", 0.5, 3600, plc_type="Micro800"), + PLCChannel(PLC_IP_ADDRESS, "oil_batt_volts","OilTxBatVolts","REAL", 5, 3600, plc_type="Micro800"), + PLCChannel(PLC_IP_ADDRESS, "oil_rssi","OilTxRSSI","REAL", 5, 3600, plc_type="Micro800"), + PLCChannel(PLC_IP_ADDRESS, "oil_hihi_spt","OilTx_HHSP","REAL", 5, 3600, plc_type="Micro800"), + PLCChannel(PLC_IP_ADDRESS, "oil_hi_spt","OilTx_HSP ","REAL", 5, 3600, plc_type="Micro800"), + PLCChannel(PLC_IP_ADDRESS, "oil_hihi_alm","OilTx_HHAlrm","BOOL", 0, 3600, plc_type="Micro800"), + PLCChannel(PLC_IP_ADDRESS, "oil_hi_alm","OilTx_HAlrm","BOOL", 0, 3600, plc_type="Micro800"), + PLCChannel(PLC_IP_ADDRESS, "oil_batt_lolo_alm","OilTxBatVolts_LLAlrm","BOOL", 0, 3600, plc_type="Micro800"), + PLCChannel(PLC_IP_ADDRESS, "oil_comm_fail_alm","OilTx_RF_CommFail","BOOL", 0, 3600, plc_type="Micro800"), + PLCChannel(PLC_IP_ADDRESS, "alarmreset","Reset","BOOL", 0, 3600, plc_type="Micro800") +] \ No newline at end of file diff --git a/meshifyDrivers/tankalarms/config.txt b/meshifyDrivers/tankalarms/config.txt new file mode 100644 index 0000000..20519a9 --- /dev/null +++ b/meshifyDrivers/tankalarms/config.txt @@ -0,0 +1,14 @@ +{ + "files": { + "file3": "file_logger.py", + "file2": "Channel.py", + "file1": "tankalarms.py", + "file6": "persistence.py", + "file5": "utilities.py", + "file4": "Tags.py" + }, + "deviceName": "tankalarms", + "releaseVersion": "3", + "driverFileName": "tankalarms.py", + "driverId": "0100" +} \ No newline at end of file diff --git a/meshifyDrivers/tankalarms/device_base.py b/meshifyDrivers/tankalarms/device_base.py new file mode 100644 index 0000000..9745b1a --- /dev/null +++ b/meshifyDrivers/tankalarms/device_base.py @@ -0,0 +1,200 @@ +import types +import traceback +import binascii +import threading +import time +import thread +import os +import struct +import sys +import textwrap + +class deviceBase(): + + def __init__(self, name=None, number=None, mac=None, Q=None, mcu=None, companyId=None, offset=None, mqtt=None, Nodes=None): + self.offset = offset + self.company = companyId + self.name = name + self.number = number + self.q = Q + self.deviceName = name + '_[' + mac + ':' + number[0:2] + ':' + number[2:] + ']!' + self.chName = "M1" + '_[' + mac + ':' + self.chName2 = '_[' + mac + ':' + print 'device name is:' + print self.deviceName + mac2 = mac.replace(":", "") + self.mac = mac2.upper() + self.address = 1 + self.debug = True + self.mcu = mcu + self.firstRun = True + self.mqtt = mqtt + self.nodes = Nodes + #local dictionary of derived nodes ex: localNodes[tank_0199] = self + self.localNodes = {} + os.system("chmod 777 /root/reboot") + os.system("echo nameserver 8.8.8.8 > /etc/resolv.conf") + + + def sendtodbLoc(self, ch, channel, value, timestamp, deviceName, mac): + + + #this will add your derived nodes the master nodes list, allowing them to receive sets!! + localNodesName = deviceName + "_" + str(ch) + "99" + + if not self.localNodes.has_key(localNodesName): + self.localNodes[localNodesName] = True + self.nodes[localNodesName] = self + + #make the techname + lst = textwrap.wrap(str(mac), width=2) + tech = "" + for i in range(len(lst)): + tech += lst[i].lower() + ":" + + + chName2 = '_[' + tech + + if int(ch) < 10: + ch = "0" + str(int(ch)) + + if len(ch) > 2: + ch = ch[:-2] + + dname = deviceName + chName2 + str(ch) + ":98]!" + + if int(timestamp) == 0: + timestamp = self.getTime() + + topic = 'meshify/db/%s/%s/%s/%s' % (self.company, mac, dname, channel) + print topic + msg = """[ { "value":"%s", "timestamp":"%s" } ]""" % (str(value), str(timestamp)) + print msg + self.q.put([topic, msg, 0]) + + def sendtodbDevJSON(self, ch, channel, value, timestamp, deviceName): + + if int(ch) < 10: + ch = "0" + str(int(ch)) + dname = deviceName + self.chName2 + str(ch) + ":99]!" + if int(timestamp) == 0: + timestamp = self.getTime() + + topic = 'meshify/db/%s/%s/%s/%s' % (self.company, self.mac, dname, channel) + print topic + msg = """[ { "value":%s, "timestamp":"%s" } ]""" % (str(value), str(timestamp)) + print msg + self.q.put([topic, msg, 0]) + + def sendtodbLora(self, ch, channel, value, timestamp, deviceName): + + if ":" not in ch: + ch = ch[0:2] + ":" + ch[2:4] + + #this will add your derived nodes the master nodes list, allowing them to receive sets!! + localNodesName = deviceName + "_" + str(ch).replace(':', "") + + if not self.localNodes.has_key(localNodesName): + self.localNodes[localNodesName] = True + self.nodes[localNodesName] = self + + + + dname = deviceName + self.chName2 + str(ch) + "]!" + + + + if int(timestamp) == 0: + timestamp = self.getTime() + + topic = 'meshify/db/%s/%s/%s/%s' % (self.company, self.mac, dname, channel) + print topic + msg = """[ { "value":"%s", "timestamp":"%s" } ]""" % (str(value), str(timestamp)) + print msg + self.q.put([topic, msg, 0]) + + def sendtodbDev(self, ch, channel, value, timestamp, deviceName): + + + #this will add your derived nodes the master nodes list, allowing them to receive sets!! + localNodesName = deviceName + "_" + str(ch) + "99" + + if not self.localNodes.has_key(localNodesName): + self.localNodes[localNodesName] = True + self.nodes[localNodesName] = self + + if int(ch) < 10: + ch = "0" + str(int(ch)) + + dname = deviceName + self.chName2 + str(ch) + ":99]!" + + + + if int(timestamp) == 0: + timestamp = self.getTime() + + topic = 'meshify/db/%s/%s/%s/%s' % (self.company, self.mac, dname, channel) + print topic + msg = """[ { "value":"%s", "timestamp":"%s" } ]""" % (str(value), str(timestamp)) + print msg + self.q.put([topic, msg, 0]) + + def sendToTB(self, payload): + topic = 'v1/devices/me/telemetry' + print(topic, payload) + self.q.put([topic, payload, 0]) + + def sendtodbCH(self, ch, channel, value, timestamp): + + + if int(ch) < 10: + ch = "0" + str(ch) + + dname = self.chName + str(ch) + ":99]!" + + + + if int(timestamp) == 0: + timestamp = self.getTime() + + topic = 'meshify/db/%s/%s/%s/%s' % (self.company, self.mac, dname, channel) + print topic + msg = """[ { "value":"%s", "timestamp":"%s" } ]""" % (str(value), str(timestamp)) + print msg + self.q.put([topic, msg, 0]) + + def sendtodb(self, channel, value, timestamp): + + if int(timestamp) == 0: + timestamp = self.getTime() + if timestamp < 1400499858: + return + else: + timestamp = str(int(timestamp) + int(self.offset)) + + topic = 'meshify/db/%s/%s/%s/%s' % (self.company, self.mac, self.deviceName, channel) + print topic + msg = """[ { "value":"%s", "timestamp":"%s" } ]""" % (str(value), str(timestamp)) + print msg + self.q.put([topic, msg, 0]) + + def sendtodbJSON(self, channel, value, timestamp): + + if int(timestamp) == 0: + timestamp = self.getTime() + if timestamp < 1400499858: + return + else: + timestamp = str(int(timestamp) + int(self.offset)) + + topic = 'meshify/db/%s/%s/%s/%s' % (self.company, self.mac, self.deviceName, channel) + print topic + msg = """[ { "value":%s, "timestamp":"%s" } ]""" % (str(value), str(timestamp)) + print msg + self.q.put([topic, msg, 0]) + def getTime(self): + return str(int(time.time() + int(self.offset))) + + + + diff --git a/meshifyDrivers/tankalarms/file_logger.py b/meshifyDrivers/tankalarms/file_logger.py new file mode 100644 index 0000000..5895dee --- /dev/null +++ b/meshifyDrivers/tankalarms/file_logger.py @@ -0,0 +1,18 @@ +"""Logging setup for tankalarms""" +import logging +from logging.handlers import RotatingFileHandler +import sys + +log_formatter = logging.Formatter('%(asctime)s %(levelname)s %(funcName)s(%(lineno)d) %(message)s') +log_file = './tankalarms.log' +my_handler = RotatingFileHandler(log_file, mode='a', maxBytes=500*1024, + backupCount=2, encoding=None, delay=0) +my_handler.setFormatter(log_formatter) +my_handler.setLevel(logging.INFO) +filelogger = logging.getLogger('tankalarms') +filelogger.setLevel(logging.INFO) +filelogger.addHandler(my_handler) + +console_out = logging.StreamHandler(sys.stdout) +console_out.setFormatter(log_formatter) +filelogger.addHandler(console_out) diff --git a/meshifyDrivers/tankalarms/persistence.py b/meshifyDrivers/tankalarms/persistence.py new file mode 100644 index 0000000..8c8703f --- /dev/null +++ b/meshifyDrivers/tankalarms/persistence.py @@ -0,0 +1,21 @@ +"""Data persistance functions.""" +# if more advanced persistence is needed, use a sqlite database +import json + + +def load(filename="persist.json"): + """Load persisted settings from the specified file.""" + try: + with open(filename, 'r') as persist_file: + return json.load(persist_file) + except Exception: + return False + + +def store(persist_obj, filename="persist.json"): + """Store the persisting settings into the specified file.""" + try: + with open(filename, 'w') as persist_file: + return json.dump(persist_obj, persist_file, indent=4) + except Exception: + return False diff --git a/meshifyDrivers/tankalarms/tankalarms.py b/meshifyDrivers/tankalarms/tankalarms.py new file mode 100644 index 0000000..fe5f5f3 --- /dev/null +++ b/meshifyDrivers/tankalarms/tankalarms.py @@ -0,0 +1,197 @@ +"""Driver for tankalarms""" + +import threading +import json +import time +from random import randint +import os +from device_base import deviceBase +from Channel import PLCChannel, ModbusChannel,read_tag, write_tag, TAG_DATAERROR_SLEEPTIME +import persistence +from utilities import get_public_ip_address, get_private_ip_address +from file_logger import filelogger as log +PLC_IP_ADDRESS = "192.168.1.12" +from Tags import tags + +_ = None + +log.info("tankalarms startup") + +# GLOBAL VARIABLES +WAIT_FOR_CONNECTION_SECONDS = 20 +IP_CHECK_PERIOD = 60 + + +CHANNELS = tags + +# PERSISTENCE FILE +PERSIST = persistence.load() + + +class start(threading.Thread, deviceBase): + """Start class required by Meshify.""" + + def __init__(self, name=None, number=None, mac=None, Q=None, mcu=None, + companyId=None, offset=None, mqtt=None, Nodes=None): + """Initialize 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 = "3" + self.finished = threading.Event() + self.force_send = False + self.public_ip_address = "" + self.public_ip_address_last_checked = 0 + self.private_ip_address = "" + 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.sendtodb("log", "BOOM! Booted.", 0) + pass + + def run(self): + """Actually run the driver.""" + for i in range(0, WAIT_FOR_CONNECTION_SECONDS): + print("tankalarms driver will start in {} seconds".format(WAIT_FOR_CONNECTION_SECONDS - i)) + time.sleep(1) + log.info("BOOM! Starting tankalarms driver...") + + self._check_ip_address() + + self.nodes["tankalarms_0199"] = self + + send_loops = 0 + + while True: + try: + now = time.time() + if self.force_send: + log.warning("FORCE SEND: TRUE") + if int(time.time()) % 600 == 0 or self.force_send: + payload = {"ts": round(time.time()/600)*600*1000, "values": {}} + for chan in CHANNELS: + val = chan.read() + payload["values"][chan.mesh_name] = val + + self._check_ip_address() + payload["values"]["public_ip_address"] = self.public_ip_address + payload["values"]["private_ip_address"] = self.private_ip_address + self.sendToTB(json.dumps(payload)) + time.sleep(TAG_DATAERROR_SLEEPTIME) # sleep to allow Micro800 to handle ENET requests + + # print("tankalarms driver still alive...") + if self.force_send: + if send_loops > 2: + log.warning("Turning off force_send") + self.force_send = False + send_loops = 0 + else: + send_loops += 1 + + + + + except Exception as e: + log.error(e) + + + def _check_ip_address(self): + """Check the public IP address and send to Meshify if changed.""" + self.public_ip_address_last_checked = time.time() + test_public_ip = get_public_ip_address() + test_public_ip = test_public_ip + test_private_ip = get_private_ip_address() + if not test_public_ip == self.public_ip_address and not test_public_ip == "0.0.0.0": + #self.sendtodbDev(1, 'public_ip_address', test_public_ip, 0, 'tankalarms') + self.public_ip_address = test_public_ip + if not test_private_ip == self.private_ip_address: + #self.sendtodbDev(1, 'private_ip_address', test_private_ip, 0, 'tankalarms') + self.private_ip_address = test_private_ip + hostname = "8.8.8.8" + response = 1 + try: + response = os.system("ping -c 1 " + hostname + " > /dev/null 2>&1") + except Exception as e: + print("Something went wrong in ping: {}".format(e)) + + #and then check the response... + if response == 0: + print(hostname, 'is up!') + self.ping_counter = 0 + else: + print(hostname, 'is down!') + self.ping_counter += 1 + + if self.ping_counter >= 3: + print("Rebooting because no internet detected") + os.system('reboot') + + + def tankalarms_sync(self, name, value): + """Sync all data from the driver.""" + self.force_send = True + # self.sendtodb("log", "synced", 0) + return True + + def tankalarms_writeplctag(self, name, value): + """Write a value to the PLC.""" + new_val = json.loads(str(value).replace("'", '"')) + tag_n = str(new_val['tag']) # "cmd_Start" + val_n = new_val['val'] + write_res = write_tag(str(PLC_IP_ADDRESS), tag_n, val_n, plc_type="Micro800") + print("Result of tankalarms_writeplctag(self, {}, {}) = {}".format(name, value, write_res)) + if write_res is None: + write_res = "Error writing to PLC..." + return write_res + + def tankalarms_alarmreset(self, name, value): + log.info("Value received is {}".format(value)) + if value == 1 or value == "1": + write_res = write_tag(str(PLC_IP_ADDRESS), "Reset", 1, plc_type="Micro800") + log.info("Result of tankalarms_alarmreset {}, {}, {}".format(name, value, write_res)) + if write_res is None: + write_res = "Error writing to PLC..." + return write_res + return False + + def tankalarms_water_hihi_spt(self, name, value): + log.info("Value received is {}".format(value)) + write_res = write_tag(str(PLC_IP_ADDRESS), "WaterTx_HHSP", float(value), plc_type="Micro800") + log.info("Result of tankalarms_water_hihi_spt {}, {}, {}".format(name, value, write_res)) + if write_res is None: + write_res = "Error writing to PLC..." + return write_res + + + def tankalarms_water_hi_spt(self, name, value): + log.info("Value received is {}".format(value)) + write_res = write_tag(str(PLC_IP_ADDRESS), "WaterTx_HSP", float(value), plc_type="Micro800") + log.info("Result of tankalarms_water_hi_spt {}, {}, {}".format(name, value, write_res)) + if write_res is None: + write_res = "Error writing to PLC..." + return write_res + + def tankalarms_oil_hihi_spt(self, name, value): + log.info("Value received is {}".format(value)) + write_res = write_tag(str(PLC_IP_ADDRESS), "OilTx_HHSP", float(value), plc_type="Micro800") + log.info("Result of tankalarms_oil_hihi_spt {}, {}, {}".format(name, value, write_res)) + if write_res is None: + write_res = "Error writing to PLC..." + return write_res + + + def tankalarms_oil_hi_spt(self, name, value): + log.info("Value received is {}".format(value)) + write_res = write_tag(str(PLC_IP_ADDRESS), "OilTx_HSP", float(value), plc_type="Micro800") + log.info("Result of tankalarms_oil_hi_spt {}, {}, {}".format(name, value, write_res)) + if write_res is None: + write_res = "Error writing to PLC..." + return write_res + + diff --git a/meshifyDrivers/tankalarms/utilities.py b/meshifyDrivers/tankalarms/utilities.py new file mode 100644 index 0000000..fd69e47 --- /dev/null +++ b/meshifyDrivers/tankalarms/utilities.py @@ -0,0 +1,62 @@ +"""Utility functions for the driver.""" +import socket +import struct +import urllib +import contextlib +def get_private_ip_address(): + """Find the private IP Address of the host device.""" + try: + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + sock.connect(("8.8.8.8", 80)) + ip_address = sock.getsockname()[0] + sock.close() + except Exception as e: + return e + + return ip_address + +def get_public_ip_address(): + ip_address = "0.0.0.0" + try: + with contextlib.closing(urllib.urlopen("http://checkip.amazonaws.com")) as url: + ip_address = url.read() + except Exception as e: + print("Could not resolve address: {}".format(e)) + return ip_address + return ip_address[:-1] + +def int_to_float16(int_to_convert): + """Convert integer into float16 representation.""" + bin_rep = ('0' * 16 + '{0:b}'.format(int_to_convert))[-16:] + sign = 1.0 + if int(bin_rep[0]) == 1: + sign = -1.0 + exponent = float(int(bin_rep[1:6], 2)) + fraction = float(int(bin_rep[6:17], 2)) + + if exponent == float(0b00000): + return sign * 2 ** -14 * fraction / (2.0 ** 10.0) + elif exponent == float(0b11111): + if fraction == 0: + return sign * float("inf") + return float("NaN") + frac_part = 1.0 + fraction / (2.0 ** 10.0) + return sign * (2 ** (exponent - 15)) * frac_part + + +def ints_to_float(int1, int2): + """Convert 2 registers into a floating point number.""" + mypack = struct.pack('>HH', int1, int2) + f_unpacked = struct.unpack('>f', mypack) + print("[{}, {}] >> {}".format(int1, int2, f_unpacked[0])) + return f_unpacked[0] + + +def degf_to_degc(temp_f): + """Convert deg F to deg C.""" + return (temp_f - 32.0) * (5.0/9.0) + + +def degc_to_degf(temp_c): + """Convert deg C to deg F.""" + return temp_c * 1.8 + 32.0