Pure-Python BLE library for the Qingping CGD1 alarm clock
Project description
qingping-cgd1
Local BLE control for the Qingping CGD1 "Dove" alarm clock. Pure Python, no cloud. Not affiliated with Qingping.
Install
pip install qingping-cgd1
Requires Python 3.14.2+. Depends only on bleak and bleak-retry-connector.
Passive sensor reading
Temperature, humidity, and battery ride in the BLE advertisement, so you can read them without connecting to the device:
from qingping_cgd1 import parse_advertisement
reading = parse_advertisement(service_data) # the 0xfdcd service-data bytes
if reading is not None:
print(reading.temperature, reading.humidity, reading.battery)
Active control
Everything else - settings, alarms, time sync - needs a connection,
authenticated with a bleak BLEDevice:
from qingping_cgd1 import Alarm, QingpingCGD1Client, Weekday
async with QingpingCGD1Client(ble_device) as client:
settings = await client.read_settings()
await client.write_settings(settings)
alarms = await client.read_alarms()
await client.write_alarm(0, Alarm(
enabled=True,
hour=7,
minute=30,
days=frozenset({Weekday.MONDAY, Weekday.TUESDAY}),
snooze=True,
))
await client.delete_alarm(1)
await client.sync_time()
info = await client.read_firmware()
QingpingCGD1Client also has explicit connect()/disconnect() methods if
you'd rather not use it as a context manager. It authenticates with a
16-byte token (overridable via the token argument, default
DEFAULT_AUTH_TOKEN), retries a timed-out command once after a reconnect,
and disconnects automatically after disconnect_delay seconds of
inactivity (default 120s).
Working out the next alarm
next_alarm takes the list from read_alarms() and a reference time, and
returns the soonest upcoming fire time (or None if nothing is enabled):
from datetime import datetime
from qingping_cgd1 import next_alarm
upcoming = next_alarm(alarms, datetime.now())
Protocol notes
This library is reverse-engineered, not an official SDK. The protocol was mapped mainly by MrBoombastic/clOwOck, an Android app for the CGD1; this is a Python port of that work. A few things worth knowing before you rely on it:
- There are no checksums on the wire. Frames are fixed-layout and unvalidated by the device beyond their length.
- The 16-byte auth token is a per-device pairing secret, not a universal
key. A clock that's already paired (for example, one that's been used
with the official Qingping+ app) will reject the default token: the
device answers auth step 2 with
04 ff 02 00 01(fail). You need to unbind/reset the clock first - long-press its button until the Bluetooth icon flashes, or remove it in the Qingping+ app - after which it binds to the first token it's presented with. This library presents its default token, and from then on that token authenticates every time. The token is injectable via thetokenargument if you'd rather manage pairing secrets yourself. - The
0xfdcdadvertisement layout (model | mac | temperature | humidity | battery) is confirmed from a real CGD1 capture (firmware 1.0.1_0130): decoding it gave 20.0 C / 51.7% / 80%, matching the device's own display. There are still two unknown/reserved byte pairs in the frame whose meaning isn't established.
Scope
Covers passive sensor reading from advertisements, settings read/write, all 16 alarm slots, and time sync. Ringtone upload is not implemented.
How this was built
Built largely with AI assistance (Claude) and tested against a real CGD1 (firmware 1.0.1_0130). Tested and working, but a spare-time project - no warranty, no support promises.
Licence
MIT.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file qingping_cgd1-0.1.1.tar.gz.
File metadata
- Download URL: qingping_cgd1-0.1.1.tar.gz
- Upload date:
- Size: 50.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
50d329dbb9fe43e2659dcabee1feea3345bc013648e9e8b6d5d5e882793aa6ce
|
|
| MD5 |
79a16272137d6739e96a3805c2c35aac
|
|
| BLAKE2b-256 |
d91f265e9af38fb502f503c77b0fbb209e5f86d51ad88e4ba6e598137c0e504c
|
Provenance
The following attestation bundles were made for qingping_cgd1-0.1.1.tar.gz:
Publisher:
release.yml on rjocoleman/qingping-cgd1
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
qingping_cgd1-0.1.1.tar.gz -
Subject digest:
50d329dbb9fe43e2659dcabee1feea3345bc013648e9e8b6d5d5e882793aa6ce - Sigstore transparency entry: 2045067391
- Sigstore integration time:
-
Permalink:
rjocoleman/qingping-cgd1@855a955ea6b3452819c7f974157da383e987a971 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/rjocoleman
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@855a955ea6b3452819c7f974157da383e987a971 -
Trigger Event:
push
-
Statement type:
File details
Details for the file qingping_cgd1-0.1.1-py3-none-any.whl.
File metadata
- Download URL: qingping_cgd1-0.1.1-py3-none-any.whl
- Upload date:
- Size: 15.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
afb41677acef126c1ccbf578e604aad0675fe9eb22b90eac3a2db6bf5281a887
|
|
| MD5 |
11f72dcceb862b97868bd9f54d6a4585
|
|
| BLAKE2b-256 |
ba3bc56aa0f83e6de8d4d2b447e894842f5ba7340789e5a42873f6355fa32fbb
|
Provenance
The following attestation bundles were made for qingping_cgd1-0.1.1-py3-none-any.whl:
Publisher:
release.yml on rjocoleman/qingping-cgd1
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
qingping_cgd1-0.1.1-py3-none-any.whl -
Subject digest:
afb41677acef126c1ccbf578e604aad0675fe9eb22b90eac3a2db6bf5281a887 - Sigstore transparency entry: 2045067456
- Sigstore integration time:
-
Permalink:
rjocoleman/qingping-cgd1@855a955ea6b3452819c7f974157da383e987a971 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/rjocoleman
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@855a955ea6b3452819c7f974157da383e987a971 -
Trigger Event:
push
-
Statement type: