casambi-bt-revamped 0.3.10__tar.gz → 0.3.12.dev1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- casambi_bt_revamped-0.3.12.dev1/PKG-INFO +120 -0
- casambi_bt_revamped-0.3.12.dev1/README.md +99 -0
- {casambi_bt_revamped-0.3.10 → casambi_bt_revamped-0.3.12.dev1}/setup.cfg +3 -3
- {casambi_bt_revamped-0.3.10 → casambi_bt_revamped-0.3.12.dev1}/src/CasambiBt/_cache.py +9 -9
- {casambi_bt_revamped-0.3.10 → casambi_bt_revamped-0.3.12.dev1}/src/CasambiBt/_casambi.py +11 -7
- {casambi_bt_revamped-0.3.10 → casambi_bt_revamped-0.3.12.dev1}/src/CasambiBt/_client.py +144 -147
- casambi_bt_revamped-0.3.12.dev1/src/CasambiBt/_invocation.py +116 -0
- casambi_bt_revamped-0.3.12.dev1/src/CasambiBt/_switch_events.py +327 -0
- {casambi_bt_revamped-0.3.10 → casambi_bt_revamped-0.3.12.dev1}/src/CasambiBt/_unit.py +37 -1
- casambi_bt_revamped-0.3.12.dev1/src/casambi_bt_revamped.egg-info/PKG-INFO +120 -0
- {casambi_bt_revamped-0.3.10 → casambi_bt_revamped-0.3.12.dev1}/src/casambi_bt_revamped.egg-info/SOURCES.txt +5 -1
- {casambi_bt_revamped-0.3.10 → casambi_bt_revamped-0.3.12.dev1}/src/casambi_bt_revamped.egg-info/requires.txt +1 -1
- casambi_bt_revamped-0.3.12.dev1/tests/test_switch_event_logs.py +205 -0
- casambi_bt_revamped-0.3.12.dev1/tests/test_unit_state_logs.py +124 -0
- casambi_bt_revamped-0.3.10/PKG-INFO +0 -81
- casambi_bt_revamped-0.3.10/README.md +0 -60
- casambi_bt_revamped-0.3.10/src/casambi_bt_revamped.egg-info/PKG-INFO +0 -81
- {casambi_bt_revamped-0.3.10 → casambi_bt_revamped-0.3.12.dev1}/LICENSE +0 -0
- {casambi_bt_revamped-0.3.10 → casambi_bt_revamped-0.3.12.dev1}/pyproject.toml +0 -0
- {casambi_bt_revamped-0.3.10 → casambi_bt_revamped-0.3.12.dev1}/src/CasambiBt/__init__.py +0 -0
- {casambi_bt_revamped-0.3.10 → casambi_bt_revamped-0.3.12.dev1}/src/CasambiBt/_constants.py +0 -0
- {casambi_bt_revamped-0.3.10 → casambi_bt_revamped-0.3.12.dev1}/src/CasambiBt/_discover.py +0 -0
- {casambi_bt_revamped-0.3.10 → casambi_bt_revamped-0.3.12.dev1}/src/CasambiBt/_encryption.py +0 -0
- {casambi_bt_revamped-0.3.10 → casambi_bt_revamped-0.3.12.dev1}/src/CasambiBt/_keystore.py +0 -0
- {casambi_bt_revamped-0.3.10 → casambi_bt_revamped-0.3.12.dev1}/src/CasambiBt/_network.py +0 -0
- {casambi_bt_revamped-0.3.10 → casambi_bt_revamped-0.3.12.dev1}/src/CasambiBt/_operation.py +0 -0
- {casambi_bt_revamped-0.3.10 → casambi_bt_revamped-0.3.12.dev1}/src/CasambiBt/errors.py +0 -0
- {casambi_bt_revamped-0.3.10 → casambi_bt_revamped-0.3.12.dev1}/src/CasambiBt/py.typed +0 -0
- {casambi_bt_revamped-0.3.10 → casambi_bt_revamped-0.3.12.dev1}/src/casambi_bt_revamped.egg-info/dependency_links.txt +0 -0
- {casambi_bt_revamped-0.3.10 → casambi_bt_revamped-0.3.12.dev1}/src/casambi_bt_revamped.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: casambi-bt-revamped
|
|
3
|
+
Version: 0.3.12.dev1
|
|
4
|
+
Summary: Forked Casambi Bluetooth client library with switch event support, use original if no special need. https://github.com/lkempf/casambi-bt
|
|
5
|
+
Home-page: https://github.com/rankjie/casambi-bt
|
|
6
|
+
Author: rankjie
|
|
7
|
+
Author-email: rankjie@gmail.com
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
9
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Requires-Python: >=3.11
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Requires-Dist: bleak!=2.0.0,>=0.22
|
|
16
|
+
Requires-Dist: cryptography>=40.0.0
|
|
17
|
+
Requires-Dist: httpx>=0.25
|
|
18
|
+
Requires-Dist: bleak_retry_connector>=3.6.0
|
|
19
|
+
Requires-Dist: anyio>=4.10.0
|
|
20
|
+
Dynamic: license-file
|
|
21
|
+
|
|
22
|
+

|
|
23
|
+
[](https://discord.gg/jgZVugfx)
|
|
24
|
+
|
|
25
|
+
# Casambi Bluetooth Revamped - Python library for Casambi networks
|
|
26
|
+
|
|
27
|
+
This is a customized fork of the original [casambi-bt](https://github.com/lkempf/casambi-bt) library with additional features and should only be used for special needs:
|
|
28
|
+
|
|
29
|
+
- **Switch event support** - Receive button press/release/hold events from Casambi switches (wired + wireless)
|
|
30
|
+
- **Improved relay status handling** - Better support for relay units
|
|
31
|
+
- **Bug fixes and improvements** - Various fixes based on real-world usage
|
|
32
|
+
|
|
33
|
+
This library provides a bluetooth interface to Casambi-based lights. It is not associated with Casambi.
|
|
34
|
+
|
|
35
|
+
For Home Assistant integration using this library, see [casambi-bt-hass](https://github.com/rankjie/casambi-bt-hass).
|
|
36
|
+
|
|
37
|
+
## Getting started
|
|
38
|
+
|
|
39
|
+
This library is available on PyPi:
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
pip install casambi-bt-revamped
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Have a look at `demo.py` for a small example.
|
|
46
|
+
|
|
47
|
+
### Switch Event Support
|
|
48
|
+
|
|
49
|
+
This library supports receiving physical switch events as a decoded stream of INVOCATION frames (ground truth from the official Android app).
|
|
50
|
+
|
|
51
|
+
Event types you can expect:
|
|
52
|
+
- `button_press`
|
|
53
|
+
- `button_release`
|
|
54
|
+
- `button_hold`
|
|
55
|
+
- `button_release_after_hold`
|
|
56
|
+
- `input_event` (raw NotifyInput frame that may accompany presses/holds; useful for diagnostics and some wired devices)
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
from CasambiBt import Casambi
|
|
60
|
+
|
|
61
|
+
def handle_switch_event(event_data):
|
|
62
|
+
print(
|
|
63
|
+
"Switch event:",
|
|
64
|
+
{
|
|
65
|
+
"unit_id": event_data.get("unit_id"),
|
|
66
|
+
"button": event_data.get("button"),
|
|
67
|
+
"event": event_data.get("event"),
|
|
68
|
+
# INVOCATION metadata (useful for debugging/correlation)
|
|
69
|
+
"event_id": event_data.get("event_id"),
|
|
70
|
+
"opcode": event_data.get("opcode"),
|
|
71
|
+
"target_type": event_data.get("target_type"),
|
|
72
|
+
"origin": event_data.get("origin"),
|
|
73
|
+
"age": event_data.get("age"),
|
|
74
|
+
# NotifyInput fields (target_type=0x12)
|
|
75
|
+
"input_code": event_data.get("input_code"),
|
|
76
|
+
"input_channel": event_data.get("input_channel"),
|
|
77
|
+
"input_value16": event_data.get("input_value16"),
|
|
78
|
+
"input_mapped_event": event_data.get("input_mapped_event"),
|
|
79
|
+
},
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
casa = Casambi()
|
|
83
|
+
# ... connect to network ...
|
|
84
|
+
|
|
85
|
+
# Register switch event handler
|
|
86
|
+
casa.registerSwitchEventHandler(handle_switch_event)
|
|
87
|
+
|
|
88
|
+
# Events will be received when buttons are pressed/released
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Notes:
|
|
92
|
+
- Wireless (battery) switches typically send a "button stream" (target_type `0x06`) for press/release, and a NotifyInput stream (target_type `0x12`) for hold/release-after-hold.
|
|
93
|
+
- Wired switches often only send NotifyInput (target_type `0x12`), so `input_code` is mapped into `button_press/button_release/...` when appropriate.
|
|
94
|
+
- The library suppresses same-state retransmits at the protocol layer (edge detection), so Home Assistant-style time-window deduplication should generally not be necessary.
|
|
95
|
+
|
|
96
|
+
For the parsing details and field layout, see `doc/PROTOCOL_PARSING.md`.
|
|
97
|
+
|
|
98
|
+
### MacOS
|
|
99
|
+
|
|
100
|
+
MacOS [does not expose the Bluetooth MAC address via their official API](https://github.com/hbldh/bleak/issues/140),
|
|
101
|
+
if you're running this library on MacOS, it will use an undocumented IOBluetooth API to get the MAC Address.
|
|
102
|
+
Without the real MAC address the integration with Casambi will not work.
|
|
103
|
+
If you're running into problems fetching the MAC address on MacOS, try it on a Raspberry Pi.
|
|
104
|
+
|
|
105
|
+
### Casambi network setup
|
|
106
|
+
|
|
107
|
+
If you have problems connecting to the network please check that your network is configured appropriately before creating an issue. The network I test this with uses the **Evoultion firmware** and is configured as follows (screenshots are for the iOS app but the Android app should look very similar):
|
|
108
|
+
|
|
109
|
+

|
|
110
|
+

|
|
111
|
+

|
|
112
|
+
|
|
113
|
+
## Development / Offline Testing
|
|
114
|
+
|
|
115
|
+
This repo includes log-driven unit tests for switch parsing:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
cd casambi-bt
|
|
119
|
+
python -m unittest -v
|
|
120
|
+
```
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+

|
|
2
|
+
[](https://discord.gg/jgZVugfx)
|
|
3
|
+
|
|
4
|
+
# Casambi Bluetooth Revamped - Python library for Casambi networks
|
|
5
|
+
|
|
6
|
+
This is a customized fork of the original [casambi-bt](https://github.com/lkempf/casambi-bt) library with additional features and should only be used for special needs:
|
|
7
|
+
|
|
8
|
+
- **Switch event support** - Receive button press/release/hold events from Casambi switches (wired + wireless)
|
|
9
|
+
- **Improved relay status handling** - Better support for relay units
|
|
10
|
+
- **Bug fixes and improvements** - Various fixes based on real-world usage
|
|
11
|
+
|
|
12
|
+
This library provides a bluetooth interface to Casambi-based lights. It is not associated with Casambi.
|
|
13
|
+
|
|
14
|
+
For Home Assistant integration using this library, see [casambi-bt-hass](https://github.com/rankjie/casambi-bt-hass).
|
|
15
|
+
|
|
16
|
+
## Getting started
|
|
17
|
+
|
|
18
|
+
This library is available on PyPi:
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
pip install casambi-bt-revamped
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Have a look at `demo.py` for a small example.
|
|
25
|
+
|
|
26
|
+
### Switch Event Support
|
|
27
|
+
|
|
28
|
+
This library supports receiving physical switch events as a decoded stream of INVOCATION frames (ground truth from the official Android app).
|
|
29
|
+
|
|
30
|
+
Event types you can expect:
|
|
31
|
+
- `button_press`
|
|
32
|
+
- `button_release`
|
|
33
|
+
- `button_hold`
|
|
34
|
+
- `button_release_after_hold`
|
|
35
|
+
- `input_event` (raw NotifyInput frame that may accompany presses/holds; useful for diagnostics and some wired devices)
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
from CasambiBt import Casambi
|
|
39
|
+
|
|
40
|
+
def handle_switch_event(event_data):
|
|
41
|
+
print(
|
|
42
|
+
"Switch event:",
|
|
43
|
+
{
|
|
44
|
+
"unit_id": event_data.get("unit_id"),
|
|
45
|
+
"button": event_data.get("button"),
|
|
46
|
+
"event": event_data.get("event"),
|
|
47
|
+
# INVOCATION metadata (useful for debugging/correlation)
|
|
48
|
+
"event_id": event_data.get("event_id"),
|
|
49
|
+
"opcode": event_data.get("opcode"),
|
|
50
|
+
"target_type": event_data.get("target_type"),
|
|
51
|
+
"origin": event_data.get("origin"),
|
|
52
|
+
"age": event_data.get("age"),
|
|
53
|
+
# NotifyInput fields (target_type=0x12)
|
|
54
|
+
"input_code": event_data.get("input_code"),
|
|
55
|
+
"input_channel": event_data.get("input_channel"),
|
|
56
|
+
"input_value16": event_data.get("input_value16"),
|
|
57
|
+
"input_mapped_event": event_data.get("input_mapped_event"),
|
|
58
|
+
},
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
casa = Casambi()
|
|
62
|
+
# ... connect to network ...
|
|
63
|
+
|
|
64
|
+
# Register switch event handler
|
|
65
|
+
casa.registerSwitchEventHandler(handle_switch_event)
|
|
66
|
+
|
|
67
|
+
# Events will be received when buttons are pressed/released
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Notes:
|
|
71
|
+
- Wireless (battery) switches typically send a "button stream" (target_type `0x06`) for press/release, and a NotifyInput stream (target_type `0x12`) for hold/release-after-hold.
|
|
72
|
+
- Wired switches often only send NotifyInput (target_type `0x12`), so `input_code` is mapped into `button_press/button_release/...` when appropriate.
|
|
73
|
+
- The library suppresses same-state retransmits at the protocol layer (edge detection), so Home Assistant-style time-window deduplication should generally not be necessary.
|
|
74
|
+
|
|
75
|
+
For the parsing details and field layout, see `doc/PROTOCOL_PARSING.md`.
|
|
76
|
+
|
|
77
|
+
### MacOS
|
|
78
|
+
|
|
79
|
+
MacOS [does not expose the Bluetooth MAC address via their official API](https://github.com/hbldh/bleak/issues/140),
|
|
80
|
+
if you're running this library on MacOS, it will use an undocumented IOBluetooth API to get the MAC Address.
|
|
81
|
+
Without the real MAC address the integration with Casambi will not work.
|
|
82
|
+
If you're running into problems fetching the MAC address on MacOS, try it on a Raspberry Pi.
|
|
83
|
+
|
|
84
|
+
### Casambi network setup
|
|
85
|
+
|
|
86
|
+
If you have problems connecting to the network please check that your network is configured appropriately before creating an issue. The network I test this with uses the **Evoultion firmware** and is configured as follows (screenshots are for the iOS app but the Android app should look very similar):
|
|
87
|
+
|
|
88
|
+

|
|
89
|
+

|
|
90
|
+

|
|
91
|
+
|
|
92
|
+
## Development / Offline Testing
|
|
93
|
+
|
|
94
|
+
This repo includes log-driven unit tests for switch parsing:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
cd casambi-bt
|
|
98
|
+
python -m unittest -v
|
|
99
|
+
```
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
[metadata]
|
|
2
2
|
name = casambi-bt-revamped
|
|
3
|
-
version = 0.3.
|
|
3
|
+
version = 0.3.12.dev1
|
|
4
4
|
author = rankjie
|
|
5
5
|
author_email = rankjie@gmail.com
|
|
6
|
-
description =
|
|
6
|
+
description = Forked Casambi Bluetooth client library with switch event support, use original if no special need. https://github.com/lkempf/casambi-bt
|
|
7
7
|
long_description = file: README.md
|
|
8
8
|
long_description_content_type = text/markdown
|
|
9
9
|
url = https://github.com/rankjie/casambi-bt
|
|
@@ -23,7 +23,7 @@ install_requires =
|
|
|
23
23
|
cryptography>=40.0.0
|
|
24
24
|
httpx>=0.25
|
|
25
25
|
bleak_retry_connector >= 3.6.0
|
|
26
|
-
|
|
26
|
+
anyio>=4.10.0
|
|
27
27
|
|
|
28
28
|
[options.packages.find]
|
|
29
29
|
where = src
|
|
@@ -6,28 +6,28 @@ import shutil
|
|
|
6
6
|
from types import TracebackType
|
|
7
7
|
from typing import Final
|
|
8
8
|
|
|
9
|
-
from
|
|
9
|
+
from anyio import Path
|
|
10
10
|
|
|
11
11
|
_LOGGER = logging.getLogger(__name__)
|
|
12
12
|
|
|
13
|
-
CACHE_PATH_DEFAULT: Final =
|
|
13
|
+
CACHE_PATH_DEFAULT: Final = Path(os.getcwd()) / "casambi-bt-store"
|
|
14
14
|
CACHE_VERSION: Final = 2
|
|
15
15
|
|
|
16
|
-
# We need a global lock since there could be multiple
|
|
16
|
+
# We need a global lock since there could be multiple Casambi instances
|
|
17
17
|
# with their own cache instances pointing to the same folder.
|
|
18
18
|
_cacheLock = asyncio.Lock()
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
def _blocking_delete(path:
|
|
21
|
+
def _blocking_delete(path: Path) -> None:
|
|
22
22
|
shutil.rmtree(pathlib.Path(path))
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
class Cache:
|
|
26
|
-
def __init__(self, cachePath:
|
|
26
|
+
def __init__(self, cachePath: Path | pathlib.Path | None) -> None:
|
|
27
27
|
if cachePath is None:
|
|
28
28
|
self._cachePath = CACHE_PATH_DEFAULT
|
|
29
|
-
elif not isinstance(cachePath,
|
|
30
|
-
self._cachePath =
|
|
29
|
+
elif not isinstance(cachePath, Path):
|
|
30
|
+
self._cachePath = Path(cachePath)
|
|
31
31
|
else:
|
|
32
32
|
self._cachePath = cachePath
|
|
33
33
|
|
|
@@ -69,7 +69,7 @@ class Cache:
|
|
|
69
69
|
await self._cachePath.mkdir(mode=0o700)
|
|
70
70
|
await self._cacheVersionFile.write_text(str(CACHE_VERSION))
|
|
71
71
|
|
|
72
|
-
async def __aenter__(self) ->
|
|
72
|
+
async def __aenter__(self) -> Path:
|
|
73
73
|
await _cacheLock.acquire()
|
|
74
74
|
|
|
75
75
|
if self._uuid is None:
|
|
@@ -78,7 +78,7 @@ class Cache:
|
|
|
78
78
|
try:
|
|
79
79
|
await self._ensureCacheValid()
|
|
80
80
|
|
|
81
|
-
cacheDir =
|
|
81
|
+
cacheDir = Path(self._cachePath / self._uuid)
|
|
82
82
|
if not await cacheDir.exists():
|
|
83
83
|
_LOGGER.debug("Creating cache entry for id %s", self._uuid)
|
|
84
84
|
await cacheDir.mkdir()
|
|
@@ -473,13 +473,17 @@ class Casambi:
|
|
|
473
473
|
"""Register a new handler for switch events.
|
|
474
474
|
|
|
475
475
|
This handler is called whenever a switch event is received.
|
|
476
|
-
The handler is supplied with a dictionary containing:
|
|
477
|
-
- unit_id:
|
|
478
|
-
- button:
|
|
479
|
-
- event:
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
-
|
|
476
|
+
The handler is supplied with a dictionary containing (at minimum):
|
|
477
|
+
- unit_id: target unit id (from INVOCATION target high byte)
|
|
478
|
+
- button: best-effort "label" (typically 1..4 for 4-gang switches)
|
|
479
|
+
- event: "button_press" | "button_release" | "input_event"
|
|
480
|
+
|
|
481
|
+
Switch events are parsed from decrypted packet type=7 (INVOCATION stream),
|
|
482
|
+
matching casambi-android `v1.C1775b.Q(Q2.h)`. Extra diagnostic keys include:
|
|
483
|
+
- invocation_flags, opcode, origin, target, target_type, age, origin_handle
|
|
484
|
+
- button_event_index (0..7), param_p, param_s
|
|
485
|
+
- input_index (0..7), input_code, input_b1, input_channel, input_value16, input_mapped_event
|
|
486
|
+
- packet_sequence, arrival_sequence, raw_packet, decrypted_data, payload_hex, frame_offset, event_id
|
|
483
487
|
|
|
484
488
|
:param handler: The method to call when a switch event is received.
|
|
485
489
|
"""
|