From f114f81cb8589580a496e9dd9d173a7e81d3ce4d Mon Sep 17 00:00:00 2001 From: Spencer Young Date: Wed, 26 Dec 2018 16:11:01 -0800 Subject: [PATCH 1/8] refactor to package; rename modules --- BytePack.py | 60 ---------- CArrayWriter.py | 23 ---- Example.py | 13 ++- UUID.py | 36 ------ mooshimeter/__init__.py | 0 BGWrapper.py => mooshimeter/bg_wrapper.py | 4 +- ConfigNode.py => mooshimeter/config_node.py | 6 +- Mooshimeter.py => mooshimeter/meter.py | 19 ++- mooshimeter/utils.py | 123 ++++++++++++++++++++ mooshimeter/vendor/__init__.py | 0 bglib.py => mooshimeter/vendor/bglib.py | 0 setup.py | 13 +++ 12 files changed, 157 insertions(+), 140 deletions(-) delete mode 100644 BytePack.py delete mode 100644 CArrayWriter.py delete mode 100644 UUID.py create mode 100644 mooshimeter/__init__.py rename BGWrapper.py => mooshimeter/bg_wrapper.py (99%) rename ConfigNode.py => mooshimeter/config_node.py (98%) rename Mooshimeter.py => mooshimeter/meter.py (94%) create mode 100644 mooshimeter/utils.py create mode 100644 mooshimeter/vendor/__init__.py rename bglib.py => mooshimeter/vendor/bglib.py (100%) create mode 100644 setup.py diff --git a/BytePack.py b/BytePack.py deleted file mode 100644 index 7db14a8..0000000 --- a/BytePack.py +++ /dev/null @@ -1,60 +0,0 @@ -import struct - -class UnderflowException(Exception): - pass - -class BytePack: - """ - Helper class to pack and unpack integers and floats from a buffer - """ - def __init__(self,bytebuf=[]): - self.i = 0 - self.bytes = bytebuf[:] - def putByte(self,v): - self.bytes.append(v) - def put(self,v,b=1): - if type(v) == float: - v = struct.unpack("BBBB",struct.pack("f",v)) - for e in v: - self.putByte(e) - elif type(v) == int: - while b: - self.putByte(v&0xFF) - v >>= 8 - b -= 1 - else: - # Is it iterable? Try it, assuming it's a list of bytes - for byte in v: - self.putByte(byte) - def get(self,b=1,t=int,signed=False): - if t == int: - if b > self.getBytesRemaining(): - raise UnderflowException() - r = 0 - s = 0 - top_b = 0 - while b: - top_b = self.bytes[self.i] - r += top_b << s - s += 8 - self.i += 1 - b -= 1 - # Sign extend - if signed and top_b & 0x80: - r -= 1 << s - return r - elif t==float: - if 4 > self.getBytesRemaining(): - raise UnderflowException() - r = struct.unpack("f",struct.pack("BBBB",*self.bytes[self.i:self.i+4])) - self.i += 4 - return r[0] - def getBytes(self, max_bytes=0): - if max_bytes == 0: - rval = self.bytes[self.i:] - else: - rval = self.bytes[self.i:self.i+max_bytes] - self.i += len(rval) - return rval - def getBytesRemaining(self): - return len(self.bytes) - self.i \ No newline at end of file diff --git a/CArrayWriter.py b/CArrayWriter.py deleted file mode 100644 index 42afb3c..0000000 --- a/CArrayWriter.py +++ /dev/null @@ -1,23 +0,0 @@ -def writeHeader(file,array_name,payload): - decl_line = "const unsigned char %s[%d]"%( array_name,len(payload)) - - #Write header file - file.write('#define %s_LEN %d\n'%(array_name.upper(),len(payload))) - file.write('extern ' + decl_line + ';\n') - -def writeAsCArray(file, array_name, payload): - decl_line = "const unsigned char %s[%d]"%( array_name,len(payload)) - # Open main output file - # Write the opening lines - file.write(decl_line + '={\n') - - c_on_row = 0 - - for c in payload: - file.write("0x%02X,\t"%ord(c)) - c_on_row+=1 - if(c_on_row == 8): - c_on_row = 0 - file.write("\n") - - file.write("\n};\n") diff --git a/Example.py b/Example.py index c837c4c..4679dd8 100644 --- a/Example.py +++ b/Example.py @@ -1,4 +1,5 @@ -from Mooshimeter import * +from mooshimeter.meter import Mooshimeter +from mooshimeter import bg_wrapper import threading import time @@ -14,7 +15,7 @@ def run(self): """ Example.py -This script is meant to demonstrate use of the Mooshimeter and BGWrapper classes. +This script is meant to demonstrate use of the Mooshimeter and bg_wrapper classes. The script does the following: - Scan for BLE devices - Filter for Mooshimeters @@ -51,7 +52,7 @@ def writeCh2(self, meter, val): if __name__=="__main__": # Set up the lower level to talk to a BLED112 in port COM4 # REPLACE THIS WITH THE BLED112 PORT ON YOUR SYSTEM - BGWrapper.initialize("COM4") + bg_wrapper.initialize("COM4") inputthread = InputThread() inputthread.start() cmd_queue = [] @@ -59,7 +60,7 @@ def addToQueue(s): cmd_queue.append(s) inputthread.cb = addToQueue # Scan for 3 seconds - scan_results = BGWrapper.scan(5) + scan_results = bg_wrapper.scan(5) # Filter for devices advertising the Mooshimeter service results_wrapped = filter(lambda(p):Mooshimeter.mUUID.METER_SERVICE in p.ad_services, scan_results) if len(results_wrapped) == 0: @@ -75,7 +76,7 @@ def addToQueue(s): m.loadTree() # Wait for us to load the command tree while m.tree.getNodeAtLongname('SAMPLING:TRIGGER')==None: - BGWrapper.idle() + bg_wrapper.idle() # Unlock the meter by writing the correct CRC32 value # The CRC32 node's value is written when the tree is received m.sendCommand('admin:crc32 '+str(m.tree.getNodeAtLongname('admin:crc32').value)) @@ -103,7 +104,7 @@ def addToQueue(s): while True: # This call checks the serial port and processes new data - BGWrapper.idle() + bg_wrapper.idle() if time.time()-last_heartbeat_time > 10: last_heartbeat_time = time.time() for m in meters: diff --git a/UUID.py b/UUID.py deleted file mode 100644 index c75ef2c..0000000 --- a/UUID.py +++ /dev/null @@ -1,36 +0,0 @@ -# Utility class -class UUID: - def __init__(self, initializer): - if type(initializer) == type(""): - #String input - self.bytes = self.__stringToBytes(initializer) - elif type(initializer) == type(1): - # Integer initialized, assume a 2 byte UUID - self.bytes = ((initializer>>8)&0xFF, (initializer>>0)&0xFF) - else: - #Byte array input - self.bytes = tuple(initializer) - def __stringToBytes(self, arg): - arg = arg.upper() - arg = arg.replace("-","") - l = [int(arg[i:i+2],16) for i in range(0,len(arg),2)] - return tuple(l) - def __bytesToString(self, bytes): - l = ["%02X"%bytes[i] for i in range(len(bytes))] - if len(bytes) == 16: - l = ["%02X"%bytes[i] for i in range(16)] - l.insert( 4,'-') - l.insert( 7,'-') - l.insert(10,'-') - l.insert(13,'-') - return ''.join(l) - def asString(self): - return self.__bytesToString(self.bytes) - def __eq__(self, other): - return self.bytes==other.bytes - def __hash__(self): - return self.asString().__hash__() - def __str__(self): - return self.asString() - def __repr__(self): - return self.asString() \ No newline at end of file diff --git a/mooshimeter/__init__.py b/mooshimeter/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/BGWrapper.py b/mooshimeter/bg_wrapper.py similarity index 99% rename from BGWrapper.py rename to mooshimeter/bg_wrapper.py index 68d8715..d7e54d9 100644 --- a/BGWrapper.py +++ b/mooshimeter/bg_wrapper.py @@ -1,7 +1,7 @@ -import bglib +from mooshimeter.vendor import bglib import serial import time -from UUID import * +from mooshimeter.utils import UUID # This module is designed to be used like a singleton class # It wraps the functions of bglib in an easier to use way diff --git a/ConfigNode.py b/mooshimeter/config_node.py similarity index 98% rename from ConfigNode.py rename to mooshimeter/config_node.py index 2163b8c..2c37582 100644 --- a/ConfigNode.py +++ b/mooshimeter/config_node.py @@ -1,5 +1,5 @@ import zlib -import CArrayWriter +from mooshimeter import utils class NTYPE: PLAIN =0 # May be an informational node, or a choice in a chooser @@ -198,13 +198,13 @@ def writeCHeader(self,f): f.write('} cmd_code_t;\n') packed = self.pack() - CArrayWriter.writeHeader(f,'config_tree_packed',packed) + utils.writeHeader(f, 'config_tree_packed', packed) f.write('extern const unsigned long config_tree_crc32;\n') def writeCDec(self,f): f.write('#pragma once\n') packed = self.pack() - CArrayWriter.writeAsCArray(f,'config_tree_packed',packed) + utils.writeAsCArray(f, 'config_tree_packed', packed) crc32 = zlib.crc32(packed) # adapt for wart in zlib.crc32... signed ints diff --git a/Mooshimeter.py b/mooshimeter/meter.py similarity index 94% rename from Mooshimeter.py rename to mooshimeter/meter.py index 926eb7d..55863ee 100644 --- a/Mooshimeter.py +++ b/mooshimeter/meter.py @@ -1,14 +1,13 @@ # coding=UTF-8 -import BGWrapper -from UUID import * -from ConfigNode import * -from BytePack import * +from mooshimeter import bg_wrapper +from mooshimeter.config_node import NTYPE, ConfigNode, ConfigTree +from mooshimeter.utils import BytePack, UnderflowException, UUID import binascii -class MeterSerOut(BGWrapper.Characteristic): +class MeterSerOut(bg_wrapper.Characteristic): def __init__(self, meter, parent, handle, uuid): """ - :param other: a BGWrapper.Characteristic + :param other: a bg_wrapper.Characteristic :return: """ super(MeterSerOut,self).__init__(parent, handle, uuid) @@ -76,10 +75,10 @@ def unpack(self): self.aggregate += b.bytes[1:] # Attempt to decode a message, if we succeed pop the message off the byte queue self.interpretAggregate() -class MeterSerIn(BGWrapper.Characteristic): +class MeterSerIn(bg_wrapper.Characteristic): def __init__(self, meter, parent, handle, uuid): """ - :param other: a BGWrapper.Characteristic + :param other: a bg_wrapper.Characteristic :return: """ super(MeterSerIn,self).__init__(parent, handle, uuid) @@ -132,7 +131,7 @@ def sendToMeter(self, payload): def __init__(self, peripheral): """ Initialized instance variables - :param peripheral: a BGWrapper.Peripheral instance + :param peripheral: a bg_wrapper.Peripheral instance :return: """ self.p = peripheral @@ -221,7 +220,7 @@ def tmp_cb(): wrap[0]+=1 self.meter_serout.enableNotify(True,tmp_cb) def disconnect(self): - BGWrapper.disconnect(self.p.conn_handle) + bg_wrapper.disconnect(self.p.conn_handle) def getUUIDString(self): return self.p.getUUIDString() def attachCallback(self,node_path,notify_cb): diff --git a/mooshimeter/utils.py b/mooshimeter/utils.py new file mode 100644 index 0000000..e145757 --- /dev/null +++ b/mooshimeter/utils.py @@ -0,0 +1,123 @@ +import struct + +class UnderflowException(Exception): + pass + +class BytePack: + """ + Helper class to pack and unpack integers and floats from a buffer + """ + def __init__(self,bytebuf=[]): + self.i = 0 + self.bytes = bytebuf[:] + def putByte(self,v): + self.bytes.append(v) + def put(self,v,b=1): + if type(v) == float: + v = struct.unpack("BBBB",struct.pack("f",v)) + for e in v: + self.putByte(e) + elif type(v) == int: + while b: + self.putByte(v&0xFF) + v >>= 8 + b -= 1 + else: + # Is it iterable? Try it, assuming it's a list of bytes + for byte in v: + self.putByte(byte) + def get(self,b=1,t=int,signed=False): + if t == int: + if b > self.getBytesRemaining(): + raise UnderflowException() + r = 0 + s = 0 + top_b = 0 + while b: + top_b = self.bytes[self.i] + r += top_b << s + s += 8 + self.i += 1 + b -= 1 + # Sign extend + if signed and top_b & 0x80: + r -= 1 << s + return r + elif t==float: + if 4 > self.getBytesRemaining(): + raise UnderflowException() + r = struct.unpack("f",struct.pack("BBBB",*self.bytes[self.i:self.i+4])) + self.i += 4 + return r[0] + def getBytes(self, max_bytes=0): + if max_bytes == 0: + rval = self.bytes[self.i:] + else: + rval = self.bytes[self.i:self.i+max_bytes] + self.i += len(rval) + return rval + def getBytesRemaining(self): + return len(self.bytes) - self.i + + +def writeHeader(file, array_name, payload): + decl_line = "const unsigned char %s[%d]" % (array_name, len(payload)) + + # Write header file + file.write('#define %s_LEN %d\n' % (array_name.upper(), len(payload))) + file.write('extern ' + decl_line + ';\n') + + +def writeAsCArray(file, array_name, payload): + decl_line = "const unsigned char %s[%d]" % (array_name, len(payload)) + # Open main output file + # Write the opening lines + file.write(decl_line + '={\n') + + c_on_row = 0 + + for c in payload: + file.write("0x%02X,\t" % ord(c)) + c_on_row += 1 + if (c_on_row == 8): + c_on_row = 0 + file.write("\n") + + file.write("\n};\n") + +# Utility class +class UUID: + def __init__(self, initializer): + if type(initializer) == type(""): + #String input + self.bytes = self.__stringToBytes(initializer) + elif type(initializer) == type(1): + # Integer initialized, assume a 2 byte UUID + self.bytes = ((initializer>>8)&0xFF, (initializer>>0)&0xFF) + else: + #Byte array input + self.bytes = tuple(initializer) + def __stringToBytes(self, arg): + arg = arg.upper() + arg = arg.replace("-","") + l = [int(arg[i:i+2],16) for i in range(0,len(arg),2)] + return tuple(l) + def __bytesToString(self, bytes): + l = ["%02X"%bytes[i] for i in range(len(bytes))] + if len(bytes) == 16: + l = ["%02X"%bytes[i] for i in range(16)] + l.insert( 4,'-') + l.insert( 7,'-') + l.insert(10,'-') + l.insert(13,'-') + return ''.join(l) + def asString(self): + return self.__bytesToString(self.bytes) + def __eq__(self, other): + return self.bytes==other.bytes + def __hash__(self): + return self.asString().__hash__() + def __str__(self): + return self.asString() + def __repr__(self): + return self.asString() \ No newline at end of file diff --git a/mooshimeter/vendor/__init__.py b/mooshimeter/vendor/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/bglib.py b/mooshimeter/vendor/bglib.py similarity index 100% rename from bglib.py rename to mooshimeter/vendor/bglib.py diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..341564c --- /dev/null +++ b/setup.py @@ -0,0 +1,13 @@ +from setuptools import setup + +setup( + name='mooshimeter', + version='1.0', + packages=['mooshimeter', 'mooshimeter.vendor'], + url='', + license='GPLv3', + author='James Whong', + author_email='', + description='', + install_requires=['pyserial'] +) From a942df275920e852626f567f54b707838e7640ef Mon Sep 17 00:00:00 2001 From: Spencer Young Date: Wed, 26 Dec 2018 16:13:27 -0800 Subject: [PATCH 2/8] fix bare raise bug --- mooshimeter/bg_wrapper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mooshimeter/bg_wrapper.py b/mooshimeter/bg_wrapper.py index d7e54d9..46efc3b 100644 --- a/mooshimeter/bg_wrapper.py +++ b/mooshimeter/bg_wrapper.py @@ -126,7 +126,7 @@ def findHandleForUUID(self,uuid): if c.uuid == uuid: rval.append(c.handle) if len(rval) != 1: - raise + raise Exception('Expected rval length to be exactly 1. Was %d length' % len(rval)) return rval[0] def readByHandle(self,char_handle): return read(self.conn_handle,char_handle) From 6edc684080e38dce2888f6a282d9ec6591a4c221 Mon Sep 17 00:00:00 2001 From: Spencer Young Date: Wed, 26 Dec 2018 16:26:51 -0800 Subject: [PATCH 3/8] appveyor setup --- README.md | 7 +++++++ appveyor.yml | 31 +++++++++++++++++++++++++++++++ setup.py | 6 +++--- 3 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 appveyor.yml diff --git a/README.md b/README.md index 917bddd..bbc7335 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,13 @@ # Mooshimeter-PythonAPI A python API relying on the BLED112 to talk to the Mooshimeter +# Install + +``` +pip install mooshimeter +``` + + Example.py: This script is meant to demonstrate use of the Mooshimeter and BGWrapper classes. The script does the following: diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..6024409 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,31 @@ +install: + - ps: | + py -2.7 -m virtualenv venv + .\venv\Scripts\activate.ps1 + python -m pip install --upgrade pip + python -m pip install --upgrade . + + +build_script: + - ps: | + .\venv\Scripts\activate.ps1 + python setup.py sdist bdist_wheel + +artifacts: + - name: dist + path: dist\* + +test: off + +deploy_script: + - ps: | + if ($env:APPVEYOR_REPO_TAG -eq "true") { + py -2.7 -m virtualenv deploy_venv + .\deploy_venv\Scripts\activate.ps1 + python -m pip install --upgrade pip + pip install --upgrade wheel + pip install --upgrade twine + twine upload dist\* + } else { + echo "Skipping Deploy Because this is not a tagged commit" + } diff --git a/setup.py b/setup.py index 341564c..9d9d392 100644 --- a/setup.py +++ b/setup.py @@ -2,12 +2,12 @@ setup( name='mooshimeter', - version='1.0', + version='0.1.0', packages=['mooshimeter', 'mooshimeter.vendor'], url='', license='GPLv3', - author='James Whong', + author='', author_email='', - description='', + description='UNOFFICIAL! This package is a fork from the original mooshimeter python API', install_requires=['pyserial'] ) From ebd04ab53d58e8ba780542eae8ef6598ad2006aa Mon Sep 17 00:00:00 2001 From: Spencer Young Date: Wed, 26 Dec 2018 16:30:45 -0800 Subject: [PATCH 4/8] add setup metadata --- README.md | 3 +++ setup.py | 7 ++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index bbc7335..57368c6 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ # Mooshimeter-PythonAPI A python API relying on the BLED112 to talk to the Mooshimeter +[![Build status](https://ci.appveyor.com/api/projects/status/5ajjxonexbaqsu6c/branch/master?svg=true)](https://ci.appveyor.com/project/spyoungtech/mooshimeter-pythonapi/branch/master) + + # Install ``` diff --git a/setup.py b/setup.py index 9d9d392..88d4328 100644 --- a/setup.py +++ b/setup.py @@ -4,10 +4,11 @@ name='mooshimeter', version='0.1.0', packages=['mooshimeter', 'mooshimeter.vendor'], - url='', + url='https://github.com/spyoungtech/Mooshimeter-PythonAPI', license='GPLv3', - author='', - author_email='', + author='James Whong', + maintainer='Spencer Young', + maintainer_email='spencer.young@spyoung.com', description='UNOFFICIAL! This package is a fork from the original mooshimeter python API', install_requires=['pyserial'] ) From 1b96f5952b68aadc0895ae4bd492b8aaa9a30fbe Mon Sep 17 00:00:00 2001 From: Spencer Young Date: Wed, 26 Dec 2018 16:33:25 -0800 Subject: [PATCH 5/8] add metadata --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 88d4328..947597c 100644 --- a/setup.py +++ b/setup.py @@ -7,6 +7,7 @@ url='https://github.com/spyoungtech/Mooshimeter-PythonAPI', license='GPLv3', author='James Whong', + author_email='hello@moosh.im', maintainer='Spencer Young', maintainer_email='spencer.young@spyoung.com', description='UNOFFICIAL! This package is a fork from the original mooshimeter python API', From baa88bf699667083940e8ed9f9f7ad66f716ec86 Mon Sep 17 00:00:00 2001 From: Spencer Young Date: Wed, 26 Dec 2018 16:49:00 -0800 Subject: [PATCH 6/8] add notice --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 57368c6..1f40468 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,9 @@ A python API relying on the BLED112 to talk to the Mooshimeter pip install mooshimeter ``` +**NOTE:** This package is maintained unofficially. +It has no official connection to the [official Mooshim repository](https://github.com/mooshim/Mooshimeter-PythonAPI). + Example.py: This script is meant to demonstrate use of the Mooshimeter and BGWrapper classes. From 2952b3bce883a4aa260420b7936cd02d6bc04939 Mon Sep 17 00:00:00 2001 From: Spencer Young Date: Wed, 26 Dec 2018 16:50:34 -0800 Subject: [PATCH 7/8] add requirements file --- requirements.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..d4d1f9b --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +pyserial \ No newline at end of file From 5cd602430db0dc551d4d0d0c0e043c589dda5b6a Mon Sep 17 00:00:00 2001 From: Spencer Young Date: Wed, 26 Dec 2018 16:52:58 -0800 Subject: [PATCH 8/8] include license in distributions --- License.txt => LICENSE | 0 MANIFEST.in | 2 ++ setup.cfg | 2 ++ 3 files changed, 4 insertions(+) rename License.txt => LICENSE (100%) create mode 100644 MANIFEST.in create mode 100644 setup.cfg diff --git a/License.txt b/LICENSE similarity index 100% rename from License.txt rename to LICENSE diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..689e50f --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,2 @@ +include LICENSE +include README.md \ No newline at end of file diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..29b21fb --- /dev/null +++ b/setup.cfg @@ -0,0 +1,2 @@ +[metadata] +license_file = LICENSE \ No newline at end of file