Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions colmi_r02_client/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,16 @@ async def reboot(client: Client) -> None:
click.echo("Ring rebooted")


@cli_client.command()
@click.pass_obj
async def firehose(client: Client) -> None:
"""Start firehose data dump for 5 seconds"""

async with client:
await client.get_firehose()
click.echo("Firehose completed")


@cli_client.command()
@click.pass_obj
@click.option(
Expand Down
35 changes: 33 additions & 2 deletions colmi_r02_client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,19 @@
from bleak import BleakClient
from bleak.backends.characteristic import BleakGATTCharacteristic

from colmi_r02_client import battery, date_utils, steps, set_time, blink_twice, hr, hr_settings, packet, reboot, real_time
from colmi_r02_client import (
battery,
date_utils,
steps,
set_time,
blink_twice,
hr,
hr_settings,
packet,
reboot,
real_time,
firehose,
)

UART_SERVICE_UUID = "6E40FFF0-B5A3-F393-E0A9-E50E24DCCA9E"
UART_RX_CHAR_UUID = "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
Expand Down Expand Up @@ -48,6 +60,7 @@ class FullData:
hr.CMD_READ_HEART_RATE: hr.HeartRateLogParser().parse,
set_time.CMD_SET_TIME: empty_parse,
hr_settings.CMD_HEART_RATE_LOG_SETTINGS: hr_settings.parse_heart_rate_log_settings,
firehose.CMD_FIREHOSE: firehose.parse_firehose,
}
"""
TODO put these somewhere nice
Expand Down Expand Up @@ -105,7 +118,7 @@ def _handle_tx(self, _: BleakGATTCharacteristic, packet: bytearray) -> None:

assert len(packet) == 16, f"Packet is the wrong length {packet}"
packet_type = packet[0]
assert packet_type < 127, f"Packet has error bit set {packet}"
# assert packet_type < 127, f"Packet has error bit set {packet}"

if packet_type in COMMAND_HANDLERS:
result = COMMAND_HANDLERS[packet_type](packet)
Expand Down Expand Up @@ -154,6 +167,7 @@ async def _poll_real_time_reading(self, reading_type: real_time.RealTimeReading)
except TimeoutError:
tries += 1

# TODO, probably make this a try / finally so we do our best to stop
await self.send_packet(stop_packet)
if error:
return None
Expand Down Expand Up @@ -257,3 +271,20 @@ async def get_full_data(self, start: datetime, end: datetime) -> FullData:
sport_detail_logs.append(await self.get_steps(d))

return FullData(self.address, heart_rates=heart_rate_logs, sport_details=sport_detail_logs)

async def get_firehose(self):
try:
await self.send_packet(firehose.START_FIREHOSE_PACKET)
tries = 0
while tries < 20:
tries += 1
try:
data = await asyncio.wait_for(
self.queues[firehose.CMD_FIREHOSE].get(),
timeout=2,
)
logger.error(data)
except TimeoutError:
pass
finally:
await self.send_packet(firehose.STOP_FIREHOSE_PACKET)
72 changes: 72 additions & 0 deletions colmi_r02_client/firehose.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
from enum import IntEnum
from dataclasses import dataclass
import logging

from colmi_r02_client.packet import make_packet

logger = logging.getLogger(__name__)

CMD_FIREHOSE = 161 # 0xa1

START_FIREHOSE_PACKET = make_packet(CMD_FIREHOSE, bytearray([4]))
STOP_FIREHOSE_PACKET = make_packet(CMD_FIREHOSE, bytearray([2]))


class Kind(IntEnum):
SPO2 = 1
PPG = 2
ACCELEROMETER = 3


@dataclass
class SpO2:
current: int
max: int
min: int
diff: int


@dataclass
class PPG:
current: int
max: int
min: int
diff: int


@dataclass
class Accelerometer:
"""I think this is the x, y, z axis"""

x: int
y: int
z: int


def parse_firehose(packet: bytearray) -> SpO2 | PPG | Accelerometer | None:
r"""
bytearray(b'\xa1\x03\x1d\t\x07\x08\xfa\x00\x00\x00\x00\x00\x00\x00\x00\xd3')
"""
kind = packet[1]
if kind == Kind.SPO2:
return SpO2(
current=(packet[2] << 8) | packet[3],
max=packet[5],
min=packet[7],
diff=packet[9],
)
elif kind == Kind.PPG:
return PPG(
current=(packet[2] << 8) | packet[3],
max=(packet[4] << 8) | packet[5],
min=(packet[6] << 8) | packet[7],
diff=(packet[8] << 8) | packet[9],
)
elif kind == Kind.ACCELEROMETER:
x = ((packet[6] << 4) | (packet[7] & 0xF)) - (1 << 11) if packet[6] & 0x8 else ((packet[6] << 4) | (packet[7] & 0xF))
y = ((packet[3] << 4) | (packet[3] & 0xF)) - (1 << 11) if packet[2] & 0x8 else ((packet[2] << 4) | (packet[3] & 0xF))
z = ((packet[4] << 4) | (packet[5] & 0xF)) - (1 << 11) if packet[4] & 0x8 else ((packet[4] << 4) | (packet[5] & 0xF))
return Accelerometer(x=x, y=y, z=z)
else:
logging.error(f"Unexpected kind of firehose packet {packet}")
return None