bluetooth_clocks package#

Set and get the time on various Bluetooth clocks.

This project offers a way to easily recognize Bluetooth Low Energy clocks from their advertisements and has a device-independent API to set and get the time on them.

class bluetooth_clocks.BluetoothClock(device: BLEDevice)[source]#

Bases: ABC

Abstract class that represents the definition of a Bluetooth clock.

Support for every type of Bluetooth clock is implemented as a separate subclass by giving the class variables a value and/or by overriding methods or implementing abstract methods of this class.

address#

The Bluetooth address of the device.

Type:

str

name#

The name of the device, or None if it doesn’t have a name.

Type:

str | None

CHAR_UUID: ClassVar[UUID]#

The UUID of the characteristic used to read/write the time.

DEVICE_TYPE: ClassVar[str]#

The name of the device type.

LOCAL_NAME: ClassVar[str | None]#

The local name used to recognize this type of device.

This is None if the local name isn’t used to recognize the device.

LOCAL_NAME_STARTS_WITH: ClassVar[bool | None]#

Whether the local name should start with LOCAL_NAME.

True if the start of LOCAL_NAME is used to recognize this type of device. False if the local name should exactly match LOCAL_NAME. This is None if the local name isn’t used to recognize the device.

SERVICE_UUID: ClassVar[UUID]#

The UUID of the service used to read/write the time.

TIME_GET_FORMAT: ClassVar[str | None]#

The format string to convert bytes read from the device to a time.

This is None if the device doesn’t support reading the time.

TIME_SET_FORMAT: ClassVar[str]#

The format string to convert a time to bytes written to the device.

WRITE_WITH_RESPONSE: ClassVar[bool]#

True if the bytes to set the time should use write with response.

classmethod create_from_advertisement(device: BLEDevice, advertisement_data: AdvertisementData) BluetoothClock[source]#

Create object of a BluetoothClock subclass from advertisement data.

This is a factory method that you use if you don’t know the exact device type beforehand. This method automatically recognizes the device type and creates an object of the corresponding subclass.

Parameters:
Raises:

UnsupportedDeviceError – If the device with address address isn’t supported.

Returns:

An object of the subclass corresponding to the recognized device type.

Return type:

BluetoothClock

abstract get_bytes_from_time(timestamp: float, ampm: bool = False) bytes[source]#

Generate the bytes to set the time on this device.

Override this method in a subclass to implement the device’s time format.

Parameters:
  • timestamp (float) – The time encoded as a Unix timestamp.

  • ampm (bool) – True if the device should show the time with AM/PM, False if it should use 24-hour format. Devices that don’t support choosing the mode can ignore this argument.

Returns:

The bytes needed to set the time of the device to timestamp.

Return type:

bytes

Example

>>> from bluetooth_clocks.devices.thermopro import TP393
>>> from bleak.backends.device import BLEDevice
>>> from datetime import datetime
>>> clock = TP393(BLEDevice("10:76:36:14:2A:3D", "TP393 (2A3D)", {}, -67))
>>> timestamp = datetime.fromisoformat("2023-01-07 17:32:50").timestamp()
>>> clock.get_bytes_from_time(timestamp, ampm=True).hex()
'a517010711203206005a'
async get_time() float[source]#

Get the time of the Bluetooth clock.

Raises:

TimeNotReadableError – If the device doesn’t support getting the time.

Returns:

The time of the Bluetooth clock.

Return type:

float

get_time_from_bytes(time_bytes: bytes) float[source]#

Convert bytes read from a device to a timestamp.

Override this method in a subclass for a device that supports getting the time.

Parameters:

time_bytes (bytes) – The raw bytes read from the device.

Raises:
Returns:

The time encoded as a Unix timestamp.

Return type:

float

Example

>>> from bluetooth_clocks.devices.xiaomi import LYWSD02
>>> from bleak.backends.device import BLEDevice
>>> from datetime import datetime
>>> clock = LYWSD02(BLEDevice("E7:2E:00:B1:38:96", "", {}, -67))
>>> timestamp = clock.get_time_from_bytes(
...             bytes([0xdd, 0xbc, 0xb9, 0x63, 0x00]))
>>> print(datetime.utcfromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M:%S"))
2023-01-07 18:41:33
classmethod is_readable() bool[source]#

Test whether you can read the time from this device.

Returns:

True if this device supports reading the time, False otherwise.

Return type:

bool

Example

>>> from bluetooth_clocks.devices.xiaomi import LYWSD02
>>> from bluetooth_clocks.devices.qingping import CGC1
>>> LYWSD02.is_readable()
True
>>> CGC1.is_readable()
False
classmethod recognize(device: BLEDevice, advertisement_data: AdvertisementData) bool[source]#

Recognize this device type from advertisement data.

By default this checks whether the advertisement data has a local name that is equal to or starts with LOCAL_NAME, by calling recognize_from_local_name().

Override this method in a subclass if the device type should be recognized in another way from advertisement data.

Parameters:
Returns:

True if this subclass of BluetoothClock recognizes the device, False otherwise.

Return type:

bool

classmethod recognize_from_local_name(local_name: str | None) bool[source]#

Recognize the device from an advertised local name.

This is a helper method that subclasses can use to implement their recognize() method.

Parameters:

local_name (str | None = None) – The local name of the device, or None if it doesn’t advertise its local name.

Returns:

True if this subclass of BluetoothClock recognizes the device from its local name local_name, False otherwise.

Return type:

bool

classmethod recognize_from_service_uuids(service_uuids: list[str] | None) bool[source]#

Recognize this device type from service UUIDs.

This is a helper method that subclasses can use to implement their recognize() method.

Parameters:

service_uuids (list[str] | None = None) – Service UUIDs of the device, or None if the device doesn’t advertise service UUIDs.

Returns:

True if this subclass of BluetoothClock recognizes the device from the service UUIDs in service_uuids, False otherwise.

Return type:

bool

async set_time(timestamp: float | None = None, ampm: bool = False) None[source]#

Set the time of the Bluetooth clock.

Parameters:
  • timestamp (float | None = None) – The timestamp to write to the clock. If this is None, the current time is used.

  • ampm (bool) – True if the device should show the time with AM/PM, False if it should use 24-hour format. Devices that don’t support choosing the mode can ignore this argument.

bluetooth_clocks.MICROSECONDS = 1000000#

The number of microseconds in a second.

You can use this constant in subclasses of BluetoothClock.

bluetooth_clocks.SECONDS_IN_HOUR = 3600#

The number of seconds in an hour.

You can use this constant in subclasses of BluetoothClock.

bluetooth_clocks.supported_devices() list[str][source]#

Get a list of names of supported devices.

Returns:

A list of the names of devices supported by this library.

Return type:

list[str]

Example

>>> from bluetooth_clocks import supported_devices
>>> "ThermoPro TP393" in supported_devices()
True

Subpackages#

Submodules#

bluetooth_clocks.exceptions module#

Module with exceptions raised by this library.

exception bluetooth_clocks.exceptions.BluetoothClocksError[source]#

Bases: Exception

Base class for all exceptions raised by this library.

exception bluetooth_clocks.exceptions.InvalidTimeBytesError[source]#

Bases: BluetoothClocksError

Exception raised when bytes read from a device don’t have the right format.

exception bluetooth_clocks.exceptions.TimeNotReadableError[source]#

Bases: BluetoothClocksError

Exception raised when reading the time on a device that doesn’t support this.

exception bluetooth_clocks.exceptions.UnsupportedDeviceError[source]#

Bases: BluetoothClocksError

Exception raised when a device is not supported.

bluetooth_clocks.scanners module#

Module with functions to scan for Bluetooth clocks.

async bluetooth_clocks.scanners.discover_clocks(callback: Callable[[BluetoothClock], None], scan_duration: float = 5.0) None[source]#

Discover Bluetooth clocks.

Parameters:
  • callback (Callable[[BluetoothClock], None]) – Function to call when a clock has been discovered. This function gets passed the discovered BluetoothClock object as its argument.

  • scan_duration (float) – The scan duration for discovering devices. Defaults to 5 seconds.

async bluetooth_clocks.scanners.find_clock(address: str, scan_duration: float = 5.0) BluetoothClock | None[source]#

Get BluetoothClock object from Bluetooth address.

Parameters:
  • address (str) – The Bluetooth address of the device.

  • scan_duration (float) – The scan duration for finding the device. Defaults to 5 seconds.

Raises:

UnsupportedDeviceError – If the device with address address isn`t supported.

Returns:

A BluetoothClock object for the device, or None if the device isn’t found.

Return type:

BluetoothClock | None