python-broadlink 1.0.0__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.
- python_broadlink-1.0.0/CHANGELOG.md +98 -0
- python_broadlink-1.0.0/LICENSE +22 -0
- python_broadlink-1.0.0/MANIFEST.in +5 -0
- python_broadlink-1.0.0/PKG-INFO +360 -0
- python_broadlink-1.0.0/README.md +329 -0
- python_broadlink-1.0.0/TROUBLESHOOTING.md +9 -0
- python_broadlink-1.0.0/broadlink/__init__.py +342 -0
- python_broadlink-1.0.0/broadlink/alarm.py +43 -0
- python_broadlink-1.0.0/broadlink/climate.py +474 -0
- python_broadlink-1.0.0/broadlink/const.py +5 -0
- python_broadlink-1.0.0/broadlink/cover.py +182 -0
- python_broadlink-1.0.0/broadlink/device.py +470 -0
- python_broadlink-1.0.0/broadlink/exceptions.py +161 -0
- python_broadlink-1.0.0/broadlink/helpers.py +43 -0
- python_broadlink-1.0.0/broadlink/hub.py +98 -0
- python_broadlink-1.0.0/broadlink/light.py +200 -0
- python_broadlink-1.0.0/broadlink/protocol.py +50 -0
- python_broadlink-1.0.0/broadlink/remote.py +521 -0
- python_broadlink-1.0.0/broadlink/sensor.py +90 -0
- python_broadlink-1.0.0/broadlink/switch.py +472 -0
- python_broadlink-1.0.0/cli/README.md +156 -0
- python_broadlink-1.0.0/cli/broadlink_cli +221 -0
- python_broadlink-1.0.0/cli/broadlink_discovery +37 -0
- python_broadlink-1.0.0/protocol.md +204 -0
- python_broadlink-1.0.0/pyproject.toml +66 -0
- python_broadlink-1.0.0/python_broadlink.egg-info/PKG-INFO +360 -0
- python_broadlink-1.0.0/python_broadlink.egg-info/SOURCES.txt +40 -0
- python_broadlink-1.0.0/python_broadlink.egg-info/dependency_links.txt +1 -0
- python_broadlink-1.0.0/python_broadlink.egg-info/requires.txt +6 -0
- python_broadlink-1.0.0/python_broadlink.egg-info/top_level.txt +1 -0
- python_broadlink-1.0.0/setup.cfg +4 -0
- python_broadlink-1.0.0/tests/__init__.py +0 -0
- python_broadlink-1.0.0/tests/oracle/__init__.py +1 -0
- python_broadlink-1.0.0/tests/oracle/cases.py +402 -0
- python_broadlink-1.0.0/tests/oracle/fixtures.json +4099 -0
- python_broadlink-1.0.0/tests/oracle/harness.py +158 -0
- python_broadlink-1.0.0/tests/oracle/record.py +38 -0
- python_broadlink-1.0.0/tests/test_capture.py +553 -0
- python_broadlink-1.0.0/tests/test_helpers.py +109 -0
- python_broadlink-1.0.0/tests/test_oracle.py +57 -0
- python_broadlink-1.0.0/tests/test_remote.py +63 -0
- python_broadlink-1.0.0/tests/test_transport.py +511 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are recorded here. The format follows
|
|
4
|
+
Keep a Changelog; versions follow Semantic Versioning.
|
|
5
|
+
|
|
6
|
+
## 1.0.0 - 2026-09-05
|
|
7
|
+
|
|
8
|
+
This is the first release of `python-broadlink`, a maintained fork of
|
|
9
|
+
`mjg59/python-broadlink` (PyPI `broadlink`, last released as 0.19.0). The
|
|
10
|
+
history below starts at that fork point.
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- **The library is asynchronous.** Every method that talks to a device is
|
|
15
|
+
now a coroutine: `await device.auth()`, `await device.send_data(...)`,
|
|
16
|
+
`await device.check_sensors()`, and so on. Discovery is
|
|
17
|
+
`await broadlink.discover(...)`, `broadlink.hello(...)` and `setup(...)`
|
|
18
|
+
are coroutines, and `xdiscover(...)` is an async generator. The packet
|
|
19
|
+
helpers (`pulses_to_data`, `data_to_pulses`), CRC and datetime helpers
|
|
20
|
+
stay synchronous. There is no synchronous compatibility layer: a call
|
|
21
|
+
without `await` returns a coroutine and does nothing.
|
|
22
|
+
- Each device keeps one UDP endpoint for its lifetime (the previous
|
|
23
|
+
version opened a socket per call) and serializes requests on it with an
|
|
24
|
+
`asyncio.Lock`. The old code declared a lock but never acquired it.
|
|
25
|
+
`async with device:` or `await device.aclose()` releases the endpoint;
|
|
26
|
+
it reopens on the next call.
|
|
27
|
+
- When a device reports that the session key has expired, the library
|
|
28
|
+
re-authenticates once and repeats the request. Callers no longer need
|
|
29
|
+
their own re-auth loop.
|
|
30
|
+
- Retry and timeout behaviour is unchanged: a request is repeated every
|
|
31
|
+
second until `timeout` elapses, then `NetworkTimeoutError` is raised.
|
|
32
|
+
- `dooya.set_percentage_and_wait` sleeps with `asyncio.sleep`.
|
|
33
|
+
- The CLI tools run their body under `asyncio.run`. `broadlink_cli
|
|
34
|
+
--learn` and `--rflearn` use `capture()` / `capture_rf()`, so a learning
|
|
35
|
+
session no longer goes deaf when the device times out partway through;
|
|
36
|
+
`--window` sets how long to listen, `--keep` prints every code heard, and
|
|
37
|
+
`--send --durations --repeat N` sets the repeat count. The CLI README's
|
|
38
|
+
`--rfscanlearn` was a typo for `--rflearn` (mjg59/python-broadlink#803,
|
|
39
|
+
#830).
|
|
40
|
+
- `pulses_to_data` returns `bytes` (it returned a `bytearray`, against its
|
|
41
|
+
own annotation).
|
|
42
|
+
- Packaging moved to `pyproject.toml`; `setup.py` and the stale
|
|
43
|
+
`requirements.txt` pin are gone. The distribution name is now
|
|
44
|
+
`python-broadlink`; the import name stays `broadlink`. Python 3.13 or
|
|
45
|
+
newer is required.
|
|
46
|
+
- Continuous integration now runs `ruff` and `pytest` on Python 3.13 and
|
|
47
|
+
3.14, and builds the sdist and wheel on every pull request. Releases are
|
|
48
|
+
published to PyPI from version tags using trusted publishing.
|
|
49
|
+
|
|
50
|
+
### Fixed
|
|
51
|
+
|
|
52
|
+
- The IR tick constant used by `pulses_to_data` and `data_to_pulses` is now
|
|
53
|
+
`TICK = 8192 / 269` (about 30.45 us), matching the device's 32768 Hz
|
|
54
|
+
timebase as documented in `protocol.md`. The previous value, 32.84, was
|
|
55
|
+
the inverse ratio applied the wrong way round and compressed IR codes
|
|
56
|
+
built from true microsecond timings by about 7 percent. Codes learned and
|
|
57
|
+
replayed through the same device were unaffected. Verified on an RM4 Pro
|
|
58
|
+
against an independent receiver in both directions.
|
|
59
|
+
(mjg59/python-broadlink#839, #841)
|
|
60
|
+
- `pulses_to_data` rounds each duration to the nearest tick instead of
|
|
61
|
+
truncating, which removes up to one tick of systematic shortening per
|
|
62
|
+
pulse.
|
|
63
|
+
|
|
64
|
+
### Added
|
|
65
|
+
|
|
66
|
+
- `capture()` and `capture_rf()`, async generators that own the arm, poll,
|
|
67
|
+
timeout and re-arm loop of a learning session and yield each signal as a
|
|
68
|
+
`CapturedSignal` (device packet, decoded pulses at the correct tick,
|
|
69
|
+
kind, repeat count, and for RF the carrier frequency). They re-arm on a
|
|
70
|
+
timer, because the device leaves learning mode silently, and after any
|
|
71
|
+
`send_data`, because a transmission ends the session; both intervals and
|
|
72
|
+
the poll cadence were set from a bench on an RM4 Pro. Only one window can
|
|
73
|
+
be open per device. `capture_rf()` (Pro models only) takes the carrier
|
|
74
|
+
frequency directly and falls back to the on-device sweep when it is not
|
|
75
|
+
given.
|
|
76
|
+
- Packet helpers: `pulses_to_data` takes `kind` and `repeat`, `parse_packet`
|
|
77
|
+
is its inverse, and `SignalKind` names the IR, 433 MHz and 315 MHz bands.
|
|
78
|
+
A device's returned RF packet does not always use the canonical type byte
|
|
79
|
+
(an RM4 Pro answers a 433 MHz capture with 0xB1, not 0xB2), so the kind is
|
|
80
|
+
read by band and a capture is tagged from what it armed rather than the
|
|
81
|
+
byte.
|
|
82
|
+
- Devices, carried over from pull requests against the original repository
|
|
83
|
+
with their authors' commits intact: RM Max 0xAF8B (#838, Alexey Masolov);
|
|
84
|
+
RM5 plus 0x5224 with a new `rm5plus` class (#831, Anil Daoud); RM mini 3
|
|
85
|
+
OEM 0xA544 (#823, Bartłomiej Nogaś); RM mini 3 CMCC 0x27C8 (#802,
|
|
86
|
+
shuxin); LB26 R1 0xA517 (#812, techitapart); SP mini 3-AL 0x7D15 (#805,
|
|
87
|
+
bbcbbk); LEDVANCE SMART+ WIFI CEILING TW 24W 0x6498 (#799, Felipe Martins
|
|
88
|
+
Diel).
|
|
89
|
+
- Devices reported in issues against the original repository, added by
|
|
90
|
+
model name to the existing class for that family and not yet confirmed on
|
|
91
|
+
hardware: MP1-1K3S2U 0x4EDA (#816) and SP4 0xA57A (#758). Please open an
|
|
92
|
+
issue if either does not behave.
|
|
93
|
+
- `cryptography` 43 or newer is required, the first release with wheels for
|
|
94
|
+
Python 3.13 (supersedes mjg59/python-broadlink#749).
|
|
95
|
+
- A test suite. The `tests/oracle` package records the exact request bytes
|
|
96
|
+
every public method of every device class sends, and the results it
|
|
97
|
+
decodes from canned responses, so that later changes to the transport
|
|
98
|
+
can be checked byte for byte against the original behavior.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2014 Mike Ryan
|
|
4
|
+
Copyright (c) 2016 Matthew Garrett
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in
|
|
14
|
+
all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
22
|
+
THE SOFTWARE.
|
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: python-broadlink
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Python API for controlling Broadlink devices
|
|
5
|
+
Author: DAB-LABS
|
|
6
|
+
Author-email: Matthew Garrett <mjg59@srcf.ucam.org>
|
|
7
|
+
Maintainer: DAB-LABS
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
Project-URL: Homepage, https://github.com/DAB-LABS/python-broadlink
|
|
10
|
+
Project-URL: Repository, https://github.com/DAB-LABS/python-broadlink
|
|
11
|
+
Project-URL: Issues, https://github.com/DAB-LABS/python-broadlink/issues
|
|
12
|
+
Project-URL: Changelog, https://github.com/DAB-LABS/python-broadlink/blob/master/CHANGELOG.md
|
|
13
|
+
Project-URL: Upstream, https://github.com/mjg59/python-broadlink
|
|
14
|
+
Keywords: broadlink,infrared,rf,home-assistant,rm4,rm-pro
|
|
15
|
+
Classifier: Development Status :: 4 - Beta
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
+
Classifier: Topic :: Home Automation
|
|
22
|
+
Requires-Python: >=3.13
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Requires-Dist: cryptography>=43
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
28
|
+
Requires-Dist: ruff>=0.5; extra == "dev"
|
|
29
|
+
Requires-Dist: build; extra == "dev"
|
|
30
|
+
Dynamic: license-file
|
|
31
|
+
|
|
32
|
+
# python-broadlink
|
|
33
|
+
|
|
34
|
+
A Python module and CLI for controlling Broadlink devices locally.
|
|
35
|
+
|
|
36
|
+
> **About this fork.** This repository is a maintained fork of
|
|
37
|
+
> [mjg59/python-broadlink](https://github.com/mjg59/python-broadlink), which
|
|
38
|
+
> has not accepted changes since 2024. It exists so that Home Assistant's
|
|
39
|
+
> Broadlink integration has a library that can take fixes and new devices.
|
|
40
|
+
> The distribution on PyPI is `python-broadlink`; the import name stays
|
|
41
|
+
> `broadlink`. The first release corrects the IR timing constant reported in
|
|
42
|
+
> upstream [#839](https://github.com/mjg59/python-broadlink/issues/839)
|
|
43
|
+
> (fix in [#841](https://github.com/mjg59/python-broadlink/pull/841)) and
|
|
44
|
+
> adds the devices waiting in upstream's pull request queue, including the
|
|
45
|
+
> RM Max and RM5 Plus. Version 1.0 is asynchronous and adds `capture()`;
|
|
46
|
+
> see `CHANGELOG.md`. Upstream's credit and MIT license are preserved.
|
|
47
|
+
|
|
48
|
+
## Version 1.0 is asynchronous
|
|
49
|
+
|
|
50
|
+
Every call that reaches a device is a coroutine and must be awaited. This
|
|
51
|
+
is the whole change from the original library's API; method names,
|
|
52
|
+
arguments and return values are the same.
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
import asyncio
|
|
56
|
+
import broadlink
|
|
57
|
+
|
|
58
|
+
async def main():
|
|
59
|
+
devices = await broadlink.discover(timeout=5)
|
|
60
|
+
device = devices[0]
|
|
61
|
+
await device.auth()
|
|
62
|
+
print(await device.check_sensors())
|
|
63
|
+
|
|
64
|
+
asyncio.run(main())
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Calling a device method without `await` returns a coroutine object and
|
|
68
|
+
sends nothing; Python prints a `RuntimeWarning: coroutine ... was never
|
|
69
|
+
awaited` when it is garbage collected. If you need the old synchronous
|
|
70
|
+
behaviour, pin the original distribution (`broadlink==0.19.0`) instead.
|
|
71
|
+
|
|
72
|
+
The following devices are supported:
|
|
73
|
+
|
|
74
|
+
- **Universal remotes**: RM home, RM mini 3, RM plus, RM pro, RM pro+, RM4 mini, RM4 pro, RM4C mini, RM4S, RM4 TV mate, RM Max, RM5 plus
|
|
75
|
+
- **Smart plugs**: SP mini, SP mini 3, SP mini+, SP1, SP2, SP2-BR, SP2-CL, SP2-IN, SP2-UK, SP3, SP3-EU, SP3S-EU, SP3S-US, SP4L-AU, SP4L-EU, SP4L-UK, SP4M, SP4M-US, SP mini 3-AL, Ankuoo NEO, Ankuoo NEO PRO, Efergy Ego, BG AHC/U-01
|
|
76
|
+
- **Switches**: MCB1, SC1, SCB1E, SCB2
|
|
77
|
+
- **Outlets**: BG 800, BG 900
|
|
78
|
+
- **Power strips**: MP1-1K3S2U, MP1-1K4S, MP2
|
|
79
|
+
- **Environment sensors**: A1
|
|
80
|
+
- **Alarm kits**: S1C, S2KIT
|
|
81
|
+
- **Light bulbs**: LB1, LB26 R1, LB27 R1, SB800TD, LEDVANCE SMART+ WIFI CEILING TW 24W
|
|
82
|
+
- **Curtain motors**: Dooya DT360E-45/20
|
|
83
|
+
- **Thermostats**: Hysen HY02B05H
|
|
84
|
+
- **Hubs**: S3
|
|
85
|
+
|
|
86
|
+
## Installation
|
|
87
|
+
|
|
88
|
+
Use pip3 to install the latest version of this module.
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
pip3 install python-broadlink
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
If the original `broadlink` distribution is also installed in the same
|
|
95
|
+
environment, remove it first (`pip3 uninstall broadlink`); both provide the
|
|
96
|
+
`broadlink` package.
|
|
97
|
+
|
|
98
|
+
## Basic functions
|
|
99
|
+
|
|
100
|
+
The examples below are written as they would appear inside an `async def`
|
|
101
|
+
function run with `asyncio.run(...)`, as in the snippet above. To try them
|
|
102
|
+
interactively, start Python with `python3 -m asyncio`, which gives you a
|
|
103
|
+
prompt where `await` works at the top level.
|
|
104
|
+
|
|
105
|
+
```python3
|
|
106
|
+
import broadlink
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Now let's try some functions...
|
|
110
|
+
|
|
111
|
+
### Setup
|
|
112
|
+
|
|
113
|
+
In order to control the device, you need to connect it to your local network. If you have already configured the device with the Broadlink app, this step is not necessary.
|
|
114
|
+
|
|
115
|
+
1. Put the device into AP Mode.
|
|
116
|
+
- Long press the reset button until the blue LED is blinking quickly.
|
|
117
|
+
- Long press again until blue LED is blinking slowly.
|
|
118
|
+
- Manually connect to the WiFi SSID named BroadlinkProv.
|
|
119
|
+
2. Connect the device to your local network with the setup function.
|
|
120
|
+
```python3
|
|
121
|
+
await broadlink.setup('myssid', 'mynetworkpass', 3)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Security mode options are (0 = none, 1 = WEP, 2 = WPA1, 3 = WPA2, 4 = WPA1/2)
|
|
125
|
+
|
|
126
|
+
#### Advanced options
|
|
127
|
+
|
|
128
|
+
You may need to specify a broadcast address if setup is not working.
|
|
129
|
+
```python3
|
|
130
|
+
await broadlink.setup('myssid', 'mynetworkpass', 3, ip_address='192.168.0.255')
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Discovery
|
|
134
|
+
|
|
135
|
+
Use this function to discover devices:
|
|
136
|
+
|
|
137
|
+
```python3
|
|
138
|
+
devices = await broadlink.discover()
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
#### Advanced options
|
|
142
|
+
You may need to specify `local_ip_address` or `discover_ip_address` if discovery does not return any devices.
|
|
143
|
+
|
|
144
|
+
Using the IP address of your local machine:
|
|
145
|
+
```python3
|
|
146
|
+
devices = await broadlink.discover(local_ip_address='192.168.0.100')
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Using the broadcast address of your subnet:
|
|
150
|
+
```python3
|
|
151
|
+
devices = await broadlink.discover(discover_ip_address='192.168.0.255')
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
If the device is locked, it may not be discoverable with broadcast. In such cases, you can use the unicast version `broadlink.hello()` for direct discovery:
|
|
155
|
+
```python3
|
|
156
|
+
device = await broadlink.hello('192.168.0.16')
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
If you are a perfomance freak, use `broadlink.xdiscover()` to create devices instantly:
|
|
160
|
+
```python3
|
|
161
|
+
async for device in broadlink.xdiscover():
|
|
162
|
+
print(device) # Example action. Do whatever you want here.
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Authentication
|
|
166
|
+
After discovering the device, call the `auth()` method to obtain the authentication key required for further communication:
|
|
167
|
+
```python3
|
|
168
|
+
await device.auth()
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
The next steps depend on the type of device you want to control.
|
|
172
|
+
|
|
173
|
+
## Universal remotes
|
|
174
|
+
|
|
175
|
+
### Learning IR codes
|
|
176
|
+
|
|
177
|
+
Learning IR codes takes place in three steps.
|
|
178
|
+
|
|
179
|
+
1. Enter learning mode:
|
|
180
|
+
```python3
|
|
181
|
+
await device.enter_learning()
|
|
182
|
+
```
|
|
183
|
+
2. When the LED blinks, point the remote at the Broadlink device and press the button you want to learn.
|
|
184
|
+
3. Get the IR packet.
|
|
185
|
+
```python3
|
|
186
|
+
packet = await device.check_data()
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Learning RF codes
|
|
190
|
+
|
|
191
|
+
Learning RF codes takes place in six steps.
|
|
192
|
+
|
|
193
|
+
1. Sweep the frequency:
|
|
194
|
+
```python3
|
|
195
|
+
await device.sweep_frequency()
|
|
196
|
+
```
|
|
197
|
+
2. When the LED blinks, point the remote at the Broadlink device for the first time and long press the button you want to learn.
|
|
198
|
+
3. Check if the frequency was successfully identified:
|
|
199
|
+
```python3
|
|
200
|
+
ok, frequency = await device.check_frequency()
|
|
201
|
+
if ok:
|
|
202
|
+
print(f'Frequency found: {frequency} MHz')
|
|
203
|
+
```
|
|
204
|
+
4. Enter learning mode:
|
|
205
|
+
```python3
|
|
206
|
+
await device.find_rf_packet()
|
|
207
|
+
```
|
|
208
|
+
5. When the LED blinks, point the remote at the Broadlink device for the second time and short press the button you want to learn.
|
|
209
|
+
6. Get the RF packet:
|
|
210
|
+
```python3
|
|
211
|
+
packet = await device.check_data()
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
#### Notes
|
|
215
|
+
|
|
216
|
+
Universal remotes with product id 0x2712 use the same method for learning IR and RF codes. They don't need to sweep frequency. Just call `device.enter_learning()` and `device.check_data()`.
|
|
217
|
+
|
|
218
|
+
### Canceling learning
|
|
219
|
+
|
|
220
|
+
You can exit the learning mode in the middle of the process by calling this method:
|
|
221
|
+
```python3
|
|
222
|
+
await device.cancel_sweep_frequency()
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Capturing signals
|
|
226
|
+
|
|
227
|
+
`capture()` wraps the arm, poll, timeout and re-arm dance above into one
|
|
228
|
+
async generator that yields each signal it hears as a `CapturedSignal`:
|
|
229
|
+
|
|
230
|
+
```python3
|
|
231
|
+
from contextlib import aclosing
|
|
232
|
+
|
|
233
|
+
async with aclosing(device.capture(window=30)) as signals:
|
|
234
|
+
async for signal in signals:
|
|
235
|
+
print(signal.kind, len(signal.pulses), "pulses")
|
|
236
|
+
await other_device.send_data(signal.packet)
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
By default the window closes after the first signal. Pass
|
|
240
|
+
`stop_after_first=False` to keep it open for the whole `window` (in seconds;
|
|
241
|
+
`window=0` runs until the generator is closed), re-arming after each signal
|
|
242
|
+
because the device holds only one code per learning session. A universal
|
|
243
|
+
remote has a single receiver, so only one capture window can be open on a
|
|
244
|
+
device at a time.
|
|
245
|
+
|
|
246
|
+
`CapturedSignal` carries the device's own `packet` bytes (ready for
|
|
247
|
+
`send_data`), the decoded `pulses` in microseconds at the correct tick, the
|
|
248
|
+
`kind` (`SignalKind.IR`, `RF_433` or `RF_315`), the `repeat` count, and for
|
|
249
|
+
RF the `frequency_mhz` the packet itself does not record.
|
|
250
|
+
|
|
251
|
+
RF works the same way on the Pro models, with the carrier as the one extra
|
|
252
|
+
input:
|
|
253
|
+
|
|
254
|
+
```python3
|
|
255
|
+
async with aclosing(device.capture_rf(window=30, frequency=433.92)) as signals:
|
|
256
|
+
async for signal in signals:
|
|
257
|
+
...
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
Pass `frequency` whenever you know it. Without it the device first sweeps
|
|
261
|
+
for the carrier while you hold a button down, then learns the code from a
|
|
262
|
+
fresh press; the sweep is unreliable on some firmware and can report a
|
|
263
|
+
carrier it never really locked, so the known-frequency path is preferred.
|
|
264
|
+
|
|
265
|
+
### Sending IR/RF packets
|
|
266
|
+
```python3
|
|
267
|
+
await device.send_data(packet)
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Fetching sensor data
|
|
271
|
+
```python3
|
|
272
|
+
data = await device.check_sensors()
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
## Switches
|
|
276
|
+
|
|
277
|
+
### Setting power state
|
|
278
|
+
```python3
|
|
279
|
+
await device.set_power(True)
|
|
280
|
+
await device.set_power(False)
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### Checking power state
|
|
284
|
+
```python3
|
|
285
|
+
state = await device.check_power()
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### Checking energy consumption
|
|
289
|
+
```python3
|
|
290
|
+
state = await device.get_energy()
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
## Power strips
|
|
294
|
+
|
|
295
|
+
### Setting power state
|
|
296
|
+
```python3
|
|
297
|
+
await device.set_power(1, True) # Example socket. It could be 2 or 3.
|
|
298
|
+
await device.set_power(1, False)
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### Checking power state
|
|
302
|
+
```python3
|
|
303
|
+
state = await device.check_power()
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
## Light bulbs
|
|
307
|
+
|
|
308
|
+
### Fetching data
|
|
309
|
+
```python3
|
|
310
|
+
state = await device.get_state()
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### Setting state attributes
|
|
314
|
+
```python3
|
|
315
|
+
await devices[0].set_state(pwr=0)
|
|
316
|
+
await devices[0].set_state(pwr=1)
|
|
317
|
+
await devices[0].set_state(brightness=75)
|
|
318
|
+
await devices[0].set_state(bulb_colormode=0)
|
|
319
|
+
await devices[0].set_state(blue=255)
|
|
320
|
+
await devices[0].set_state(red=0)
|
|
321
|
+
await devices[0].set_state(green=128)
|
|
322
|
+
await devices[0].set_state(bulb_colormode=1)
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
## Environment sensors
|
|
326
|
+
|
|
327
|
+
### Fetching sensor data
|
|
328
|
+
```python3
|
|
329
|
+
data = await device.check_sensors()
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
## Hubs
|
|
333
|
+
|
|
334
|
+
### Discovering subdevices
|
|
335
|
+
```python3
|
|
336
|
+
await device.get_subdevices()
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
### Fetching data
|
|
340
|
+
Use the DID obtained from get_subdevices() for the input parameter to query specific sub-device.
|
|
341
|
+
|
|
342
|
+
```python3
|
|
343
|
+
await device.get_state(did="00000000000000000000a043b0d06963")
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
### Setting state attributes
|
|
347
|
+
The parameters depend on the type of subdevice that is being controlled. In this example, we are controlling LC-1 switches:
|
|
348
|
+
|
|
349
|
+
#### Turn on
|
|
350
|
+
```python3
|
|
351
|
+
await device.set_state(did="00000000000000000000a043b0d0783a", pwr=1)
|
|
352
|
+
await device.set_state(did="00000000000000000000a043b0d0783a", pwr1=1)
|
|
353
|
+
await device.set_state(did="00000000000000000000a043b0d0783a", pwr2=1)
|
|
354
|
+
```
|
|
355
|
+
#### Turn off
|
|
356
|
+
```python3
|
|
357
|
+
await device.set_state(did="00000000000000000000a043b0d0783a", pwr=0)
|
|
358
|
+
await device.set_state(did="00000000000000000000a043b0d0783a", pwr1=0)
|
|
359
|
+
await device.set_state(did="00000000000000000000a043b0d0783a", pwr2=0)
|
|
360
|
+
```
|