class MyLightClient(Service): uuid = VendorUUID("6BFD8F3F-A704-4111-8DCE-F571BA26B40B") _control = Characteristic( uuid=VendorUUID("6BFD8F3E-A704-4111-8DCE-F571BA26B40B"), max_length=7) _light_level = Uint8Characteristic( uuid=VendorUUID("6BFD8F3D-A704-4111-8DCE-F571BA26B40B"), initial_value=100, properties=(Characteristic.READ | Characteristic.WRITE | Characteristic.WRITE_NO_RESPONSE)) print("my light init")
def __init__(self, service=None): _server_tx = StreamOut( uuid=VendorUUID("6E400003-B5A3-F393-E0A9-E50E24DCCA9E"), timeout=5.0, buffer_size=512, ) _server_rx = StreamIn( uuid=VendorUUID("6E400002-B5A3-F393-E0A9-E50E24DCCA9E"), timeout=5.0, buffer_size=512, ) super().__init__(service=service)
class LYWSD03MMCService(Service): """Service for reading from an LYWSD03MMC sensor.""" def __init__(self, service=None): super().__init__(service=service) # Defer creating buffers until needed, since MTU is not known yet. self._settings_result_buf = None self._readings_buf = None uuid = VendorUUID("ebe0ccb0-7a0a-4b0c-8a1a-6ff2997da3a6") readings = _Readings() @property def temperature_humidity(self): """Return a tuple of (temperature, humidity).""" if self._readings_buf is None: self._readings_buf = bytearray(self.readings.packet_size # pylint: disable=no-member ) data = self._readings_buf length = self.readings.readinto(data) # pylint: disable=no-member if length > 0: low_temp, high_temp, hum = struct.unpack_from("<BBB", data) sign = high_temp & 0x80 temp = ((high_temp & 0x7F) << 8) | low_temp if sign: temp = temp - 32767 temp = temp / 100 return (temp, hum) # No data. return None
class _EntityAttribute(Characteristic): """UTF-8 Encoded string characteristic.""" uuid = VendorUUID("C6B2F38C-23AB-46D8-A6AB-A3A870BBD5D7") def __init__(self): super().__init__(properties=Characteristic.WRITE | Characteristic.READ, read_perm=Attribute.OPEN, write_perm=Attribute.OPEN, fixed_length=False)
class CustomUART(Service): uuid = VendorUUID("8ba86973-935c-447c-91ad-bdcbad575f31") _server_rx = StreamIn( uuid=VendorUUID("8ba86974-935c-447c-91ad-bdcbad575f31"), timeout=1.0, buffer_size=64, ) def __init__(self, service=None): # just steal the uuid code from MIDISerivce super().__init__(service=service) self.connectable = True self._rx = self._server_rx def read(self, nbytes=None): return self._rx.read(nbytes)
class MagicLightService(Service): """Service for controlling a Magic Light RGB bulb.""" # These UUIDs actually use the standard base UUID even though they aren't standard. uuid = VendorUUID("0000ffe5-0000-1000-8000-00805f9b34fb") _control = Characteristic( uuid=VendorUUID("0000ffe9-0000-1000-8000-00805f9b34fb"), max_length=7 ) def __init__(self, service=None): super().__init__(service=service) self._color = 0xFFFFFF self._buf = bytearray(7) self._buf[0] = 0x56 self._buf[6] = 0xAA self._brightness = 1.0 def __getitem__(self, index): if index > 0: raise IndexError() return self._color def __setitem__(self, index, value): if index > 0: raise IndexError() if isinstance(value, int): r = (value >> 16) & 0xFF g = (value >> 8) & 0xFF b = value & 0xFF else: r, g, b = value self._buf[1] = r self._buf[2] = g self._buf[3] = b self._buf[4] = 0x00 self._buf[5] = 0xF0 self._control = self._buf self._color = value def __len__(self): return 1
class _Readings(ComplexCharacteristic): """Notify-only characteristic of temperature/humidity""" uuid = VendorUUID("ebe0ccc1-7a0a-4b0c-8a1a-6ff2997da3a6") def __init__(self): super().__init__(properties=Characteristic.NOTIFY) def bind(self, service): """Bind to an LYWSD03MMCService.""" bound_characteristic = super().bind(service) bound_characteristic.set_cccd(notify=True) # Use a PacketBuffer that can store one packet to receive the data. return _bleio.PacketBuffer(bound_characteristic, buffer_size=1)
class LEDService(Service): uuid = VendorUUID(LED_SERVICE_UUID) redChar = Uint8Characteristic(uuid=VendorUUID(RED_LED_UUID), properties=Characteristic.WRITE_NO_RESPONSE) greenChar = Uint8Characteristic(uuid=VendorUUID(GREEN_LED_UUID), properties=Characteristic.WRITE_NO_RESPONSE) phase = 0 def phaseChange(self): self.phase = self.phase + 1 if self.phase > 3: self.phase = 0 if 0 == self.phase: self.charsWrite(1, 0) elif 1 == self.phase: self.charsWrite(1, 1) elif 2 == self.phase: self.charsWrite(0, 1) elif 3 == self.phase: self.charsWrite(0, 0) def charsWrite(self, pRed, pGreen): self.redCharWrite(pRed) self.greenCharWrite(pGreen) def redCharWrite(self, pValue): self.redChar = pValue def greenCharWrite(self, pValue): self.greenChar = pValue
class _EntityUpdate(ComplexCharacteristic): """UTF-8 Encoded string characteristic.""" uuid = VendorUUID("2F7CABCE-808D-411F-9A0C-BB92BA96C102") def __init__(self): super().__init__(properties=Characteristic.WRITE | Characteristic.NOTIFY, read_perm=Attribute.OPEN, write_perm=Attribute.OPEN, max_length=128, fixed_length=False) def bind(self, service): """Binds the characteristic to the given Service.""" bound_characteristic = super().bind(service) return _bleio.PacketBuffer(bound_characteristic, buffer_size=8)
class _RemoteCommand(ComplexCharacteristic): """Endpoint for sending commands to a media player. The value read will list all available commands.""" uuid = VendorUUID("9B3C81D8-57B1-4A8A-B8DF-0E56F7CA51C2") def __init__(self): super().__init__(properties=Characteristic.WRITE_NO_RESPONSE | Characteristic.NOTIFY, read_perm=Attribute.OPEN, write_perm=Attribute.OPEN, max_length=13, fixed_length=False) def bind(self, service): """Binds the characteristic to the given Service.""" bound_characteristic = super().bind(service) return _bleio.PacketBuffer(bound_characteristic, buffer_size=1)
class _MidiCharacteristic(ComplexCharacteristic): """Endpoint for sending commands to a media player. The value read will list all available commands.""" uuid = VendorUUID("7772E5DB-3868-4112-A1A9-F2669D106BF3") def __init__(self): super().__init__(properties=Characteristic.WRITE_NO_RESPONSE | Characteristic.READ | Characteristic.NOTIFY, read_perm=Attribute.OPEN, write_perm=Attribute.OPEN, max_length=512, fixed_length=False) def bind(self, service): """Binds the characteristic to the given Service.""" bound_characteristic = super().bind(service) return _bleio.PacketBuffer(bound_characteristic, buffer_size=4)
def adafruit_service_uuid(n): """Generate a VendorUUID which fills in a 16-bit value in the standard Adafruit Service UUID: ADAFnnnn-C332-42A8-93BD-25E905756CB8. """ return VendorUUID("ADAF{:04x}-C332-42A8-93BD-25E905756CB8".format(n))
class FreeGlowService(UARTService): uuid = VendorUUID("622b6b5e-b514-4be9-81d4-e13ba87ba54f")
class AppleMediaService(Service): """View and control currently playing media. Exact functionality varies with different media apps. For example, Spotify will include the album name and artist name in `title` when controlling playback on a remote device. `artist` includes a description of the remote playback. """ uuid = VendorUUID("89D3502B-0F36-433A-8EF4-C502AD55F8DC") _remote_command = _RemoteCommand() _entity_update = _EntityUpdate() _entity_attribute = _EntityAttribute() player_name = _MediaAttribute(0, 0) """Name of the media player app""" _playback_info = _MediaAttribute(0, 1) paused = _MediaAttributePlaybackState(0) """True when playback is paused. False otherwise.""" playing = _MediaAttributePlaybackState(1) """True when playback is playing. False otherwise.""" rewinding = _MediaAttributePlaybackState(2) """True when playback is rewinding. False otherwise.""" fast_forwarding = _MediaAttributePlaybackState(3) """True when playback is fast-forwarding. False otherwise.""" playback_rate = _MediaAttributePlaybackInfo(1) """Playback rate as a decimal of normal speed.""" elapsed_time = _MediaAttributePlaybackInfo(2) """Time elapsed in the current track. Not updated as the track plays. Use (the amount of time since read elapsed time) * `playback_rate` to estimate the current `elapsed_time`.""" volume = _MediaAttribute(0, 2) """Current volume""" queue_index = _MediaAttribute(1, 0) """Current track's index in the queue.""" queue_length = _MediaAttribute(1, 1) """Count of tracks in the queue.""" shuffle_mode = _MediaAttribute(1, 2) """Current shuffle mode as an integer. Off (0), One (1), and All (2)""" repeat_mode = _MediaAttribute(1, 3) """Current repeat mode as an integer. Off (0), One (1), and All (2)""" artist = _MediaAttribute(2, 0) """Current track's artist name.""" album = _MediaAttribute(2, 1) """Current track's album name.""" title = _MediaAttribute(2, 2) """Current track's title.""" duration = _MediaAttribute(2, 3) """Current track's duration as a string.""" def __init__(self, **kwargs): super().__init__(**kwargs) self._buffer = None self._cmd = None self._register_buffer = None self._attribute_cache = {} self._supported_commands = [] self._command_buffer = None def _send_command(self, command_id): if not self._command_buffer: self._command_buffer = bytearray(13) i = self._remote_command.readinto( # pylint: disable=no-member self._command_buffer) if i > 0: self._supported_commands = list(self._command_buffer[:i]) if command_id not in self._supported_commands: if not self._supported_commands: return raise UnsupportedCommand() if not self._cmd: self._cmd = bytearray(1) self._cmd[0] = command_id self._remote_command.write(self._cmd) # pylint: disable=no-member def play(self): """Plays the current track. Does nothing if already playing.""" self._send_command(0) def pause(self): """Pauses the current track. Does nothing if already paused.""" self._send_command(1) def toggle_play_pause(self): """Plays the current track if it is paused. Otherwise it pauses the track.""" self._send_command(2) def next_track(self): """Stops playing the current track and plays the next one.""" self._send_command(3) def previous_track(self): """Stops playing the current track and plays the previous track.""" self._send_command(4) def volume_up(self): """Increases the playback volume.""" self._send_command(5) def volume_down(self): """Decreases the playback volume.""" self._send_command(6) def advance_repeat_mode(self): """Advances the repeat mode. Modes are: Off, One and All""" self._send_command(7) def advance_shuffle_mode(self): """Advances the shuffle mode. Modes are: Off, One and All""" self._send_command(8) def skip_forward(self): """Skips forwards in the current track""" self._send_command(9) def skip_backward(self): """Skips backwards in the current track""" self._send_command(10) def like_track(self): """Likes the current track""" self._send_command(11) def dislike_track(self): """Dislikes the current track""" self._send_command(12) def bookmark_track(self): """Bookmarks the current track""" self._send_command(13)
def physbryk_service_uuid(n): """Generate a VendorUUID which fills in a 16-bit value in the standard PhysBryk Service UUID: a0d1839c-0eaa-5b52-nnnn-818888dc7dc5. """ # return VendorUUID("ADAF{:04x}-C332-42A8-93BD-25E905756CB8".format(n)) return VendorUUID('a0d1{:04x}-0eaa-5b52-bc84-818888dc7dc5'.format(n))
class SubkeypadService(UARTService): uuid = VendorUUID('39A00001-C17A-7AE2-EA02-A50E24DCCA9E') left_state = Uint8Characteristic( uuid=VendorUUID('39A00003-C1AB-74A2-EFA2-E50EA4DCCA9E')) right_state = Uint8Characteristic( uuid=VendorUUID('39A00004-C1AB-74A2-EFA2-E50EA4DCCA9E'))
class AppleMediaService(Service): """View and control currently playing media. Unimplemented.""" uuid = VendorUUID("89D3502B-0F36-433A-8EF4-C502AD55F8DC") _remote_command = _RemoteCommand() _entity_update = _EntityUpdate() _entity_attribute = _EntityAttribute() player_name = _MediaAttribute(0, 0) _playback_info = _MediaAttribute(0, 1) paused = _MediaAttributePlaybackState(0) playing = _MediaAttributePlaybackState(1) rewinding = _MediaAttributePlaybackState(2) fast_forwarding = _MediaAttributePlaybackState(3) playback_rate = _MediaAttributePlaybackInfo(1) elapsed_time = _MediaAttributePlaybackInfo(2) volume = _MediaAttribute(0, 2) queue_index = _MediaAttribute(1, 0) queue_length = _MediaAttribute(1, 1) shuffle_mode = _MediaAttribute(1, 2) repeat_mode = _MediaAttribute(1, 3) artist = _MediaAttribute(2, 0) album = _MediaAttribute(2, 1) title = _MediaAttribute(2, 2) duration = _MediaAttribute(2, 3) def __init__(self, **kwargs): super().__init__(**kwargs) self._buffer = None self._cmd = None self._register_buffer = None self._attribute_cache = {} self._supported_commands = [] self._command_buffer = None def _send_command(self, command_id): if not self._command_buffer: self._command_buffer = bytearray(13) i = self._remote_command.readinto(self._command_buffer) if i > 0: self._supported_commands = list(self._command_buffer[:i]) if command_id not in self._supported_commands: if not self._supported_commands: return raise UnsupportedCommand() if not self._cmd: self._cmd = bytearray(1) self._cmd[0] = command_id self._remote_command.write(self._cmd) def play(self): self._send_command(0) def pause(self): self._send_command(1) def toggle_play_pause(self): self._send_command(2) def next_track(self): self._send_command(3) def previous_track(self): self._send_command(4) def volume_up(self): self._send_command(5) def volume_down(self): self._send_command(6) def advance_repeat_mode(self): self._send_command(7) def advance_shuffle_mode(self): self._send_command(8) def skip_forward(self): self._send_command(9) def skip_backward(self): self._send_command(10) def like_track(self): self._send_command(11) def dislike_track(self): self._send_command(12) def bookmark_track(self): self._send_command(13)
class MIDIService(Service): uuid = VendorUUID("03B80E5A-EDE8-4B33-A751-6CE34EC4C700") _raw = _MidiCharacteristic() def __init__(self, **kwargs): super().__init__(**kwargs) self._in_buffer = bytearray(self._raw.packet_length) self._out_buffer = None shared_buffer = memoryview(bytearray(4)) self._buffers = [ None, shared_buffer[:1], shared_buffer[:2], shared_buffer[:3], shared_buffer[:4] ] self._header = bytearray(1) self._in_sysex = False self._message_target_length = None self._message_length = 0 self._pending_realtime = None def read(self, length): self._raw.read(self._in_buffer) return None def write(self, buf, length): timestamp_ms = time.monotonic_ns() // 1000000 self._header[0] = (timestamp_ms >> 7 & 0x3f) | 0x80 i = 0 while i < length: data = buf[i] command = data & 0x80 != 0 if self._in_sysex: if command: # End of sysex or real time b = self._buffers[2] b[0] = 0x80 | (timestamp_ms & 0x7f) b[1] = 0xf7 self._raw.write(b, self._header) self._in_sysex = data == 0xf7 else: b = self._buffers[1] b[0] = data self._raw.write(b, self._header) elif command: self._in_sysex = data == 0xf0 b = self._buffers[2] b[0] = 0x80 | (timestamp_ms & 0x7f) b[1] = data if 0xf6 <= data <= 0xff or self._in_sysex: # Real time, command only or start sysex if self._message_target_length: self._pending_realtime = b else: self._raw.write(b, self._header) else: if 0x80 <= data <= 0xbf or 0xe0 <= data <= 0xef or data == 0xf2: # Two following bytes self._message_target_length = 4 else: self._message_target_length = 3 b = self._buffers[self._message_target_length] # All of the buffers share memory so the timestamp and data have already been set. self._message_length = 2 self._out_buffer = b else: self._out_buffer[self._message_length] = data self._message_length += 1 if self._message_target_length == self._message_length: self._raw.write(self._out_buffer, self._header) if _pending_realtime: self._raw.write(self._pending_realtime, self._header) self._pending_realtime = None self._message_target_length = None
from adafruit_ble.uuid import VendorUUID from adafruit_ble.characteristics import Characteristic, ComplexCharacteristic import adafruit_ble from adafruit_ble.advertising.standard import ProvideServicesAdvertisement from adafruit_ble.services.standard.device_info import DeviceInfoService from adafruit_ble_cycling_speed_and_cadence import CyclingSpeedAndCadenceService from PySide2.QtWidgets import (QAction, QApplication, QLabel, QPushButton, QVBoxLayout, QWidget, QMainWindow, QSizePolicy) from PySide2.QtCore import QObject, Signal, Slot, Qt, QRect, QPoint from PySide2.QtGui import QFont, QPaintEvent, QPainterPath, QPainter, QPen, QBrush, QKeySequence Buf = Union[bytes, bytearray, memoryview] UUID_SB20_SERVICE = VendorUUID("0c46beaf-9c22-48ff-ae0e-c6eae1a2f4e5") UUID_SB20_STATUS = VendorUUID("0c46beb0-9c22-48ff-ae0e-c6eae1a2f4e5") UUID_SB20_COMMAND = VendorUUID("0c46beb1-9c22-48ff-ae0e-c6eae1a2f4e5") class StatusQueue: """Accumulates a Characteristic's incoming values in a FIFO buffer.""" def __init__(self, characteristic: Characteristic, *, timeout: float = -1, buffer_size: int = 64): """Monitor the given Characteristic. Each time a new value is written to the Characteristic add the newly-written bytes to a FIFO buffer. :param Characteristic characteristic: The Characteristic to monitor.
class TransparentUARTService(Service): """ Provide UART-like functionality via MicroChip :param int timeout: the timeout in seconds to wait for the first character and between subsequent characters. :param int buffer_size: buffer up to this many bytes. If more bytes are received, older bytes will be discarded. """ # pylint: disable=no-member uuid = VendorUUID("49535343-FE7D-4AE5-8FA9-9FAFD205E455") _server_tx = StreamOut( uuid=VendorUUID("49535343-1E4D-4BD9-BA61-23C647249616"), timeout=1.0, buffer_size=64, ) _server_rx = StreamIn( uuid=VendorUUID("49535343-8841-43F4-A8D4-ECBE34729BB3"), timeout=1.0, buffer_size=64, ) def __init__(self, service=None): super().__init__(service=service) self.connectable = True if not service: self._rx = self._server_rx self._tx = self._server_tx else: # If we're a client then swap the characteristics we use. self._tx = self._server_rx self._rx = self._server_tx def read(self, nbytes=None): """ Read characters. If ``nbytes`` is specified then read at most that many bytes. Otherwise, read everything that arrives until the connection times out. Providing the number of bytes expected is highly recommended because it will be faster. :return: Data read :rtype: bytes or None """ return self._rx.read(nbytes) def readinto(self, buf, nbytes=None): """ Read bytes into the ``buf``. If ``nbytes`` is specified then read at most that many bytes. Otherwise, read at most ``len(buf)`` bytes. :return: number of bytes read and stored into ``buf`` :rtype: int or None (on a non-blocking error) """ return self._rx.readinto(buf, nbytes) def readline(self): """ Read a line, ending in a newline character. :return: the line read :rtype: bytes or None """ return self._rx.readline() @property def in_waiting(self): """The number of bytes in the input buffer, available to be read.""" return self._rx.in_waiting def reset_input_buffer(self): """Discard any unread characters in the input buffer.""" self._rx.reset_input_buffer() def write(self, buf): """Write a buffer of bytes.""" self._tx.write(buf)
class AppleNotificationCenterService(Service): """Notification service. Documented by Apple here: https://developer.apple.com/library/archive/documentation/CoreBluetooth/Reference/AppleNotificationCenterServiceSpecification/Specification/Specification.html """ uuid = VendorUUID("7905F431-B5CE-4E99-A40F-4B1E122D00D0") control_point = StreamIn( uuid=VendorUUID("69D1D8F3-45E1-49A8-9821-9BBDFDAAD9D9")) data_source = StreamOut( uuid=VendorUUID("22EAC6E9-24D6-4BB5-BE44-B36ACE7C7BFB"), buffer_size=1024) notification_source = StreamOut( uuid=VendorUUID("9FBF120D-6301-42D9-8C58-25E699A21DBD"), buffer_size=8 * 100) def __init__(self, service=None): super().__init__(service=service) self._active_notifications = {} def _update(self): # Pylint is incorrectly inferring the type of self.notification_source so disable no-member. while self.notification_source.in_waiting > 7: # pylint: disable=no-member buffer = self.notification_source.read(8) # pylint: disable=no-member event_id, event_flags, category_id, category_count, nid = struct.unpack( "<BBBBI", buffer) if event_id == 0: self._active_notifications[nid] = Notification( nid, event_flags, category_id, category_count, control_point=self.control_point, data_source=self.data_source, ) yield self._active_notifications[nid] elif event_id == 1: self._active_notifications[nid].update(event_flags, category_id, category_count) yield None elif event_id == 2: self._active_notifications[nid].removed = True del self._active_notifications[nid] yield None def wait_for_new_notifications(self, timeout=None): """Waits for new notifications and yields them. Returns on timeout, update, disconnect or clear.""" start_time = time.monotonic() while timeout is None or timeout > time.monotonic() - start_time: try: new_notification = next(self._update()) except StopIteration: return if new_notification: yield new_notification @property def active_notifications(self): """A dictionary of active notifications keyed by id.""" for _ in self._update(): pass return self._active_notifications
class MIDIService(Service): """BLE MIDI service. It acts just like a USB MIDI PortIn and PortOut and can be used as a drop in replacement. BLE MIDI's protocol includes timestamps for MIDI messages. This class automatically adds them to MIDI data written out and strips them from MIDI data read in.""" uuid = VendorUUID("03B80E5A-EDE8-4B33-A751-6CE34EC4C700") _raw = _MidiCharacteristic() # _raw gets shadowed for each MIDIService instance by a PacketBuffer. PyLint doesn't know this # so it complains about missing members. # pylint: disable=no-member def __init__(self, **kwargs): super().__init__(**kwargs) # Defer creating _in_buffer until we're definitely connected. self._in_buffer = None self._out_buffer = None shared_buffer = memoryview(bytearray(4)) self._buffers = [ None, shared_buffer[:1], shared_buffer[:2], shared_buffer[:3], shared_buffer[:4], ] self._header = bytearray(1) self._in_sysex = False self._message_target_length = None self._message_length = 0 self._pending_realtime = None self._in_length = 0 self._in_index = 1 self._last_data = True def readinto(self, buf, length): """Reads up to ``length`` bytes into ``buf`` starting at index 0. Returns the number of bytes written into ``buf``.""" if self._in_buffer is None: self._in_buffer = bytearray(self._raw.packet_size) i = 0 while i < length: if self._in_index < self._in_length: byte = self._in_buffer[self._in_index] if self._last_data and byte & 0x80 != 0: # Maybe manage timing here. Not done now because we're likely slower than we # need to be already. # low_ms = byte & 0x7f # print("low", low_ms) self._in_index += 1 self._last_data = False continue self._in_index += 1 self._last_data = True buf[i] = byte i += 1 else: self._in_length = self._raw.readinto(self._in_buffer) if self._in_length == 0: break # high_ms = self._in_buffer[0] & 0x3f # print("high", high_ms) self._in_index = 1 self._last_data = True return i def read(self, length): """Reads up to ``length`` bytes and returns them.""" result = bytearray(length) i = self.readinto(result, length) return result[:i] def write(self, buf, length): """Writes ``length`` bytes out.""" # pylint: disable=too-many-branches timestamp_ms = time.monotonic_ns() // 1000000 self._header[0] = (timestamp_ms >> 7 & 0x3F) | 0x80 i = 0 while i < length: data = buf[i] command = data & 0x80 != 0 if self._in_sysex: if command: # End of sysex or real time b = self._buffers[2] b[0] = 0x80 | (timestamp_ms & 0x7F) b[1] = 0xF7 self._raw.write(b, header=self._header) self._in_sysex = data == 0xF7 else: b = self._buffers[1] b[0] = data self._raw.write(b, header=self._header) elif command: self._in_sysex = data == 0xF0 b = self._buffers[2] b[0] = 0x80 | (timestamp_ms & 0x7F) b[1] = data if (0xF6 <= data <= 0xFF or self._in_sysex ): # Real time, command only or start sysex if self._message_target_length: self._pending_realtime = b else: self._raw.write(b, header=self._header) else: if (0x80 <= data <= 0xBF or 0xE0 <= data <= 0xEF or data == 0xF2): # Two following bytes self._message_target_length = 4 else: self._message_target_length = 3 b = self._buffers[self._message_target_length] # All of the buffers share memory so the timestamp and data have already been # set. self._message_length = 2 self._out_buffer = b else: self._out_buffer[self._message_length] = data self._message_length += 1 if self._message_target_length == self._message_length: self._raw.write(self._out_buffer, header=self._header) if self._pending_realtime: self._raw.write(self._pending_realtime, header=self._header) self._pending_realtime = None self._message_target_length = None i += 1