From 3826ecc91fc3960e9064c3621ea734d559419f63 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 26 Aug 2026 02:58:04 +0000 Subject: [PATCH 1/3] docs: Split BX2 documentation from SDK PR Split README updates, BX2.md, and homeassistant-example.md from PR #1 into a separate documentation PR. These docs complement but are not required to land the BX2 SDK changes. Co-authored-by: mhand --- README.md | 53 ++++++++++++++++++-- docs/BX2.md | 91 +++++++++++++++++++++++++++++++++++ docs/homeassistant-example.md | 67 ++++++++++++++++++++++++++ 3 files changed, 208 insertions(+), 3 deletions(-) create mode 100644 docs/BX2.md create mode 100644 docs/homeassistant-example.md diff --git a/README.md b/README.md index 3fa413f..2c560e6 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Holman Python SDK -[Holman CO3015](https://www.holmanindustries.com.au/products/bluetooth-tap-timer-co3015/) -and [BTX1](https://www.holmanindustries.com.au/products/btx1-tap-mounted-smart-valve/) are Bluetooth tap timers made by [Holman](https://www.holmanindustries.com.au/). +[Holman CO3015](https://www.holmanindustries.com.au/products/bluetooth-tap-timer-co3015/), +[BTX1](https://www.holmanindustries.com.au/products/btx1-tap-mounted-smart-valve/), +and [BTX2](https://www.holmanindustries.com.au/products/btx2-dual-outlet-bluetooth-tap-timer/) are Bluetooth tap timers made by [Holman](https://www.holmanindustries.com.au/). The Holman Python SDK for Linux allows you to integrate your Holman(s) into any type of Linux application or script that can execute Python code. @@ -97,6 +98,38 @@ manager.start_discovery() manager.run() ``` +### Configuring accepted device aliases + +By default the SDK accepts devices whose advertised alias is exactly `Tap Timer` (upstream BX1) or `BX2` (this device). Match is membership (`if alias not in self.accepted_aliases: return None`); an empty alias does not match. Pass `accepted_aliases` or set `HOLMAN_ACCEPTED_ALIASES` (comma-separated) to add another exact name, not a prefix. + +```python +manager = holman.TapTimerManager( + adapter_name='hci0', + accepted_aliases=('Tap Timer', 'BX2', 'BX3')) +``` + +```bash +export HOLMAN_ACCEPTED_ALIASES="BX3" +sudo holmanctl --discover +``` + +### Configuring accepted BLE service UUIDs + +By default discovery **and** connect-time service pick use the same three known Holman vendor services: CO3015 (`0a75f000-f9ad-467a-e564-3c19163ad543`), CO3012 (`aacaebbb-af4b-baf3-7361-989ffeb0b129`, some BTX2), and CO3011 (`c521f000-0d70-4d4f-8e43-40d84c50ab38`, BTX1 / the on-air BX2 UUID). Named constants stay on `TapTimer`. + +Set `HOLMAN_SERVICE_UUIDS` to a comma-separated list to **replace** that filter (not add to it). Unset = hardcoded defaults; set = exactly those UUIDs. Or pass `service_uuids` to `TapTimerManager` (constructor wins over env). + +```python +manager = holman.TapTimerManager( + adapter_name='hci0', + service_uuids=('c521f000-0d70-4d4f-8e43-40d84c50ab38',)) +``` + +```bash +export HOLMAN_SERVICE_UUIDS="c521f000-0d70-4d4f-8e43-40d84c50ab38" +sudo holmanctl --discover +``` + ### Connecting to a Holman tap timer and receiving user input events Once `TapTimerManager` has discovered a Holman tap timer you can use the `TapTimer` object(s) that you retrieved from `TapTimerManager.tap_timers()` to connect to it. Alternatively you can create a new instance of `TapTimer` using the name of your Bluetooth adapter (typically `hci0`) and Holman's MAC address. @@ -121,7 +154,21 @@ As with Holman tap timer discovery, remember to start the Bluetooth event loop w ### Start the tap running -Once a Holman tap timer is connected you can start the tap with `TapTimer.start(runtime=1)`. Pass this a runtime (in minutes) for how long to run the tap. +Once a Holman tap timer is connected you can start the tap with `TapTimer.start(runtime=1)`. Pass a runtime in minutes. On dual-outlet BX2/BTX2 units, pass `zone=1` (Sprinkler, tap 0, `[0x01, 0x00, 0x00, mins]`) or `zone=2` (Hose, tap 1, `[0x01, 0x01, 0x00, mins]`). Byte 1 is the outlet. + +```python +tap_timer.start(runtime=5, zone=1) +tap_timer.stop() +``` + +From the CLI: + +``` +sudo holmanctl --start AA:BB:CC:DD:EE:FF --minutes 5 --zone 1 +sudo holmanctl --stop AA:BB:CC:DD:EE:FF +``` + +See [docs/BX2.md](docs/BX2.md) for the BX2 GATT notes (zones, unlock, what not to read) and [docs/homeassistant-example.md](docs/homeassistant-example.md) for a generic Home Assistant / Lovelace sketch (Sprinkler / Hose; no site addresses). ## Support diff --git a/docs/BX2.md b/docs/BX2.md new file mode 100644 index 0000000..fede51e --- /dev/null +++ b/docs/BX2.md @@ -0,0 +1,91 @@ +# Holman BX2 notes + +Protocol notes for a Holman **BX2** (dual outlet, advertised name `BX2`) with this SDK. No device addresses or credentials belong in this tree. + +`AE 8E` is a shared session unlock also used by public BX1 ESPHome configs, not a per-device secret. + +## Identity + +- Advertised alias is exactly `BX2` (bluetoothctl Name/Alias and Bleak `local_name`). Not `Holman BX2` or `BTX2`. Discovery matches exact names `Tap Timer` (BX1) and `BX2`. +- This BX2 advertises vendor service `c521f000-0d70-4d4f-8e43-40d84c50ab38` (already labelled BTX1 / CO3011). Discovery and connect-time service pick use the same list: CO3015, CO3012, and CO3011 by default (the on-air BX2 UUID is CO3011). Set `HOLMAN_SERVICE_UUIDS` to a comma-separated list to replace that filter, or pass `service_uuids` to `TapTimerManager`. Another BTX2 UUID (`aacaebbb-...`) stays in the default list for other units. +- Manufacturer company id `0x0374`. BLE address type is **random**. + +## GATT (safe) + +| UUID | Role | +| --- | --- | +| `c521f000-...` | Vendor service | +| `0000c001-...` | Write. Session unlock `AE 8E`. | +| `0000f006-...` | Write. Manual start/stop. | +| `0000f004-...` | Read. 12-byte state. Last byte `01` means running **when the official app started the valve**. | + +`start()` / `stop()` send the unlock when `c001` is present. + +## Manual payload + +4 bytes on `f006`: + + [0x01, tap, 0x00, minutes] + +| Tap | Name | Start write | Hex | +| --- | --- | --- | --- | +| 0 | Sprinkler | `[0x01, 0x00, 0x00, minutes]` | `010000NN` | +| 1 | Hose | `[0x01, 0x01, 0x00, minutes]` | `010100NN` | +| — | Stop | `[0x00, 0x00, 0x00, 0x00]` | `00000000` | + +- Byte 0 is on/off (`0x01` start, `0x00` stop). +- Byte 1 is the outlet: tap `0x00` = Sprinkler, tap `0x01` = Hose. +- `minutes` is `1...255`. +- Stop is all-off (both outlets). +- Sprinkler matches the original single-outlet SDK ON payload `01 00 00 `. BX1 stays compatible if callers leave the default tap 0. + +A 10-byte ESPHome-style pad (`01 00 00 mins` + six zeros) is accepted if written **without** response. A 10-byte write **with** response returned ATT `0x0e` and dropped the link. Prefer the 4-byte form. + +If a 4-byte write **with** response fails (ATT `0x0e`), retry **without** response. That is common while a run is already active. + +## Dual outlet behaviour + +- The timer can run **one outlet at a time**. Starting Hose while Sprinkler is open is not a second concurrent valve. +- To switch outlets: write stop (`00 00 00 00`), then start the other tap. A start-while-running write with response often errors; stop first. +- There is no extra reset characteristic required after a manual run. Stop is the all-zero `f006` write. + +## What not to do + +- **Do not read `0000e002-...`.** That drops the connection. +- Reading `f004` without a prior `c001` unlock can also return ATT `0x0e` and drop the link. +- First LE connect often fails with `le-connection-abort-by-local` / "failed to discover services, device disconnected". Retry. Two clients racing the same adapter makes this worse. +- `f004` last byte is **not a reliable water-is-flowing flag** after an SDK write. The official app sets it to `01`. A 4-byte start can open the valve while last byte stays `00`. Treat a successful write as optimistic; confirm physically if it matters. +- A successful GATT write can still look dry if that outlet is blocked. Confirm water, not only BLE ACKs. + +## Other characteristics + +Seen on the same service, not required for manual run: + +- `f003` — identity blob (MAC bytes reversed), readable without unlock. +- `f005`, `e001`, `c002` — read/write. Unlock may be required. Not needed for start/stop. +- `46a60001-ca26-425a-9bc6-d917829d2906` — write + notify. Untouched. + +## App pairing vs session unlock + +BlueZ `Paired`/`Bonded` can stay **no**. The Holman app still talks to the timer. Multiple phones can start a **manual** run at the same time. `AE 8E` is a session unlock, not exclusive SMP pairing. + +The printed manual's "one smartphone" line is about **scheduling ownership**, not a hard lock on manual GATT writes. The physical dial can still disable onboard schedules; that does not block these manual `f006` writes. + +## Suggested PR surface + +1. Accept exact aliases `Tap Timer` and `BX2` (optional `HOLMAN_ACCEPTED_ALIASES` adds more exact names). +2. Discover and connect with the same service UUID list (defaults include the on-air BX2 UUID CO3011 / `c521f000-...`). `HOLMAN_SERVICE_UUIDS` or `service_uuids=` fully replaces the list when set. +3. Unlock `c001` with `AE 8E` when the characteristic exists. +4. `start(runtime, zone=1)` writes `[0x01, tap, 0, mins]` (tap `0x00` Sprinkler / `0x01` Hose); `stop()` writes zeros. +5. CLI `--start` / `--stop` / `--minutes` / `--zone`. +6. README mention of BTX2 / BX2 and a link here. + +Leave Home Assistant bindings, retries, and site addresses out of the SDK. + +## CLI + +``` +holmanctl --discover +holmanctl --start AA:BB:CC:DD:EE:FF --minutes 2 --zone 1 +holmanctl --stop AA:BB:CC:DD:EE:FF +``` diff --git a/docs/homeassistant-example.md b/docs/homeassistant-example.md new file mode 100644 index 0000000..9d7cd01 --- /dev/null +++ b/docs/homeassistant-example.md @@ -0,0 +1,67 @@ +# Home Assistant / Lovelace example (generic) + +Sprinkler = tap 0. Hose = tap 1. One outlet at a time. No site IPs or MACs. + +f006 start bytes used by this SDK: + +| Tap | Name | Start | Hex | +| --- | --- | --- | --- | +| 0 | Sprinkler | `[0x01, 0x00, 0x00, minutes]` | `010000NN` | +| 1 | Hose | `[0x01, 0x01, 0x00, minutes]` | `010100NN` | +| — | Stop | `[0x00, 0x00, 0x00, 0x00]` | `00000000` | + +## Package snippet + +```yaml +holman_bt: + - mac: AA:BB:CC:DD:EE:FF + name: Holman BX2 + default_runtime: 5 +``` + +## Lovelace cards + +Entities are named from the device name plus the tap +(`switch.holman_bx2_sprinkler`, `switch.holman_bx2_hose`). + +```yaml +type: vertical-stack +cards: + - type: entities + title: Holman BX2 + entities: + - entity: number.holman_bx2_runtime + name: Minutes + - entity: switch.holman_bx2_sprinkler + name: Sprinkler + icon: mdi:sprinkler + - entity: switch.holman_bx2_hose + name: Hose + icon: mdi:hose + - type: horizontal-stack + cards: + - type: button + name: Start Sprinkler + icon: mdi:sprinkler + tap_action: + action: call-service + service: holman_bt.start + data: + zone: 1 + minutes: 5 + - type: button + name: Start Hose + icon: mdi:hose + tap_action: + action: call-service + service: holman_bt.start + data: + zone: 2 + minutes: 5 + - type: button + name: Stop + icon: mdi:water-off + tap_action: + action: call-service + service: holman_bt.stop +``` From c6be60a3caf18d55dcd19f0bb314e2c2c713bd4b Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 26 Aug 2026 03:02:42 +0000 Subject: [PATCH 2/3] Use generic zone terminology in all documentation Replace house-specific names (Sprinkler/Hose) with zone 1/zone 2 and tap index 0/1 throughout documentation files. Co-authored-by: mhand --- README.md | 4 ++-- docs/BX2.md | 16 ++++++++-------- docs/homeassistant-example.md | 32 ++++++++++++++++---------------- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 2c560e6..c10b02e 100644 --- a/README.md +++ b/README.md @@ -154,7 +154,7 @@ As with Holman tap timer discovery, remember to start the Bluetooth event loop w ### Start the tap running -Once a Holman tap timer is connected you can start the tap with `TapTimer.start(runtime=1)`. Pass a runtime in minutes. On dual-outlet BX2/BTX2 units, pass `zone=1` (Sprinkler, tap 0, `[0x01, 0x00, 0x00, mins]`) or `zone=2` (Hose, tap 1, `[0x01, 0x01, 0x00, mins]`). Byte 1 is the outlet. +Once a Holman tap timer is connected you can start the tap with `TapTimer.start(runtime=1)`. Pass a runtime in minutes. On dual-outlet BX2/BTX2 units, pass `zone=1` (tap index 0, `[0x01, 0x00, 0x00, mins]`) or `zone=2` (tap index 1, `[0x01, 0x01, 0x00, mins]`). Byte 1 is the tap index. ```python tap_timer.start(runtime=5, zone=1) @@ -168,7 +168,7 @@ sudo holmanctl --start AA:BB:CC:DD:EE:FF --minutes 5 --zone 1 sudo holmanctl --stop AA:BB:CC:DD:EE:FF ``` -See [docs/BX2.md](docs/BX2.md) for the BX2 GATT notes (zones, unlock, what not to read) and [docs/homeassistant-example.md](docs/homeassistant-example.md) for a generic Home Assistant / Lovelace sketch (Sprinkler / Hose; no site addresses). +See [docs/BX2.md](docs/BX2.md) for the BX2 GATT notes (zones, unlock, what not to read) and [docs/homeassistant-example.md](docs/homeassistant-example.md) for a generic Home Assistant / Lovelace sketch (zone 1 / zone 2; no site addresses). ## Support diff --git a/docs/BX2.md b/docs/BX2.md index fede51e..c1f0f70 100644 --- a/docs/BX2.md +++ b/docs/BX2.md @@ -25,19 +25,19 @@ Protocol notes for a Holman **BX2** (dual outlet, advertised name `BX2`) with th 4 bytes on `f006`: - [0x01, tap, 0x00, minutes] + [0x01, tap_index, 0x00, minutes] -| Tap | Name | Start write | Hex | +| Tap index | Zone | Start write | Hex | | --- | --- | --- | --- | -| 0 | Sprinkler | `[0x01, 0x00, 0x00, minutes]` | `010000NN` | -| 1 | Hose | `[0x01, 0x01, 0x00, minutes]` | `010100NN` | +| 0 | Zone 1 | `[0x01, 0x00, 0x00, minutes]` | `010000NN` | +| 1 | Zone 2 | `[0x01, 0x01, 0x00, minutes]` | `010100NN` | | — | Stop | `[0x00, 0x00, 0x00, 0x00]` | `00000000` | - Byte 0 is on/off (`0x01` start, `0x00` stop). -- Byte 1 is the outlet: tap `0x00` = Sprinkler, tap `0x01` = Hose. +- Byte 1 is the tap index: `0x00` = zone 1, `0x01` = zone 2. - `minutes` is `1...255`. - Stop is all-off (both outlets). -- Sprinkler matches the original single-outlet SDK ON payload `01 00 00 `. BX1 stays compatible if callers leave the default tap 0. +- Zone 1 matches the original single-outlet SDK ON payload `01 00 00 `. BX1 stays compatible if callers leave the default tap 0. A 10-byte ESPHome-style pad (`01 00 00 mins` + six zeros) is accepted if written **without** response. A 10-byte write **with** response returned ATT `0x0e` and dropped the link. Prefer the 4-byte form. @@ -45,7 +45,7 @@ If a 4-byte write **with** response fails (ATT `0x0e`), retry **without** respon ## Dual outlet behaviour -- The timer can run **one outlet at a time**. Starting Hose while Sprinkler is open is not a second concurrent valve. +- The timer can run **one outlet at a time**. Starting zone 2 while zone 1 is open is not a second concurrent valve. - To switch outlets: write stop (`00 00 00 00`), then start the other tap. A start-while-running write with response often errors; stop first. - There is no extra reset characteristic required after a manual run. Stop is the all-zero `f006` write. @@ -76,7 +76,7 @@ The printed manual's "one smartphone" line is about **scheduling ownership**, no 1. Accept exact aliases `Tap Timer` and `BX2` (optional `HOLMAN_ACCEPTED_ALIASES` adds more exact names). 2. Discover and connect with the same service UUID list (defaults include the on-air BX2 UUID CO3011 / `c521f000-...`). `HOLMAN_SERVICE_UUIDS` or `service_uuids=` fully replaces the list when set. 3. Unlock `c001` with `AE 8E` when the characteristic exists. -4. `start(runtime, zone=1)` writes `[0x01, tap, 0, mins]` (tap `0x00` Sprinkler / `0x01` Hose); `stop()` writes zeros. +4. `start(runtime, zone=1)` writes `[0x01, tap_index, 0, mins]` (tap index `0x00` zone 1 / `0x01` zone 2); `stop()` writes zeros. 5. CLI `--start` / `--stop` / `--minutes` / `--zone`. 6. README mention of BTX2 / BX2 and a link here. diff --git a/docs/homeassistant-example.md b/docs/homeassistant-example.md index 9d7cd01..f098d56 100644 --- a/docs/homeassistant-example.md +++ b/docs/homeassistant-example.md @@ -1,13 +1,13 @@ # Home Assistant / Lovelace example (generic) -Sprinkler = tap 0. Hose = tap 1. One outlet at a time. No site IPs or MACs. +Zone 1 = tap index 0. Zone 2 = tap index 1. One outlet at a time. No site IPs or MACs. f006 start bytes used by this SDK: -| Tap | Name | Start | Hex | +| Tap index | Zone | Start | Hex | | --- | --- | --- | --- | -| 0 | Sprinkler | `[0x01, 0x00, 0x00, minutes]` | `010000NN` | -| 1 | Hose | `[0x01, 0x01, 0x00, minutes]` | `010100NN` | +| 0 | Zone 1 | `[0x01, 0x00, 0x00, minutes]` | `010000NN` | +| 1 | Zone 2 | `[0x01, 0x01, 0x00, minutes]` | `010100NN` | | — | Stop | `[0x00, 0x00, 0x00, 0x00]` | `00000000` | ## Package snippet @@ -21,8 +21,8 @@ holman_bt: ## Lovelace cards -Entities are named from the device name plus the tap -(`switch.holman_bx2_sprinkler`, `switch.holman_bx2_hose`). +Entities are named from the device name plus the zone +(`switch.holman_bx2_zone_1`, `switch.holman_bx2_zone_2`). ```yaml type: vertical-stack @@ -32,17 +32,17 @@ cards: entities: - entity: number.holman_bx2_runtime name: Minutes - - entity: switch.holman_bx2_sprinkler - name: Sprinkler - icon: mdi:sprinkler - - entity: switch.holman_bx2_hose - name: Hose - icon: mdi:hose + - entity: switch.holman_bx2_zone_1 + name: Zone 1 + icon: mdi:water + - entity: switch.holman_bx2_zone_2 + name: Zone 2 + icon: mdi:water - type: horizontal-stack cards: - type: button - name: Start Sprinkler - icon: mdi:sprinkler + name: Start Zone 1 + icon: mdi:water tap_action: action: call-service service: holman_bt.start @@ -50,8 +50,8 @@ cards: zone: 1 minutes: 5 - type: button - name: Start Hose - icon: mdi:hose + name: Start Zone 2 + icon: mdi:water tap_action: action: call-service service: holman_bt.start From dd075f878231dede1fa80b827ee96d8fef352499 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 26 Aug 2026 03:07:14 +0000 Subject: [PATCH 3/3] Remove README from docs PR (moved to code PR #1) Co-authored-by: mhand --- README.md | 183 ------------------------------------------------------ 1 file changed, 183 deletions(-) delete mode 100644 README.md diff --git a/README.md b/README.md deleted file mode 100644 index c10b02e..0000000 --- a/README.md +++ /dev/null @@ -1,183 +0,0 @@ -# Holman Python SDK -[Holman CO3015](https://www.holmanindustries.com.au/products/bluetooth-tap-timer-co3015/), -[BTX1](https://www.holmanindustries.com.au/products/btx1-tap-mounted-smart-valve/), -and [BTX2](https://www.holmanindustries.com.au/products/btx2-dual-outlet-bluetooth-tap-timer/) are Bluetooth tap timers made by [Holman](https://www.holmanindustries.com.au/). - -The Holman Python SDK for Linux allows you to integrate your Holman(s) into any type of Linux application or script that can execute Python code. - -## Prerequisites -The Holman SDK requires [Python 3.4+](https://www.python.org) and a recent installation of [BlueZ](http://www.bluez.org/). It is tested to work fine with BlueZ 5.44, slightly older versions should however work, too. - -## Installation -These instructions assume a Debian-based Linux. - -On Linux the [BlueZ](http://www.bluez.org/) library is necessary to access your built-in Bluetooth controller or Bluetooth USB dongle. Some Linux distributions provide a more up-to-date BlueZ package, some other distributions only install older versions that don't implement all Bluetooth features needed for this SDK. In those cases you want to either update BlueZ or build it from sources. - -### Updating/installing BlueZ via apt-get - -1. `bluetoothd --version` Obtains the version of the pre-installed BlueZ. `bluetoothd` daemon must run at startup to expose the Bluetooth API via D-Bus. -2. `sudo apt-get install --no-install-recommends bluetooth` Installs BlueZ -3. If the installed version is too old, proceed with next step: [Installing BlueZ from sources](#installing-bluez-from-sources) - -### Installing BlueZ from sources - -The `bluetoothd` daemon provides BlueZ's D-Bus interfaces that is accessed by the Holman SDK to communicate with Holman Bluetooth tap timers. The following commands download BlueZ 5.44 sources, built them and replace any pre-installed `bluetoothd` daemon. It's not suggested to remove any pre-installed BlueZ package as its deinstallation might remove necessary Bluetooth drivers as well. - -1. `sudo systemctl stop bluetooth` -2. `sudo apt-get update` -3. `sudo apt-get install libusb-dev libdbus-1-dev libglib2.0-dev libudev-dev libical-dev libreadline-dev libdbus-glib-1-dev unzip` -4. `cd` -5. `mkdir bluez` -6. `cd bluez` -7. `wget http://www.kernel.org/pub/linux/bluetooth/bluez-5.44.tar.xz` -8. `tar xf bluez-5.44.tar.xz` -9. `cd bluez-5.44` -10. `./configure --prefix=/usr --sysconfdir=/etc --localstatedir=/var --enable-library` -11. `make` -12. `sudo make install` -13. `sudo ln -svf /usr/libexec/bluetooth/bluetoothd /usr/sbin/` -14. `sudo install -v -dm755 /etc/bluetooth` -15. `sudo install -v -m644 src/main.conf /etc/bluetooth/main.conf` -16. `sudo systemctl daemon-reload` -17. `sudo systemctl start bluetooth` -18. `bluetoothd --version` # should now print 5.44 - -Please note that some distributions might use a different directory for system deamons, apply step 13 only as needed. - -### Enabling your Bluetooth adapter - -1. `echo "power on" | sudo bluetoothctl` Enables your built-in Bluetooth adapter or external Bluetooth USB dongle - -### Using BlueZ commandline tools -BlueZ also provides an interactive commandline tool to interact with Bluetooth devices. You know that your BlueZ installation is working fine if it discovers any Bluetooth devices nearby. - -`sudo bluetoothctl` Starts an interactive mode to talk to BlueZ - * `power on` Enables the Bluetooth adapter - * `scan on` Start Bluetooth device scanning and lists all found devices with MAC addresses - * `connect AA:BB:CC:DD:EE:FF` Connects to a Holman tap timer with specified MAC address - * `exit` Quits the interactive mode - -### Installing Holman Python SDK - -To install Holman module and the Python3 D-Bus dependency globally, run: - -``` -sudo pip3 install holman -sudo apt-get install python3-dbus -``` - -#### Running the Holman control script - -To test if your setup is working, run the following command. Note that it must be run as root because on Linux, Bluetooth discovery is a restricted operation. - -``` -sudo holmanctl --discover -sudo holmanctl --connect AA:BB:CC:DD:EE:FF # Replace the MAC address with your Holman's MAC address -sudo holmanctl --help # To list all available commands -``` - -## SDK Usage - -### Discovering nearby Holman tap timers - -The SDK entry point is the `TapTimerManager` class. Check the following example to dicover any Holman tap timer nearby. - -Please note that communication with your Bluetooth adapter happens over BlueZ's D-Bus API, hence an event loop needs to be run in order to receive all Bluetooth related events. You can start and stop the event loop via `run()` and `stop()` calls to your `TapTimerManager` instance. - - -```python -import holman - -class TapTimerManagerPrintListener(holman.TapTimerManagerListener): - def tap_timer_discovered(self, tap_timer): - print("Discovered Holman tap_timer", tap_timer.mac_address) - -manager = holman.TapTimerManager(adapter_name='hci0') -manager.listener = TapTimerManagerPrintListener() -manager.start_discovery() -manager.run() -``` - -### Configuring accepted device aliases - -By default the SDK accepts devices whose advertised alias is exactly `Tap Timer` (upstream BX1) or `BX2` (this device). Match is membership (`if alias not in self.accepted_aliases: return None`); an empty alias does not match. Pass `accepted_aliases` or set `HOLMAN_ACCEPTED_ALIASES` (comma-separated) to add another exact name, not a prefix. - -```python -manager = holman.TapTimerManager( - adapter_name='hci0', - accepted_aliases=('Tap Timer', 'BX2', 'BX3')) -``` - -```bash -export HOLMAN_ACCEPTED_ALIASES="BX3" -sudo holmanctl --discover -``` - -### Configuring accepted BLE service UUIDs - -By default discovery **and** connect-time service pick use the same three known Holman vendor services: CO3015 (`0a75f000-f9ad-467a-e564-3c19163ad543`), CO3012 (`aacaebbb-af4b-baf3-7361-989ffeb0b129`, some BTX2), and CO3011 (`c521f000-0d70-4d4f-8e43-40d84c50ab38`, BTX1 / the on-air BX2 UUID). Named constants stay on `TapTimer`. - -Set `HOLMAN_SERVICE_UUIDS` to a comma-separated list to **replace** that filter (not add to it). Unset = hardcoded defaults; set = exactly those UUIDs. Or pass `service_uuids` to `TapTimerManager` (constructor wins over env). - -```python -manager = holman.TapTimerManager( - adapter_name='hci0', - service_uuids=('c521f000-0d70-4d4f-8e43-40d84c50ab38',)) -``` - -```bash -export HOLMAN_SERVICE_UUIDS="c521f000-0d70-4d4f-8e43-40d84c50ab38" -sudo holmanctl --discover -``` - -### Connecting to a Holman tap timer and receiving user input events - -Once `TapTimerManager` has discovered a Holman tap timer you can use the `TapTimer` object(s) that you retrieved from `TapTimerManager.tap_timers()` to connect to it. Alternatively you can create a new instance of `TapTimer` using the name of your Bluetooth adapter (typically `hci0`) and Holman's MAC address. - -Make sure to assign a `TapTimerListener` object to the `listener` attribute of your TapTimer instance. It will notify you about all Holman tap timer related events such connection, disconnection and user input events. - -The following example connects to a Holman tap timer manually: - -```python -import holman - -manager = holman.TapTimerManager(adapter_name='hci0') - -tap_timer = holman.TapTimer(mac_address='AA:BB:CC:DD:EE:FF', manager=manager) -tap_timer.listener = holman.TapTimerListener() # Use an instance of your own holman.TapTimerListener subclass -tap_timer.connect() - -manager.run() -``` - -As with Holman tap timer discovery, remember to start the Bluetooth event loop with `TapTimerManager.run()`. - -### Start the tap running - -Once a Holman tap timer is connected you can start the tap with `TapTimer.start(runtime=1)`. Pass a runtime in minutes. On dual-outlet BX2/BTX2 units, pass `zone=1` (tap index 0, `[0x01, 0x00, 0x00, mins]`) or `zone=2` (tap index 1, `[0x01, 0x01, 0x00, mins]`). Byte 1 is the tap index. - -```python -tap_timer.start(runtime=5, zone=1) -tap_timer.stop() -``` - -From the CLI: - -``` -sudo holmanctl --start AA:BB:CC:DD:EE:FF --minutes 5 --zone 1 -sudo holmanctl --stop AA:BB:CC:DD:EE:FF -``` - -See [docs/BX2.md](docs/BX2.md) for the BX2 GATT notes (zones, unlock, what not to read) and [docs/homeassistant-example.md](docs/homeassistant-example.md) for a generic Home Assistant / Lovelace sketch (zone 1 / zone 2; no site addresses). - -## Support - -Please open an issue. - -## Contributing - -Contributions are welcome via pull requests. Please open an issue first in case you want to discus your possible improvements to this SDK. - -## License - -The Holman Python SDK is available under the MIT License.