Skip to main content

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 the token argument if you'd rather manage pairing secrets yourself.
  • The 0xfdcd advertisement 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

qingping_cgd1-0.1.1.tar.gz (50.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

qingping_cgd1-0.1.1-py3-none-any.whl (15.1 kB view details)

Uploaded Python 3

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

Hashes for qingping_cgd1-0.1.1.tar.gz
Algorithm Hash digest
SHA256 50d329dbb9fe43e2659dcabee1feea3345bc013648e9e8b6d5d5e882793aa6ce
MD5 79a16272137d6739e96a3805c2c35aac
BLAKE2b-256 d91f265e9af38fb502f503c77b0fbb209e5f86d51ad88e4ba6e598137c0e504c

See more details on using hashes here.

Provenance

The following attestation bundles were made for qingping_cgd1-0.1.1.tar.gz:

Publisher: release.yml on rjocoleman/qingping-cgd1

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for qingping_cgd1-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 afb41677acef126c1ccbf578e604aad0675fe9eb22b90eac3a2db6bf5281a887
MD5 11f72dcceb862b97868bd9f54d6a4585
BLAKE2b-256 ba3bc56aa0f83e6de8d4d2b447e894842f5ba7340789e5a42873f6355fa32fbb

See more details on using hashes here.

Provenance

The following attestation bundles were made for qingping_cgd1-0.1.1-py3-none-any.whl:

Publisher: release.yml on rjocoleman/qingping-cgd1

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page