class RileyLink(PacketRadio): def __init__(self): self.peripheral = None self.pa_level_index = PA_LEVELS.index(0x84) self.data_handle = None self.logger = getLogger() self.address = None if os.path.exists(RILEYLINK_MAC_FILE): with open(RILEYLINK_MAC_FILE, "r") as stream: self.address = stream.read() self.service = None self.response_handle = None self.notify_event = Event() self.initialized = False def connect(self, force_initialize=False): try: if self.address is None: self.address = self._findRileyLink() if self.peripheral is None: self.peripheral = Peripheral() try: state = self.peripheral.getState() if state == "conn": return except BTLEException: pass self._connect_retry(3) self.service = self.peripheral.getServiceByUUID(RILEYLINK_SERVICE_UUID) data_char = self.service.getCharacteristics(RILEYLINK_DATA_CHAR_UUID)[0] self.data_handle = data_char.getHandle() char_response = self.service.getCharacteristics(RILEYLINK_RESPONSE_CHAR_UUID)[0] self.response_handle = char_response.getHandle() response_notify_handle = self.response_handle + 1 notify_setup = b"\x01\x00" self.peripheral.writeCharacteristic(response_notify_handle, notify_setup) while self.peripheral.waitForNotifications(0.05): self.peripheral.readCharacteristic(self.data_handle) if self.initialized: self.init_radio(force_initialize) else: self.init_radio(True) except BTLEException: if self.peripheral is not None: self.disconnect() raise def disconnect(self, ignore_errors=True): try: if self.peripheral is None: self.logger.info("Already disconnected") return self.logger.info("Disconnecting..") if self.response_handle is not None: response_notify_handle = self.response_handle + 1 notify_setup = b"\x00\x00" self.peripheral.writeCharacteristic(response_notify_handle, notify_setup) except BTLEException: if not ignore_errors: raise finally: try: if self.peripheral is not None: self.peripheral.disconnect() self.peripheral = None except BTLEException: if ignore_errors: self.logger.exception("Ignoring btle exception during disconnect") else: raise def get_info(self): try: self.connect() bs = self.peripheral.getServiceByUUID(XGATT_BATTERYSERVICE_UUID) bc = bs.getCharacteristics(XGATT_BATTERY_CHAR_UUID)[0] bch = bc.getHandle() battery_value = int(self.peripheral.readCharacteristic(bch)[0]) self.logger.debug("Battery level read: %d", battery_value) version, v_major, v_minor = self._read_version() return { "battery_level": battery_value, "mac_address": self.address, "version_string": version, "version_major": v_major, "version_minor": v_minor } except BTLEException as btlee: raise PacketRadioError("Error communicating with RileyLink") from btlee finally: self.disconnect() def _read_version(self): version = None try: if os.path.exists(RILEYLINK_VERSION_FILE): with open(RILEYLINK_VERSION_FILE, "r") as stream: version = stream.read() else: response = self._command(Command.GET_VERSION) if response is not None and len(response) > 0: version = response.decode("ascii") self.logger.debug("RL reports version string: %s" % version) try: with open(RILEYLINK_VERSION_FILE, "w") as stream: stream.write(version) except IOError: self.logger.exception("Failed to store version in file") if version is None: return "0.0", 0, 0 try: m = re.search(".+([0-9]+)\\.([0-9]+)", version) if m is None: raise PacketRadioError("Failed to parse firmware version string: %s" % version) v_major = int(m.group(1)) v_minor = int(m.group(2)) self.logger.debug("Interpreted version major: %d minor: %d" % (v_major, v_minor)) return version, v_major, v_minor except Exception as ex: raise PacketRadioError("Failed to parse firmware version string: %s" % version) from ex except IOError: self.logger.exception("Error reading version file") except PacketRadioError: raise response = self._command(Command.GET_VERSION) if response is not None and len(response) > 0: version = response.decode("ascii") self.logger.debug("RL reports version string: %s" % version) try: m = re.search(".+([0-9]+)\\.([0-9]+)", version) if m is None: raise PacketRadioError("Failed to parse firmware version string: %s" % version) v_major = int(m.group(1)) v_minor = int(m.group(2)) self.logger.debug("Interpreted version major: %d minor: %d" % (v_major, v_minor)) return (version, v_major, v_minor) except PacketRadioError: raise except Exception as ex: raise PacketRadioError("Failed to parse firmware version string: %s" % version) from ex def init_radio(self, force_init=False): try: version, v_major, v_minor = self._read_version() if v_major < 2: self.logger.error("Firmware version is below 2.0") raise PacketRadioError("Unsupported RileyLink firmware %d.%d (%s)" % (v_major, v_minor, version)) if not force_init: if v_major == 2 and v_minor < 3: response = self._command(Command.READ_REGISTER, bytes([Register.SYNC1, 0x00])) else: response = self._command(Command.READ_REGISTER, bytes([Register.SYNC1])) if response is not None and len(response) > 0 and response[0] == 0xA5: return self._command(Command.RADIO_RESET_CONFIG) self._command(Command.SET_SW_ENCODING, bytes([Encoding.MANCHESTER])) frequency = int(433910000 / (24000000 / pow(2, 16))) self._command(Command.SET_PREAMBLE, bytes([0x66, 0x65])) self._command(Command.UPDATE_REGISTER, bytes([Register.FREQ0, frequency & 0xff])) self._command(Command.UPDATE_REGISTER, bytes([Register.FREQ1, (frequency >> 8) & 0xff])) self._command(Command.UPDATE_REGISTER, bytes([Register.FREQ2, (frequency >> 16) & 0xff])) self._command(Command.UPDATE_REGISTER, bytes([Register.PKTCTRL1, 0x20])) self._command(Command.UPDATE_REGISTER, bytes([Register.PKTCTRL0, 0x00])) self._command(Command.UPDATE_REGISTER, bytes([Register.FSCTRL1, 0x06])) self._command(Command.UPDATE_REGISTER, bytes([Register.MDMCFG4, 0xCA])) self._command(Command.UPDATE_REGISTER, bytes([Register.MDMCFG3, 0xBC])) self._command(Command.UPDATE_REGISTER, bytes([Register.MDMCFG2, 0x06])) self._command(Command.UPDATE_REGISTER, bytes([Register.MDMCFG1, 0x70])) self._command(Command.UPDATE_REGISTER, bytes([Register.MDMCFG0, 0x11])) self._command(Command.UPDATE_REGISTER, bytes([Register.DEVIATN, 0x44])) self._command(Command.UPDATE_REGISTER, bytes([Register.MCSM0, 0x18])) self._command(Command.UPDATE_REGISTER, bytes([Register.FOCCFG, 0x17])) self._command(Command.UPDATE_REGISTER, bytes([Register.FSCAL3, 0xE9])) self._command(Command.UPDATE_REGISTER, bytes([Register.FSCAL2, 0x2A])) self._command(Command.UPDATE_REGISTER, bytes([Register.FSCAL1, 0x00])) self._command(Command.UPDATE_REGISTER, bytes([Register.FSCAL0, 0x1F])) self._command(Command.UPDATE_REGISTER, bytes([Register.TEST1, 35])) self._command(Command.UPDATE_REGISTER, bytes([Register.TEST0, 0x09])) self._command(Command.UPDATE_REGISTER, bytes([Register.PATABLE0, PA_LEVELS[self.pa_level_index]])) self._command(Command.UPDATE_REGISTER, bytes([Register.FREND0, 0x00])) self._command(Command.UPDATE_REGISTER, bytes([Register.SYNC1, 0xA5])) self._command(Command.UPDATE_REGISTER, bytes([Register.SYNC0, 0x5A])) response = self._command(Command.GET_STATE) if response != b"OK": raise PacketRadioError("Rileylink state is not OK. Response returned: %s" % response) self.initialized = True except PacketRadioError as rle: self.logger.error("Error while initializing rileylink radio: %s", rle) raise def tx_up(self): if self.pa_level_index < len(PA_LEVELS) - 1: self.pa_level_index += 1 self._set_amp(self.pa_level_index) def tx_down(self): if self.pa_level_index > 0: self.pa_level_index -= 1 self._set_amp(self.pa_level_index) def set_tx_power(self, tx_power): if tx_power is None: return elif tx_power == TxPower.Lowest: self._set_amp(0) elif tx_power == TxPower.Low: self._set_amp(PA_LEVELS.index(0x12)) elif tx_power == TxPower.Normal: self._set_amp(PA_LEVELS.index(0x60)) elif tx_power == TxPower.High: self._set_amp(PA_LEVELS.index(0xC8)) elif tx_power == TxPower.Highest: self._set_amp(PA_LEVELS.index(0xC0)) def get_packet(self, timeout=5.0): try: self.connect() return self._command(Command.GET_PACKET, struct.pack(">BL", 0, int(timeout * 1000)), timeout=float(timeout)+0.5) except PacketRadioError as rle: self.logger.error("Error while receiving data: %s", rle) raise def send_and_receive_packet(self, packet, repeat_count, delay_ms, timeout_ms, retry_count, preamble_ext_ms): try: self.connect() return self._command(Command.SEND_AND_LISTEN, struct.pack(">BBHBLBH", 0, repeat_count, delay_ms, 0, timeout_ms, retry_count, preamble_ext_ms) + packet, timeout=30) except PacketRadioError as rle: self.logger.error("Error while sending and receiving data: %s", rle) raise def send_packet(self, packet, repeat_count, delay_ms, preamble_extension_ms): try: self.connect() result = self._command(Command.SEND_PACKET, struct.pack(">BBHH", 0, repeat_count, delay_ms, preamble_extension_ms) + packet, timeout=30) return result except PacketRadioError as rle: self.logger.error("Error while sending data: %s", rle) raise def _set_amp(self, index=None): try: self.connect() if index is not None: self.pa_level_index = index self._command(Command.UPDATE_REGISTER, bytes([Register.PATABLE0, PA_LEVELS[self.pa_level_index]])) self.logger.debug("Setting pa level to index %d of %d" % (self.pa_level_index, len(PA_LEVELS))) except PacketRadioError: self.logger.exception("Error while setting tx amplification") raise def _findRileyLink(self): scanner = Scanner() found = None self.logger.debug("Scanning for RileyLink") retries = 10 while found is None and retries > 0: retries -= 1 for result in scanner.scan(1.0): if result.getValueText(7) == RILEYLINK_SERVICE_UUID: self.logger.debug("Found RileyLink") found = result.addr try: with open(RILEYLINK_MAC_FILE, "w") as stream: stream.write(result.addr) except IOError: self.logger.warning("Cannot store rileylink mac radio_address for later") break if found is None: raise PacketRadioError("Could not find RileyLink") return found def _connect_retry(self, retries): while retries > 0: retries -= 1 self.logger.info("Connecting to RileyLink, retries left: %d" % retries) try: self.peripheral.connect(self.address) self.logger.info("Connected") break except BTLEException as btlee: self.logger.warning("BTLE exception trying to connect: %s" % btlee) try: p = subprocess.Popen(["ps", "-A"], stdout=subprocess.PIPE) out, err = p.communicate() for line in out.splitlines(): if "bluepy-helper" in line: pid = int(line.split(None, 1)[0]) os.kill(pid, 9) break except: self.logger.warning("Failed to kill bluepy-helper") time.sleep(1) def _command(self, command_type, command_data=None, timeout=10.0): try: if command_data is None: data = bytes([1, command_type]) else: data = bytes([len(command_data) + 1, command_type]) + command_data self.peripheral.writeCharacteristic(self.data_handle, data, withResponse=True) if not self.peripheral.waitForNotifications(timeout): raise PacketRadioError("Timed out while waiting for a response from RileyLink") response = self.peripheral.readCharacteristic(self.data_handle) if response is None or len(response) == 0: raise PacketRadioError("RileyLink returned no response") else: if response[0] == Response.COMMAND_SUCCESS: return response[1:] elif response[0] == Response.COMMAND_INTERRUPTED: self.logger.warning("A previous command was interrupted") return response[1:] elif response[0] == Response.RX_TIMEOUT: return None else: raise PacketRadioError("RileyLink returned error code: %02X. Additional response data: %s" % (response[0], response[1:]), response[0]) except Exception as e: raise PacketRadioError("Error executing command") from e
class Nuimo(object): SERVICE_UUIDS = [ UUID('0000180f-0000-1000-8000-00805f9b34fb'), # Battery UUID('f29b1525-cb19-40f3-be5c-7241ecb82fd2'), # Sensors UUID('f29b1523-cb19-40f3-be5c-7241ecb82fd1') # LED Matrix ] CHARACTERISTIC_UUIDS = { UUID('00002a19-0000-1000-8000-00805f9b34fb'): 'BATTERY', UUID('f29b1529-cb19-40f3-be5c-7241ecb82fd2'): 'BUTTON', UUID('f29b1528-cb19-40f3-be5c-7241ecb82fd2'): 'ROTATION', UUID('f29b1527-cb19-40f3-be5c-7241ecb82fd2'): 'SWIPE', UUID('f29b1526-cb19-40f3-be5c-7241ecb82fd2'): 'FLY', UUID('f29b1524-cb19-40f3-be5c-7241ecb82fd1'): 'LED_MATRIX' } NOTIFICATION_CHARACTERISTIC_UUIDS = [ 'BATTERY', # Uncomment only if you are not using the iOS emulator (iOS does't support battery updates without authentication) 'BUTTON', 'ROTATION', 'SWIPE', 'FLY'] # Notification data NOTIFICATION_ON = struct.pack("BB", 0x01, 0x00) NOTIFICATION_OFF = struct.pack("BB", 0x00, 0x00) def __init__(self, mac_address): self.macAddress = mac_address self.delegate=NuimoDelegate(self) def set_delegate(self, delegate): self.delegate = delegate def connect(self): self.peripheral = Peripheral(self.macAddress, addrType='random') # Retrieve all characteristics from desired services and map them from their UUID characteristics = list(itertools.chain(*[self.peripheral.getServiceByUUID(uuid).getCharacteristics() for uuid in Nuimo.SERVICE_UUIDS])) characteristics = dict((c.uuid, c) for c in characteristics) # Store each characteristic's value handle for each characteristic name self.characteristicValueHandles = dict((name, characteristics[uuid].getHandle()) for uuid, name in Nuimo.CHARACTERISTIC_UUIDS.items()) # Subscribe for notifications for name in Nuimo.NOTIFICATION_CHARACTERISTIC_UUIDS: self.peripheral.writeCharacteristic(self.characteristicValueHandles[name] + 1, Nuimo.NOTIFICATION_ON, True) self.peripheral.setDelegate(self.delegate) def wait_for_notifications(self): self.peripheral.wait_for_notifications(1.0) def display_led_matrix(self, matrix, timeout, brightness=1.0): matrix = '{:<81}'.format(matrix[:81]) bites = list(map(lambda leds: reduce(lambda acc, led: acc + (1 << led if leds[led] not in [' ', '0'] else 0), range(0, len(leds)), 0), [matrix[i:i+8] for i in range(0, len(matrix), 8)])) self.peripheral.writeCharacteristic(self.characteristicValueHandles['LED_MATRIX'], struct.pack('BBBBBBBBBBBBB', bites[0], bites[1], bites[2], bites[3], bites[4], bites[5], bites[6], bites[7], bites[8], bites[9], bites[10], max(0, min(255, int(255.0 * brightness))), max(0, min(255, int(timeout * 10.0)))), True)
class MyTest(): BLE_Connected=False p=None bRunning=False DoWorkThread = 0 start_time=time.time() ReconnectIntervalSecond = 10 def __init__(self, index, mac_address): self.index=index self.mac_address=mac_address def Connect(self): print("Start To Connect BLE-" + str(self.index)) try: self.p = Peripheral(self.mac_address) self.p.setDelegate(MyDelegate(self.index)) self.BLE_Connected = True try: se10=self.p.getServiceByUUID('ebe0ccb0-7a0a-4b0c-8a1a-6ff2997da3a6') ch10=se10.getCharacteristics('ebe0ccc1-7a0a-4b0c-8a1a-6ff2997da3a6') ccc_desc = ch10[0].getDescriptors(forUUID=0x2902)[0] ccc_desc.write(b"\x02") except: print("Machine-" + str(self.index) + " Set Notification Error") except: self.BLE_Connected = False print("Machine-" + str(self.index) + " Connect Error") def Run(self): try: self.bRunning = True self.DoWorkThread = threading.Thread(target=self.DoWork) self.DoWorkThread.start() except: print("Machine-" + str(self.index) + " Run Threading Fail") finally: print("Machine-" + str(self.index) + " Run Threading Success") def DoWork(self): while self.bRunning: if self.BLE_Connected: try: self.p.waitForNotifications(0.5) except: self.BLE_Connected = False print("Machine-" + str(self.index) + " - Wait For Notification Error") else: if (int(time.time()-self.start_time)>self.ReconnectIntervalSecond): self.start_time=time.time() self.Connect() time.sleep(0.5) def Close(self): self.bRunning = False if self.BLE_Connected == True: try: self.p.disconnect() print("Machine-" + str(self.index) + " - Disconnect Success") except: self.p=None print("Machine-" + str(self.index) + " - Disconnect Fail")
device_msg[0] = dev.addr print("%s %s %s" % (device_msg[0], value, device_msg[1])) break return device_msg #scan for your device scanner = Scanner().withDelegate(ScanDelegate()) devices = scanner.scan(3.0) remote = blue_scan() #connect your device print(remote) p = Peripheral(remote[0]) #remote[0] p.setDelegate(ScanDelegate()) s = p.getServiceByUUID("0000ffe0-0000-1000-8000-00805f9b34fb") c = s.getCharacteristics()[0] def notification_thread(): while True: if p.waitForNotifications(2.0): # handleNotification() was called continue def send_thread(): while True: cmd = input() tmp = c.write(bytes(cmd, "utf-8"), True) if tmp : print("Send: " + cmd)
class YeelightService: """ YeelightService is the yeelight control util for python author zhaohui.sol """ SERVICE = "0000FFF0-0000-1000-8000-00805F9B34FB" CHAR_CONTROL = "0000FFF1-0000-1000-8000-00805F9B34FB" CHAR_DELAY = "0000FFF2-0000-1000-8000-00805F9B34FB" CHAR_DELAY_QUERY = "0000FFF3-0000-1000-8000-00805F9B34FB" CHAR_DELAY_NOTIFY = "0000FFF4-0000-1000-8000-00805F9B34FB" CHAR_QUERY = "0000FFF5-0000-1000-8000-00805F9B34FB" CHAR_NOTIFY = "0000FFF6-0000-1000-8000-00805F9B34FB" CHAR_COLOR_FLOW = "0000FFF7-0000-1000-8000-00805F9B34FB" CHAR_NAME = "0000FFF8-0000-1000-8000-00805F9B34FB" CHAR_NAME_NOTIFY = "0000FFF9-0000-1000-8000-00805F9B34FB" CHAR_COLOR_EFFECT = "0000FFFC-0000-1000-8000-00805F9B34FB" class NotifyDelegate(DefaultDelegate): def __init__(self): DefaultDelegate.__init__(self) self.queue = list() def register(self,callback): self.queue.append(callback) def deregister(self,callback): self.queue.remove(callback) def handleNotification(self,handle,data): logging.warning("notify data %s from %s." % (data,handle)) res = dict() res['data'] = data res['handle'] = handle for c in self.queue: c(res) def __init__(self,address): """ address is the yeelight blue ble hardware address """ self.data = dict() self.address = address self.delegate = YeelightService.NotifyDelegate() self.peripher = Peripheral(deviceAddr = address) self.service = self.peripher.getServiceByUUID(YeelightService.SERVICE) self.peripher.withDelegate(self.delegate) def __character_by_uuid__(self,uuid): ''' get character by a special uuid ''' characters = self.service.getCharacteristics(forUUID=uuid) return characters[0] if characters else None def __write_character__(self,uuid,strdata): ''' write data to a special uuid ''' logging.info(u"write %s to %s." % (strdata,uuid)) character = self.__character_by_uuid__(uuid) if character: character.write(strdata) else: pass def __read_character__(self,uuid): ''' read data from a special uuid,may be it's wrong ''' logging.info(u"read data from %s." % uuid) character = self.__character_by_uuid__(uuid) if character: return character.read() else: return None def __notify_character__(self,_to,_write): ''' write data to the uuid and wait data notify ''' res = dict() def callback(data): for k in data: res[k] = data[k] self.delegate.register(callback) self.__write_character__(_to,_write) if self.peripher.waitForNotifications(5): logging.info("notify incoming.") self.delegate.deregister(callback) return res else: logging.warning("notify timeout.") self.delegate.deregister(callback) return None def __format_request__(self,strdata,length = 18): ''' format the data to a special length ''' if strdata and length >= len(strdata) > 0: l = len(strdata) strdata += "".join(["," for i in range(length - l)]) return strdata else: return "".join(["," for i in range(length)]) def turn_on(self, brightness = 100): ''' turn on the light with white and full brightness ''' self.control(255,255,255,brightness) def turn_off(self): ''' turn off the light ''' self.control(0,0,0,0) def control(self,r,g,b,a): ''' turn on the light with special color ''' assert 0 <= r <= 255 assert 0 <= g <= 255 assert 0 <= b <= 255 assert 0 <= a <= 100 self.__write_character__(YeelightService.CHAR_CONTROL,self.__format_request__("%d,%d,%d,%d"%(r,g,b,a),18)) def delay_on(self,mins = 5): ''' turn on the light with a min param delay ''' assert 0 < mins < 24 * 60 self.__write_character__(YeelightService.CHAR_DELAY,self.__format_request__("%d,1" % mins,8)) def delay_off(self,mins = 5): ''' turn off the light with a min param delay ''' assert 0 < mins < 24 * 60 self.__write_character__(YeelightService.CHAR_DELAY,self.__format_request__("%d,0" % mins,8)) def delay_status(self): ''' query the delay status , this method return a raw dict with two key 'handle' and 'data' see http://www.yeelight.com/download/yeelight_blue_message_interface_v1.0.pdf ''' return self.__notify_character__(YeelightService.CHAR_DELAY_QUERY,self.__format_request__("RT",2)) def control_status(self): ''' query the light status , this method return a raw dict with two key 'handle' and 'data' see http://www.yeelight.com/download/yeelight_blue_message_interface_v1.0.pdf ''' return self.__notify_character__(YeelightService.CHAR_QUERY,self.__format_request__("S",1)) def start_color_flow(self,flows): ''' start color flow with a list of params, each param should contain 5 element which is r,g,b,brightness,delay see http://www.yeelight.com/download/yeelight_blue_message_interface_v1.0.pdf ''' assert len(flows) <= 9 for i,e in enumerate(flows): assert len(e) == 5 assert 0 <= e[0] <= 255 assert 0 <= e[1] <= 255 assert 0 <= e[2] <= 255 assert 0 <= e[3] <= 100 assert 0 <= e[4] <= 10 self.__write_character__(YeelightService.CHAR_COLOR_FLOW,self.__format_request__("%d,%d,%d,%d,%d,%d" % (i,e[0],e[1],e[2],e[3],e[4]),20)) self.__write_character__(YeelightService.CHAR_COLOR_FLOW,self.__format_request__("CB",20)) def stop_color_flow(self): ''' stop color flow ''' self.__write_character__(YeelightService.CHAR_COLOR_FLOW,self.__format_request__("CE",20)) def effect_smooth(self): ''' make the color change smooth ''' self.__write_character__(YeelightService.CHAR_COLOR_EFFECT,self.__format_request__("TS",2)) def effect_immediate(self): ''' make the color changes immediate ''' self.__write_character__(YeelightService.CHAR_COLOR_EFFECT,self.__format_request__("TE",2)) def effect_current_color(self): ''' use the current color as a default startup color ''' self.__write_character__(YeelightService.CHAR_COLOR_EFFECT,self.__format_request__("DF",2))
class CharDelegate(DefaultDelegate): def __init__(self): DefaultDelegate.__init__(self) def handleNotification(self, cHandle, data): [lux] = struct.unpack('<f', data) print("Value: " + str(lux) + " lux") YOUR_ADDRESS = "c0:98:e5:49:00:00" # Replace address with your device address YOUR_SERVICE_UUID = "de97aeee-0e7e-4720-8038-4dc47aa9562f" YOUR_CHAR_UUID = "de97aeef-0e7e-4720-8038-4dc47aa9562f" try: buckler = Peripheral(YOUR_ADDRESS) buckler.setDelegate(CharDelegate()) print("connected") sv = buckler.getServiceByUUID(YOUR_SERVICE_UUID) # Enable notifications: ch = sv.getCharacteristics(YOUR_CHAR_UUID)[0] buckler.writeCharacteristic(ch.valHandle + 1, b"\x01\x00") while True: if buckler.waitForNotifications(2.0): print("Got notification") finally: buckler.disconnect()
def handleNotification(self, cHandle, data): print "inside notification" print "Handle=", cHandle, "Battery level=", ord(data) devices = Scanner() # Scanner Object temp = devices.scan(10) # Start Scan for dev in temp: if dev.getScanData( )[3][2] == "mr. singh": # Check for the target BLE device name if dev.connectable: p = Peripheral(dev.addr, "random") # Connect to the Device p.setDelegate( ScanDelegate() ) # Create internal Object of scandelegate class to handle the notifications try: ch = p.getServiceByUUID(0x180F).getCharacteristics()[ 0] # get battery level characteristic for battery Service print ch if (ch.read()): print ord(ch.read()) # Read the characteristic Value while True: if p.waitForNotifications(1.0): # handleNotification() was called continue print "Waiting..." finally: p.disconnect()
class RobotController(): def __init__(self, address): self.robot = Peripheral(addr) self.robot.setDelegate(RobotDelegate(self)) print("connected") # get service from robot # get characteristic handles from service/robot self.sv = self.robot.getServiceByUUID(SERVICE_UUID) self.drive_command = self.sv.getCharacteristics(CHAR_UUID)[0] self.data = self.sv.getCharacteristics(DATA_UUID)[0] self.ack = self.sv.getCharacteristics(ACK_UUID)[0] self.data_ready = self.sv.getCharacteristics(DATA_READY_UUID)[0] self.data_list = [] # Set trial parameters ls = float(input("Left Speed: ")) rs = float(input("Right Speed: ")) t = float(input("Time (float seconds): ")) name = "l_{}_r_{}".format(int(ls), int(rs)) n = int(input("Num trials: ")) for i in range(n): while True: ack = struct.unpack("B", self.ack.read())[0] # Send command when the robot is ready if ack == 0: if self.data_list: self.write_data(ls, rs, "{}_{}".format(name, i)) self.data_list = [] break input("Ready?") self.send_command(ls, rs, t) ack = struct.unpack("B", self.ack.read())[0] # Wait until robot acknowledges before proceding while ack != 1: continue else: data_ready = struct.unpack("B", self.data_ready.read())[0] # Read data when the robot is ready if data_ready: data = struct.unpack("f" * 30, self.data.read()) self.data_list.extend(data) self.data_ready.write(struct.pack('B', *[False])) else: print("Waiting...") def send_command(self, ls, rs, t): # Tell Romi to drive specified left and right speeds for set amount of time self.ch.write(struct.pack('fff', *[ls, rs, t])) def write_data(self, ls, rs, name): # Write data to CSV file left_input = [ls] * (len(self.data_list) // 3) right_input = [rs] * (len(self.data_list) // 3) left_dists = self.data_list[::3] right_dists = self.data_list[1::3] times = self.data_list[2::3] header = [ "left_input", "right_input", "left_distance", "right_distance", "time" ] all_data = [left_input, right_input, left_dists, right_dists, times] df = pd.DataFrame(all_data).transpose() df.columns = header print(df) df.to_csv("data/{}.csv".format(name), index=False) def __enter__(self): return self def __exit__(self, exc_type, exc_value, traceback): self.robot.disconnect()
class BLESession(Session): """ Manage a session for Bluetooth Low Energy device such as micro:bit """ INITIAL = 1 DISCOVERY = 2 CONNECTED = 3 DONE = 4 ADTYPE_COMP_16B = 0x3 ADTYPE_COMP_128B = 0x7 class BLEThread(threading.Thread): """ Separated thread to control notifications to Scratch. It handles device discovery notification in DISCOVERY status and notifications from BLE devices in CONNECTED status. """ def __init__(self, session): threading.Thread.__init__(self) self.session = session def run(self): while True: logger.debug("loop in BLE thread") if self.session.status == self.session.DISCOVERY: logger.debug("send out found devices") devices = self.session.found_devices for d in devices: params = { 'rssi': d.rssi } params['peripheralId'] = devices.index(d) params['name'] = d.getValueText(0x9) self.session.notify('didDiscoverPeripheral', params) time.sleep(1) elif self.session.status == self.session.CONNECTED: logger.debug("in connected status:") delegate = self.session.delegate if delegate and len(delegate.handles) > 0: if not delegate.restart_notification_event.is_set(): delegate.restart_notification_event.wait() try: self.session.lock.acquire() self.session.perip.waitForNotifications(1.0) self.session.lock.release() except Exception as e: logger.error(e) self.session.close() break else: time.sleep(0.0) # To avoid repeated lock by this single thread, # yield CPU to other lock waiting threads. time.sleep(0) else: # Nothing to do: time.sleep(1) class BLEDelegate(DefaultDelegate): """ A bluepy handler to receive notifictions from BLE devices. """ def __init__(self, session): DefaultDelegate.__init__(self) self.session = session self.handles = {} self.restart_notification_event = threading.Event() self.restart_notification_event.set() def add_handle(self, serviceId, charId, handle): logger.debug(f"add handle for notification: {handle}") params = { 'serviceId': UUID(serviceId).getCommonName(), 'characteristicId': charId, 'encoding': 'base64' } self.handles[handle] = params def handleNotification(self, handle, data): logger.debug(f"BLE notification: {handle} {data}") if not self.restart_notification_event.is_set(): return params = self.handles[handle] params['message'] = base64.standard_b64encode(data).decode('ascii') self.session.notify('characteristicDidChange', params) def __init__(self, websocket, loop): super().__init__(websocket, loop) self.status = self.INITIAL self.found_devices = [] self.device = None self.perip = None self.delegate = None def close(self): self.status = self.DONE if self.perip: logger.info(f"disconnect to BLE peripheral: {self.perip}") self.perip.disconnect() def __del__(self): self.close() def matches(self, dev, filters): """ Check if the found BLE device mathces the filters Scracth specifies. """ logger.debug(f"in matches {dev} {filters}") for f in filters: if 'services' in f: for s in f['services']: logger.debug(f"sevice to check: {s}") given_uuid = s logger.debug(f"given: {given_uuid}") service_class_uuid = dev.getValueText(self.ADTYPE_COMP_128B) logger.debug(f"adtype 128b: {service_class_uuid}") if not service_class_uuid: service_class_uuid = dev.getValueText(self.ADTYPE_COMP_16B) logger.debug(f"adtype 16b: {service_class_uuid}") if not service_class_uuid: continue dev_uuid = UUID(service_class_uuid) logger.debug(f"dev: {dev_uuid}") logger.debug(given_uuid == dev_uuid) if given_uuid == dev_uuid: logger.debug("match...") return True if 'name' in f or 'manufactureData' in f: logger.error("name/manufactureData filters not implemented") # TODO: implement other filters defined: # ref: https://github.com/LLK/scratch-link/blob/develop/Documentation/BluetoothLE.md return False def handle_request(self, method, params): """Handle requests from Scratch""" if self.delegate: # Do not allow notification during request handling to avoid # websocket server errors self.delegate.restart_notification_event.clear() logger.debug("handle request to BLE device") logger.debug(method) if len(params) > 0: logger.debug(params) res = { "jsonrpc": "2.0" } if self.status == self.INITIAL and method == 'discover': scanner = Scanner() devices = scanner.scan(1.0) for dev in devices: if self.matches(dev, params['filters']): self.found_devices.append(dev) if len(self.found_devices) == 0: err_msg = f"BLE service not found for {params['filters']}" res["error"] = { "message": err_msg } self.status = self.DONE else: res["result"] = None self.status = self.DISCOVERY self.ble_thread = self.BLEThread(self) self.ble_thread.start() elif self.status == self.DISCOVERY and method == 'connect': logger.debug("connecting to the BLE device") self.device = self.found_devices[params['peripheralId']] try: self.perip = Peripheral(self.device.addr, self.device.addrType) logger.info(f"connect to BLE peripheral: {self.perip}") except BTLEDisconnectError as e: logger.error(f"failed to connect to BLE device: {e}") self.status = self.DONE if self.perip: res["result"] = None self.status = self.CONNECTED self.delegate = self.BLEDelegate(self) self.perip.withDelegate(self.delegate) else: err_msg = f"BLE connect failed :{self.device}" res["error"] = { "message": err_msg } self.status = self.DONE elif self.status == self.CONNECTED and method == 'read': logger.debug("handle read request") service_id = params['serviceId'] chara_id = params['characteristicId'] charas = self.perip.getCharacteristics(uuid=chara_id) c = charas[0] if c.uuid != UUID(chara_id): logger.error("Failed to get characteristic {chara_id}") self.status = self.DONE else: self.lock.acquire() b = c.read() self.lock.release() message = base64.standard_b64encode(b).decode('ascii') res['result'] = { 'message': message, 'encode': 'base64' } if params['startNotifications'] == True: logger.debug(f"start notification for {chara_id}") service = self.perip.getServiceByUUID(UUID(service_id)) chas = service.getCharacteristics(forUUID=chara_id) handle = chas[0].getHandle() # prepare notification handler self.delegate.add_handle(service_id, chara_id, handle) # request notification to the BLE device self.lock.acquire() self.perip.writeCharacteristic(chas[0].getHandle() + 1, b"\x01\x00", True) self.lock.release() elif self.status == self.CONNECTED and method == 'write': logger.debug("handle write request") service_id = params['serviceId'] chara_id = params['characteristicId'] charas = self.perip.getCharacteristics(uuid=chara_id) c = charas[0] if c.uuid != UUID(chara_id): logger.error("Failed to get characteristic {chara_id}") self.status = self.DONE else: if params['encoding'] != 'base64': logger.error("encoding other than base 64 is not " "yet supported: ", params['encoding']) msg_bstr = params['message'].encode('ascii') data = base64.standard_b64decode(msg_bstr) self.lock.acquire() c.write(data) self.lock.release() res['result'] = len(data) logger.debug(res) return res def end_request(self): logger.debug("end_request of BLESession") if self.delegate: self.delegate.restart_notification_event.set() return self.status == self.DONE
import sys import binascii import struct import time from bluepy.btle import UUID, Peripheral button_service_uuid = UUID(0xA000) button_char_uuid = UUID(0xA001) LED_service_uuid = UUID(0xA100) LED_char_uuid = UUID(0xA101) p = Peripheral("e5:d3:1d:fc:b0:d3", "random") ButtonService = p.getServiceByUUID(button_service_uuid) LEDService = p.getServiceByUUID(LED_service_uuid) try: ch = ButtonService.getCharacteristics(button_char_uuid)[0] ch2 = LEDService.getCharacteristics(LED_char_uuid)[0] while 1: if (ch.supportsRead()): val = binascii.b2a_hex(ch.read()) if (val == "00"): print("The button is not pressed.") else: print("The button is pressed.") LED_in = raw_input("Turn on the LED ? (y/n)") writevalue = 0 if (LED_in == 'y'): writevalue = "01" else: writevalue = "00"
class LightBlueBean(DefaultDelegate): # https://github.com/PunchThrough/bean-documentation/blob/master/app_message_types.md MSG_ID_SERIAL_DATA = 0x0000 MSG_ID_BT_SET_ADV = 0x0500 MSG_ID_BT_SET_CONN = 0x0502 MSG_ID_BT_SET_LOCAL_NAME = 0x0504 MSG_ID_BT_SET_PIN = 0x0506 MSG_ID_BT_SET_TX_PWR = 0x0508 MSG_ID_BT_GET_CONFIG = 0x0510 MSG_ID_BT_ADV_ONOFF = 0x0512 MSG_ID_BT_SET_SCRATCH = 0x0514 MSG_ID_BT_GET_SCRATCH = 0x0515 MSG_ID_BT_RESTART = 0x0520 MSG_ID_GATING = 0x0550 MSG_ID_BL_CMD = 0x1000 MSG_ID_BL_FW_BLOCK = 0x1001 MSG_ID_BL_STATUS = 0x1002 MSG_ID_CC_LED_WRITE = 0x2000 MSG_ID_CC_LED_WRITE_ALL = 0x2001 MSG_ID_CC_LED_READ_ALL = 0x2002 MSG_ID_CC_LED_DATA = 0x2082 MSG_ID_CC_ACCEL_READ = 0x2010 MSG_ID_CC_ACCEL_DATA = 0x2090 MSG_ID_CC_TEMP_READ = 0x2011 MSG_ID_CC_TEMP_DATA = 0x2091 MSG_ID_CC_BATT_READ = 0x2015 MSG_ID_CC_BATT_DATA = 0x2095 MSG_ID_AR_SET_POWER = 0x3000 MSG_ID_AR_GET_CONFIG = 0x3006 MSG_ID_DB_LOOPBACK = 0xFE00 MSG_ID_DB_COUNTER = 0xFE01 def __init__(self, mac): self.conn = Peripheral(mac) self.conn.setDelegate(self) self.count = 0 self.buffin = [None]*10 self.got1 = False print('connected') self.service = self.conn.getServiceByUUID(_LBN_UUID(0x10)) self.serial = self.service.getCharacteristics(_LBN_UUID(0x11)) [0] #print(self.serial.propertiesToString()) # Turn on notificiations self.conn.writeCharacteristic(0x2f, '\x01\x00', False) i = 0 while True: #print(self.serial.read()) self.write("a" * 60) #self.write("a" * 5) #self.sendCmd(LightBlueBean.MSG_ID_CC_ACCEL_READ) self.conn.waitForNotifications(1) time.sleep(1) self.conn.disconnect() def write(self, data): self.sendCmd(LightBlueBean.MSG_ID_SERIAL_DATA, data) def sendCmd(self, cmd, data = ""): # https://github.com/PunchThrough/bean-documentation/blob/master/serial_message_protocol.md gst = struct.pack("!BxH", len(data)+2, cmd) + data crc = struct.pack("<H", crc16(gst, 0xFFFF)) gst += crc gt_qty = len(gst)/19 if len(gst) % 19 != 0: gt_qty += 1 #amnt = len(gst) / gt_qty optimal_packet_size = 19 for ch in xrange(0, gt_qty): data = gst[:optimal_packet_size] gst = gst[optimal_packet_size:] gt = 0 if ch == 0: gt = 0x80 gt |= self.count << 5 gt |= gt_qty - ch - 1 gt = struct.pack("B", gt) + data #print("<", hexdump(gt)) self.serial.write(gt) #time.sleep(0.1) self.count = (self.count + 1) % 4 def writeRaw(self, data): self.conn.writeCharacteristic(0x0b, data, False) def handleNotification(self, cHandle, data): #print(">", hexdump(data)) gt = struct.unpack("B", data[0]) [0] #gt_cntr = gt & 0x60 gt_left = gt & 0x1F if gt & 0x80: self.got1 = True self.buffin = self.buffin[:gt_left+1] self.buffin[gt_left] = data[1:] if self.got1 and not self.buffin.count(None): #print("Got ", len(self.buffin), "packets") self.buffin.reverse() self.buffin = ''.join(self.buffin) crc_ = crc16(self.buffin[:-2], 0xFFFF) dlen, cmd = struct.unpack("!BxH", self.buffin[:4]) crc = struct.unpack("<H", self.buffin[-2:]) [0] if crc == crc_: print(self.buffin[4:-2]) else: print("CRC check failure") self.buffin = [None]*10 self.got1 = False
class SBrickCommunications(threading.Thread, IdleObject): def __init__(self, sbrick_addr): threading.Thread.__init__(self) IdleObject.__init__(self) self.lock = threading.RLock() self.drivingLock = threading.RLock() self.eventSend = threading.Event() self.sBrickAddr = sbrick_addr self.owner_password = None self.brickChannels = [ SBrickChannelDrive(0, self.eventSend), SBrickChannelDrive(1, self.eventSend), SBrickChannelDrive(2, self.eventSend), SBrickChannelDrive(3, self.eventSend), ] self.SBrickPeripheral = None self.stopFlag = False self.characteristicRemote = None self.need_authentication = False self.authenticated = False self.channel_config_ids = dict() def set_channel_config_id(self, channel, config_id): self.channel_config_ids[config_id] = channel self.brickChannels[channel].set_config_id(config_id) def terminate(self): self.stopFlag = True def is_driving(self): locked = self.drivingLock.acquire(False) if locked: self.drivingLock.release() return not locked def connect_to_sbrick(self, owner_password): self.owner_password = owner_password self.start() def run(self): try: monotime = 0.0 self.SBrickPeripheral = Peripheral() self.SBrickPeripheral.connect(self.sBrickAddr) service = self.SBrickPeripheral.getServiceByUUID('4dc591b0-857c-41de-b5f1-15abda665b0c') characteristics = service.getCharacteristics('02b8cbcc-0e25-4bda-8790-a15f53e6010f') for characteristic in characteristics: if characteristic.uuid == '02b8cbcc-0e25-4bda-8790-a15f53e6010f': self.characteristicRemote = characteristic if self.characteristicRemote is None: return self.emit('sbrick_connected') self.need_authentication = self.get_need_authentication() self.authenticated = not self.need_authentication if self.need_authentication: if self.password_owner is not None: self.authenticate_owner(self.password_owner) while not self.stopFlag: if self.authenticated: if monotonic.monotonic() - monotime >= 0.05: self.send_command() monotime = monotonic.monotonic() self.eventSend.wait(0.01) for channel in self.brickChannels: if channel.decrement_run_timer(): monotime = 0.0 self.drivingLock.release() # print("stop run normal") self.emit("sbrick_channel_stop", channel.channel) if channel.decrement_brake_timer(): self.drivingLock.release() # print("stop brake timer") monotime = 0.0 self.emit("sbrick_channel_stop", channel.channel) if self.authenticated: self.stop_all() self.send_command() self.SBrickPeripheral.disconnect() self.emit('sbrick_disconnected_ok') except BTLEException as ex: self.emit("sbrick_disconnected_error", ex.message) def get_channel(self, channel): if isinstance(channel, six.integer_types): return self.brickChannels[channel] if isinstance(channel, six.string_types): return self.brickChannels[self.channel_config_ids[channel]] return None def drive(self, channel, pwm, reverse, time, brake_after_time=False): with self.lock: ch = self.get_channel(channel) if ch is not None: ch.drive(pwm, reverse, time, brake_after_time) self.emit("sbrick_drive_sent", ch.channel, time) self.eventSend.set() def stop(self, channel, braked=False): with self.lock: ch = self.get_channel(channel) if ch is not None: ch.stop(braked) self.emit("sbrick_drive_sent", ch.channel, -2) self.eventSend.set() def stop_all(self): with self.lock: for channel in self.brickChannels: channel.stop() self.eventSend.set() def change_pwm(self, channel, pwm, change_reverse=False): with self.lock: ch = self.get_channel(channel) if ch is not None: ch.set_pwm(pwm, change_reverse) self.eventSend.set() def change_reverse(self, channel, reverse): with self.lock: ch = self.get_channel(channel) if ch is not None: ch.set_reverse(reverse) self.eventSend.set() def send_command(self): with self.lock: # try: drivecmd = bytearray([0x01]) brakecmd = bytearray([0x00]) for channel in self.brickChannels: drivecmd = channel.get_command_drive(drivecmd) brakecmd = channel.get_command_brake(brakecmd) if len(drivecmd) > 1: self.drivingLock.acquire() self.characteristicRemote.write(drivecmd, True) self.print_hex_string("drive sent", drivecmd) if len(brakecmd) > 1: self.characteristicRemote.write(brakecmd, True) self.print_hex_string("brake sent", brakecmd) # return True # except Exception as ex: # self.emit("sbrick_disconnected_error",ex.message) # return False def disconnect_sbrick(self): with self.lock: self.stopFlag = True @staticmethod def print_hex_string(what, strin): out = what + " -> " for chrx in strin: out = "%s %0X" % (out, chrx) print(out) def get_voltage(self): with self.lock: try: self.characteristicRemote.write(b"\x0f\x00") value = self.characteristicRemote.read() valueint = struct.unpack("<H", value)[0] return (valueint * 0.83875) / 2047.0 except BTLEException as ex: self.emit("sbrick_disconnected_error", ex.message) def get_temperature(self): with self.lock: try: self.characteristicRemote.write(b"\x0f\x0e") value = self.characteristicRemote.read() valueint = struct.unpack("<H", value)[0] return valueint / 118.85795 - 160 except BTLEException as ex: self.emit("sbrick_disconnected_error", ex.message) def get_thermal_limit(self): with self.lock: try: self.characteristicRemote.write(b'\x15') value = self.characteristicRemote.read() valueint = struct.unpack("<H", value)[0] return valueint / 118.85795 - 160 except BTLEException as ex: self.emit("sbrick_disconnected_error", ex.message) def get_watchdog_timeout(self): with self.lock: try: self.characteristicRemote.write(b'\x0e') value = self.characteristicRemote.read() return struct.unpack("<B", value)[0] * 0.1 except BTLEException as ex: self.emit("sbrick_disconnected_error", ex.message) def get_authentication_timeout(self): with self.lock: try: self.characteristicRemote.write(b'\x09') value = self.characteristicRemote.read() return struct.unpack("<B", value)[0] * 0.1 except BTLEException as ex: self.emit("sbrick_disconnected_error", ex.message) def get_power_cycle_counter(self): with self.lock: try: self.characteristicRemote.write(b'\x28') value = self.characteristicRemote.read() return struct.unpack("<I", value)[0] except BTLEException as ex: self.emit("sbrick_disconnected_error", ex.message) def get_uptime(self): with self.lock: try: self.characteristicRemote.write(b'\x29') value = self.characteristicRemote.read() seconds = struct.unpack("<I", value)[0] * 0.1 minutes = seconds // 60 hours = minutes // 60 return "%02d:%02d:%02d" % (hours, minutes % 60, seconds % 60) except BTLEException as ex: self.emit("sbrick_disconnected_error", ex.message) def get_hardware_version(self): try: return self.SBrickPeripheral.readCharacteristic(0x000c).decode("utf-8") except BTLEException as ex: self.emit("sbrick_disconnected_error", ex.message) def get_software_version(self): try: return self.SBrickPeripheral.readCharacteristic(0x000a).decode("utf-8") except BTLEException as ex: self.emit("sbrick_disconnected_error", ex.message) def get_brick_id(self): with self.lock: try: self.characteristicRemote.write(b'\x0a') value = self.characteristicRemote.read() return "%0X %0X %0X %0X %0X %0X" % ( value[0], value[1], value[2], value[3], value[4], value[5]) except BTLEException as ex: self.emit("sbrick_disconnected_error", ex.message) def get_need_authentication(self): with self.lock: try: self.characteristicRemote.write(b'\x02') value = self.characteristicRemote.read() return struct.unpack("<B", value)[0] == 1 except BTLEException as ex: self.emit("sbrick_disconnected_error", ex.message) def get_is_authenticated(self): with self.lock: try: self.characteristicRemote.write(b'\x03') value = self.characteristicRemote.read() return struct.unpack("<B", value)[0] == 1 except BTLEException as ex: self.emit("sbrick_disconnected_error", ex.message) def get_user_id(self): with self.lock: try: self.characteristicRemote.write(b'\x04') value = self.characteristicRemote.read() return struct.unpack("<B", value)[0] == 1 except BTLEException as ex: self.emit("sbrick_error", ex.message) def authenticate_owner(self, password): with self.lock: try: self.authenticated = False cmd = bytearray([0x05, 0x00]) for ch in password: cmd.append(ord(ch)) self.characteristicRemote.write(cmd) self.authenticated = True except BTLEException as ex: self.emit("sbrick_error", ex.message) def authenticate_guest(self, password): with self.lock: try: self.authenticated = False cmd = bytearray([0x05, 0x01]) for ch in password: cmd.append(ord(ch)) self.characteristicRemote.write(cmd) self.authenticated = True except BTLEException as ex: self.emit("sbrick_error", ex.message) def clear_owner_password(self): with self.lock: try: self.characteristicRemote.write(b'\x06\x00') except BTLEException as ex: self.emit("sbrick_error", ex.message) def clear_guest_password(self): with self.lock: try: self.characteristicRemote.write(b'\x06\x01') except BTLEException as ex: self.emit("sbrick_error", ex.message) def set_owner_password(self, password): with self.lock: try: cmd = bytearray([0x07, 0x00]) for ch in password: cmd.append(ord(ch)) self.characteristicRemote.write(cmd) except BTLEException as ex: self.emit("sbrick_error", ex.message) def set_guest_password(self, password): with self.lock: try: cmd = bytearray([0x07, 0x01]) for ch in password: cmd.append(ord(ch)) self.characteristicRemote.write(cmd) except BTLEException as ex: self.emit("sbrick_error", ex.message) def set_authentication_timeout(self, seconds): with self.lock: try: cmd = bytearray([0x08, seconds / 0.1]) self.characteristicRemote.write(cmd) except BTLEException as ex: self.emit("sbrick_error", ex.message)
class OpenBCIGanglion(object): """ Handle a connection to an OpenBCI board. Args: port: MAC address of the Ganglion Board. "None" to attempt auto-detect. aux: enable on not aux channels (i.e. switch to 18bit mode if set) impedance: measures impedance when start streaming timeout: in seconds, if set will try to disconnect / reconnect after a period without new data -- should be high if impedance check max_packets_to_skip: will try to disconnect / reconnect after too many packets are skipped baud, filter_data, daisy: Not used, for compatibility with v3 """ def __init__(self, port=None, baud=0, filter_data=False, scaled_output=True, daisy=False, log=True, aux=False, impedance=False, timeout=2, max_packets_to_skip=20): # unused, for compatibility with Cyton v3 API self.daisy = False # these one are used self.log = log # print_incoming_text needs log self.aux = aux self.streaming = False self.timeout = timeout self.max_packets_to_skip = max_packets_to_skip self.scaling_output = scaled_output self.impedance = impedance # might be handy to know API self.board_type = "ganglion" print("Looking for Ganglion board") if port == None: port = self.find_port() self.port = port # find_port might not return string self.connect() self.streaming = False # number of EEG channels and (optionally) accelerometer channel self.eeg_channels_per_sample = 4 self.aux_channels_per_sample = 3 self.imp_channels_per_sample = 5 self.read_state = 0 self.log_packet_count = 0 self.packets_dropped = 0 self.time_last_packet = 0 # Disconnects from board when terminated atexit.register(self.disconnect) def getBoardType(self): """ Returns the version of the board """ return self.board_type def setImpedance(self, flag): """ Enable/disable impedance measure """ self.impedance = bool(flag) def connect(self): """ Connect to the board and configure it. Note: recreates various objects upon call. """ print("Init BLE connection with MAC: " + self.port) print("NB: if it fails, try with root privileges.") self.gang = Peripheral(self.port, 'random') # ADDR_TYPE_RANDOM print("Get mainservice...") self.service = self.gang.getServiceByUUID(BLE_SERVICE) print("Got:" + str(self.service)) print("Get characteristics...") self.char_read = self.service.getCharacteristics(BLE_CHAR_RECEIVE)[0] print("receive, properties: " + str(self.char_read.propertiesToString()) + ", supports read: " + str(self.char_read.supportsRead())) self.char_write = self.service.getCharacteristics(BLE_CHAR_SEND)[0] print("write, properties: " + str(self.char_write.propertiesToString()) + ", supports read: " + str(self.char_write.supportsRead())) self.char_discon = self.service.getCharacteristics(BLE_CHAR_DISCONNECT)[0] print("disconnect, properties: " + str(self.char_discon.propertiesToString()) + ", supports read: " + str(self.char_discon.supportsRead())) # set delegate to handle incoming data self.delegate = GanglionDelegate(self.scaling_output) self.gang.setDelegate(self.delegate) # enable AUX channel if self.aux: print("Enabling AUX data...") try: self.ser_write(b'n') except Exception as e: print("Something went wrong while enabling aux channels: " + str(e)) print("Turn on notifications") # nead up-to-date bluepy, cf https://github.com/IanHarvey/bluepy/issues/53 self.desc_notify = self.char_read.getDescriptors(forUUID=0x2902)[0] try: self.desc_notify.write(b"\x01") except Exception as e: print("Something went wrong while trying to enable notification: " + str(e)) print("Connection established") def init_streaming(self): """ Tell the board to record like crazy. """ try: if self.impedance: print("Starting with impedance testing") self.ser_write(b'z') else: self.ser_write(b'b') except Exception as e: print("Something went wrong while asking the board to start streaming: " + str(e)) self.streaming = True self.packets_dropped = 0 self.time_last_packet = timeit.default_timer() def find_port(self): """Detects Ganglion board MAC address If more than 1 around, will select first. Needs root privilege. """ print("Try to detect Ganglion MAC address. " "NB: Turn on bluetooth and run as root for this to work!" "Might not work with every BLE dongles.") scan_time = 5 print("Scanning for 5 seconds nearby devices...") # From bluepy example class ScanDelegate(DefaultDelegate): def __init__(self): DefaultDelegate.__init__(self) def handleDiscovery(self, dev, isNewDev, isNewData): if isNewDev: print("Discovered device: " + dev.addr) elif isNewData: print("Received new data from: " + dev.addr) scanner = Scanner().withDelegate(ScanDelegate()) devices = scanner.scan(scan_time) nb_devices = len(devices) if nb_devices < 1: print("No BLE devices found. Check connectivity.") return "" else: print("Found " + str(nb_devices) + ", detecting Ganglion") list_mac = [] list_id = [] for dev in devices: # "Ganglion" should appear inside the "value" associated # to "Complete Local Name", e.g. "Ganglion-b2a6" for (adtype, desc, value) in dev.getScanData(): if desc == "Complete Local Name" and value.startswith("Ganglion"): list_mac.append(dev.addr) list_id.append(value) print("Got Ganglion: " + value + ", with MAC: " + dev.addr) break nb_ganglions = len(list_mac) if nb_ganglions < 1: print("No Ganglion found ;(") raise OSError('Cannot find OpenBCI Ganglion MAC address') if nb_ganglions > 1: print("Found " + str(nb_ganglions) + ", selecting first") print("Selecting MAC address " + list_mac[0] + " for " + list_id[0]) return list_mac[0] def ser_write(self, b): """Access serial port object for write""" self.char_write.write(b) def ser_read(self): """Access serial port object for read""" return self.char_read.read() def ser_inWaiting(self): """ Slightly different from Cyton API, return True if ASCII messages are incoming.""" # FIXME: might have a slight problem with thread because of notifications... if self.delegate.receiving_ASCII: # in case the packet indicating the end of the message drops, we use a 1s timeout if timeit.default_timer() - self.delegate.time_last_ASCII > 2: self.delegate.receiving_ASCII = False return self.delegate.receiving_ASCII def getSampleRate(self): return SAMPLE_RATE def getNbEEGChannels(self): """Will not get new data on impedance check.""" return self.eeg_channels_per_sample def getNbAUXChannels(self): """Might not be used depending on the mode.""" return self.aux_channels_per_sample def getNbImpChannels(self): """Might not be used depending on the mode.""" return self.imp_channels_per_sample def start_streaming(self, callback, lapse=-1): """ Start handling streaming data from the board. Call a provided callback for every single sample that is processed Args: callback: A callback function or a list of functions that will receive a single argument of the OpenBCISample object captured. """ if not self.streaming: self.init_streaming() start_time = timeit.default_timer() # Enclose callback funtion in a list if it comes alone if not isinstance(callback, list): callback = [callback] while self.streaming: # should the board get disconnected and we could not wait for notification # anymore, a reco should be attempted through timeout mechanism try: # at most we will get one sample per packet self.waitForNotifications(1. / self.getSampleRate()) except Exception as e: print("Something went wrong while waiting for a new sample: " + str(e)) # retrieve current samples on the stack samples = self.delegate.getSamples() self.packets_dropped = self.delegate.getMaxPacketsDropped() if samples: self.time_last_packet = timeit.default_timer() for call in callback: for sample in samples: call(sample) if (lapse > 0 and timeit.default_timer() - start_time > lapse): self.stop() if self.log: self.log_packet_count = self.log_packet_count + 1 # Checking connection -- timeout and packets dropped self.check_connection() def waitForNotifications(self, delay): """ Allow some time for the board to receive new data. """ self.gang.waitForNotifications(delay) def test_signal(self, signal): """ Enable / disable test signal """ if signal == 0: self.warn("Disabling synthetic square wave") try: self.char_write.write(b']') except Exception as e: print("Something went wrong while setting signal: " + str(e)) elif signal == 1: self.warn("Eisabling synthetic square wave") try: self.char_write.write(b'[') except Exception as e: print("Something went wrong while setting signal: " + str(e)) else: self.warn( "%s is not a known test signal. Valid signal is 0-1" % signal) def set_channel(self, channel, toggle_position): """ Enable / disable channels """ try: # Commands to set toggle to on position if toggle_position == 1: if channel is 1: self.ser.write(b'!') if channel is 2: self.ser.write(b'@') if channel is 3: self.ser.write(b'#') if channel is 4: self.ser.write(b'$') # Commands to set toggle to off position elif toggle_position == 0: if channel is 1: self.ser.write(b'1') if channel is 2: self.ser.write(b'2') if channel is 3: self.ser.write(b'3') if channel is 4: self.ser.write(b'4') except Exception as e: print("Something went wrong while setting channels: " + str(e)) """ Clean Up (atexit) """ def stop(self): print("Stopping streaming...") self.streaming = False # connection might be already down here try: if self.impedance: print("Stopping with impedance testing") self.ser_write(b'Z') else: self.ser_write(b's') except Exception as e: print("Something went wrong while asking the board to stop streaming: " + str(e)) if self.log: logging.warning('sent <s>: stopped streaming') def disconnect(self): if (self.streaming == True): self.stop() print("Closing BLE..") try: self.char_discon.write(b' ') except Exception as e: print("Something went wrong while asking the board to disconnect: " + str(e)) # should not try to read/write anything after that, will crash try: self.gang.disconnect() except Exception as e: print("Something went wrong while shutting down BLE link: " + str(e)) logging.warning('BLE closed') """ SETTINGS AND HELPERS """ def warn(self, text): if self.log: # log how many packets where sent succesfully in between warnings if self.log_packet_count: logging.info('Data packets received:' + str(self.log_packet_count)) self.log_packet_count = 0 logging.warning(text) print("Warning: %s" % text) def check_connection(self): """ Check connection quality in term of lag and number of packets drop. Reinit connection if necessary. FIXME: parameters given to the board will be lost. """ # stop checking when we're no longer streaming if not self.streaming: return # check number of dropped packets and duration without new packets, deco/reco if too large if self.packets_dropped > self.max_packets_to_skip: self.warn("Too many packets dropped, attempt to reconnect") self.reconnect() elif self.timeout > 0 and timeit.default_timer() - self.time_last_packet > self.timeout: self.warn("Too long since got new data, attempt to reconnect") # if error, attempt to reconect self.reconnect() def reconnect(self): """ In case of poor connection, will shut down and relaunch everything. FIXME: parameters given to the board will be lost.""" self.warn('Reconnecting') self.stop() self.disconnect() self.connect() self.init_streaming()
washer_service_uuid = UUID("6e400001-b5a3-f393-e0a9-e50e24dcca9e") battery_service_uuid = UUID("180F") #washer service Characteristics의 센서에서 전송하는 정보이며 Access Permisson은 Notify washerTX_char_uuid = UUID("6e400003-b5a3-f393-e0a9-e50e24dcca9e") battery_level_uuid = UUID("00002a19-0000-1000-8000-00805f9b34fb") #if len(sys.argv) != 2: # print ("Fatal, must pass device address:", sys.argv[0], "<device address="">") # quit() p = Peripheral("c7:74:31:9A:F8:D1","random") p.setDelegate( MyDelegate(p) ) #WasherService, BatteryService를 가져온다. WasherService=p.getServiceByUUID(washer_service_uuid) BatteryService =p.getServiceByUUID(battery_service_uuid) #TX의 Characteristics를 가져온다. WasherTX_C = WasherService.getCharacteristics(washerTX_char_uuid)[0] #battey의 Characteristics를 가져온다. Battery_C = BatteryService.getCharacteristics(battery_level_uuid)[0] # Client Characteristic Descriptor의 handler # 0x13 은 washerTX, 0x0F는 battery # notifications의 비트를 1로 바꿔 활성화한다. # 메인 루프 -----------------------
class BleCam(object): locked = True def __init__(self, address, pincode): print("Connecting to %s..." % address) self.pincode = pincode self.periph = Peripheral(address) self._ipcamservice() self.name = self.periph.getCharacteristics(uuid=0x2a00)[0].read().decode() # wellknown name characteristic print("Connected to '%s'" % self.name) def _ipcamservice(self): try: print("Verifying IPCam service") self.service = self.periph.getServiceByUUID(0xd001) self.handles = self.service.getCharacteristics() except BTLEEException: print("no IPCam service found for %s" % periph.address) def dumpchars(self): print("%s supports these characteristics:" % self.name) for h in self.handles: print("%s - Handle=%#06x (%s)" % (h.uuid, h.getHandle(), h.propertiesToString())) def unlock(self): if not self.locked: return True auth = self.service.getCharacteristics(0xa001)[0] state = kv2dict(auth.read().decode()) # already unlocked? if state["M"] == 0: self.locked = False return True self.challenge = state["C"] hashit = self.name + self.pincode + self.challenge self.key = base64.b64encode(hashlib.md5(hashit.encode()).digest())[:16] try: auth.write("M=0;K=".encode() + self.key, True) self.locked = False except: print("ERROR: failed to unlock %s - wrong pincode?" % self.name) return not self.locked def get_ipconfig(self): if not self.unlock(): return return kv2dict(self.service.getCharacteristics(0xa104)[0].read().decode()) def get_wificonfig(self): if not self.unlock(): return return kv2dict(self.service.getCharacteristics(0xa101)[0].read().decode()) def wifilink(self): if not self.unlock(): return r = kv2dict(self.service.getCharacteristics(0xa103)[0].read().decode()) return r["S"] == "1" def sysinfo(self): if not self.unlock(): return return kv2dict(self.service.getCharacteristics(0xa200)[0].read().decode()) def setup_wifi(self, essid, passwd): for net in self.wifi_scan(): if net["I"] == essid: cfg = "M=" + net["M"] + ";I=" + essid + ";S=" + net["S"] + ";E=" + net["E"] + ";K=" + passwd print("Will configure: %s" % cfg) self.service.getCharacteristics(0xa101)[0].write(cfg.encode(), True) self.service.getCharacteristics(0xa102)[0].write("C=1".encode(), True) return True print("%s cannot see the '%s' network" % (self.name, essid)) return False def wifi_scan(self): def _wifi2dict(wifistr): return kv2dict(wifistr[2:], ",") if not self.unlock(): return print("%s is scanning for WiFi networks..." % self.name) scan = self.service.getCharacteristics(0xa100)[0] p = -1 n = 0 result = "" while p < n: t = scan.read().decode().split(";", 3) result = result + t[2] if not t[0].startswith("N=") or not t[1].startswith("P="): return n = int(t[0].split("=",2)[1]) p = int(t[1].split("=",2)[1]) # print("read page %d of %d" % (p, n)) return map(_wifi2dict, result.split("&", 50)) def run_command(self, command): if not self.unlock(): return run = "P=" + self.pincode + ";N=" + self.pincode + "&&(" + command + ")&" if len(run) > 128: print("ERROR: command is too long") return print("Attempting to run '%s' on %s by abusing the 'set admin password' request" % (command, self.name)) try: self.service.getCharacteristics(0xa201)[0].write(run.encode(), True) except: # try repeating with an empty password, which seems to be te initial state after factory reset run = "P=;N=" + self.pincode + "&&(" + command + ")&" try: self.service.getCharacteristics(0xa201)[0].write(run.encode(), True) except: print("ERROR: Failed - is the admin password different from the pincode?")
class BleCam(object): locked = True def __init__(self, address, pincode): print("Connecting to %s..." % address) self.pincode = pincode self.periph = Peripheral(address) self.periph.setMTU(256) self._ipcamservice() #self.name = self.periph.getCharacteristics(uuid=0xa200)[0].read().decode() # wellknown name characteristic #self.idstring = self.periph.getCharacteristics(uuid=0xa200)[0] #self.idstate = kv2dict(self.idstring.read().decode()) #self.name = self.idstate["N"] + "-" + self.idstate["M"][8:] # CHANGE #### to the last two mac octets in hex when using on a factory reset DCS-8600LH self.name = "DCS-8600LH-####" print("Connected to '%s'" % self.name) def _ipcamservice(self): try: print("Verifying IPCam service") self.service = self.periph.getServiceByUUID(0xd001) self.handles = self.service.getCharacteristics() #print("self.service = '%s'" % self.service) #print("self.handles = '%s'" % '\n'.join(map(str,self.handles))) except BTLEEException: print("no IPCam service found for %s" % periph.address) def dumpchars(self): print("%s supports these characteristics:" % self.name) for h in self.handles: print("%s - Handle=%#06x (%s)" % (h.uuid, h.getHandle(), h.propertiesToString())) def unlock(self): if not self.locked: return True auth = self.service.getCharacteristics(0xa001)[0] state = kv2dict(auth.read().decode()) #print("auth = '%s'" % auth) #print("state = '%s'" % state) # already unlocked? if state["M"] == 0: self.locked = False return True self.challenge = state["C"] #print("self.challenge = '%s'" % self.challenge) hashit = self.name + self.pincode + self.challenge #print("hashit = '%s'" % hashit) self.key = base64.b64encode(hashlib.md5(hashit.encode()).digest())[:16] #print("self.key = '%s'" % self.key) try: auth.write("M=0;K=".encode() + self.key, True) self.locked = False except: print("ERROR: failed to unlock %s - wrong pincode?" % self.name) return not self.locked def get_ipconfig(self): if not self.unlock(): return return kv2dict( self.service.getCharacteristics(0xa104)[0].read().decode()) def get_wificonfig(self): if not self.unlock(): return return kv2dict( self.service.getCharacteristics(0xa101)[0].read().decode()) def wifilink(self): if not self.unlock(): return r = kv2dict(self.service.getCharacteristics(0xa103)[0].read().decode()) return r["S"] == "1" def sysinfo(self): if not self.unlock(): return return kv2dict( self.service.getCharacteristics(0xa200)[0].read().decode()) def setup_wifi(self, essid, passwd): for net in self.wifi_scan(): if net["I"] == essid: cfg = "M=" + net["M"] + ";I=" + essid + ";S=" + net[ "S"] + ";E=" + net["E"] + ";K=" + passwd print("Will configure: %s" % cfg) self.service.getCharacteristics(0xa101)[0].write( cfg.encode(), True) self.service.getCharacteristics(0xa102)[0].write( "C=1".encode(), True) return True print("%s cannot see the '%s' network" % (self.name, essid)) return False def wifi_scan(self): def _wifi2dict(wifistr): return kv2dict(wifistr[2:], ",") if not self.unlock(): return print("%s is scanning for WiFi networks..." % self.name) scan = self.service.getCharacteristics(0xa100)[0] p = -1 n = 0 result = "" while p < n: t = scan.read().decode().split(";", 3) result = result + t[2] if not t[0].startswith("N=") or not t[1].startswith("P="): return n = int(t[0].split("=", 2)[1]) p = int(t[1].split("=", 2)[1]) # print("read page %d of %d" % (p, n)) return map(_wifi2dict, result.split("&", 50)) def run_command(self, command): if not self.unlock(): return #run = "P=" + self.pincode + ";N=" + self.pincode + "\"&&(" + command + ")&\"" runa = "P=" + self.pincode + ";N=" runb = self.pincode + "\"&&(" + command + ")&\"" run = runa + runb #run = "P=;N=" + self.pincode # sprintf(cmd,"/usr/bin/mdb set admin_passwd \"%s\"",newpass); #print("COMMAND: /usr/bin/mdb set admin_passwd \"%s\"" % runb) #print("run = '%s'" % run) #return if len(run) > 128: print("ERROR: command is too long") return print( "Attempting to run '%s' on %s by abusing the 'set admin password' request" % (command, self.name)) try: self.service.getCharacteristics(0xa201)[0].write( run.encode(), True) except: print("Failed - trying again with blank password") # try repeating with an empty password, which seems to be the initial state after factory reset #run = "P=;N=" + self.pincode + "&&(" + command + ")&" run = "P=;N=" + runb + "\"&&(" + command + ")&\"" try: self.service.getCharacteristics(0xa201)[0].write( run.encode(), True) except: print( "ERROR: Failed - is the admin password different from the pincode?" )
class OpenBCIGanglion(object): """ OpenBCIGanglion handles the connection to an OpenBCI Ganglion board. The OpenBCIGanglion class interfaces with the Cyton Dongle and the Cyton board to parse the data received and output it to Python as a OpenBCISample object. Args: mac: A string representing the Ganglion board mac address. It should be a string comprising six hex bytes separated by colons, e.g. "11:22:33:ab:cd:ed". If no mac address specified, a connection will be stablished with the first Ganglion found (Will need root privilages). max_packets_skipped: An integer specifying how many packets can be dropped before attempting to reconnect. """ def __init__(self, mac=None, max_packets_skipped=15): if not mac: self.mac_address = self.find_mac() else: self.mac_address = mac self.max_packets_skipped = max_packets_skipped self.streaming = False self.board_type = 'Ganglion' atexit.register(self.disconnect) self.connect() def write_command(self, command): """Sends string command to the Ganglion board.""" self.char_write.write(str.encode(command)) def connect(self): """Establishes connection with the specified Ganglion board.""" self.ganglion = Peripheral(self.mac_address, 'random') self.service = self.ganglion.getServiceByUUID(BLE_SERVICE) self.char_read = self.service.getCharacteristics(BLE_CHAR_RECEIVE)[0] self.char_write = self.service.getCharacteristics(BLE_CHAR_SEND)[0] self.char_discon = self.service.getCharacteristics(BLE_CHAR_DISCONNECT)[0] self.ble_delegate = GanglionDelegate(self.max_packets_skipped) self.ganglion.setDelegate(self.ble_delegate) self.desc_notify = self.char_read.getDescriptors(forUUID=0x2902)[0] try: self.desc_notify.write(b"\x01") except Exception as e: print("Something went wrong while trying to enable notification: " + str(e)) sys.exit(2) print("Connection established") def disconnect(self): """Disconnets from the Ganglion board.""" if self.streaming: self.stop_stream() self.char_discon.write(b' ') self.ganglion.disconnect() def find_mac(self): """Finds and returns the mac address of the first Ganglion board found""" scanner = Scanner() devices = scanner.scan(5) if len(devices) < 1: raise OSError('No nearby Devices found. Make sure your Bluetooth Connection is on.') else: gang_macs = [] for dev in devices: for adtype, desc, value in dev.getScanData(): if desc == 'Complete Local Name' and value.startswith('Ganglion'): gang_macs.append(dev.addr) print(value) if len(gang_macs) < 1: raise OSError('Cannot find OpenBCI Ganglion Mac address.') else: print("Connecting to Ganglion with mac address: "+ gang_macs[0]) return gang_macs[0] def stop_stream(self): """Stops Ganglion Stream.""" self.streaming = False self.write_command('s') def start_stream(self, callback): """Start handling streaming data from the Ganglion board. Call a provided callback for every single sample that is processed.""" if not self.streaming: self.streaming = True self.dropped_packets = 0 self.write_command('b') if not isinstance(callback, list): callback = [callback] while self.streaming: try: self.ganglion.waitForNotifications(1./SAMPLE_RATE) except Exception as e: print(e) print('Something went wrong') sys.exit(1) samples = self.ble_delegate.getSamples() if samples: for sample in samples: for call in callback: call(sample)
Address = input_str.split("&")[1].split("=")[1] AES_Key = input_str.split("&")[2].split("=")[1] Service_UUID = input_str.split("&")[3].split("=")[1] TotalChar_UUID = [input_str.split("&")[4].split("=")[1][i:i+8] for i in range(0, len(input_str.split("&")[4].split("=")[1]), 8)] print ("Wallet Address:",Address) Characteristics = ["Transaction_UUID","Txn_UUID","AddERC20_UUID","Balance_UUID","General_CMD_UUID","General_Data_UUID"] MainCharacteristics_head = {"Transaction_UUID":TotalChar_UUID[0],"Txn_UUID":TotalChar_UUID[1],"AddERC20_UUID":TotalChar_UUID[2],"Balance_UUID":TotalChar_UUID[3],"General_CMD_UUID":TotalChar_UUID[4],"General_Data_UUID":TotalChar_UUID[5]} MainCharacteristics = {"Transaction_UUID":"","Txn_UUID":"","AddERC20_UUID":"","Balance_UUID":"","General_CMD_UUID":"","General_Data_UUID":""} service = p.getServiceByUUID(Service_UUID) for Character in service.getCharacteristics(): print(Character.uuid) for x in range(len(MainCharacteristics_head)): if Character.uuid.getCommonName().startswith(MainCharacteristics_head[Characteristics[x]]): print(Characteristics[x]," Get!") MainCharacteristics[Characteristics[x]] = Character.uuid pass Balance_GATT = p.getCharacteristics(uuid=MainCharacteristics['Balance_UUID'])[0]
class Miflora: def __init__(self, deviceInformation): self._deviceInformation = deviceInformation self.name = deviceInformation.localName self.id = deviceInformation.id def connectAndSetup(self): print("Connecting to", self._deviceInformation.addr) for i in range(0, 10): try: ADDR_TYPE_PUBLIC = "public" self.peripheral = Peripheral(self._deviceInformation.addr, ADDR_TYPE_PUBLIC) print("Connected to", self._deviceInformation.addr) return True except BTLEException as ex: if i < 9: print("Retrying (" + str(i) + ")") else: print("BTLE Exception", ex) continue print("Connection to", self._deviceInformation.addr, "failed") return False def __str__(self): str = '{{name: "{}" addr: "{}"'.format(self.name, self._deviceInformation.addr) if self._deviceInformation.eventData is not None: str = str + ' eventData: "{}"'.format( self._deviceInformation.eventData) str = str + "}}" return str ################ def readCharacteristic(self, serviceUuid, characteristicUuid): try: ch = self.peripheral.getCharacteristics(uuid=characteristicUuid)[0] if (ch.supportsRead()): val = ch.read() return val except BTLEException as ex: print("BTLE Exception", ex) print("Error on readCharacteristic") return None def readDataCharacteristic(self, serviceUuid, characteristicUuid): try: ch = self.peripheral.getCharacteristics(uuid=characteristicUuid)[0] if (ch.supportsRead()): val = ch.read() val = binascii.b2a_hex(val) val = binascii.unhexlify(val) return val except BTLEException as ex: print("BTLE Exception", ex) print("Error on readDataCharacteristic") return None class NotifyDelegate(DefaultDelegate): def __init__(self, miflora): DefaultDelegate.__init__(self) self.miflora = miflora def handleNotification(self, cHandle, data): print("handleNotification", cHandle, data) if cHandle == 33: self.miflora.onRealtimeData(data) def notifyCharacteristic(self, serviceUuid, characteristicUuid, enable): service = self.peripheral.getServiceByUUID(serviceUuid) char = service.getCharacteristics(forUUID=characteristicUuid)[0] charDescr = service.getDescriptors(forUUID=characteristicUuid)[0] charHandle = char.getHandle() if enable: notifyDelegate = Miflora.NotifyDelegate(self) self.peripheral.withDelegate(notifyDelegate) charDescr.write(struct.pack('<BB', 0xA0, 0x1F), True) else: charDescr.write(struct.pack('<BB', 0xC0, 0x1F), True) self.peripheral.withDelegate(None) return charHandle ################ def getBattery(self): data = self.readDataCharacteristic(DATA_SERVICE_UUID, DATA_BATTERY_VERSION_UUID) if data is None: return None #print("DATA", " ".join("{:02x}".format(c) for c in data)) try: batteryLevel = data[0] except Exception as ex: print("Error parsing battery level", ex, "Data=", data) batteryLevel = 0 return batteryLevel def getDeviceFirmwareVersion(self): data = self.readDataCharacteristic(DATA_SERVICE_UUID, DATA_BATTERY_VERSION_UUID) if data is None: return None try: version = data[5:] except Exception as ex: print("Error parsing version", ex) version = "Unknown" return batteryLevel def getEventData(self): return self._deviceInformation.eventData class RealtimeData: temperature = None unknown = None light = None moisture = None conductivity = None battery = None def __init__(self): self.temperature = None self.unknown = None self.light = None self.moisture = None self.conductivity = None self.battery = None def __str__(self): str = "" if self.moisture is not None: str = str + '{{moisture: "{}"}}'.format(self.moisture) if self.conductivity is not None: str = str + '{{conductivity: "{}"}}'.format(self.conductivity) if self.light is not None: str = str + '{{light: "{}"}}'.format(self.light) if self.temperature is not None: str = str + '{{temperature: "{}"}}'.format(self.temperature) if self.unknown is not None: str = str + '{{humidity: "{}"}}'.format(self.unknown) if self.battery is not None: str = str + '{{battery: "{}"}}'.format(self.battery) return str def getRealtimeData(self): realtimeData = Miflora.RealtimeData() try: self.notifyCharacteristic(DATA_SERVICE_UUID, DATA_WRITE_MODE_CHANGE_UUID, True) self.waitingForData = True while self.waitingForData: notified = self.peripheral.waitForNotifications(10) if notified: pass data = self.readDataCharacteristic(DATA_SERVICE_UUID, DATA_DATA_UUID) #print("DATA", " ".join("{:02x}".format(ord(c)) for c in data)) #09 01 #00 #fa 00 00 #00 #00 #00 00 #02 3c 00 fb 34 9b #f3 00 #00 #23 00 00 #00 #0d #30 00 #02 3c 00 fb 34 9b realtimeData.temperature = (struct.unpack(STRUCT_Int16LE, data[0:2])[0]) / 10.0 realtimeData.unknown = struct.unpack(STRUCT_UInt8LE, data[2:3])[0] realtimeData.light = struct.unpack(STRUCT_UInt32LE, data[3:6] + "\0")[0] realtimeData.moisture = struct.unpack(STRUCT_UInt8LE, data[7:8])[0] realtimeData.conductivity = struct.unpack(STRUCT_UInt16LE, data[8:10])[0] realtimeData.light = (realtimeData.light * 1.0) / 1000.0 self.notifyCharacteristic(DATA_SERVICE_UUID, DATA_WRITE_MODE_CHANGE_UUID, False) except Exception as ex: print(ex) return realtimeData def onRealtimeData(self, data): self.waitingForData = False
print " %s = %s" % (desc, value) print " " #Debugging loop #while 1: # print "BNDSW %s || SPIRO %s" % (bndsw.getServiceByUUID(UART_UUID).getCharacteristics()[0].read(), # spiro.getServiceByUUID(UART_UUID).getCharacteristics()[0].read()) #end #Writing to doc loop #delete old file for testing os.system("sudo rm data/data_from_nodes.csv") file = open("data/data_from_nodes.csv", "a") while 1: try: bndsw_data = str(bndsw.getServiceByUUID(UART_UUID).getCharacteristics()[0].read()) spiro_data = str(spiro.getServiceByUUID(UART_UUID).getCharacteristics()[0].read()) #Drop bad packets data from sensor if (bndsw_data[0]=='X') and ('Y' in bndsw_data) and ('Z' in bndsw_data): split_accel_data(bndsw_data) file.write("N1, " + strftime("%Y-%m-%d %H:%M:%S", gmtime()) + ", " +datax +", " +datay +", " +dataz) file.write("N2, " + strftime("%Y-%m-%d %H:%M:%S", gmtime()) + ", " + str(spiro_data)) print "." except KeyboardInterrupt: print "Python script was killed, closing file..." file.close() sys.exit() end
class BLEJoyController: def __init__(self, save_name=""): self.prev_fwd = 0 self.prev_turn = 0 self.prev_vert = 0 self.prev_side = 0 self.fwd = 0 self.turn = 0 self.vert = 0 self.side = 0 self.joy_sub = rospy.Subscriber("joy", Joy, self.joyCallback) #self.batt_pub = rospy.Publisher("batt", Int16, queue_size=1) self.orientation_pub = rospy.Publisher("yaw", Int16, queue_size=1) self.blimp = Peripheral("84:68:3e:03:eb:aa") #self.blimp = Peripheral("84:68:3e:03:ee:df") self.initConnections() #self.onOff = False #self.battService = self.blimp.getServiceByUUID(0x180F) #self.battChar = self.battService.getCharacteristics(0x2A19)[0] #self.orientationService = self.blimp.getServiceByUUID("BEEF") #self.orientationChar = self.orientationService.getCharacteristics("FEED")[0] #self.orientationService = self.blimp.getServiceByUUID("19b10000e8f2537e4f6cd104768a1216") #self.orientationChar = self.orientationService.getCharacteristics("FFF2")[0] #self.orientationChar2 = self.orientationService.getCharacteristics("FFF1")[0] #self.commandService = self.blimp.getServiceByUUID("19b10000e8f2537e4f6cd104768a1214") #self.commandChar = self.commandService.getCharacteristics("19b10001e8f2537e4f6cd104768a1214")[0] self.uxSub = rospy.Subscriber("u_x", Float64, self.uxCallback) self.uySub = rospy.Subscriber("u_y", Float64, self.uyCallback) self.uzSub = rospy.Subscriber("u_z", Float64, self.uzCallback) self.desiredYawSub = rospy.Subscriber("cmd_yaw", Int16, self.yawCallback) #print self.commandChar.getHandle(), #print self.orientationChar.getHandle(), #print self.orientationChar2.getHandle() #print self.battChar.getHandle() #self.enable_notify("301c9b44-a61b-408a-a8bf-5efcd95a3486") #self.enable_notify(self.battChar.uuid) #self.enable_notify("FEED") #self.enable_notify("FFF2") #self.enable_notify("FFF1") self.save = len(save_name) > 0 if self.save: self.file_name = pathname+"/log/"+time.strftime("%m%d_%H%M")+"-commands_"+save_name+".csv" self.f = open(self.file_name, 'wb') self.writer = csv.writer(self.f) self.writer.writerow("time,u_x,u_y,u_z,desired_yaw".split(',')) def initConnections(self): self.commandService = self.blimp.getServiceByUUID("301c9b20-a61b-408a-a8bf-5efcd95a3486") self.commandChar = self.commandService.getCharacteristics("301c9b21-a61b-408a-a8bf-5efcd95a3486")[0] self.orientationService = self.blimp.getServiceByUUID("301c9b40-a61b-408a-a8bf-5efcd95a3486") self.orientationChar = self.orientationService.getCharacteristics("301c9b44-a61b-408a-a8bf-5efcd95a3486")[0] self.blimp.setDelegate(MyDelegate(self)) self.enable_notify(self.orientationChar.uuid) def joyCallback(self, msg): if abs(msg.axes[3]) > 0.005 or abs(msg.axes[2]) > 0.005 or abs(msg.axes[1] > 0.005) or abs(msg.axes[0]) > 0.005: self.fwd = int(round(64*msg.axes[3])) self.turn = int(round(64*msg.axes[2])) self.vert = int(round(-64*msg.axes[1])) self.side = int(round(-64*msg.axes[0])) def uxCallback(self, msg): self.prev_fwd = self.fwd self.fwd = int(round(msg.data)) def uyCallback(self, msg): self.prev_side = self.side self.side = int(round(msg.data)) def uzCallback(self, msg): self.prev_vert = self.vert self.vert = int(round(msg.data)) def yawCallback(self, msg): self.prev_turn = self.turn self.turn = msg.data def writeJoy(self): if (self.fwd != self.prev_fwd) or (self.turn != self.prev_turn) or (self.vert != self.prev_vert) or (self.side != self.prev_side): # Value changed, need to send new message to the blimp cmd = pack(">hhhh", self.fwd, self.turn, self.vert, self.side) self.commandChar.write(cmd) self.prev_fwd = self.fwd self.prev_turn = self.turn self.prev_vert = self.vert self.prev_side = self.side # rospy.loginfo("Command sent") if self.save: try: data = "%.6f,%d,%d,%d,%d" % (rospy.Time.now().to_time(), self.fwd, self.side, self.vert, self.turn) self.writer.writerow(data.split(',')) except csv.Error as e: sys.exit('File %s, line %d: %s' % (self.file_name, self.writer.line_num, e)) # cmd = pack(">hhhh", random.randint(-255,255), random.randint(-255,255), random.randint(-255,255), random.randint(-255,255)) # self.commandChar.write(cmd) #rospy.loginfo("Command sent") def enable_notify(self, chara_uuid): setup_data = b"\x01\x00" notify = self.blimp.getCharacteristics(uuid=chara_uuid)[0] print notify notify_handle = notify.getHandle() + 1 print notify_handle self.blimp.writeCharacteristic(notify_handle, setup_data, withResponse=False)
parser = argparse.ArgumentParser(description='OMRONの環境センサーからLatestDataを取得します') parser.add_argument("--addr", required=True, type=str, help='環境センサーのMACアドレスを指定する') args = parser.parse_args() # 環境センサーに接続する ble_peripheral = Peripheral() print(f"connecting... {args.addr}") ble_peripheral.connect(addr=args.addr, addrType="random") print(f"ble_peripheral={ble_peripheral}") # BLE サービスを取得 service = ble_peripheral.getServiceByUUID(uuidVal=OMRON_SENSOR_SERVICE_UUID) print(f"service = {service}") # BLE Characteristicsを取得 ble_char = service.getCharacteristics(forUUID=OMRON_LATEST_DATA_UUID)[0] print(f"ble_char = {ble_char}") # LatestDataから測定データの読み出し raw_data = ble_char.read() print(f"raw_data = {raw_data}") # 生の測定データを変換 (row_number, temperature, humidity, light, uv_index, pressure, noise, discomfort_index, heat_stroke, battery_level) = struct.unpack('<BhhhhhhhhH', raw_data) temperature /= 100
from bluepy.btle import Peripheral with open(os.path.join(sys.path[0], "config", "test.yaml"), "r") as f: config = yaml.safe_load(f) info = config['devices'] #print("info =", info) print("info mac address:", info['mac']) print("info service uuid:", str(info['service_uuid'])) p_device = Peripheral(info['mac'], "random") #p_device = Peripheral.connect(info['mac'],"random") try: #for p_service in p_device.getServices(): p_service = p_device.getServiceByUUID(info['service_uuid']) print("p_service uuid is ", p_service.uuid) print("p_service common name is ", p_service.uuid.getCommonName()) print("Characteristics information") chars = p_service.getCharacteristics() for char in chars: #hnd = char.getHandle() print(" -----------------------------------") print(" common name: ", char.uuid.getCommonName()) print(" uuid : ", char.uuid) print(" properties : ", char.propertiesToString()) if char.supportsRead(): #print(" READ value : ", char.read()) val = char.read() txt = ""
class OpenBCIGanglion(object): """ Handle a connection to an OpenBCI board. Args: port: MAC address of the Ganglion Board. "None" to attempt auto-detect. aux: enable on not aux channels (i.e. switch to 18bit mode if set) impedance: measures impedance when start streaming timeout: in seconds, if set will try to disconnect / reconnect after a period without new data -- should be high if impedance check max_packets_to_skip: will try to disconnect / reconnect after too many packets are skipped baud, filter_data, daisy: Not used, for compatibility with v3 """ def __init__(self, port=None, baud=0, filter_data=False, scaled_output=True, daisy=False, log=True, aux=False, impedance=False, timeout=2, max_packets_to_skip=20): # unused, for compatibility with Cyton v3 API self.daisy = False # these one are used self.log = log # print_incoming_text needs log self.aux = aux self.streaming = False self.timeout = timeout self.max_packets_to_skip = max_packets_to_skip self.scaling_output = scaled_output self.impedance = impedance # might be handy to know API self.board_type = "ganglion" print("Looking for Ganglion board") if port == None: port = self.find_port() self.port = port # find_port might not return string self.connect() self.streaming = False # number of EEG channels and (optionally) accelerometer channel self.eeg_channels_per_sample = 4 self.aux_channels_per_sample = 3 self.imp_channels_per_sample = 5 self.read_state = 0 self.log_packet_count = 0 self.packets_dropped = 0 self.time_last_packet = 0 # Disconnects from board when terminated atexit.register(self.disconnect) def getBoardType(self): """ Returns the version of the board """ return self.board_type def setImpedance(self, flag): """ Enable/disable impedance measure """ self.impedance = bool(flag) def connect(self): """ Connect to the board and configure it. Note: recreates various objects upon call. """ print ("Init BLE connection with MAC: " + self.port) print ("NB: if it fails, try with root privileges.") self.gang = Peripheral(self.port, 'random') # ADDR_TYPE_RANDOM print ("Get mainservice...") self.service = self.gang.getServiceByUUID(BLE_SERVICE) print ("Got:" + str(self.service)) print ("Get characteristics...") self.char_read = self.service.getCharacteristics(BLE_CHAR_RECEIVE)[0] print ("receive, properties: " + str(self.char_read.propertiesToString()) + ", supports read: " + str(self.char_read.supportsRead())) self.char_write = self.service.getCharacteristics(BLE_CHAR_SEND)[0] print ("write, properties: " + str(self.char_write.propertiesToString()) + ", supports read: " + str(self.char_write.supportsRead())) self.char_discon = self.service.getCharacteristics(BLE_CHAR_DISCONNECT)[0] print ("disconnect, properties: " + str(self.char_discon.propertiesToString()) + ", supports read: " + str(self.char_discon.supportsRead())) # set delegate to handle incoming data self.delegate = GanglionDelegate(self.scaling_output) self.gang.setDelegate(self.delegate) # enable AUX channel if self.aux: print("Enabling AUX data...") try: self.ser_write(b'n') except Exception as e: print("Something went wrong while enabling aux channels: " + str(e)) print("Turn on notifications") # nead up-to-date bluepy, cf https://github.com/IanHarvey/bluepy/issues/53 self.desc_notify = self.char_read.getDescriptors(forUUID=0x2902)[0] try: self.desc_notify.write(b"\x01") except Exception as e: print("Something went wrong while trying to enable notification: " + str(e)) print("Connection established") def init_streaming(self): """ Tell the board to record like crazy. """ try: if self.impedance: print("Starting with impedance testing") self.ser_write(b'z') else: self.ser_write(b'b') except Exception as e: print("Something went wrong while asking the board to start streaming: " + str(e)) self.streaming = True self.packets_dropped = 0 self.time_last_packet = timeit.default_timer() def find_port(self): """Detects Ganglion board MAC address -- if more than 1 around, will select first. Needs root privilege.""" print("Try to detect Ganglion MAC address. NB: Turn on bluetooth and run as root for this to work! Might not work with every BLE dongles.") scan_time = 5 print("Scanning for 5 seconds nearby devices...") # From bluepy example class ScanDelegate(DefaultDelegate): def __init__(self): DefaultDelegate.__init__(self) def handleDiscovery(self, dev, isNewDev, isNewData): if isNewDev: print ("Discovered device: " + dev.addr) elif isNewData: print ("Received new data from: " + dev.addr) scanner = Scanner().withDelegate(ScanDelegate()) devices = scanner.scan(scan_time) nb_devices = len(devices) if nb_devices < 1: print("No BLE devices found. Check connectivity.") return "" else: print("Found " + str(nb_devices) + ", detecting Ganglion") list_mac = [] list_id = [] for dev in devices: # "Ganglion" should appear inside the "value" associated to "Complete Local Name", e.g. "Ganglion-b2a6" for (adtype, desc, value) in dev.getScanData(): if desc == "Complete Local Name" and value.startswith("Ganglion"): list_mac.append(dev.addr) list_id.append(value) print("Got Ganglion: " + value + ", with MAC: " + dev.addr) break nb_ganglions = len(list_mac) if nb_ganglions < 1: #print("No Ganglion found ;(") raise OSError('Cannot find OpenBCI Ganglion MAC address') if nb_ganglions > 1: print("Found " + str(nb_ganglions) + ", selecting first") print("Selecting MAC address " + list_mac[0] + " for " + list_id[0]) return list_mac[0] def ser_write(self, b): """Access serial port object for write""" self.char_write.write(b) def ser_read(self): """Access serial port object for read""" return self.char_read.read() def ser_inWaiting(self): """ Slightly different from Cyton API, return True if ASCII messages are incoming.""" # FIXME: might have a slight problem with thread because of notifications... if self.delegate.receiving_ASCII: # in case the packet indicating the end of the message drops, we use a 1s timeout if timeit.default_timer() - self.delegate.time_last_ASCII > 2: self.delegate.receiving_ASCII = False return self.delegate.receiving_ASCII def getSampleRate(self): return SAMPLE_RATE def getNbEEGChannels(self): """Will not get new data on impedance check.""" return self.eeg_channels_per_sample def getNbAUXChannels(self): """Might not be used depending on the mode.""" return self.aux_channels_per_sample def getNbImpChannels(self): """Might not be used depending on the mode.""" return self.imp_channels_per_sample def start_streaming(self, callback, lapse=-1): """ Start handling streaming data from the board. Call a provided callback for every single sample that is processed Args: callback: A callback function -- or a list of functions -- that will receive a single argument of the OpenBCISample object captured. """ if not self.streaming: self.init_streaming() start_time = timeit.default_timer() # Enclose callback funtion in a list if it comes alone if not isinstance(callback, list): callback = [callback] while self.streaming: # should the board get disconnected and we could not wait for notification anymore, a reco should be attempted through timeout mechanism try: # at most we will get one sample per packet self.waitForNotifications(1./self.getSampleRate()) except Exception as e: print("Something went wrong while waiting for a new sample: " + str(e)) # retrieve current samples on the stack samples = self.delegate.getSamples() self.packets_dropped = self.delegate.getMaxPacketsDropped() if samples: self.time_last_packet = timeit.default_timer() for call in callback: for sample in samples: call(sample) if(lapse > 0 and timeit.default_timer() - start_time > lapse): self.stop(); if self.log: self.log_packet_count = self.log_packet_count + 1; # Checking connection -- timeout and packets dropped self.check_connection() def waitForNotifications(self, delay): """ Allow some time for the board to receive new data. """ self.gang.waitForNotifications(delay) def test_signal(self, signal): """ Enable / disable test signal """ if signal == 0: self.warn("Disabling synthetic square wave") try: self.char_write.write(b']') except Exception as e: print("Something went wrong while setting signal: " + str(e)) elif signal == 1: self.warn("Eisabling synthetic square wave") try: self.char_write.write(b'[') except Exception as e: print("Something went wrong while setting signal: " + str(e)) else: self.warn("%s is not a known test signal. Valid signal is 0-1" %(signal)) def set_channel(self, channel, toggle_position): """ Enable / disable channels """ try: #Commands to set toggle to on position if toggle_position == 1: if channel is 1: self.ser.write(b'!') if channel is 2: self.ser.write(b'@') if channel is 3: self.ser.write(b'#') if channel is 4: self.ser.write(b'$') #Commands to set toggle to off position elif toggle_position == 0: if channel is 1: self.ser.write(b'1') if channel is 2: self.ser.write(b'2') if channel is 3: self.ser.write(b'3') if channel is 4: self.ser.write(b'4') except Exception as e: print("Something went wrong while setting channels: " + str(e)) """ Clean Up (atexit) """ def stop(self): print("Stopping streaming...") self.streaming = False # connection might be already down here try: if self.impedance: print("Stopping with impedance testing") self.ser_write(b'Z') else: self.ser_write(b's') except Exception as e: print("Something went wrong while asking the board to stop streaming: " + str(e)) if self.log: logging.warning('sent <s>: stopped streaming') def disconnect(self): if(self.streaming == True): self.stop() print("Closing BLE..") try: self.char_discon.write(b' ') except Exception as e: print("Something went wrong while asking the board to disconnect: " + str(e)) # should not try to read/write anything after that, will crash try: self.gang.disconnect() except Exception as e: print("Something went wrong while shutting down BLE link: " + str(e)) logging.warning('BLE closed') """ SETTINGS AND HELPERS """ def warn(self, text): if self.log: #log how many packets where sent succesfully in between warnings if self.log_packet_count: logging.info('Data packets received:'+str(self.log_packet_count)) self.log_packet_count = 0; logging.warning(text) print("Warning: %s" % text) def check_connection(self): """ Check connection quality in term of lag and number of packets drop. Reinit connection if necessary. FIXME: parameters given to the board will be lost.""" # stop checking when we're no longer streaming if not self.streaming: return #check number of dropped packets and duration without new packets, deco/reco if too large if self.packets_dropped > self.max_packets_to_skip: self.warn("Too many packets dropped, attempt to reconnect") self.reconnect() elif self.timeout > 0 and timeit.default_timer() - self.time_last_packet > self.timeout: self.warn("Too long since got new data, attempt to reconnect") #if error, attempt to reconect self.reconnect() def reconnect(self): """ In case of poor connection, will shut down and relaunch everything. FIXME: parameters given to the board will be lost.""" self.warn('Reconnecting') self.stop() self.disconnect() self.connect() self.init_streaming()
p.setDelegate(MyDelegate()) try: chList = p.getCharacteristics() seList = p.getServices() for se in seList: try: print(se.uuid) except: print("SE Error") print("---------------------------") print("Get Service1") try: se1 = p.getServiceByUUID('0000180f-0000-1000-8000-00805f9b34fb') ch1 = se1.getCharacteristics('00002a19-0000-1000-8000-00805f9b34fb') for _ch1 in ch1: print(_ch1.uuid) print("----------------------------------") print(ch1[0].uuid) print(ch1[0].read()) except: print("SE1 Error") print("-------------------------------------------") print("Get Data") try: se10 = p.getServiceByUUID('ebe0ccb0-7a0a-4b0c-8a1a-6ff2997da3a6')
else: suppString = str(i[1]) if len(str(i[0])) == 1: suppString = '0' + str(i[0]) + suppString else: suppString = str(i[0]) + suppString ble_data.append(str.encode(suppString)) # if int(i[1]/1000) == 0: # ble_data.append(str.encode(str(i[0])+'0'+str(i[1]))) # elif len(str(i[0])) == 1: # ble_data.append(str.encode('0'+str(i[0])+'0'+str(i[1]))) # else: # ble_data.append(str.encode(str(i[0])+str(i[1]))) print(ble_data) input() p = Peripheral(addr, 'public') s = p.getServiceByUUID("20ff121c-94e1-11e9-bc42-526af7764f64") print("conectado") carachteristics = s.getCharacteristics()[0] for j in ble_data: carac = int(carachteristics.read()) print(carac) # p.waitForNotifications() while carac != 33: if carac == 32: break pass carachteristics.write(j) print(j) p.disconnect()
import binascii from bluepy.btle import Peripheral entity_id = data.get('entity_id') action = data.get('action') # find your switchbot address by # pi@raspberry:~ $ sudo hcitool lescan # replace "ff:..:ff" # if entity_id == "stefan_radiator": # switchbot = "EA:E1:11:E2:B1:77" p = Peripheral("EA:E1:11:E2:B1:77", "random") hand_service = p.getServiceByUUID("cba20d00-224d-11e6-9fb8-0002a5d5c51b") hand = hand_service.getCharacteristics("cba20002-224d-11e6-9fb8-0002a5d5c51b")[0] if action == 'turn_on': hand.write(binascii.a2b_hex("570101")) hass.states.set('sensor.stefan_radiator', 'on') elif action == 'turn_off': hand.write(binascii.a2b_hex("570102")) hass.states.set('sensor.' + entity_id, 'off') elif action == 'press': hand.write(binascii.a2b_hex("570100")) else: hass.states.set('sensor.' + entity_id , 'unknown')
os.system("rfkill unblock bluetooth") time.sleep(3) nowSecs = 0 nowKitchenSecs = 0 connectedToKitchen = False kitchenMAC = "empty" kitchenMAC = getMAC("KitchenBin") kitchenPConnected = False if kitchenMAC != "empty": kitchenP = Peripheral(kitchenMAC) kitchenP.setDelegate(MyDelegate()) # Setup to turn notifications on, e.g. svc = kitchenP.getServiceByUUID('9a4587b1-4d85-4c75-b88b-faa619295a18') ch = svc.getCharacteristics()[0] print(ch.valHandle) kitchenP.writeCharacteristic(ch.valHandle + 1, "\x01\x00") kitchenPConnected = True print("connected to KitchenBin and waiting for notifications") while 1: if kitchenPConnected == True: if kitchenP.waitForNotifications(1.0): continue else: while nowKitchenSecs < time.time():
tempContainer = [] chList = p.getCharacteristics() for ch in chList: tempContainer.append(str(ch.uuid)) return tempContainer.pop() print("Setting up connection with beacon stones...") p = Peripheral(sys.argv[1], "public") led_service_uuid = getSvc(p) led_char_uuid = getChar(p) print("SVC : " + led_service_uuid + " / Char : " + led_char_uuid) print("Setup is complete. Starting service...") LedService = p.getServiceByUUID(led_service_uuid) try: ch = LedService.getCharacteristics(led_char_uuid)[0] while True: ch.write(struct.pack('<B', 0x00)) print("Led2 on") time.sleep(2) ch.write(struct.pack('<B', 0x01)) print("Led2 off") time.sleep(2) except (KeyboardInterrupt, SystemExit): print("Pressed keyboard for interruption!") finally: p.disconnect() print("Terminating program...")
scanDeviceList.append(dev.addr) for (adtype, desc, value) in dev.getScanData(): print " %s = %s" % (desc, value) # deviceIndex = 1 deviceIndex = int(input("Input the device Index:")) # print deviceIndex print scanDeviceList[deviceIndex] p = Peripheral(scanDeviceList[deviceIndex], "random") pq = MyDelegate(p) p.setDelegate(pq) #Get UART Service UARTService = p.getServiceByUUID(UART_service_uuid) # Get The UART-Characteristics UARTC = UARTService.getCharacteristics(UART_rx_char_uuid)[0] #Get The handle the UART-Characteristics hUARTC = UARTC.getHandle() # Search and get Get The UART-Characteristics "property" (UUID-0x2902 CCC-Client Characteristic Configuration)) # wich is located in a handle in the range defined by the boundries of the UARTService for desriptor in p.getDescriptors( hUARTC): # The handle range should be read from the services if (desriptor.uuid == 0x2902 ): # but is not done due to a Bluez/BluePy bug :( print( "Trackle UART Client Characteristic Configuration found at handle 0x" + format(desriptor.handle, "02X")) hUARTCCC = desriptor.handle
deviceFrequency = 30 #takes input of device adress and if no adress present then logs error if len(sys.argv) != 2: print("Fatal, must pass device address:", sys.argv[0], "<device address=" ">") quit() p = Peripheral(sys.argv[1]) print("Connected") #for service in p.getServices(): # print(service.uuid) ButtonService = p.getServiceByUUID(service_uuid) print("Got service") try: ch_light = ButtonService.getCharacteristics(char_light_uuid)[0] ch_temp = ButtonService.getCharacteristics(char_temp_uuid)[0] ch_humid = ButtonService.getCharacteristics(char_humid_uuid)[0] ch_pressure = ButtonService.getCharacteristics(char_pressure_uuid)[0] ch_voc = ButtonService.getCharacteristics(char_voc_uuid)[0] ch_co2 = ButtonService.getCharacteristics(char_co2_uuid)[0] print("Got characteristics") while 1: print("read") lightVal = str(struct.unpack('<f', ch_light.read())[0]) tempVal = str(struct.unpack('<f', ch_temp.read())[0])
class BtleDevice(DefaultDelegate): """Base class for a connection to a Bluetooth Low Energy device. Built on bluepy (which is built on bluez), and uses some bluepy classes, so this class is just a helper not really a full API. See https://ianharvey.github.io/bluepy-doc/index.html **** NOTE ***** As of 2019-12-06, you have to fix a bug in the btle.py library: Comment out the _getResp in disconnect() or it can hang on readline when a 'stat disc' reply from the helper is overlooked (as in during a call to setMTU). **** NOTE ***** """ # Common Services: generic_access_service_uuid = UUID('1800') device_info_service_uuid = UUID('180a') # Common Descriptors: characteristic_description_uuid = UUID('2901') characteristic_configure_uuid = UUID('2902') # Common Characteristics: model_number_uuid = UUID('2a24') # As string serial_number_uuid = UUID('2a25') # As string firmware_revision_uuid = UUID('2a26') # As string hardware_revision_uuid = UUID('2a27') # As string manufacturer_name_uuid = UUID('2a29') # As string def __init__(self, mac_addy, mtu=0xff, max_rate=10): """Connect to the device at the specified addy. Max_rate is the maximum rate in messages/second at which we will send messages to the device. I imagine this could/should be automatically inferred from the Peripheral Preferred Connection Parameters, but that's above my pay grade. All I know is, if you don't meter the outgoing messages, some peripherals will just start dropping requests, resulting in timeouts waiting for the acks. For an Airthings Wave+, max_rate 10 works with dumb throttling, or max_rate 5 with smart. The whole issue seems fishy to me -- seems like the lower level protocols should handle this better. But pragmatically, it regularly hangs without this. """ DefaultDelegate.__init__(self) assert mac_addy self.mac_addy = mac_addy self.mtu = mtu self.min_delay = 1. / max_rate self.peripheral = None self.cache = None # Will be dict mapping UUIDs to Characteristics; Created/cleared by connect() self.last_notification = None # This is only used transiently by the default notification handler. Ignore! if smart_throttle: self.last_xmt_time = 0 # Used to throttle outgoing messages. Is the time() of last sent message (or 0) #===== # If you don't know the MAC address, you'll need to scan, but that requires root... #===== @staticmethod def scan(match=None, duration=5, just_one=False, match_scan_entry=False): """This looks through any advertising btle devices for matching ones. *** Note you must run this as root *** match(scan_data) should return True if scan_data is from the device you're looking for. If no match function is provided, it is treated as always returning True (you want to see all devices that respond within duration). scan_data is a dict mapping description to value, unless match_scan_entry is True in which case scan_data is a btle ScanEntry object. Duration is how many seconds to scan before returning. If just_one is False, scans for the full duration and then returns a list of matching ScanEntry objects. (https://ianharvey.github.io/bluepy-doc/scanentry.html) If just_one is True, this returns the first match immediately (no list) or None after full duration timeout. Note (match==None and just_one==True) results in just the first device to respond being immediately returned. """ scanner = Scanner().withDelegate(DefaultDelegate()) scanner.clear() scanner.start() if not just_one: found = [] try: for i in range(max(int(duration * 10), 1)): # Break duration into 10ths scanner.process( 0.1) # Handle incoming messages for 1/10th of a second... devices = scanner.getDevices() for dev in devices: if debug: BtleDevice.dump_scan_entry(dev) if match_scan_entry: params = dev else: params = { name: val for tag, name, val in dev.getScanData() } if match is None or match(params): if just_one: return dev else: found.append(dev) scanner.clear() finally: scanner.stop() if just_one: return None return found def disconnect(self): """Do call this when you are done! (But no sooner. This will automatically re-connect if you try to do anything after this...) """ if self.peripheral is not None: if debug: print( "Disconnecting." ) # TODO - need to wrap all these debugging messages in a better logger... self.throttle() self.peripheral.disconnect() self.peripheral = None self.cache = None return True return False def connect(self): """Optional: Connects to the device. Returns True if this causes the connection, or False if we were already connected. Most of the methods here will call this on-demand, so you only need to call this explicitly if you want to access self.peripheral directly straight away. """ if self.peripheral is None: if debug: print("Connecting to %s." % (self.mac_addy, )) self.cache = {} self.peripheral = Peripheral(self.mac_addy).withDelegate(self) if self.mtu is not None: self.throttle() self.peripheral.setMTU( self.mtu ) # Notification and Indications may not happen if we don't do this. return True return False #===== # Most of the good stuff is here -- ways to read and write values: #===== def __getitem__(self, char): """This reads and returns the current value of the given characteristic. Char can be a uuid string, UUID object, or Characteristic object. """ self.throttle() return self.get_characteristic(char).read() def __setitem__(self, char, val): """This writes the byte array val to the specified characteristic. Char can be a uuid string, UUID object, or Characteristic object. """ self.throttle() self.get_characteristic(char).write(val, True) def get_handle(self, char): """This returns the Handle (small integer) of the specified characteristic. Char can be a uuid string, UUID object, or Characteristic object. Mainly useful to compare to the handle returned by wait_for_notification(). """ return self.get_characteristic(char).getHandle() def get_characteristic(self, uuid): """If you're just doing basic sets, queries and commands with the high level interface you probably don't need to call this directly. This accepts strings or UUID objects and returns the associated Characteristic object. (Or the first one if there are multiple with the same UUID. So, this is mainly useful for high level variables, not for meta information attributes or such.) As a convenience, if you pass a Characteristic, this will return it. (Which is useful for functions that want to allow flexible identification of the Characteristic.) Results are cached for efficiency, so don't be shy about using it. Raises KeyError if uuid can't be found. """ self.connect() if isinstance(uuid, Characteristic): return uuid if not isinstance(uuid, UUID): uuid = UUID(uuid) if uuid not in self.cache: self.throttle() c = self.peripheral.getCharacteristics(uuid=uuid) if c: self.cache[uuid] = c[0] else: self.cache[uuid] = None c = self.cache[uuid] if c is None: raise KeyError("Can't find characteristic %s" % (uuid, )) return c #===== # If you want Notifications or Indications, you need to enable each characteristic accordingly. #===== def can_notify(self, char): """Returns non-zero if this characteristic can generate Notifications or Indications. Char can be a uuid string, UUID object, or Characteristic object. Technically the return value can be further inspected to discern whether it supports Notifications, Indications, or (does this ever happen?) both. """ char = self.get_characteristic(char) return char.properties & (char.props['INDICATE'] | char.props['NOTIFY']) def enable(self, char): """Usually this is the easiest way to enable notifications from a characteristic. You have to call this (or one of the more specific ones below) before you will get any notifications from a given Characteristic. This enables characteristic char for either Indications or Notifications depending on which sort it claims to generate. Char can be a uuid string, UUID object, or Characteristic object. """ char = self.get_characteristic(char) if char.properties & char.props['INDICATE']: self.configure_characteristic(char, 2) elif char.properties & char.props['NOTIFY']: self.configure_characteristic(char, 1) else: raise Exception( "Characteristic does not support any notifications: %s" % (char, )) def disable(self, char): self.configure_characteristic(char, 0) def wait_for_notification(self, timeout): """This waits for the next notification and then returns it as a (handle, data) tuple. This only works if you do not override handleNotifications. This only handles notifications that come in during this call. If you call this successively, you should catch all notifications as long as you don't make any other Btle calls in between (since those calls could let a notification slip into the gap). Returns None on timeout. """ try: self.last_notification = False # False means we're waiting; None means we're not! if self.peripheral.waitForNotifications(timeout): return self.last_notification # Python should grab it before the finally clause resets it... else: return (None, None) finally: self.last_notification = None # Make sure handleNotifications alerts us to notifications that come out of band. def handleNotification(self, handle, data): """Subclass can override this to do whatever it wants with notifications/indications. """ if self.last_notification is None: # Don't put this under Debug flag. This alerts you to Notifications you aren't handling yet... print("UNHANDLED NOTIFICATION: handle=%r data=%r" % (handle, data.hex())) else: self.last_notification = (handle, data) #--- Unlikely you need to call any of these directly: def enable_notifications(self, char): """If you want to get Notifications from a characteristic, you need to call this first. Char can be a uuid string, UUID object, or Characteristic object. Note this also disables Indications for this characteristic. Use configure_characteristic to enable both. """ self.configure_characteristic(char, 1) def enable_indications(self, char): """If you want to get value Indications from a characteristic, you need to call this first. Char can be a uuid string, UUID object, or Characteristic object. Note this also disables Notifications for this characteristic. Use configure_characteristic to enable both. """ self.configure_characteristic(char, 2) def configure_characteristic(self, char, val): """This looks up the handle for the configuration descriptor for this characteristic, and then sends val to it. Char can be a uuid string, UUID object, or Characteristic object. Typically val here is 1 (enable Notifications) or 2 (enable Indications) or 0 (disable). """ char = self.get_characteristic(char) if debug: print("Finding configuration descriptor for handle 0x%x" % (char.getHandle(), )) self.throttle() d, = char.getDescriptors(forUUID=self.characteristic_configure_uuid) if debug: print("Found (handle 0x%x). Setting to 0x%02x" % (d.handle, val)) self.throttle() d.write(struct.pack('<H', val), True) def throttle(self): """This internal utility just waits until self.min_delay from the last time this method was called. """ if smart_throttle: now = time() delay = self.last_xmt_time + self.min_delay - now if delay > 0: sleep(delay) now += delay self.last_xmt_time = now else: sleep(self.min_delay) #===== # Below here are just utilities for displaying the device's data layout, # Which also serve as examples of how to access the devices's attributes. #===== def dump_services(self, read=False, indent=''): """This calls dump_service on all services this device provides. If read is True, it reads and prints each characteristic's value. This is a good way to manually discover the layout of your device. """ self.connect() for s in self.peripheral.getServices(): self.dump_service(s, read, indent) def dump_service(self, service, read=False, indent=''): """This calls dump_characteristic on all the characteristics in the given service. If service is not a Service object, it's assumed to be a UUID and the service will be looked up by that. """ self.connect() if not isinstance(service, Service): try: service = self.peripheral.getServiceByUUID(service) except BTLEEException: print("%sService %r: NOT FOUND" % (indent, service)) return print("%sService %r (%s):" % (indent, service.uuid.getCommonName(), service.uuid)) for c in service.getCharacteristics(): self.dump_characteristic(c, read, indent + " ") def dump_characteristic(self, c, read=False, indent=''): """Dumps whatever we can find out about a characteristic. If read is True, also reads and prints its value (when applicable) """ print("%sCharacteristic %r (%s):" % (indent, c.uuid.getCommonName(), c.uuid)) print("%s Handle: %s (%x)" % (indent, c.getHandle(), c.getHandle())) print("%s Readable: %s" % ( indent, c.supportsRead(), )) print("%s Properties: %s" % ( indent, c.propertiesToString(), )) try: descriptors = c.getDescriptors() except BTLEGattError: pass else: for d in descriptors: print("%s Descriptor: %s (handle 0x%x; uuid %s)" % (indent, d, d.handle, d.uuid)) if d.uuid == self.characteristic_description_uuid: self.throttle() print("%s ------> %r" % (indent, d.read())) if c.supportsRead(): try: self.throttle() val = c.read() except: print("%s (Read) Value: [READ FAILED]" % (indent, )) else: print("%s (Read) Value: %s (0x%s)" % (indent, val, val.hex())) @staticmethod def dump_scan_entry(se): """Just prints the info that comes back from a Scan response. """ print("Scanned Device:") print(" MAC: %s" % (se.addr, )) print(" Addr Type: %s" % (se.addrType, )) print(" Signal Strength: %s dB" % (se.rssi, )) print(" Connectable: %s" % (se.connectable, )) print(" Update Count: %s" % (se.updateCount, )) for t in se.getScanData(): print(" %r" % (t, ))
class BLEClient(): def __init__(self, address="", duty=0): self.address = address self.tx_active = False self.tx_start = 0 self.tx_end = 0 self.nexttransmit = 0 self.duty = duty self.service = None self.periph = None def setdestination(self, address): self.address = address.lower().strip( ) # only lower case address is supported by BluePy!! def connect(self): if self.tx_active or self.address == "": return False self.tx_start = millis() if self.tx_start < self.nexttransmit: print("Next possible transmit ", self.nexttransmit) return False self.tx_active = True try: self.periph = Peripheral(self.address) except Exception as e: # print("connect error",e) self.tx_active = False self.tx_end = millis() return False try: self.service = self.periph.getServiceByUUID(BLE_SERVICE_ID) except Exception as e: # print("service error ",e) self.tx_active = False self.tx_end = millis() return False return True def writepayload(self, apayload): try: ch = self.service.getCharacteristics(BLE_RECEIVER_CHAR)[0] # print(ch,apayload,len(apayload)) ch.write(base64.b64encode(apayload), True) except Exception as e: # print("write error ",e) return False return True def readpayload(self): payload = [] try: ch = self.service.getCharacteristics(BLE_INFO_CHAR)[0] payload = ch.read() payload = base64.b64decode(payload.decode("utf-8")) except Exception as e: # print("read error ",e) pass return payload def disconnect(self): try: self.periph.disconnect() except: return False self.tx_active = False self.tx_end = millis() if self.duty > 0: self.nexttransmit = ( (self.tx_end - self.tx_start) * self.duty) + self.tx_end return True def send(self, spayload): if self.connect(): self.writepayload(spayload) self.disconnect()
class BleDevice: def __init__(self): self.scanner = Scanner().withDelegate(ScanDelegate()) def Scan(self, time): self.devices = self.scanner.scan(time) def ListDevices(self, scan_data_flag): for dev in self.devices: # print("Device {} ({}){}, RSSI={} dB".format(dev.addr, dev.addrType, ' [Connectable]' if dev.connectable else '', dev.rssi)) if scan_data_flag: for (adtype, desc, value) in dev.getScanData(): print(" {} {} = {}".format(adtype, desc, value)) def FindBoogie(self): boogie = None for dev in self.devices: for (adtype, desc, value) in dev.getScanData(): if adtype == 9: if value == "Boogie": boogie = dev print('boogie seen') self.boogie = dev return boogie def ConnectToDevice(self, device): # Function returns Connected to device when device is not there # print('EXIT IS : {}'.format(device)) if device is None: print("No device detected") else: try: self.peripheral = Peripheral(device) print("Connected to device") except BTLEException: print('Connection failed') # def GetServices(self,): # # services = self.peripheral.getServices() # print('Services') # # for service in services: # print('My Service : {}'.format(service)) # characteristics = service.getCharacteristics() # # for characteristic in characteristics: # print('{}'.format(characteristic)) # # self.services = services def GetCharacteristicsByUUID(self): CHARACT_ack = False CharacteristicUUID = "7895cecb-a923-4ce4-bc58-27e8ce6e00ea" Service_characteristics = self.service.getCharacteristics() for characteristic in Service_characteristics: print('My CHARACT : {}'.format(characteristic)) if str(characteristic.uuid) == CharacteristicUUID: CHARACT_ack = True print('Characteristic Verified') else: print('Characteristic error') def GetServiceByUUID(self): UUID_ack = False ServiceUUID = "1a49d922-e3a9-4b00-9253-c4c72a1bcb5d" self.service = self.peripheral.getServiceByUUID(ServiceUUID) # print('service found is {}, service searched is {}'.format(str(self.service.uuid), ServiceUUID)) if str(self.service.uuid) == ServiceUUID: print('My UUID service : {}'.format(self.service.uuid)) UUID_ack = True else: UUID_ack = False if UUID_ack is True: print('Search Service by UUID Verified') ble.GetCharacteristicsByUUID() else: print('Search Service by UUID error') # def ReadNotification(self) def ConnectToBoogie(self): # Function find & connect to boogie. Validate connection with a known service and characteristic # Service : 1a49d922-e3a9-4b00-9253-c4c72a1bcb5d Characteristic : 7895cecb-a923-4ce4-bc58-27e8ce6e00ea Expected value : (0x32) or if char = (2) boogie = ble.FindBoogie() ble.ConnectToDevice(boogie) ble.GetServiceByUUID()
devices = Scanner() # Scanner Object temp=devices.scan(10) # Start Scan try: for dev in temp: if dev.getScanData()[3][2] == "mr. singh": # Check for the target BLE device name if dev.connectable: p = Peripheral(dev.addr, "random") p.setDelegate(ScanDelegate()) # Create internal Object of scandelegate class to handle the notifications except (RuntimeError, TypeError, NameError): print "Device not found" exit(1) ################### Check for the Services and its characteristics ################# print p.getServices()[0] print p.getServices()[1] print p.getServices()[2] Heart_Rate_Measurement = p.getServiceByUUID(0x180D).getCharacteristics()[0] Body_Sensor_Location = p.getServiceByUUID(0x180D).getCharacteristics()[1] Heart_Rate_Control_Point = p.getServiceByUUID(0x180D).getCharacteristics()[2] print Heart_Rate_Measurement , Heart_Rate_Measurement.uuid # Print characteristic and its uuid print Body_Sensor_Location , Body_Sensor_Location.uuid print Heart_Rate_Control_Point , Heart_Rate_Control_Point.uuid ################## Print the Value ########################### body_sensor=["not_selected","Chest","Wrist","Finger","Hand","Ear Lobe","Foot"] # List for body sensor location try: ch = p.getServiceByUUID(0x180D).getCharacteristics()[1] # body sensor location characteristics print ch if (ch.read()): print ord(ch.read()) # Print the location
class MiKettle(object): """" A class to control mi kettle device. """ def __init__(self, mac, product_id, cache_timeout=600, retries=3, iface=None, token=None): """ Initialize a Mi Kettle for the given MAC address. """ _LOGGER.debug('Init Mikettle with mac %s and pid %s', mac, product_id) self._mac = mac self._reversed_mac = MiKettle.reverseMac(mac) self._cache = None self._cache_timeout = timedelta(seconds=cache_timeout) self._last_read = None self.retries = retries self.ble_timeout = 10 self.lock = Lock() self._product_id = product_id self._iface = iface # Generate token if not supplied if token is None: token = MiKettle.generateRandomToken() self._token = token self._p = None self._authenticated = False def connect(self): if self._p is None: self._p = Peripheral(deviceAddr=self._mac, iface=self._iface) self._p.setDelegate(self) def name(self): """Return the name of the device.""" self.connect() self.auth() name = self._p.readCharacteristic(_HANDLE_READ_NAME) if not name: raise Exception("Could not read NAME using handle %s" " from Mi Kettle %s" % (_HANDLE_READ_NAME, self._mac)) return ''.join(chr(n) for n in name) def firmware_version(self): """Return the firmware version.""" self.connect() self.auth() firmware_version = self._p.readCharacteristic(_HANDLE_READ_FIRMWARE_VERSION) if not firmware_version: raise Exception("Could not read FIRMWARE_VERSION using handle %s" " from Mi Kettle %s" % (_HANDLE_READ_FIRMWARE_VERSION, self._mac)) return ''.join(chr(n) for n in firmware_version) def parameter_value(self, parameter, read_cached=True): """Return a value of one of the monitored paramaters. This method will try to retrieve the data from cache and only request it by bluetooth if no cached value is stored or the cache is expired. This behaviour can be overwritten by the "read_cached" parameter. """ # Use the lock to make sure the cache isn't updated multiple times with self.lock: if (read_cached is False) or \ (self._last_read is None) or \ (datetime.now() - self._cache_timeout > self._last_read): self.fill_cache() else: _LOGGER.debug("Using cache (%s < %s)", datetime.now() - self._last_read, self._cache_timeout) if self.cache_available(): return self._cache[parameter] else: raise Exception("Could not read data from MiKettle %s" % self._mac) def fill_cache(self): """Fill the cache with new data from the sensor.""" _LOGGER.debug('Filling cache with new sensor data.') try: _LOGGER.debug('Connect') self.connect() _LOGGER.debug('Auth') self.auth() _LOGGER.debug('Subscribe') self.subscribeToData() _LOGGER.debug('Wait for data') self._p.waitForNotifications(self.ble_timeout) # If a sensor doesn't work, wait 5 minutes before retrying except Exception as error: _LOGGER.debug('Error %s', error) self._last_read = datetime.now() - self._cache_timeout + \ timedelta(seconds=300) return def clear_cache(self): """Manually force the cache to be cleared.""" self._cache = None self._last_read = None def cache_available(self): """Check if there is data in the cache.""" return self._cache is not None def _parse_data(self, data): """Parses the byte array returned by the sensor.""" _LOGGER.debug('parsing') res = dict() res[MI_ACTION] = MI_ACTION_MAP[int(data[0])] res[MI_MODE] = MI_MODE_MAP[int(data[1])] res[MI_SET_TEMPERATURE] = int(data[4]) _LOGGER.debug('Mode: %s', int(data[1])) res[MI_CURRENT_TEMPERATURE] = int(data[5]) res[MI_KW_TYPE] = MI_KW_TYPE_MAP[int(data[6])] res[MI_KW_TIME] = MiKettle.bytes_to_int(data[7:8]) return res @staticmethod def bytes_to_int(bytes): result = 0 for b in bytes: result = result * 256 + int(b) return result def auth(self): if self._authenticated: return auth_service = self._p.getServiceByUUID(_UUID_SERVICE_KETTLE) auth_descriptors = auth_service.getDescriptors() self._p.writeCharacteristic(_HANDLE_AUTH_INIT, _KEY1, "true") auth_descriptors[1].write(_SUBSCRIBE_TRUE, "true") self._p.writeCharacteristic(_HANDLE_AUTH, MiKettle.cipher(MiKettle.mixA(self._reversed_mac, self._product_id), self._token), "true") self._p.waitForNotifications(10.0) self._p.writeCharacteristic(_HANDLE_AUTH, MiKettle.cipher(self._token, _KEY2), "true") self._p.readCharacteristic(_HANDLE_VERSION) self._authenticated = True def subscribeToData(self): controlService = self._p.getServiceByUUID(_UUID_SERVICE_KETTLE_DATA) controlDescriptors = controlService.getDescriptors() controlDescriptors[3].write(_SUBSCRIBE_TRUE, "true") # TODO: Actually generate random token instead of static one @staticmethod def generateRandomToken() -> bytes: return bytes([0x01, 0x5C, 0xCB, 0xA8, 0x80, 0x0A, 0xBD, 0xC1, 0x2E, 0xB8, 0xED, 0x82]) @staticmethod def reverseMac(mac) -> bytes: parts = mac.split(":") reversedMac = bytearray() leng = len(parts) for i in range(1, leng + 1): reversedMac.extend(bytearray.fromhex(parts[leng - i])) return reversedMac @staticmethod def mixA(mac, productID) -> bytes: return bytes([mac[0], mac[2], mac[5], (productID & 0xff), (productID & 0xff), mac[4], mac[5], mac[1]]) @staticmethod def mixB(mac, productID) -> bytes: return bytes([mac[0], mac[2], mac[5], ((productID >> 8) & 0xff), mac[4], mac[0], mac[5], (productID & 0xff)]) @staticmethod def _cipherInit(key) -> bytes: perm = bytearray() for i in range(0, 256): perm.extend(bytes([i & 0xff])) keyLen = len(key) j = 0 for i in range(0, 256): j += perm[i] + key[i % keyLen] j = j & 0xff perm[i], perm[j] = perm[j], perm[i] return perm @staticmethod def _cipherCrypt(input, perm) -> bytes: index1 = 0 index2 = 0 output = bytearray() for i in range(0, len(input)): index1 = index1 + 1 index1 = index1 & 0xff index2 += perm[index1] index2 = index2 & 0xff perm[index1], perm[index2] = perm[index2], perm[index1] idx = perm[index1] + perm[index2] idx = idx & 0xff outputByte = input[i] ^ perm[idx] output.extend(bytes([outputByte & 0xff])) return output @staticmethod def cipher(key, input) -> bytes: perm = MiKettle._cipherInit(key) return MiKettle._cipherCrypt(input, perm) def handleNotification(self, cHandle, data): if cHandle == _HANDLE_AUTH: if(MiKettle.cipher(MiKettle.mixB(self._reversed_mac, self._product_id), MiKettle.cipher(MiKettle.mixA(self._reversed_mac, self._product_id), data)) != self._token): raise Exception("Authentication failed.") elif cHandle == _HANDLE_STATUS: _LOGGER.debug("Status update:") if data is None: return _LOGGER.debug("Parse data: %s", data) self._cache = self._parse_data(data) _LOGGER.debug("data parsed %s", self._cache) if self.cache_available(): self._last_read = datetime.now() else: # If a sensor doesn't work, wait 5 minutes before retrying self._last_read = datetime.now() - self._cache_timeout + \ timedelta(seconds=300) else: _LOGGER.error("Unknown notification from handle: %s with Data: %s", cHandle, data.hex())
import struct import time import urllib import json import httplib import requests from bluepy.btle import UUID, Peripheral temp_uuid = UUID(0x180A) p = Peripheral("E0:95:58:90:DD:9D", "random") prev = '' try: svc = p.getServiceByUUID("6E400001-B5A3-F393-E0A9-E50E24DCCA9E") ch = svc.getCharacteristics("6E400003-B5A3-F393-E0A9-E50E24DCCA9E")[0] print(ch.read()) prev = '' state = 0 while 1: try: data = str(ch.read()).strip() if data == 'st1' or data == 'st2': print("stop") state = 0 prev = 'stop' elif data == "wlk": print("walk") state = 1 prev = 'walk'
class Bluepy(DefaultDelegate): def __init__(self): DefaultDelegate.__init__(self) self._peripheral_address = None self._peripheral_address_type = btle.ADDR_TYPE_PUBLIC self._peripheral = None self._scanner = Scanner().withDelegate(self) self._devicesScanned = [] self._service_uuid = "b9e875c0-1cfa-11e6-b797-0002a5d5c51b" self._char_read_uuid = "1ed9e2c0-266f-11e6-850b-0002a5d5c51b" self._char_write_uuid = "0c68d100-266f-11e6-b388-0002a5d5c51b" self._descs = None self._svc = None self._ch_read = None self._ch_write = None self.lastValue = {} self.lastValue['value'] = 0 self.lastValue['datetime'] = "placeholder" def reset(self): self._peripheral = None self._descs = None self._svc = None self._ch_read = None self._ch_write = None def scan(self, time=3): devices = self._scanner.scan(time) for dev in devices: print ("Device "+dev.addr+" ("+dev.addrType+"), RSSI="+str(dev.rssi)+" dB") for (adtype, desc, value) in dev.getScanData(): if desc == "Complete Local Name": self._devicesScanned.append({ 'name': value, 'id': dev.addr }) def findXDKAddress(self): for dev in self._devicesScanned: if dev['name'].startswith( 'XDK' ): self._peripheral_address = dev['id'] return self._peripheral_address def setXDKAddress(self, xdk_mac): self._peripheral_address = xdk_mac def handleNotification(self, cHandle, data): data = re.findall(r'\d+', str(data))[0] self.lastValue['value'] = data self.lastValue['datetime'] = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3] print ("Received data: "+ str(data)) #self.lastValue = int(data) def handleDiscovery(self, dev, isNewDev, isNewData): if isNewDev: print ("Discovered device "+ dev.addr) elif isNewData: print ("Received new data from"+ dev.addr) def connect(self): triesCounter = 0 while self._peripheral == None : try: print ('\nConnecting...') self._peripheral = Peripheral(self._peripheral_address, "random") if(self._peripheral != None): print ('Connected!') except BTLEException as e: triesCounter = triesCounter + 1 print ("Number of tries: "+str(triesCounter)) print ("Trying to connect again after 5s\n") print(e) time.sleep(5) def setDelegate(self): self._peripheral.setDelegate(self) def discoverSvc(self): while self._svc == None or self._descs == None or self._ch_read == None or self._ch_write == None: print ("\nFinding service...") self._svc = self._peripheral.getServiceByUUID(self._service_uuid) print ("Finding descriptors...") self._descs = self._svc.getDescriptors() print ("Finding read characteristic...") self._ch_read = self._svc.getCharacteristics(self._char_read_uuid)[0] print ("Finding write characteristic...\n") self._ch_write = self._svc.getCharacteristics(self._char_write_uuid)[0] print ("All characteristics found!!!\n") def enableSensor(self): print ("Turning sensor on...\n") self._ch_write.write(b'\x31') time.sleep(1.0) def disableSensor(self): print ("Turning sensor off...\n") self._ch_write.write(b'\x30') time.sleep(1.0) def disconnect(self): print ('Disconnecting...') self._peripheral.disconnect() self._peripheral = None print ('Disconnected!\n') def readValues(self): print ("Reading values...\n") count = 0 while True: if self._peripheral.waitForNotifications(5.0): count = count + 1 continue def BluetoothFlow(self): self.scan(3) #time.sleep(3.0) xdk_address = self.findXDKAddress() print ("\nXDK MAC: "+ xdk_address) disconnectCounter = 0 active = True while active == True: try: self.reset() self.connect() self.setDelegate() self.discoverSvc() self.enableSensor() try: self.readValues() except BTLEException as e: print(e) disconnectCounter = disconnectCounter + 1 print ("\nDisconnection number: "+str(disconnectCounter)) print ("\nTrying to reconnect after 5s...") time.sleep(5.0) except KeyboardInterrupt: active = False print ("\nStopping...") except BTLEException as e: print(e) self.disableSensor() self.disconnect() def getLastValue(self): return (self.lastValue)
class RileyLink(PacketRadio): def __init__(self): self.peripheral = None self.pa_level_index = PA_LEVELS.index(0x84) self.data_handle = None self.logger = getLogger() self.packet_logger = get_packet_logger() self.address = g_rl_address self.service = None self.response_handle = None self.notify_event = Event() self.initialized = False self.manchester = ManchesterCodec() def connect(self, force_initialize=False): try: if self.address is None: self.address = self._findRileyLink() if self.peripheral is None: self.peripheral = Peripheral() try: state = self.peripheral.getState() if state == "conn": return except BTLEException: pass self._connect_retry(3) self.service = self.peripheral.getServiceByUUID( RILEYLINK_SERVICE_UUID) data_char = self.service.getCharacteristics( RILEYLINK_DATA_CHAR_UUID)[0] self.data_handle = data_char.getHandle() char_response = self.service.getCharacteristics( RILEYLINK_RESPONSE_CHAR_UUID)[0] self.response_handle = char_response.getHandle() response_notify_handle = self.response_handle + 1 notify_setup = b"\x01\x00" self.peripheral.writeCharacteristic(response_notify_handle, notify_setup) while self.peripheral.waitForNotifications(0.05): self.peripheral.readCharacteristic(self.data_handle) if self.initialized: self.init_radio(force_initialize) else: self.init_radio(True) except BTLEException as be: if self.peripheral is not None: self.disconnect() raise PacketRadioError("Error while connecting") from be except Exception as e: raise PacketRadioError("Error while connecting") from e def disconnect(self, ignore_errors=True): try: if self.peripheral is None: self.logger.info("Already disconnected") return self.logger.info("Disconnecting..") if self.response_handle is not None: response_notify_handle = self.response_handle + 1 notify_setup = b"\x00\x00" self.peripheral.writeCharacteristic(response_notify_handle, notify_setup) except Exception as e: if not ignore_errors: raise PacketRadioError("Error while disconnecting") from e finally: try: if self.peripheral is not None: self.peripheral.disconnect() self.peripheral = None except BTLEException as be: if ignore_errors: self.logger.exception( "Ignoring btle exception during disconnect") else: raise PacketRadioError("Error while disconnecting") from be except Exception as e: raise PacketRadioError("Error while disconnecting") from e def get_info(self): try: self.connect() bs = self.peripheral.getServiceByUUID(XGATT_BATTERYSERVICE_UUID) bc = bs.getCharacteristics(XGATT_BATTERY_CHAR_UUID)[0] bch = bc.getHandle() battery_value = int(self.peripheral.readCharacteristic(bch)[0]) self.logger.debug("Battery level read: %d", battery_value) version, v_major, v_minor = self._read_version() return { "battery_level": battery_value, "mac_address": self.address, "version_string": version, "version_major": v_major, "version_minor": v_minor } except Exception as e: raise PacketRadioError("Error communicating with RileyLink") from e finally: self.disconnect() def _read_version(self): global g_rl_version, g_rl_v_major, g_rl_v_minor version = None try: if g_rl_version is not None: return g_rl_version, g_rl_v_major, g_rl_v_minor else: response = self._command(Command.GET_VERSION) if response is not None and len(response) > 0: version = response.decode("ascii") self.logger.debug("RL reports version string: %s" % version) g_rl_version = version if version is None: return "0.0", 0, 0 try: m = re.search(".+([0-9]+)\\.([0-9]+)", version) if m is None: raise PacketRadioError( "Failed to parse firmware version string: %s" % version) g_rl_v_major = int(m.group(1)) g_rl_v_minor = int(m.group(2)) self.logger.debug("Interpreted version major: %d minor: %d" % (g_rl_v_major, g_rl_v_minor)) return g_rl_version, g_rl_v_major, g_rl_v_minor except Exception as ex: raise PacketRadioError( "Failed to parse firmware version string: %s" % version) from ex except PacketRadioError: raise except Exception as e: raise PacketRadioError("Error while reading version") from e def init_radio(self, force_init=False): try: version, v_major, v_minor = self._read_version() if v_major < 2: self.logger.error("Firmware version is below 2.0") raise PacketRadioError( "Unsupported RileyLink firmware %d.%d (%s)" % (v_major, v_minor, version)) if not force_init: if v_major == 2 and v_minor < 3: response = self._command(Command.READ_REGISTER, bytes([Register.PKTLEN, 0x00])) else: response = self._command(Command.READ_REGISTER, bytes([Register.PKTLEN])) if response is not None and len( response) > 0 and response[0] == 0x50: return self._command(Command.RADIO_RESET_CONFIG) self._command(Command.SET_SW_ENCODING, bytes([Encoding.NONE])) self._command(Command.SET_PREAMBLE, bytes([0x66, 0x65])) #self._command(Command.SET_PREAMBLE, bytes([0, 0])) frequency = int(433910000 / (24000000 / pow(2, 16))) self._command(Command.UPDATE_REGISTER, bytes([Register.FREQ0, frequency & 0xff])) self._command(Command.UPDATE_REGISTER, bytes([Register.FREQ1, (frequency >> 8) & 0xff])) self._command(Command.UPDATE_REGISTER, bytes([Register.FREQ2, (frequency >> 16) & 0xff])) # self._command(Command.UPDATE_REGISTER, bytes([Register.FREQ0, 0x5f])) # self._command(Command.UPDATE_REGISTER, bytes([Register.FREQ1, 0x14])) # self._command(Command.UPDATE_REGISTER, bytes([Register.FREQ2, 0x12])) self._command(Command.UPDATE_REGISTER, bytes([Register.DEVIATN, 0x44])) # self._command(Command.UPDATE_REGISTER, bytes([Register.DEVIATN, 0x44])) self._command(Command.UPDATE_REGISTER, bytes([Register.PKTCTRL1, 0x20])) self._command(Command.UPDATE_REGISTER, bytes([Register.PKTCTRL0, 0x00])) self._command(Command.UPDATE_REGISTER, bytes([Register.PKTLEN, 0x50])) # self._command(Command.UPDATE_REGISTER, bytes([Register.PKTCTRL1, 0x60])) # self._command(Command.UPDATE_REGISTER, bytes([Register.PKTCTRL0, 0x04])) self._command(Command.UPDATE_REGISTER, bytes([Register.FSCTRL1, 0x06])) # self._command(Command.UPDATE_REGISTER, bytes([Register.FSCTRL1, 0x06])) self._command(Command.UPDATE_REGISTER, bytes([Register.MDMCFG4, 0xCA])) self._command(Command.UPDATE_REGISTER, bytes([Register.MDMCFG3, 0xBC])) self._command(Command.UPDATE_REGISTER, bytes([Register.MDMCFG2, 0x06])) self._command(Command.UPDATE_REGISTER, bytes([Register.MDMCFG1, 0x70])) self._command(Command.UPDATE_REGISTER, bytes([Register.MDMCFG0, 0x11])) self._command(Command.UPDATE_REGISTER, bytes([Register.MCSM0, 0x18])) # self._command(Command.UPDATE_REGISTER, bytes([Register.MDMCFG4, 0xDA])) # self._command(Command.UPDATE_REGISTER, bytes([Register.MDMCFG3, 0xB5])) # self._command(Command.UPDATE_REGISTER, bytes([Register.MDMCFG2, 0x12])) # self._command(Command.UPDATE_REGISTER, bytes([Register.MDMCFG1, 0x23])) # self._command(Command.UPDATE_REGISTER, bytes([Register.MDMCFG0, 0x11])) # self._command(Command.UPDATE_REGISTER, bytes([Register.MCSM0, 0x18])) self._command(Command.UPDATE_REGISTER, bytes([Register.FOCCFG, 0x17])) self._command(Command.UPDATE_REGISTER, bytes([Register.FSCAL3, 0xE9])) self._command(Command.UPDATE_REGISTER, bytes([Register.FSCAL2, 0x2A])) self._command(Command.UPDATE_REGISTER, bytes([Register.FSCAL1, 0x00])) self._command(Command.UPDATE_REGISTER, bytes([Register.FSCAL0, 0x1F])) self._command(Command.UPDATE_REGISTER, bytes([Register.TEST1, 0x31])) self._command(Command.UPDATE_REGISTER, bytes([Register.TEST0, 0x09])) # self._command(Command.UPDATE_REGISTER, bytes([Register.FOCCFG, 0x17])) # self._command(Command.UPDATE_REGISTER, bytes([Register.FSCAL3, 0xE9])) # self._command(Command.UPDATE_REGISTER, bytes([Register.FSCAL2, 0x2A])) # self._command(Command.UPDATE_REGISTER, bytes([Register.FSCAL1, 0x00])) # self._command(Command.UPDATE_REGISTER, bytes([Register.FSCAL0, 0x1F])) # self._command(Command.UPDATE_REGISTER, bytes([Register.TEST2, 0x81])) ## register not defined on RL # self._command(Command.UPDATE_REGISTER, bytes([Register.TEST1, 0x35])) # self._command(Command.UPDATE_REGISTER, bytes([Register.TEST0, 0x09])) self._command( Command.UPDATE_REGISTER, bytes([Register.PATABLE0, PA_LEVELS[self.pa_level_index]])) self._command(Command.UPDATE_REGISTER, bytes([Register.FREND0, 0x00])) #self._command(Command.UPDATE_REGISTER, bytes([Register.SYNC1, 0xA5])) #self._command(Command.UPDATE_REGISTER, bytes([Register.SYNC0, 0x5A])) # self._command(Command.UPDATE_REGISTER, bytes([Register.PATABLE0, PA_LEVELS[self.pa_level_index]])) # self._command(Command.UPDATE_REGISTER, bytes([Register.FREND0, 0x00])) self._command(Command.UPDATE_REGISTER, bytes([Register.SYNC1, 0xA5])) self._command(Command.UPDATE_REGISTER, bytes([Register.SYNC0, 0x5A])) response = self._command(Command.GET_STATE) if response != b"OK": raise PacketRadioError( "Rileylink state is not OK. Response returned: %s" % response) self.initialized = True except Exception as e: raise PacketRadioError( "Error while initializing rileylink radio: %s", e) def tx_up(self): try: if self.pa_level_index < len(PA_LEVELS) - 1: self.pa_level_index += 1 self._set_amp(self.pa_level_index) except Exception as e: raise PacketRadioError("Error while setting tx up") from e def tx_down(self): try: if self.pa_level_index > 0: self.pa_level_index -= 1 self._set_amp(self.pa_level_index) except Exception as e: raise PacketRadioError("Error while setting tx down") from e def set_tx_power(self, tx_power): try: if tx_power is None: return elif tx_power == TxPower.Lowest: self._set_amp(0) elif tx_power == TxPower.Low: self._set_amp(PA_LEVELS.index(0x1D)) elif tx_power == TxPower.Normal: self._set_amp(PA_LEVELS.index(0x84)) elif tx_power == TxPower.High: self._set_amp(PA_LEVELS.index(0xC8)) elif tx_power == TxPower.Highest: self._set_amp(PA_LEVELS.index(0xC0)) except Exception as e: raise PacketRadioError("Error while setting tx level") from e def get_packet(self, timeout=5.0): try: self.connect() result = self._command(Command.GET_PACKET, struct.pack(">BL", 0, int(timeout * 1000)), timeout=float(timeout) + 0.5) if result is not None: return result[0:2] + self.manchester.decode(result[2:]) else: return None except Exception as e: raise PacketRadioError("Error while getting radio packet") from e def send_and_receive_packet(self, packet, repeat_count, delay_ms, timeout_ms, retry_count, preamble_ext_ms): try: self.connect() data = self.manchester.encode(packet) result = self._command( Command.SEND_AND_LISTEN, struct.pack(">BBHBLBH", 0, repeat_count, delay_ms, 0, timeout_ms, retry_count, preamble_ext_ms) + data, timeout=30) if result is not None: return result[0:2] + self.manchester.decode(result[2:]) else: return None except Exception as e: raise PacketRadioError( "Error while sending and receiving data") from e def send_packet(self, packet, repeat_count, delay_ms, preamble_extension_ms): try: self.connect() data = self.manchester.encode(packet) result = self._command( Command.SEND_PACKET, struct.pack(">BBHH", 0, repeat_count, delay_ms, preamble_extension_ms) + data, timeout=30) return result except Exception as e: raise PacketRadioError("Error while sending data") from e def _set_amp(self, index=None): try: if index is not None: previous_level = self.pa_level_index self.pa_level_index = index if PA_LEVELS[previous_level] == PA_LEVELS[index]: return self.connect() self._command( Command.UPDATE_REGISTER, bytes([Register.PATABLE0, PA_LEVELS[self.pa_level_index]])) self.packet_logger.debug("Setting pa to %02X (%d of %d)" % (PA_LEVELS[self.pa_level_index], self.pa_level_index, len(PA_LEVELS))) except PacketRadioError: self.logger.exception("Error while setting tx amplification") raise def _findRileyLink(self): global g_rl_address scanner = Scanner() g_rl_address = None self.logger.debug("Scanning for RileyLink") retries = 10 while g_rl_address is None and retries > 0: retries -= 1 for result in scanner.scan(1.0): if result.getValueText(7) == RILEYLINK_SERVICE_UUID: self.logger.debug("Found RileyLink") g_rl_address = result.addr if g_rl_address is None: raise PacketRadioError("Could not find RileyLink") return g_rl_address def _connect_retry(self, retries): while retries > 0: retries -= 1 self.logger.info("Connecting to RileyLink, retries left: %d" % retries) try: self.peripheral.connect(self.address) self.logger.info("Connected") break except BTLEException as btlee: self.logger.warning("BTLE exception trying to connect: %s" % btlee) try: p = subprocess.Popen(["ps", "-A"], stdout=subprocess.PIPE) out, err = p.communicate() for line in out.splitlines(): if "bluepy-helper" in line: pid = int(line.split(None, 1)[0]) os.kill(pid, 9) break except: self.logger.warning("Failed to kill bluepy-helper") time.sleep(1) def _command(self, command_type, command_data=None, timeout=10.0): try: if command_data is None: data = bytes([1, command_type]) else: data = bytes([len(command_data) + 1, command_type ]) + command_data self.peripheral.writeCharacteristic(self.data_handle, data, withResponse=True) if not self.peripheral.waitForNotifications(timeout): raise PacketRadioError( "Timed out while waiting for a response from RileyLink") response = self.peripheral.readCharacteristic(self.data_handle) if response is None or len(response) == 0: raise PacketRadioError("RileyLink returned no response") else: if response[0] == Response.COMMAND_SUCCESS: return response[1:] elif response[0] == Response.COMMAND_INTERRUPTED: self.logger.warning("A previous command was interrupted") return response[1:] elif response[0] == Response.RX_TIMEOUT: return None else: raise PacketRadioError( "RileyLink returned error code: %02X. Additional response data: %s" % (response[0], response[1:]), response[0]) except PacketRadioError: raise except Exception as e: raise PacketRadioError("Error executing command") from e
class OmronEnvSensor: @retry(tries=3, delay=1, logger=logger) def __init__(self, address: str): super().__init__() self.address = address self.ble_peripheral = Peripheral(address, addrType=bluepy.btle.ADDR_TYPE_RANDOM) self.ble_peripheral.discoverServices() @property def peripheral(self): return self.ble_peripheral # @retry(tries=5, delay=1, logger=logger) def read_char_base(self, service_uuid, char_uuid) -> (btle.Characteristic, bytes): """ BLEのCharacteristicsを読む Args: service_uuid: Omron環境センサBLEサービスのshort UUID char_uuid: Omron環境センサBLE Characteristicsのshort UUID Returns: (btle.Characteristic, bytes): """ time.sleep(BLE_READ_CHARA_WAIT_SEC) service = self.ble_peripheral.getServiceByUUID(uuidVal=service_uuid) ble_char = service.getCharacteristics(forUUID=char_uuid)[0] time.sleep(BLE_READ_WAIT_SEC) raw_data = ble_char.read() return ble_char, raw_data # @retry(tries=5, delay=1, logger=logger) def write_char_base(self, service_uuid, char_uuid, write_value: bytes) -> None: """ BLEのCharacteristicsに値を書く Args: service_uuid: Omron環境センサBLEサービスのshort UUID char_uuid: Omron環境センサBLE Characteristicsのshort UUID write_value: 書き込む値 Returns: None """ time.sleep(BLE_READ_CHARA_WAIT_SEC) service = self.ble_peripheral.getServiceByUUID(uuidVal=service_uuid) ble_char = service.getCharacteristics(forUUID=char_uuid)[0] time.sleep(BLE_WRITE_WAIT_SEC) ble_char.write(write_value) def __MSG(self, level: int, *args) -> None: """ Bluetoothデバイスのアドレスをつけてログ出力する Args: level: ログ出力レベル *args: ログ出力内容 Returns: None """ msg = " ".join([str(a) for a in args]) logger.log(level, F'{self.address}: {msg}') def read_device_name(self) -> str: """ BLEデバイスの名前を読む(Generic Access:0x1800, Device Name:0x2a00) Returns: str: BLEデバイスの名前 """ (ble_chara, raw_data) = self.read_char_base(0x1800, 0x2a00) str_device_name = raw_data.decode('utf-8') self.__MSG( DEBUG, f'char={ble_chara}, raw_data={raw_data}, str={str_device_name}') return str_device_name def check_omron_env_sensor(self) -> bool: """ デバイスがOmronの環境センサーかどうかを判定する Returns: bool: TrueならOmronの環境センサー """ str_device_name = self.read_device_name() if str_device_name == "EnvSensor-BL01": return True else: self.__MSG(WARN, f'this device is not OMRON ') return False def read_latest_data(self) -> OmronLatestData: """ 環境センサーのLatest Dataを読み出す Returns: LatestData """ latest_data = OmronLatestData() self.__MSG(DEBUG, F'reading LatestData(uuid={latest_data.shortUuid:#04x})') (ble_chara, raw_data) = self.read_char_base(latest_data.serviceUuid, latest_data.uuid) return latest_data.parse(raw_data) def read_latest_page(self) -> OmronLatestPage: """ 環境センサーのLatest Pageを読み出す Returns: LatestPage """ latest_page = OmronLatestPage() self.__MSG(DEBUG, F'reading LatestPage(uuid={latest_page.shortUuid:#04x})') (ble_chara, raw_data) = self.read_char_base(latest_page.serviceUuid, latest_page.uuid) return latest_page.parse(raw_data) def write_request_page(self, page: int, row: int) -> None: """ 環境センサーへ読み出したいページのRequest Page要求を出す Args: page: 読み出したいページ番号 0から2047 row: 読み出したい行数の指定 0で1行、 12で13行読み出されるっぽい Returns: None """ assert 0 <= page <= 2047 assert 0 <= row <= 12 request_page = OmronRequestPage() data = request_page.encode_data(page, row) self.__MSG( DEBUG, F'writing RequestPage(uuid={request_page.shortUuid:#04x}): data={data}' ) self.write_char_base(request_page.serviceUuid, request_page.uuid, data) def read_response_flag(self) -> OmronResponseFlag: """ 環境センサーのResponse Flagを読み出す。 Returns: ResponseFlag """ response_flag = OmronResponseFlag() self.__MSG( DEBUG, F'reading ResponseFlag(uuid={response_flag.shortUuid:#04x})') (ble_chara, raw_data) = self.read_char_base(response_flag.serviceUuid, response_flag.uuid) return response_flag.parse(raw_data) def read_response_data(self) -> OmronResponseData: """ 環境センサーのResponse Dataを読み出す Returns: ResponseData """ response_data = OmronResponseData() self.__MSG( DEBUG, F'reading ResponseData(uuid={response_data.shortUuid:#04x})') (ble_chara, raw_data) = self.read_char_base(response_data.serviceUuid, response_data.uuid) return response_data.parse(raw_data) def read_time_information(self) -> OmronTimeInformation: """ 環境センサーのTime Informationを読み出す Returns: TimeInformation """ time_information = OmronTimeInformation() self.__MSG( DEBUG, F'reading TimeInformation(uuid={time_information.shortUuid:#04x})') (ble_chara, raw_data) = self.read_char_base(time_information.serviceUuid, time_information.uuid) return time_information.parse(raw_data) def read_measurement_interval(self) -> OmronMeasurementInterval: """ 環境センサーのMeasurement Intervalを読み出す Returns: MeasurementInterval """ measurement_interval = OmronMeasurementInterval() self.__MSG( DEBUG, F'reading MeasurementInterval(uuid={measurement_interval.shortUuid:#04x})' ) (ble_chara, raw_data) = self.read_char_base(measurement_interval.serviceUuid, measurement_interval.uuid) return measurement_interval.parse(raw_data) def read_error_status(self) -> OmronErrorStatus: """ 環境センサーのError Statusを読み出す Returns: ErrorStatus """ error_status = OmronErrorStatus() self.__MSG(DEBUG, F'reading ErrorStatus(uuid={error_status.shortUuid:#04x})') (ble_chara, raw_data) = self.read_char_base(error_status.serviceUuid, error_status.uuid) return error_status.parse(raw_data) # @retry(tries=3, delay=1, logger=logger) def write_read_page_and_wait_ready(self, page: int, row: int) -> OmronResponseFlag: """ 環境センサーに読み出したいページを指定して、準備が完了するまで待つ Args: page: 読み出したいページ番号 0から2047 row: 読み出したい行数の指定 0で1行、 12で13行読み出されるっぽい Returns: ResponseFlag """ assert 0 <= page <= 2047 assert 0 <= row <= 12 self.write_request_page(page, row) for i in range(3): response_flag = self.read_response_flag() self.__MSG(DEBUG, F'response_flag={response_flag}') if response_flag.update_flag == 0x01: # 更新完了 return response_flag elif response_flag.update_flag == 0x00: # 更新中 continue else: # 更新失敗 self.__MSG(ERROR, F'response flag failed.') raise IOError self.__MSG(ERROR, F'read response flag failed after retry.') raise IOError # @retry(tries=3, delay=1, logger=logger) def read_env_sensor_data(self, page: int, latest_page: OmronLatestPage) -> List[LogData]: """ 環境センサーの指定ページを読み出す Args: page: 読み出し対象ページ latest_page: 環境センサーのLatestPage、最新ページの読み出し行数の指定と観測データの時刻計算にmeasurement_intervalを使う Returns: List[LogData]: 読みだした観測データのリスト """ try: target_row = 12 if page != latest_page.page else latest_page.row # 最新ページ以外は13行の読み出し response_flag = self.write_read_page_and_wait_ready( page, target_row) self.__MSG( INFO, f'page = {page}, start_time={response_flag.datetime} ({response_flag.unix_time})' ) page_data = [] for i in range(13 + 3): # 1ページ分のデータは最大13件だけど、予備で3回追加しておく response_data = self.read_response_data() self.__MSG(DEBUG, F'response_data = {response_data}') page_data.append(response_data) if response_data is None or response_data.row == 0: break list_log_data = list( map( lambda it: LogData(self.address, page, response_flag.unix_time, latest_page.measurement_interval, it), page_data)) self.__MSG( DEBUG, F'list_log_data={json.dumps(list_log_data, cls=DateTimeSupportJSONEncoder)}' ) return list_log_data except Exception as e: logger.exception(f"read_env_sensor_data failed: {e}") raise e def write_measurement_interval(self, new_measurement_interval: int) -> None: """ 測定間隔を変更する Args: new_measurement_interval: 測定間隔(秒) 1から3600 Returns: None """ assert 1 <= new_measurement_interval <= 3600 measurement_interval = OmronMeasurementInterval() data = measurement_interval.encode_data(new_measurement_interval) self.__MSG( DEBUG, F'writing MeasurementInterval(uuid={measurement_interval.shortUuid:#04x}): data={data}' ) self.write_char_base(measurement_interval.serviceUuid, measurement_interval.uuid, data) def write_time_information(self, unix_time) -> None: """ 環境センサーへ現在時刻を設定する Args: unix_time: 現在時刻のunixタイムスタンプ Returns: None """ time_information = OmronTimeInformation() data = time_information.encode_data(unix_time) self.__MSG( DEBUG, F'writing TimeInformation(uuid={time_information.shortUuid:#04x}): data={data}' ) self.write_char_base(time_information.serviceUuid, time_information.uuid, data)
class RobotController(): def __init__(self, address): self.robot = Peripheral(addr) print("connected") # keep state for keypresses self.pressed = {"up": False, "down": False, "right": False, "left": False, "d": False, "s": False, "a": False, "c": False, "o": False, "p": False, "h": False} # TODO get service from robot # TODO get characteristic handles from service/robot # TODO enable notifications if using notifications self.sv = self.robot.getServiceByUUID(SERVICE_UUID) self.ch = self.sv.getCharacteristics(CHAR_UUIDS)[0] keyboard.hook(self.on_key_event) def on_key_event(self, event): # print key name print(event.name) # if a key unrelated to direction keys is pressed, ignore if event.name not in self.pressed: return # if a key is pressed down if event.event_type == keyboard.KEY_DOWN: # if that key is already pressed down, ignore if self.pressed[event.name]: return # set state of key to pressed self.pressed[event.name] = True # TODO write to characteristic to change direction num = 0 if (self.pressed["h"]): num = 11 elif (self.pressed["p"]): num = 22 elif (self.pressed["o"]): num = 33 else: num = int(self.pressed["c"]) num = num * 2 + int(self.pressed["a"]) num = num * 2 + int(self.pressed["s"]) num = num * 2 + int(self.pressed["d"]) num = num * 2 + int(self.pressed["up"]) num = num * 2 + int(self.pressed["down"]) num = num * 2 + int(self.pressed["right"]) num = num * 2 + int(self.pressed["left"]) self.ch.write(bytes([num])) else: # set state of key to released self.pressed[event.name] = False # TODO write to characteristic to stop moving in this direction num = 0 if (self.pressed["h"]): num = 11 elif (self.pressed["p"]): num = 22 elif (self.pressed["o"]): num = 33 else: num = int(self.pressed["c"]) num = num * 2 + int(self.pressed["a"]) num = num * 2 + int(self.pressed["s"]) num = num * 2 + int(self.pressed["d"]) num = num * 2 + int(self.pressed["up"]) num = num * 2 + int(self.pressed["down"]) num = num * 2 + int(self.pressed["right"]) num = num * 2 + int(self.pressed["left"]) self.ch.write(bytes([num])) def __enter__(self): return self def __exit__(self, exc_type, exc_value, traceback): self.robot.disconnect()