diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bf52449 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +BlueTooth/config.json diff --git a/BlueTooth/bluemaestroscan.py b/BlueTooth/bluemaestroscan.py index 70343d3..066483e 100644 --- a/BlueTooth/bluemaestroscan.py +++ b/BlueTooth/bluemaestroscan.py @@ -52,39 +52,39 @@ def returnnumberpacket(pkt): - myInteger = 0 - multiple = 256 - for c in pkt: - myInteger += struct.unpack("B",c)[0] * multiple - multiple = 1 - return myInteger + myInteger = 0 + multiple = 256 + for c in pkt: + myInteger += struct.unpack("B",c)[0] * multiple + multiple = 1 + return myInteger def returnstringpacket(pkt): - myString = ""; - for c in pkt: - myString += "%02x" %struct.unpack("B",c)[0] - return myString + myString = ""; + for c in pkt: + myString += "%02x" %struct.unpack("B",c)[0] + return myString def printpacket(pkt): - for c in pkt: - sys.stdout.write("%02x " % struct.unpack("B",c)[0]) + for c in pkt: + sys.stdout.write("%02x " % struct.unpack("B",c)[0]) def get_packed_bdaddr(bdaddr_string): - packable_addr = [] - addr = bdaddr_string.split(':') - addr.reverse() - for b in addr: - packable_addr.append(int(b, 16)) - return struct.pack(" 0: - print "%s/temperature = %.2f" % (returnedList["mac"],returnedList["temp"]) - # Publish to the MQTT channel - try: - mqttc.connect(MOSQUITTO_HOST,MOSQUITTO_PORT); - print 'Updating temp for {0} to {1}'.format(returnedList["mac"],float(returnedList["temp"])) - (result1,mid) = mqttc.publish("%s/temperature" % returnedList["mac"],float(returnedList["temp"])) - time.sleep(1) - - mqttc.connect(MOSQUITTO_HOST,MOSQUITTO_PORT); - (result2,mid) = mqttc.publish("%s/humidity" % returnedList["mac"],float(returnedList["humidity"])) - print 'Updating humidity {0} = {1}'.format(returnedList["mac"],float(returnedList["humidity"])) - time.sleep(1) - - mqttc.connect(MOSQUITTO_HOST,MOSQUITTO_PORT); - (result3,mid) = mqttc.publish("%s/dewpoint" % returnedList["mac"],float(returnedList["dewpoint"])) - print 'Updating {0}/dewpoint = {1}'.format( returnedList["mac"],returnedList["dewpoint"]) - time.sleep(1) - - mqttc.connect(MOSQUITTO_HOST,MOSQUITTO_PORT); - (result4,mid) = mqttc.publish("%s/name" % returnedList["mac"],returnedList["name"]) - print 'Updating name {0}/name = {1}'.format(returnedList["mac"],returnedList["name"]) - time.sleep(1) - - mqttc.connect(MOSQUITTO_HOST,MOSQUITTO_PORT); - (result5,mid) = mqttc.publish("%s/battery" % returnedList["mac"],float(returnedList["battery"])) - print 'Updating battery {0}/battery = {1}'.format(returnedList["mac"],float(returnedList["battery"])) - print 'Updated Battery' - #print 'MQTT Updated result {0} and {1} and {2} and {3} and {4} and {5}'.format(result1,result2,result3,result4,result5) - - if result1 == 1 or result2 == 1 or result3 == 1 or result4 == 1 or result5 ==1: - raise ValueError('Result for one message was not 0') -# mqttc.disconnect() - print "Sleeping for %d seconds" % FREQUENCY_SECONDS - time.sleep(FREQUENCY_SECONDS) - mqttc.loop(1) - - - except Exception,e: - # Null out the worksheet so a login is performed at the top of the loop. - mqttc.disconnect() - print('Append error, logging in again: ' + str(e)) - print "Sleeping for 60 seconds" - time.sleep(60) - continue - else: - print "Sleeping for 30 seconds" - time.sleep(30) - except Exception,e: - # Error appending data, most likely because credentials are stale. - # Null out the worksheet so a login is performed at the top of the loop. - print('Append error, logging in again: ' + str(e)) - print "Sleeping for 60 seconds" - time.sleep(60) - continue - diff --git a/BlueTooth/mqtt.bluetooth/Dockerfile b/BlueTooth/mqtt.bluetooth/Dockerfile new file mode 100644 index 0000000..6c383c3 --- /dev/null +++ b/BlueTooth/mqtt.bluetooth/Dockerfile @@ -0,0 +1,11 @@ +FROM python:3 + +WORKDIR /app +RUN mkdir /config + +#COPY requirements.txt ./ +RUN pip install --no-cache-dir pybluez homie + +COPY . . +RUN ls -la +CMD [ "python", "mqtt.bluetooth.py", "-c","/config/config.json" ] \ No newline at end of file diff --git a/BlueTooth/mqtt.bluetooth/bluemaestroscan.py b/BlueTooth/mqtt.bluetooth/bluemaestroscan.py new file mode 100644 index 0000000..066483e --- /dev/null +++ b/BlueTooth/mqtt.bluetooth/bluemaestroscan.py @@ -0,0 +1,208 @@ +# BlueMastro Tempo Disc Advertising packet decoder +# Called from bluemaestro.py +# This class has the specificis for decoding the advertising packets for the Blue Maestro Tempo Disc https://www.bluemaestro.com/product/tempo-disc-temperature/ +# Unsure if there are any other Blue Maestro products that it would work with. +# David@andc.nz 15/12/2016 + +DEBUG = True +# BLE Scanner based on from JCS 06/07/14 +# BLE scanner based on https://github.com/adamf/BLE/blob/master/ble-scanner.py +# BLE scanner, based on https://code.google.com/p/pybluez/source/browse/trunk/examples/advanced/inquiry-with-rssi.py + +# https://github.com/pauloborges/bluez/blob/master/tools/hcitool.c for lescan +# https://kernel.googlesource.com/pub/scm/bluetooth/bluez/+/5.6/lib/hci.h for opcodes +# https://github.com/pauloborges/bluez/blob/master/lib/hci.c#L2782 for functions used by lescan + +# performs a simple device inquiry, and returns a list of ble advertizements +# discovered device + +# NOTE: Python's struct.pack() will add padding bytes unless you make the endianness explicit. Little endian +# should be used for BLE. Always start a struct.pack() format string with "<" + +import collections +import os +import sys +import struct +import bluetooth._bluetooth as bluez + +LE_META_EVENT = 0x3e +LE_PUBLIC_ADDRESS=0x00 +LE_RANDOM_ADDRESS=0x01 +LE_SET_SCAN_PARAMETERS_CP_SIZE=7 +OGF_LE_CTL=0x08 +OCF_LE_SET_SCAN_PARAMETERS=0x000B +OCF_LE_SET_SCAN_ENABLE=0x000C +OCF_LE_CREATE_CONN=0x000D + +LE_ROLE_MASTER = 0x00 +LE_ROLE_SLAVE = 0x01 + +# these are actually subevents of LE_META_EVENT +EVT_LE_CONN_COMPLETE=0x01 +EVT_LE_ADVERTISING_REPORT=0x02 +EVT_LE_CONN_UPDATE_COMPLETE=0x03 +EVT_LE_READ_REMOTE_USED_FEATURES_COMPLETE=0x04 + +# Advertisment event types +ADV_IND=0x00 +ADV_DIRECT_IND=0x01 +ADV_SCAN_IND=0x02 +ADV_NONCONN_IND=0x03 +ADV_SCAN_RSP=0x04 + + +def returnnumberpacket(pkt): + myInteger = 0 + multiple = 256 + for c in pkt: + myInteger += struct.unpack("B",c)[0] * multiple + multiple = 1 + return myInteger + +def returnstringpacket(pkt): + myString = ""; + for c in pkt: + myString += "%02x" %struct.unpack("B",c)[0] + return myString + +def printpacket(pkt): + for c in pkt: + sys.stdout.write("%02x " % struct.unpack("B",c)[0]) + +def get_packed_bdaddr(bdaddr_string): + packable_addr = [] + addr = bdaddr_string.split(':') + addr.reverse() + for b in addr: + packable_addr.append(int(b, 16)) + return struct.pack(" 0: + time.sleep( FREQUENCY_SECONDS ) + else: + time.sleep( 1 ) + + except Exception as e: + # Error appending data, most likely because credentials are stale. + # Null out the worksheet so a login is performed at the top of the loop. + print('Append error, logging in again: ' + str(e)) + print ("Sleeping for 60 seconds") + time.sleep(60) + continue + +if __name__ == '__main__': + try: + parser= argparse.ArgumentParser( description="MQTT Based Bluetooth Reader" ) + parser.add_argument( '-c','--configfile', help='Configuration filename (json)',required=True ) + args = parser.parse_args() + main(args.configfile) + except (KeyboardInterrupt, SystemExit): + mqttc.loop_stop() + mqttc.disconnect() + print ("Top-level exception - terminating.") \ No newline at end of file diff --git a/BlueTooth/mqtt.homie.bluetooth/Dockerfile b/BlueTooth/mqtt.homie.bluetooth/Dockerfile new file mode 100644 index 0000000..9c4a7a2 --- /dev/null +++ b/BlueTooth/mqtt.homie.bluetooth/Dockerfile @@ -0,0 +1,11 @@ +FROM python:3 + +WORKDIR /app +RUN mkdir /config + +#COPY requirements.txt ./ +RUN pip install --no-cache-dir pybluez homie + +COPY . . +RUN ls -la +CMD [ "python", "mqtt.homie.bluetooth.py", "-c","/config/config.json" ] \ No newline at end of file diff --git a/BlueTooth/mqtt.homie.bluetooth/bluemaestroscan.py b/BlueTooth/mqtt.homie.bluetooth/bluemaestroscan.py new file mode 100644 index 0000000..066483e --- /dev/null +++ b/BlueTooth/mqtt.homie.bluetooth/bluemaestroscan.py @@ -0,0 +1,208 @@ +# BlueMastro Tempo Disc Advertising packet decoder +# Called from bluemaestro.py +# This class has the specificis for decoding the advertising packets for the Blue Maestro Tempo Disc https://www.bluemaestro.com/product/tempo-disc-temperature/ +# Unsure if there are any other Blue Maestro products that it would work with. +# David@andc.nz 15/12/2016 + +DEBUG = True +# BLE Scanner based on from JCS 06/07/14 +# BLE scanner based on https://github.com/adamf/BLE/blob/master/ble-scanner.py +# BLE scanner, based on https://code.google.com/p/pybluez/source/browse/trunk/examples/advanced/inquiry-with-rssi.py + +# https://github.com/pauloborges/bluez/blob/master/tools/hcitool.c for lescan +# https://kernel.googlesource.com/pub/scm/bluetooth/bluez/+/5.6/lib/hci.h for opcodes +# https://github.com/pauloborges/bluez/blob/master/lib/hci.c#L2782 for functions used by lescan + +# performs a simple device inquiry, and returns a list of ble advertizements +# discovered device + +# NOTE: Python's struct.pack() will add padding bytes unless you make the endianness explicit. Little endian +# should be used for BLE. Always start a struct.pack() format string with "<" + +import collections +import os +import sys +import struct +import bluetooth._bluetooth as bluez + +LE_META_EVENT = 0x3e +LE_PUBLIC_ADDRESS=0x00 +LE_RANDOM_ADDRESS=0x01 +LE_SET_SCAN_PARAMETERS_CP_SIZE=7 +OGF_LE_CTL=0x08 +OCF_LE_SET_SCAN_PARAMETERS=0x000B +OCF_LE_SET_SCAN_ENABLE=0x000C +OCF_LE_CREATE_CONN=0x000D + +LE_ROLE_MASTER = 0x00 +LE_ROLE_SLAVE = 0x01 + +# these are actually subevents of LE_META_EVENT +EVT_LE_CONN_COMPLETE=0x01 +EVT_LE_ADVERTISING_REPORT=0x02 +EVT_LE_CONN_UPDATE_COMPLETE=0x03 +EVT_LE_READ_REMOTE_USED_FEATURES_COMPLETE=0x04 + +# Advertisment event types +ADV_IND=0x00 +ADV_DIRECT_IND=0x01 +ADV_SCAN_IND=0x02 +ADV_NONCONN_IND=0x03 +ADV_SCAN_RSP=0x04 + + +def returnnumberpacket(pkt): + myInteger = 0 + multiple = 256 + for c in pkt: + myInteger += struct.unpack("B",c)[0] * multiple + multiple = 1 + return myInteger + +def returnstringpacket(pkt): + myString = ""; + for c in pkt: + myString += "%02x" %struct.unpack("B",c)[0] + return myString + +def printpacket(pkt): + for c in pkt: + sys.stdout.write("%02x " % struct.unpack("B",c)[0]) + +def get_packed_bdaddr(bdaddr_string): + packable_addr = [] + addr = bdaddr_string.split(':') + addr.reverse() + for b in addr: + packable_addr.append(int(b, 16)) + return struct.pack(" 0: - print "%s/temperature = %.2f" % (returnedList["mac"],returnedList["temp"]) + print("%s/temperature = %.2f" % (returnedList["mac"],returnedList["temp"])) logging.info("%s/temperature = %.2f" % (returnedList["mac"],returnedList["temp"])) # Publish to the MQTT channel try: @@ -102,29 +102,29 @@ def main(configfile='homie-bluetooth.json'): # Homie.setNodeProperty(temperatureNode,"temperature",float(returnedList["temp"]),True) # nodes[mac+"temperature"] = temperatureNode # print("Added new temperature node for mac" + mac) - print 'Updating temp for {0} to {1}'.format(returnedList["mac"],float(returnedList["temp"])) - logging.info('Updating temp for {0} to {1}'.format(returnedList["mac"],float(returnedList["temp"]))) + print('Updating temp for {0} to {1}'.format(returnedList["mac"],float(returnedList["temp"]))) + logging.info('Updating temp for {0} to {1}'.format(returnedList["mac"],float(returnedList["temp"]))) print("CHecking nodes for " + mac+"humidity") updatenode(Homie, nodes,mac,"humidity",float(returnedList["humidity"])) #humidityNode = Homie.Node(mac,"humidity") #Homie.setNodeProperty(humidityNode,"humidity",float(returnedList["humidity"]),True) - print 'Updating humidity {0} = {1}'.format(returnedList["mac"],float(returnedList["humidity"])) - logging.info('Updating humidity {0} = {1}'.format(returnedList["mac"],float(returnedList["humidity"]))) + print ( 'Updating humidity {0} = {1}'.format(returnedList["mac"],float(returnedList["humidity"]))) + logging.info('Updating humidity {0} = {1}'.format(returnedList["mac"],float(returnedList["humidity"]))) print("CHecking nodes for " + mac+"battery") updatenode(Homie, nodes,mac,"battery",float(returnedList["battery"])) #batteryNode = Homie.Node(mac,"battery") #Homie.setNodeProperty(batteryNode,"battery",float(returnedList["battery"]),True) - print 'Updating battery {0}/battery = {1}'.format(returnedList["mac"],float(returnedList["battery"])) + print ('Updating battery {0}/battery = {1}'.format(returnedList["mac"],float(returnedList["battery"]))) logging.info('Updating battery {0}/battery = {1}'.format(returnedList["mac"],float(returnedList["battery"]))) print("CHecking nodes for " + mac+"dewpoint") updatenode(Homie, nodes,mac,"dewpoint",float(returnedList["dewpoint"])) #dewpointNode = Homie.Node(mac,"dewpoint") #Homie.setNodeProperty(dewpointNode,"dewpoint",float(returnedList["dewpoint"]),True) - print 'Updating {0}/dewpoint = {1}'.format( returnedList["mac"],returnedList["dewpoint"]) + print( 'Updating {0}/dewpoint = {1}'.format( returnedList["mac"],returnedList["dewpoint"])) logging.info('Updating {0}/dewpoint = {1}'.format( returnedList["mac"],returnedList["dewpoint"])) print("CHecking nodes for " + mac+"name") @@ -132,38 +132,40 @@ def main(configfile='homie-bluetooth.json'): #nameNode = Homie.Node(mac,"name") #Homie.setNodeProperty(nameNode,"name",returnedList["name"],True) - print 'Updating name {0}/name = {1}'.format(returnedList["mac"],returnedList["name"]) + print ( 'Updating name {0}/name = {1}'.format(returnedList["mac"],returnedList["name"])) logging.info('Updating name {0}/name = {1}'.format(returnedList["mac"],returnedList["name"])) time.sleep(1) - except Exception,e: + except Exception as e: # Null out the worksheet so a login is performed at the top of the loop. logging.error('Append error, logging in again: ' + str(e)) logging.error("Sleeping for 60 seconds") time.sleep(60) continue + # if len(returnedList) > 0: else: - print "Sleeping for 30 seconds" + print ("Sleeping for 30 seconds" ) logging.info("Sleeping for 30 seconds" ) time.sleep(30) - logging.info("Sleeping for %s seconds" % FREQUENCY_SECONDS) - print("Sleeping for %s seconds" % FREQUENCY_SECONDS) + logging.info("Sleeping for %s seconds" % FREQUENCY_SECONDS) + print("Sleeping for %s seconds" % FREQUENCY_SECONDS) + #for beacon in returnedList: time.sleep(FREQUENCY_SECONDS) - - except Exception,e: - # Error appending data, most likely because credentials are stale. + #try + except Exception as e: + # Error appending data, most likely because credentials are stale. # Null out the worksheet so a login is performed at the top of the loop. print('Append error, logging in again: ' + str(e)) - print "Sleeping for 60 seconds" + print ("Sleeping for 60 seconds") time.sleep(60) continue - + #while True: if __name__ == '__main__': - try: - parser= argparse.ArgumentParser(description="Homie Based Bluetooth Reader") - parser.add_argument('-c','--configfile', help='Configuration filename (json)',required=True) - args = parser.parse_args() - main(args.configfile) - except (KeyboardInterrupt, SystemExit): - print "quitting." + try: + parser= argparse.ArgumentParser(description="Homie Based Bluetooth Reader") + parser.add_argument('-c','--configfile', help='Configuration filename (json)',required=True) + args = parser.parse_args() + main(args.configfile) + except (KeyboardInterrupt, SystemExit): + print ("quitting.")