mirror of
https://github.com/tahnok/colmi_r02_client.git
synced 2026-02-06 10:47:28 +00:00
Handle set_time_packet response by ignoring it
There is an implementation included for parsing the response, which is some weird capabilities response but it's wrong
This commit is contained in:
parent
41c3e8b9b9
commit
974982db72
@ -50,6 +50,7 @@ COMMAND_HANDLERS: dict[int, Callable[[bytearray], Any]] = {
|
||||
real_time_heart_rate.CMD_STOP_HEART_RATE: empty_parse,
|
||||
steps.CMD_GET_STEP_SOMEDAY: steps.SportDetailParser().parse,
|
||||
heart_rate.CMD_READ_HEART_RATE: heart_rate.HeartRateLogParser().parse,
|
||||
set_time.CMD_SET_TIME: empty_parse,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
A python client for connecting to the Colmi R02 Smart ring
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
import logging
|
||||
import time
|
||||
@ -76,14 +76,20 @@ async def get_heart_rate_log(client: Client, target: datetime) -> None:
|
||||
|
||||
@cli_client.command()
|
||||
@click.option(
|
||||
"--target",
|
||||
"--when",
|
||||
type=click.DateTime(),
|
||||
required=True,
|
||||
help="The date you want logs for",
|
||||
required=False,
|
||||
help="The date and time you want to set the ring to",
|
||||
)
|
||||
@click.pass_obj
|
||||
async def set_time(client: Client, target: datetime) -> None:
|
||||
await client.set_time(target)
|
||||
async def set_time(client: Client, when: datetime | None) -> None:
|
||||
"""
|
||||
Set the time on the ring, required if you want to be able to interpret any of the logged data
|
||||
"""
|
||||
|
||||
if when is None:
|
||||
when = datetime.now(tz=timezone.utc)
|
||||
await client.set_time(when)
|
||||
|
||||
|
||||
DEVICE_NAME_PREFIXES = [
|
||||
|
||||
@ -1,12 +1,26 @@
|
||||
from datetime import datetime
|
||||
"""
|
||||
The smart ring has it's own internal clock that is used to determine what time a given heart rate or step took
|
||||
place for accurate counting.
|
||||
|
||||
We always set the time in UTC.
|
||||
"""
|
||||
|
||||
from datetime import datetime, timezone
|
||||
import logging
|
||||
|
||||
from colmi_r02_client.packet import make_packet
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
CMD_SET_TIME = 1
|
||||
|
||||
|
||||
def set_time_packet(target: datetime) -> bytearray:
|
||||
if target.tzinfo != timezone.utc:
|
||||
logger.info("Converting target time to utc")
|
||||
target = target.astimezone(tz=timezone.utc)
|
||||
|
||||
assert target.year >= 2000
|
||||
data = bytearray(7)
|
||||
data[0] = byte_to_bcd(target.year % 2000)
|
||||
data[1] = byte_to_bcd(target.month)
|
||||
@ -25,3 +39,55 @@ def byte_to_bcd(b: int) -> int:
|
||||
tens = b // 10
|
||||
ones = b % 10
|
||||
return (tens << 4) | ones
|
||||
|
||||
|
||||
def parse_set_time_packet(packet: bytearray) -> dict[str, bool | int]:
|
||||
"""
|
||||
Parse the response to the set time packet which is some kind of capability response.
|
||||
|
||||
It seems useless. It does correctly say avatar is not supported and that heart rate is supported.
|
||||
But it also says there's wechat support and it supports 20 contacts.
|
||||
|
||||
I think this is safe to swallow and ignore.
|
||||
"""
|
||||
assert packet[0] == CMD_SET_TIME
|
||||
bArr = packet[1:]
|
||||
data: dict[str, bool | int] = {}
|
||||
data["mSupportTemperature"] = bArr[0] == 1
|
||||
data["mSupportPlate"] = bArr[1] == 1
|
||||
data["mSupportMenstruation"] = True
|
||||
data["mSupportCustomWallpaper"] = (bArr[3] & 1) != 0
|
||||
data["mSupportBloodOxygen"] = (bArr[3] & 2) != 0
|
||||
data["mSupportBloodPressure"] = (bArr[3] & 4) != 0
|
||||
data["mSupportFeature"] = (bArr[3] & 8) != 0
|
||||
data["mSupportOneKeyCheck"] = (bArr[3] & 16) != 0
|
||||
data["mSupportWeather"] = (bArr[3] & 32) != 0
|
||||
data["mSupportWeChat"] = (bArr[3] & 64) == 0
|
||||
data["mSupportAvatar"] = (bArr[3] & 128) != 0
|
||||
# data["#width"] = ByteUtil.bytesToInt(Arrays.copyOfRange(bArr, 4, 6))
|
||||
# data["#height"] = ByteUtil.bytesToInt(Arrays.copyOfRange(bArr, 6, 8))
|
||||
data["mNewSleepProtocol"] = bArr[8] == 1
|
||||
data["mMaxWatchFace"] = bArr[9]
|
||||
data["mSupportContact"] = (bArr[10] & 1) != 0
|
||||
data["mSupportLyrics"] = (bArr[10] & 2) != 0
|
||||
data["mSupportAlbum"] = (bArr[10] & 4) != 0
|
||||
data["mSupportGPS"] = (bArr[10] & 8) != 0
|
||||
data["mSupportJieLiMusic"] = (bArr[10] & 16) != 0
|
||||
data["mSupportManualHeart"] = (bArr[11] & 1) != 0
|
||||
data["mSupportECard"] = (bArr[11] & 2) != 0
|
||||
data["mSupportLocation"] = (bArr[11] & 4) != 0
|
||||
data["mMusicSupport"] = (bArr[11] & 16) != 0
|
||||
data["rtkMcu"] = (bArr[11] & 32) != 0
|
||||
data["mEbookSupport"] = (bArr[11] & 64) != 0
|
||||
data["mSupportBloodSugar"] = (bArr[11] & 128) != 0
|
||||
if bArr[12] == 0:
|
||||
data["mMaxContacts"] = 20
|
||||
else:
|
||||
data["mMaxContacts"] = bArr[12] * 10
|
||||
data["bpSettingSupport"] = (bArr[13] & 2) != 0
|
||||
data["mSupport4G"] = (bArr[13] & 4) != 0
|
||||
data["mSupportNavPicture"] = (bArr[13] & 8) != 0
|
||||
data["mSupportPressure"] = (bArr[13] & 16) != 0
|
||||
data["mSupportHrv"] = (bArr[13] & 32) != 0
|
||||
|
||||
return data
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
from colmi_r02_client.set_time import byte_to_bcd
|
||||
from datetime import datetime, timezone, timedelta
|
||||
|
||||
from colmi_r02_client.set_time import byte_to_bcd, set_time_packet, CMD_SET_TIME, parse_set_time_packet
|
||||
|
||||
import pytest
|
||||
|
||||
@ -12,3 +14,38 @@ def test_byte_to_bcd(normal, bcd):
|
||||
def test_byte_to_bcd_bad(bad):
|
||||
with pytest.raises(AssertionError):
|
||||
byte_to_bcd(bad)
|
||||
|
||||
|
||||
def test_set_time_packet():
|
||||
ts = datetime(2024, 1, 1, 0, 0, 0, tzinfo=timezone.utc)
|
||||
expected = bytearray(b"\x01$\x01\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00(")
|
||||
|
||||
actual = set_time_packet(ts)
|
||||
|
||||
assert actual == expected
|
||||
|
||||
assert actual[0] == CMD_SET_TIME
|
||||
|
||||
|
||||
def test_set_time_1999():
|
||||
ts = datetime(1999, 1, 1, 0, 0, 0)
|
||||
with pytest.raises(AssertionError):
|
||||
set_time_packet(ts)
|
||||
|
||||
|
||||
def test_set_time_with_timezone():
|
||||
ts = datetime(2024, 1, 1, 0, 0, 0, tzinfo=timezone(timedelta(hours=-4)))
|
||||
expected = bytearray(b"\x01$\x01\x01\x04\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00,")
|
||||
|
||||
actual = set_time_packet(ts)
|
||||
|
||||
assert actual == expected
|
||||
|
||||
|
||||
def test_parse_set_time_response():
|
||||
packet = bytearray(b'\x01\x00\x01\x00"\x00\x00\x00\x00\x01\x000\x01\x00\x10f')
|
||||
|
||||
capabilities = parse_set_time_packet(packet)
|
||||
|
||||
assert capabilities["mSupportManualHeart"]
|
||||
assert not capabilities["mSupportBloodSugar"]
|
||||
|
||||
Loading…
Reference in New Issue
Block a user