ruuvitag-sensor 2.2.0__tar.gz → 2.3.1__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.

Potentially problematic release.


This version of ruuvitag-sensor might be problematic. Click here for more details.

Files changed (33) hide show
  1. {ruuvitag_sensor-2.2.0 → ruuvitag_sensor-2.3.1}/PKG-INFO +95 -69
  2. {ruuvitag_sensor-2.2.0 → ruuvitag_sensor-2.3.1}/README.md +91 -67
  3. {ruuvitag_sensor-2.2.0 → ruuvitag_sensor-2.3.1}/pyproject.toml +5 -3
  4. {ruuvitag_sensor-2.2.0 → ruuvitag_sensor-2.3.1}/ruuvitag_sensor/adapters/__init__.py +40 -10
  5. {ruuvitag_sensor-2.2.0 → ruuvitag_sensor-2.3.1}/ruuvitag_sensor/adapters/bleak_ble.py +17 -5
  6. {ruuvitag_sensor-2.2.0 → ruuvitag_sensor-2.3.1}/ruuvitag_sensor/adapters/bleson.py +4 -0
  7. ruuvitag_sensor-2.3.1/ruuvitag_sensor/adapters/utils.py +2 -0
  8. {ruuvitag_sensor-2.2.0 → ruuvitag_sensor-2.3.1}/ruuvitag_sensor/data_formats.py +1 -1
  9. {ruuvitag_sensor-2.2.0 → ruuvitag_sensor-2.3.1}/ruuvitag_sensor/ruuvi.py +20 -6
  10. {ruuvitag_sensor-2.2.0 → ruuvitag_sensor-2.3.1}/ruuvitag_sensor.egg-info/PKG-INFO +96 -70
  11. {ruuvitag_sensor-2.2.0 → ruuvitag_sensor-2.3.1}/ruuvitag_sensor.egg-info/SOURCES.txt +1 -0
  12. {ruuvitag_sensor-2.2.0 → ruuvitag_sensor-2.3.1}/ruuvitag_sensor.egg-info/requires.txt +4 -1
  13. {ruuvitag_sensor-2.2.0 → ruuvitag_sensor-2.3.1}/LICENSE +0 -0
  14. {ruuvitag_sensor-2.2.0 → ruuvitag_sensor-2.3.1}/MANIFEST.in +0 -0
  15. {ruuvitag_sensor-2.2.0 → ruuvitag_sensor-2.3.1}/ruuvitag_sensor/__init__.py +0 -0
  16. {ruuvitag_sensor-2.2.0 → ruuvitag_sensor-2.3.1}/ruuvitag_sensor/__main__.py +0 -0
  17. {ruuvitag_sensor-2.2.0 → ruuvitag_sensor-2.3.1}/ruuvitag_sensor/adapters/development/__init__.py +0 -0
  18. {ruuvitag_sensor-2.2.0 → ruuvitag_sensor-2.3.1}/ruuvitag_sensor/adapters/development/dev_bleak_scanner.py +0 -0
  19. {ruuvitag_sensor-2.2.0 → ruuvitag_sensor-2.3.1}/ruuvitag_sensor/adapters/dummy.py +0 -0
  20. {ruuvitag_sensor-2.2.0 → ruuvitag_sensor-2.3.1}/ruuvitag_sensor/adapters/nix_hci.py +0 -0
  21. {ruuvitag_sensor-2.2.0 → ruuvitag_sensor-2.3.1}/ruuvitag_sensor/adapters/nix_hci_file.py +0 -0
  22. {ruuvitag_sensor-2.2.0 → ruuvitag_sensor-2.3.1}/ruuvitag_sensor/decoder.py +0 -0
  23. {ruuvitag_sensor-2.2.0 → ruuvitag_sensor-2.3.1}/ruuvitag_sensor/log.py +0 -0
  24. {ruuvitag_sensor-2.2.0 → ruuvitag_sensor-2.3.1}/ruuvitag_sensor/ruuvi_rx.py +0 -0
  25. {ruuvitag_sensor-2.2.0 → ruuvitag_sensor-2.3.1}/ruuvitag_sensor/ruuvi_types.py +0 -0
  26. {ruuvitag_sensor-2.2.0 → ruuvitag_sensor-2.3.1}/ruuvitag_sensor/ruuvitag.py +0 -0
  27. {ruuvitag_sensor-2.2.0 → ruuvitag_sensor-2.3.1}/ruuvitag_sensor.egg-info/dependency_links.txt +0 -0
  28. {ruuvitag_sensor-2.2.0 → ruuvitag_sensor-2.3.1}/ruuvitag_sensor.egg-info/top_level.txt +0 -0
  29. {ruuvitag_sensor-2.2.0 → ruuvitag_sensor-2.3.1}/setup.cfg +0 -0
  30. {ruuvitag_sensor-2.2.0 → ruuvitag_sensor-2.3.1}/tests/test_data_formats.py +0 -0
  31. {ruuvitag_sensor-2.2.0 → ruuvitag_sensor-2.3.1}/tests/test_decoder.py +0 -0
  32. {ruuvitag_sensor-2.2.0 → ruuvitag_sensor-2.3.1}/tests/test_ruuvitag_sensor.py +0 -0
  33. {ruuvitag_sensor-2.2.0 → ruuvitag_sensor-2.3.1}/tests/test_ruuvitag_sensor_async.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ruuvitag_sensor
3
- Version: 2.2.0
3
+ Version: 2.3.1
4
4
  Summary: Find RuuviTag sensors and get decoded data from selected sensors
5
5
  Author-email: Tomi Tuhkanen <tomi.tuhkanen@iki.fi>
6
6
  License: MIT License
@@ -36,6 +36,7 @@ Classifier: Programming Language :: Python :: 3.8
36
36
  Classifier: Programming Language :: Python :: 3.9
37
37
  Classifier: Programming Language :: Python :: 3.10
38
38
  Classifier: Programming Language :: Python :: 3.11
39
+ Classifier: Programming Language :: Python :: 3.12
39
40
  Classifier: License :: OSI Approved :: MIT License
40
41
  Classifier: Operating System :: OS Independent
41
42
  Classifier: Intended Audience :: Developers
@@ -49,6 +50,7 @@ Requires-Dist: reactivex
49
50
  Requires-Dist: ptyprocess; platform_system == "Linux"
50
51
  Requires-Dist: mypy-extensions; python_version < "3.8"
51
52
  Requires-Dist: importlib-metadata<4.3,>=1.1.0; python_version < "3.8"
53
+ Requires-Dist: bleak; platform_system == "Windows" or platform_system == "Darwin"
52
54
  Provides-Extra: dev
53
55
  Requires-Dist: pytest; extra == "dev"
54
56
  Requires-Dist: pytest-asyncio; extra == "dev"
@@ -56,7 +58,7 @@ Requires-Dist: flake8-pyproject; extra == "dev"
56
58
  Requires-Dist: pylint; extra == "dev"
57
59
  Requires-Dist: mypy; extra == "dev"
58
60
  Requires-Dist: isort; extra == "dev"
59
- Requires-Dist: black; extra == "dev"
61
+ Requires-Dist: black==23.3.0; extra == "dev"
60
62
 
61
63
  RuuviTag Sensor Python Package
62
64
  ---------------------------------
@@ -65,7 +67,7 @@ RuuviTag Sensor Python Package
65
67
  [![License](https://img.shields.io/pypi/l/ruuvitag-sensor.svg)](https://pypi.python.org/pypi/ruuvitag-sensor/)
66
68
  [![PyPI version](https://img.shields.io/pypi/v/ruuvitag-sensor.svg)](https://pypi.python.org/pypi/ruuvitag_-sensor)
67
69
  [![PyPI downloads](https://img.shields.io/pypi/dm/ruuvitag-sensor.svg)](https://pypistats.org/packages/ruuvitag-sensor)
68
- [![Python versions](https://img.shields.io/pypi/pyversions/ruuvitag-sensor.svg)](https://pypi.python.org/pypi/ruuvitag-sensor/)
70
+ [![Python versions](https://img.shields.io/badge/python-3.7+-blue.svg)](https://pypi.python.org/pypi/ruuvitag-sensor/)
69
71
 
70
72
  `ruuvitag-sensor` is a Python package for communicating with [RuuviTag BLE Sensor](https://ruuvi.com/) and for decoding measurement data from broadcasted BLE data.
71
73
 
@@ -73,18 +75,25 @@ RuuviTag Sensor Python Package
73
75
 
74
76
  ## Requirements
75
77
 
76
- * RuuviTag with Weather Station firmware
77
- * Setup [guide](https://lab.ruuvi.com/start/)
78
- * Supports [Data Format 2, 3, 4 and 5](https://github.com/ruuvi/ruuvi-sensor-protocols)
78
+ * RuuviTag sensor
79
+ * Setup [guide](https://ruuvi.com/quick-start/)
80
+ * Supports [Data Format 2, 3, 4 and 5](https://docs.ruuvi.com/)
79
81
  * __NOTE:__ Data Formats 2, 3 and 4 are _deprecated_ and should not be used
82
+ * [Bleak](https://github.com/hbldh/bleak) communication module (Windows, macOS and Linux)
83
+ * Default adapter for Windows and macOS
84
+ * Bleak supports
85
+ * [Async-methods](#usage)
86
+ * [Observable streams](#usage)
87
+ * [Install guide](#Bleak)
80
88
  * Bluez (Linux-only)
81
- * [BlueZ install guide](#BlueZ)
82
- * __BETA:__ Cross-platform BLE adapters
83
- * [Bleak](https://github.com/hbldh/bleak) communication module
84
- * Bleak only supports async methods (work in progress)
85
- * [Bleak install guide](#Bleak)
86
- * [Bleson](https://github.com/TheCellule/python-bleson) communication module
87
- * [Bleson install guide](#Bleson)
89
+ * Default adapter for Linux
90
+ * Bluez supports
91
+ * [Sync-methods](#usage)
92
+ * [Observable streams](#usage)
93
+ * [Install guide](#BlueZ)
94
+ * __NOTE:__ The BlueZ-adapter implementation uses deprecated BlueZ tools that are no longer supported.
95
+ * Even though BlueZ is still the default adapter, it is recommended to use the Bleak-communication adapter with Linux. Bleak will be the default adapter for Linux in the next major release.
96
+ * Bleson-adapter supports sync-methods, but please be aware that it is not fully supported due to the alpha release status of the Bleson communication module. See [Bleson](#Bleson) for more information.
88
97
  * Python 3.7+
89
98
  * For Python 2.x or <3.7 support, check [installation instructions](#python-2x-and-36-and-below) for an older version
90
99
 
@@ -113,8 +122,8 @@ Full installation guide for [Raspberry PI & Raspbian](https://github.com/ttu/ruu
113
122
 
114
123
  The package provides 3 ways to fetch data from sensors:
115
124
 
116
- 1. Synchronously with callback
117
- 2. Asynchronously with async/await (BETA)
125
+ 1. Asynchronously with async/await
126
+ 2. Synchronously with callback
118
127
  3. Observable streams with ReactiveX
119
128
 
120
129
  RuuviTag sensors can be identified using MAC addresses. Methods return a tuple with MAC and sensor data payload.
@@ -123,56 +132,11 @@ RuuviTag sensors can be identified using MAC addresses. Methods return a tuple w
123
132
  ('D2:A3:6E:C8:E0:25', {'data_format': 5, 'humidity': 47.62, 'temperature': 23.58, 'pressure': 1023.68, 'acceleration': 993.2331045630729, 'acceleration_x': -48, 'acceleration_y': -12, 'acceleration_z': 992, 'tx_power': 4, 'battery': 2197, 'movement_counter': 0, 'measurement_sequence_number': 88, 'mac': 'd2a36ec8e025', 'rssi': -80})
124
133
  ```
125
134
 
126
- ### 1. Get sensor data synchronously with callback
135
+ ### 1. Get sensor data asynchronously with async/await
127
136
 
128
- `get_data` calls the callback whenever a RuuviTag sensor broadcasts data. This method is the preferred way to use the library.
137
+ __NOTE:__ Asynchronous functionality works only with `Bleak`-adapter.
129
138
 
130
- ```python
131
- from ruuvitag_sensor.ruuvi import RuuviTagSensor
132
-
133
-
134
- def handle_data(found_data):
135
- print(f"MAC {found_data[0]}")
136
- print(f"Data {found_data[1]}")
137
-
138
-
139
- if __name__ == "__main__":
140
- RuuviTagSensor.get_data(handle_data)
141
- ```
142
-
143
- The line `if __name__ == "__main__":` is required on Windows and macOS due to the way the `multiprocessing` library works. It is not required on Linux, but it is recommended. It is omitted from the rest of the examples below.
144
-
145
- The optional list of MACs and run flag can be passed to the `get_data` function. The callback is called only for MACs in the list and setting the run flag to false will stop execution. If the run flag is not passed, the function will execute forever.
146
-
147
- ```python
148
- from ruuvitag_sensor.ruuvi import RuuviTagSensor, RunFlag
149
-
150
- counter = 10
151
- # RunFlag for stopping execution at desired time
152
- run_flag = RunFlag()
153
-
154
-
155
- def handle_data(found_data):
156
- print(f"MAC: {found_data[0]}")
157
- print(f"Data: {found_data[1]}")
158
-
159
- global counter
160
- counter = counter - 1
161
- if counter < 0:
162
- run_flag.running = False
163
-
164
-
165
- # List of MACs of sensors which will execute callback function
166
- macs = ["AA:2C:6A:1E:59:3D", "CC:2C:6A:1E:59:3D"]
167
-
168
- RuuviTagSensor.get_data(handle_data, macs, run_flag)
169
- ```
170
-
171
- ### 2. Get sensor data asynchronously
172
-
173
- __NOTE:__ Asynchronous functionality is currently in beta state and works only with `Bleak`-adapter.
174
-
175
- `get_data_async` returns the data whenever a RuuviTag sensor broadcasts data. `get_data_async` will exceute until iterator is exited. This method is the preferred way to use the library with async-adapter.
139
+ `get_data_async` returns the data whenever a RuuviTag sensor broadcasts data. `get_data_async` will execute until iterator is exited. This method is the preferred way to use the library with _Bleak_.
176
140
 
177
141
  ```py
178
142
  import asyncio
@@ -213,6 +177,53 @@ if __name__ == "__main__":
213
177
  asyncio.get_event_loop().run_until_complete(main())
214
178
  ```
215
179
 
180
+ The line `if __name__ == "__main__":` is required on Windows and macOS due to the way the `multiprocessing` library works. It is not required on Linux, but it is recommended. It is omitted from the rest of the examples below.
181
+
182
+ ### 2. Get sensor data synchronously with callback
183
+
184
+ __NOTE:__ Asynchronous functionality works only with `BlueZ`-adapter.
185
+
186
+ `get_data` calls the callback whenever a RuuviTag sensor broadcasts data. This method is the preferred way to use the library with _BlueZ_.
187
+
188
+ ```python
189
+ from ruuvitag_sensor.ruuvi import RuuviTagSensor
190
+
191
+
192
+ def handle_data(found_data):
193
+ print(f"MAC {found_data[0]}")
194
+ print(f"Data {found_data[1]}")
195
+
196
+
197
+ if __name__ == "__main__":
198
+ RuuviTagSensor.get_data(handle_data)
199
+ ```
200
+
201
+ The optional list of MACs and run flag can be passed to the `get_data` function. The callback is called only for MACs in the list and setting the run flag to false will stop execution. If the run flag is not passed, the function will execute forever.
202
+
203
+ ```python
204
+ from ruuvitag_sensor.ruuvi import RuuviTagSensor, RunFlag
205
+
206
+ counter = 10
207
+ # RunFlag for stopping execution at desired time
208
+ run_flag = RunFlag()
209
+
210
+
211
+ def handle_data(found_data):
212
+ print(f"MAC: {found_data[0]}")
213
+ print(f"Data: {found_data[1]}")
214
+
215
+ global counter
216
+ counter = counter - 1
217
+ if counter < 0:
218
+ run_flag.running = False
219
+
220
+
221
+ # List of MACs of sensors which will execute callback function
222
+ macs = ["AA:2C:6A:1E:59:3D", "CC:2C:6A:1E:59:3D"]
223
+
224
+ RuuviTagSensor.get_data(handle_data, macs, run_flag)
225
+ ```
226
+
216
227
  ### 3. Get sensor data with observable streams (ReactiveX / RxPY)
217
228
 
218
229
  `RuuviTagReactive` is a reactive wrapper and background process for RuuviTagSensor `get_data`. An optional MAC address list can be passed on the initializer and execution can be stopped with the stop function.
@@ -240,7 +251,7 @@ ruuvi_rx.get_subject().pipe(
240
251
  ops.distinct_until_changed()
241
252
  ).subscribe(lambda x: print(f"Temperature changed: {x}"))
242
253
 
243
- # Close all connections and stop bluetooth communication
254
+ # Close all connections and stop Bluetooth communication
244
255
  ruuvi_rx.stop()
245
256
  ```
246
257
 
@@ -306,7 +317,7 @@ RuuviTagSensor.find_ruuvitags()
306
317
 
307
318
  ### Using different Bluetooth device
308
319
 
309
- If you have multiple Bluetooth devices installed, a device to be used might not be the default (Linux: hci0). The device can be passed with a `bt_device`-parameter.
320
+ If you have multiple Bluetooth devices installed, a device to be used might not be the default (Linux: `hci0`). The device can be passed with a `bt_device`-parameter.
310
321
 
311
322
  ```python
312
323
  from ruuvitag_sensor.ruuvi import RuuviTagSensor
@@ -476,6 +487,18 @@ $ sudo apt-get install bluez bluez-hcidump
476
487
 
477
488
  `ruuvitag-sensor` package uses internally _hciconfig_, _hcitool_ and _hcidump_. These tools are deprecated. In case tools are missing, an older version of BlueZ is required ([Issue](https://github.com/ttu/ruuvitag-sensor/issues/31))
478
489
 
490
+ If you wish to test the library on Windows or macOS, enable it with `RUUVI_BLE_ADAPTER` environment variable.
491
+
492
+ ```sh
493
+ $ export RUUVI_BLE_ADAPTER="bluez"
494
+ ```
495
+
496
+ And install ptyprocess.
497
+
498
+ ```sh
499
+ python -m pip install ptyprocess
500
+ ```
501
+
479
502
  #### BlueZ limitations
480
503
 
481
504
  `ruuvitag-sensor` package uses BlueZ to listen to broadcasted BL information (uses _hciconf_, _hcitool_, _hcidump_). Implementation does not handle well all unexpected errors or changes, e.g. when the adapter is busy, rebooted or powered down.
@@ -484,7 +507,9 @@ In case of errors, the application tries to exit immediately, so it can be autom
484
507
 
485
508
  ### Bleak
486
509
 
487
- Bleak is not installed automatically with `ruuvitag-sensor` package. Install it manually from PyPI.
510
+ On Windows and macOS Bleak is installed and used automatically with `ruuvitag-sensor` package.
511
+
512
+ On Linux install it manually from PyPI and enable it with `RUUVI_BLE_ADAPTER` environment variable.
488
513
 
489
514
  ```sh
490
515
  $ python -m pip install bleak
@@ -495,7 +520,8 @@ Add environment variable RUUVI_BLE_ADAPTER with value Bleak. E.g.
495
520
  ```sh
496
521
  $ export RUUVI_BLE_ADAPTER="bleak"
497
522
  ```
498
- Or use `os.environ`. NOTE: this must be set before importing `ruuvitag_sensor`.
523
+
524
+ Or use `os.environ`. __NOTE:__ this must be set before importing `ruuvitag_sensor`.
499
525
 
500
526
  ```py
501
527
  import os
@@ -529,12 +555,12 @@ Bleak-adapter has a development-time generator for dummy data, which can be usef
529
555
 
530
556
  Current state and known bugs in [issue #78](https://github.com/ttu/ruuvitag-sensor/issues/78).
531
557
 
532
- Bleson works with Linux, macOS and partially with Windows.
558
+ [Bleson](https://github.com/TheCellule/python-bleson) works with Linux, macOS and partially with Windows.
533
559
 
534
560
  Bleson is not installed automatically with `ruuvitag-sensor` package. Install it manually from GitHub.
535
561
 
536
562
  ```sh
537
- $ pip install git+https://github.com/TheCellule/python-bleson
563
+ $ python -m pip install git+https://github.com/TheCellule/python-bleson
538
564
  ```
539
565
 
540
566
  Add environment variable `RUUVI_BLE_ADAPTER` with value `bleson`. E.g.
@@ -5,7 +5,7 @@ RuuviTag Sensor Python Package
5
5
  [![License](https://img.shields.io/pypi/l/ruuvitag-sensor.svg)](https://pypi.python.org/pypi/ruuvitag-sensor/)
6
6
  [![PyPI version](https://img.shields.io/pypi/v/ruuvitag-sensor.svg)](https://pypi.python.org/pypi/ruuvitag_-sensor)
7
7
  [![PyPI downloads](https://img.shields.io/pypi/dm/ruuvitag-sensor.svg)](https://pypistats.org/packages/ruuvitag-sensor)
8
- [![Python versions](https://img.shields.io/pypi/pyversions/ruuvitag-sensor.svg)](https://pypi.python.org/pypi/ruuvitag-sensor/)
8
+ [![Python versions](https://img.shields.io/badge/python-3.7+-blue.svg)](https://pypi.python.org/pypi/ruuvitag-sensor/)
9
9
 
10
10
  `ruuvitag-sensor` is a Python package for communicating with [RuuviTag BLE Sensor](https://ruuvi.com/) and for decoding measurement data from broadcasted BLE data.
11
11
 
@@ -13,18 +13,25 @@ RuuviTag Sensor Python Package
13
13
 
14
14
  ## Requirements
15
15
 
16
- * RuuviTag with Weather Station firmware
17
- * Setup [guide](https://lab.ruuvi.com/start/)
18
- * Supports [Data Format 2, 3, 4 and 5](https://github.com/ruuvi/ruuvi-sensor-protocols)
16
+ * RuuviTag sensor
17
+ * Setup [guide](https://ruuvi.com/quick-start/)
18
+ * Supports [Data Format 2, 3, 4 and 5](https://docs.ruuvi.com/)
19
19
  * __NOTE:__ Data Formats 2, 3 and 4 are _deprecated_ and should not be used
20
+ * [Bleak](https://github.com/hbldh/bleak) communication module (Windows, macOS and Linux)
21
+ * Default adapter for Windows and macOS
22
+ * Bleak supports
23
+ * [Async-methods](#usage)
24
+ * [Observable streams](#usage)
25
+ * [Install guide](#Bleak)
20
26
  * Bluez (Linux-only)
21
- * [BlueZ install guide](#BlueZ)
22
- * __BETA:__ Cross-platform BLE adapters
23
- * [Bleak](https://github.com/hbldh/bleak) communication module
24
- * Bleak only supports async methods (work in progress)
25
- * [Bleak install guide](#Bleak)
26
- * [Bleson](https://github.com/TheCellule/python-bleson) communication module
27
- * [Bleson install guide](#Bleson)
27
+ * Default adapter for Linux
28
+ * Bluez supports
29
+ * [Sync-methods](#usage)
30
+ * [Observable streams](#usage)
31
+ * [Install guide](#BlueZ)
32
+ * __NOTE:__ The BlueZ-adapter implementation uses deprecated BlueZ tools that are no longer supported.
33
+ * Even though BlueZ is still the default adapter, it is recommended to use the Bleak-communication adapter with Linux. Bleak will be the default adapter for Linux in the next major release.
34
+ * Bleson-adapter supports sync-methods, but please be aware that it is not fully supported due to the alpha release status of the Bleson communication module. See [Bleson](#Bleson) for more information.
28
35
  * Python 3.7+
29
36
  * For Python 2.x or <3.7 support, check [installation instructions](#python-2x-and-36-and-below) for an older version
30
37
 
@@ -53,8 +60,8 @@ Full installation guide for [Raspberry PI & Raspbian](https://github.com/ttu/ruu
53
60
 
54
61
  The package provides 3 ways to fetch data from sensors:
55
62
 
56
- 1. Synchronously with callback
57
- 2. Asynchronously with async/await (BETA)
63
+ 1. Asynchronously with async/await
64
+ 2. Synchronously with callback
58
65
  3. Observable streams with ReactiveX
59
66
 
60
67
  RuuviTag sensors can be identified using MAC addresses. Methods return a tuple with MAC and sensor data payload.
@@ -63,56 +70,11 @@ RuuviTag sensors can be identified using MAC addresses. Methods return a tuple w
63
70
  ('D2:A3:6E:C8:E0:25', {'data_format': 5, 'humidity': 47.62, 'temperature': 23.58, 'pressure': 1023.68, 'acceleration': 993.2331045630729, 'acceleration_x': -48, 'acceleration_y': -12, 'acceleration_z': 992, 'tx_power': 4, 'battery': 2197, 'movement_counter': 0, 'measurement_sequence_number': 88, 'mac': 'd2a36ec8e025', 'rssi': -80})
64
71
  ```
65
72
 
66
- ### 1. Get sensor data synchronously with callback
73
+ ### 1. Get sensor data asynchronously with async/await
67
74
 
68
- `get_data` calls the callback whenever a RuuviTag sensor broadcasts data. This method is the preferred way to use the library.
75
+ __NOTE:__ Asynchronous functionality works only with `Bleak`-adapter.
69
76
 
70
- ```python
71
- from ruuvitag_sensor.ruuvi import RuuviTagSensor
72
-
73
-
74
- def handle_data(found_data):
75
- print(f"MAC {found_data[0]}")
76
- print(f"Data {found_data[1]}")
77
-
78
-
79
- if __name__ == "__main__":
80
- RuuviTagSensor.get_data(handle_data)
81
- ```
82
-
83
- The line `if __name__ == "__main__":` is required on Windows and macOS due to the way the `multiprocessing` library works. It is not required on Linux, but it is recommended. It is omitted from the rest of the examples below.
84
-
85
- The optional list of MACs and run flag can be passed to the `get_data` function. The callback is called only for MACs in the list and setting the run flag to false will stop execution. If the run flag is not passed, the function will execute forever.
86
-
87
- ```python
88
- from ruuvitag_sensor.ruuvi import RuuviTagSensor, RunFlag
89
-
90
- counter = 10
91
- # RunFlag for stopping execution at desired time
92
- run_flag = RunFlag()
93
-
94
-
95
- def handle_data(found_data):
96
- print(f"MAC: {found_data[0]}")
97
- print(f"Data: {found_data[1]}")
98
-
99
- global counter
100
- counter = counter - 1
101
- if counter < 0:
102
- run_flag.running = False
103
-
104
-
105
- # List of MACs of sensors which will execute callback function
106
- macs = ["AA:2C:6A:1E:59:3D", "CC:2C:6A:1E:59:3D"]
107
-
108
- RuuviTagSensor.get_data(handle_data, macs, run_flag)
109
- ```
110
-
111
- ### 2. Get sensor data asynchronously
112
-
113
- __NOTE:__ Asynchronous functionality is currently in beta state and works only with `Bleak`-adapter.
114
-
115
- `get_data_async` returns the data whenever a RuuviTag sensor broadcasts data. `get_data_async` will exceute until iterator is exited. This method is the preferred way to use the library with async-adapter.
77
+ `get_data_async` returns the data whenever a RuuviTag sensor broadcasts data. `get_data_async` will execute until iterator is exited. This method is the preferred way to use the library with _Bleak_.
116
78
 
117
79
  ```py
118
80
  import asyncio
@@ -153,6 +115,53 @@ if __name__ == "__main__":
153
115
  asyncio.get_event_loop().run_until_complete(main())
154
116
  ```
155
117
 
118
+ The line `if __name__ == "__main__":` is required on Windows and macOS due to the way the `multiprocessing` library works. It is not required on Linux, but it is recommended. It is omitted from the rest of the examples below.
119
+
120
+ ### 2. Get sensor data synchronously with callback
121
+
122
+ __NOTE:__ Asynchronous functionality works only with `BlueZ`-adapter.
123
+
124
+ `get_data` calls the callback whenever a RuuviTag sensor broadcasts data. This method is the preferred way to use the library with _BlueZ_.
125
+
126
+ ```python
127
+ from ruuvitag_sensor.ruuvi import RuuviTagSensor
128
+
129
+
130
+ def handle_data(found_data):
131
+ print(f"MAC {found_data[0]}")
132
+ print(f"Data {found_data[1]}")
133
+
134
+
135
+ if __name__ == "__main__":
136
+ RuuviTagSensor.get_data(handle_data)
137
+ ```
138
+
139
+ The optional list of MACs and run flag can be passed to the `get_data` function. The callback is called only for MACs in the list and setting the run flag to false will stop execution. If the run flag is not passed, the function will execute forever.
140
+
141
+ ```python
142
+ from ruuvitag_sensor.ruuvi import RuuviTagSensor, RunFlag
143
+
144
+ counter = 10
145
+ # RunFlag for stopping execution at desired time
146
+ run_flag = RunFlag()
147
+
148
+
149
+ def handle_data(found_data):
150
+ print(f"MAC: {found_data[0]}")
151
+ print(f"Data: {found_data[1]}")
152
+
153
+ global counter
154
+ counter = counter - 1
155
+ if counter < 0:
156
+ run_flag.running = False
157
+
158
+
159
+ # List of MACs of sensors which will execute callback function
160
+ macs = ["AA:2C:6A:1E:59:3D", "CC:2C:6A:1E:59:3D"]
161
+
162
+ RuuviTagSensor.get_data(handle_data, macs, run_flag)
163
+ ```
164
+
156
165
  ### 3. Get sensor data with observable streams (ReactiveX / RxPY)
157
166
 
158
167
  `RuuviTagReactive` is a reactive wrapper and background process for RuuviTagSensor `get_data`. An optional MAC address list can be passed on the initializer and execution can be stopped with the stop function.
@@ -180,7 +189,7 @@ ruuvi_rx.get_subject().pipe(
180
189
  ops.distinct_until_changed()
181
190
  ).subscribe(lambda x: print(f"Temperature changed: {x}"))
182
191
 
183
- # Close all connections and stop bluetooth communication
192
+ # Close all connections and stop Bluetooth communication
184
193
  ruuvi_rx.stop()
185
194
  ```
186
195
 
@@ -246,7 +255,7 @@ RuuviTagSensor.find_ruuvitags()
246
255
 
247
256
  ### Using different Bluetooth device
248
257
 
249
- If you have multiple Bluetooth devices installed, a device to be used might not be the default (Linux: hci0). The device can be passed with a `bt_device`-parameter.
258
+ If you have multiple Bluetooth devices installed, a device to be used might not be the default (Linux: `hci0`). The device can be passed with a `bt_device`-parameter.
250
259
 
251
260
  ```python
252
261
  from ruuvitag_sensor.ruuvi import RuuviTagSensor
@@ -416,6 +425,18 @@ $ sudo apt-get install bluez bluez-hcidump
416
425
 
417
426
  `ruuvitag-sensor` package uses internally _hciconfig_, _hcitool_ and _hcidump_. These tools are deprecated. In case tools are missing, an older version of BlueZ is required ([Issue](https://github.com/ttu/ruuvitag-sensor/issues/31))
418
427
 
428
+ If you wish to test the library on Windows or macOS, enable it with `RUUVI_BLE_ADAPTER` environment variable.
429
+
430
+ ```sh
431
+ $ export RUUVI_BLE_ADAPTER="bluez"
432
+ ```
433
+
434
+ And install ptyprocess.
435
+
436
+ ```sh
437
+ python -m pip install ptyprocess
438
+ ```
439
+
419
440
  #### BlueZ limitations
420
441
 
421
442
  `ruuvitag-sensor` package uses BlueZ to listen to broadcasted BL information (uses _hciconf_, _hcitool_, _hcidump_). Implementation does not handle well all unexpected errors or changes, e.g. when the adapter is busy, rebooted or powered down.
@@ -424,7 +445,9 @@ In case of errors, the application tries to exit immediately, so it can be autom
424
445
 
425
446
  ### Bleak
426
447
 
427
- Bleak is not installed automatically with `ruuvitag-sensor` package. Install it manually from PyPI.
448
+ On Windows and macOS Bleak is installed and used automatically with `ruuvitag-sensor` package.
449
+
450
+ On Linux install it manually from PyPI and enable it with `RUUVI_BLE_ADAPTER` environment variable.
428
451
 
429
452
  ```sh
430
453
  $ python -m pip install bleak
@@ -435,7 +458,8 @@ Add environment variable RUUVI_BLE_ADAPTER with value Bleak. E.g.
435
458
  ```sh
436
459
  $ export RUUVI_BLE_ADAPTER="bleak"
437
460
  ```
438
- Or use `os.environ`. NOTE: this must be set before importing `ruuvitag_sensor`.
461
+
462
+ Or use `os.environ`. __NOTE:__ this must be set before importing `ruuvitag_sensor`.
439
463
 
440
464
  ```py
441
465
  import os
@@ -469,12 +493,12 @@ Bleak-adapter has a development-time generator for dummy data, which can be usef
469
493
 
470
494
  Current state and known bugs in [issue #78](https://github.com/ttu/ruuvitag-sensor/issues/78).
471
495
 
472
- Bleson works with Linux, macOS and partially with Windows.
496
+ [Bleson](https://github.com/TheCellule/python-bleson) works with Linux, macOS and partially with Windows.
473
497
 
474
498
  Bleson is not installed automatically with `ruuvitag-sensor` package. Install it manually from GitHub.
475
499
 
476
500
  ```sh
477
- $ pip install git+https://github.com/TheCellule/python-bleson
501
+ $ python -m pip install git+https://github.com/TheCellule/python-bleson
478
502
  ```
479
503
 
480
504
  Add environment variable `RUUVI_BLE_ADAPTER` with value `bleson`. E.g.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ruuvitag_sensor"
7
- version = "2.2.0"
7
+ version = "2.3.1"
8
8
  authors = [
9
9
  { name="Tomi Tuhkanen", email="tomi.tuhkanen@iki.fi" },
10
10
  ]
@@ -19,6 +19,7 @@ classifiers = [
19
19
  "Programming Language :: Python :: 3.9",
20
20
  "Programming Language :: Python :: 3.10",
21
21
  "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
22
23
  "License :: OSI Approved :: MIT License",
23
24
  "Operating System :: OS Independent",
24
25
  "Intended Audience :: Developers",
@@ -31,7 +32,8 @@ dependencies = [
31
32
  "reactivex",
32
33
  "ptyprocess;platform_system=='Linux'",
33
34
  "mypy-extensions;python_version<'3.8'",
34
- "importlib-metadata<4.3,>=1.1.0;python_version<'3.8'"
35
+ "importlib-metadata<4.3,>=1.1.0;python_version<'3.8'",
36
+ "bleak;platform_system=='Windows' or platform_system=='Darwin'",
35
37
  ]
36
38
 
37
39
  [project.optional-dependencies]
@@ -42,7 +44,7 @@ dev = [
42
44
  "pylint",
43
45
  "mypy",
44
46
  "isort",
45
- "black"
47
+ "black==23.3.0" # Lock black to the latest version that supports Python 3.7
46
48
  ]
47
49
 
48
50
  [project.urls]
@@ -5,29 +5,49 @@ from typing import AsyncGenerator, Generator, List
5
5
 
6
6
  from ruuvitag_sensor.ruuvi_types import MacAndRawData, RawData
7
7
 
8
- # pylint: disable=import-outside-toplevel, cyclic-import
8
+ # pylint: disable=import-outside-toplevel, cyclic-import, too-many-return-statements
9
9
 
10
10
 
11
11
  def get_ble_adapter():
12
- if "bleak" in os.environ.get("RUUVI_BLE_ADAPTER", "").lower():
13
- from ruuvitag_sensor.adapters.bleak_ble import BleCommunicationBleak
12
+ forced_ble_adapter = os.environ.get("RUUVI_BLE_ADAPTER", "").lower()
13
+ use_ruuvi_nix_from_file = "RUUVI_NIX_FROMFILE" in os.environ
14
+ is_ci_env = "CI" in os.environ
14
15
 
15
- return BleCommunicationBleak()
16
- if "bleson" in os.environ.get("RUUVI_BLE_ADAPTER", "").lower():
17
- from ruuvitag_sensor.adapters.bleson import BleCommunicationBleson
16
+ if forced_ble_adapter:
17
+ if "bleak" in forced_ble_adapter:
18
+ from ruuvitag_sensor.adapters.bleak_ble import BleCommunicationBleak
19
+
20
+ return BleCommunicationBleak()
21
+ if "bleson" in forced_ble_adapter:
22
+ from ruuvitag_sensor.adapters.bleson import BleCommunicationBleson
23
+
24
+ return BleCommunicationBleson()
25
+ if "bluez" in forced_ble_adapter:
26
+ from ruuvitag_sensor.adapters.nix_hci import BleCommunicationNix
18
27
 
19
- return BleCommunicationBleson()
20
- if "RUUVI_NIX_FROMFILE" in os.environ:
28
+ return BleCommunicationNix()
29
+
30
+ raise RuntimeError(f"Unknown BLE adapter: {forced_ble_adapter}")
31
+
32
+ if use_ruuvi_nix_from_file:
21
33
  # Emulate BleCommunicationNix by reading hcidump data from a file
22
34
  from ruuvitag_sensor.adapters.nix_hci_file import BleCommunicationNixFile
23
35
 
24
36
  return BleCommunicationNixFile()
25
- if not sys.platform.startswith("linux") or "CI" in os.environ:
26
- # Use BleCommunicationDummy also for CI as it can't use bluez
37
+
38
+ if is_ci_env:
39
+ # Use BleCommunicationDummy for CI as it can't use BlueZ
27
40
  from ruuvitag_sensor.adapters.dummy import BleCommunicationDummy
28
41
 
29
42
  return BleCommunicationDummy()
30
43
 
44
+ # Use default adapter for platform
45
+ if sys.platform.startswith("win") or sys.platform.startswith("darwin"):
46
+ from ruuvitag_sensor.adapters.bleak_ble import BleCommunicationBleak
47
+
48
+ return BleCommunicationBleak()
49
+
50
+ # BlueZ is default for Linux
31
51
  from ruuvitag_sensor.adapters.nix_hci import BleCommunicationNix
32
52
 
33
53
  return BleCommunicationNix()
@@ -41,6 +61,16 @@ def is_async_from_env():
41
61
  return "bleak" in os.environ.get("RUUVI_BLE_ADAPTER", "").lower()
42
62
 
43
63
 
64
+ def throw_if_not_sync_adapter(ble: object):
65
+ if is_async_adapter(ble):
66
+ raise RuntimeError("Sync BLE adapter required")
67
+
68
+
69
+ def throw_if_not_async_adapter(ble: object):
70
+ if not is_async_adapter(ble):
71
+ raise RuntimeError("Async BLE adapter required")
72
+
73
+
44
74
  class BleCommunication:
45
75
  """Bluetooth LE communication"""
46
76
 
@@ -6,15 +6,16 @@ import sys
6
6
  from typing import AsyncGenerator, List, Tuple
7
7
 
8
8
  from bleak import BleakScanner
9
- from bleak.backends.scanner import AdvertisementData, BLEDevice
9
+ from bleak.backends.scanner import AdvertisementData, AdvertisementDataCallback, BLEDevice
10
10
 
11
11
  from ruuvitag_sensor.adapters import BleCommunicationAsync
12
+ from ruuvitag_sensor.adapters.utils import rssi_to_hex
12
13
  from ruuvitag_sensor.ruuvi_types import MacAndRawData, RawData
13
14
 
14
15
  MAC_REGEX = "[0-9a-f]{2}([:])[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$"
15
16
 
16
17
 
17
- def _get_scanner(detection_callback):
18
+ def _get_scanner(detection_callback: AdvertisementDataCallback, bt_device: str = ""):
18
19
  # NOTE: On Linux - bleak.exc.BleakError: passive scanning mode requires bluez or_patterns
19
20
  # NOTE: On macOS - bleak.exc.BleakError: macOS does not support passive scanning
20
21
  scanning_mode = "passive" if sys.platform.startswith("win") else "active"
@@ -25,7 +26,12 @@ def _get_scanner(detection_callback):
25
26
 
26
27
  return DevBleakScanner(detection_callback, scanning_mode)
27
28
 
28
- return BleakScanner(detection_callback=detection_callback, scanning_mode=scanning_mode)
29
+ if bt_device:
30
+ return BleakScanner(
31
+ detection_callback=detection_callback, scanning_mode=scanning_mode, adapter=bt_device
32
+ ) # type: ignore[arg-type]
33
+
34
+ return BleakScanner(detection_callback=detection_callback, scanning_mode=scanning_mode) # type: ignore[arg-type]
29
35
 
30
36
 
31
37
  # TODO: Python 3.7 - TypeError: 'type' object is not subscriptable
@@ -71,15 +77,19 @@ class BleCommunicationBleak(BleCommunicationAsync):
71
77
  if 1177 not in advertisement_data.manufacturer_data:
72
78
  return
73
79
 
80
+ log.debug("Received data: %s", advertisement_data)
81
+
74
82
  data = BleCommunicationBleak._parse_data(advertisement_data.manufacturer_data[1177])
75
83
 
76
84
  # Add RSSI to encoded data as hex. All adapters use a common decoder.
77
- data += hex((advertisement_data.rssi + (1 << 8)) % (1 << 8)).replace("0x", "")
85
+ data += rssi_to_hex(advertisement_data.rssi)
78
86
  await queue.put((mac, data))
79
87
 
80
- scanner = _get_scanner(detection_callback)
88
+ scanner = _get_scanner(detection_callback, bt_device)
81
89
  await scanner.start()
82
90
 
91
+ log.debug("Bleak scanner started")
92
+
83
93
  try:
84
94
  while True:
85
95
  next_item: Tuple[str, str] = await queue.get()
@@ -93,6 +103,8 @@ class BleCommunicationBleak(BleCommunicationAsync):
93
103
 
94
104
  await scanner.stop()
95
105
 
106
+ log.debug("Bleak scanner stopped")
107
+
96
108
  @staticmethod
97
109
  async def get_first_data(mac: str, bt_device: str = "") -> RawData:
98
110
  """
@@ -8,6 +8,7 @@ from typing import Generator, List
8
8
  from bleson import Observer, get_provider
9
9
 
10
10
  from ruuvitag_sensor.adapters import BleCommunication
11
+ from ruuvitag_sensor.adapters.utils import rssi_to_hex
11
12
  from ruuvitag_sensor.ruuvi_types import MacAndRawData, RawData
12
13
 
13
14
  log = logging.getLogger(__name__)
@@ -56,6 +57,9 @@ class BleCommunicationBleson(BleCommunication):
56
57
  data = f"FF{data.hex()}"
57
58
  data = f"{(len(data) >> 1):02x}{data}"
58
59
  data = f"{(len(data) >> 1):02x}{data}"
60
+
61
+ # Add RSSI to encoded data as hex. All adapters use a common decoder.
62
+ data += rssi_to_hex(advertisement.rssi)
59
63
  queue.put((mac, data.upper()))
60
64
  except GeneratorExit:
61
65
  break
@@ -0,0 +1,2 @@
1
+ def rssi_to_hex(rssi: int) -> str:
2
+ return f"{(rssi + (1 << 8)) % (1 << 8):x}"
@@ -78,7 +78,7 @@ class DataFormats:
78
78
  break
79
79
  except ShortDataError as ex:
80
80
  # Data might be from RuuviTag, but received data was invalid
81
- # e.g. it's possile that bluetooth stack received only partial data
81
+ # e.g. it's possible that Bluetooth stack received only partial data
82
82
  # Set the format to None, and data to '', this allows the
83
83
  # caller to determine that we did indeed see a Ruuvitag.
84
84
  log.debug("Error parsing advertisement data: %s", ex)
@@ -5,7 +5,7 @@ from multiprocessing.managers import ListProxy
5
5
  from typing import AsyncGenerator, Callable, Dict, Generator, List, Optional
6
6
  from warnings import warn
7
7
 
8
- from ruuvitag_sensor.adapters import get_ble_adapter, is_async_adapter
8
+ from ruuvitag_sensor.adapters import get_ble_adapter, throw_if_not_async_adapter, throw_if_not_sync_adapter
9
9
  from ruuvitag_sensor.data_formats import DataFormats
10
10
  from ruuvitag_sensor.decoder import get_decoder, parse_mac
11
11
  from ruuvitag_sensor.ruuvi_types import DataFormatAndRawSensorData, Mac, MacAndRawData, MacAndSensorData, SensorData
@@ -42,6 +42,7 @@ class RuuviTagSensor:
42
42
  Returns:
43
43
  tuple (int, string): Data Format type and raw Sensor data
44
44
  """
45
+ throw_if_not_sync_adapter(ble)
45
46
 
46
47
  raw = ble.get_first_data(mac, bt_device)
47
48
  return DataFormats.convert_data(raw)
@@ -60,6 +61,8 @@ class RuuviTagSensor:
60
61
  Returns:
61
62
  tuple (int, string): Data Format type and raw Sensor data
62
63
  """
64
+ throw_if_not_async_adapter(ble)
65
+
63
66
  raw = await ble.get_first_data(mac, bt_device)
64
67
  return DataFormats.convert_data(raw)
65
68
 
@@ -74,6 +77,7 @@ class RuuviTagSensor:
74
77
  Returns:
75
78
  dict: MAC and state of found sensors
76
79
  """
80
+ throw_if_not_sync_adapter(ble)
77
81
 
78
82
  log.info("Finding RuuviTags. Stop with Ctrl+C.")
79
83
 
@@ -99,9 +103,7 @@ class RuuviTagSensor:
99
103
  Returns:
100
104
  dict: MAC and state of found sensors
101
105
  """
102
-
103
- if not is_async_adapter(ble):
104
- raise Exception("Only Bleak BLE communication is supported")
106
+ throw_if_not_async_adapter(ble)
105
107
 
106
108
  log.info("Finding RuuviTags. Stop with Ctrl+C.")
107
109
 
@@ -135,6 +137,7 @@ class RuuviTagSensor:
135
137
  Returns:
136
138
  dict: MAC and state of found sensors
137
139
  """
140
+ throw_if_not_sync_adapter(ble)
138
141
 
139
142
  log.info("Get latest data for sensors. Stop with Ctrl+C.")
140
143
  log.info("Stops automatically in %ss", search_duratio_sec)
@@ -162,6 +165,7 @@ class RuuviTagSensor:
162
165
  Returns:
163
166
  dict: MAC and state of found sensors
164
167
  """
168
+ throw_if_not_async_adapter(ble)
165
169
 
166
170
  log.info("Get latest data for sensors. Stop with Ctrl+C.")
167
171
  log.info("Stops automatically in %ss", search_duratio_sec)
@@ -180,8 +184,16 @@ class RuuviTagSensor:
180
184
 
181
185
  @staticmethod
182
186
  async def get_data_async(macs: List[str] = [], bt_device: str = "") -> AsyncGenerator[MacAndSensorData, None]:
183
- if not is_async_adapter(ble):
184
- raise Exception("Only Bleak BLE communication is supported")
187
+ """
188
+ Get data for all ruuvitag sensors or sensors in the MAC's list.
189
+
190
+ Args:
191
+ macs (list): MAC addresses
192
+ bt_device (string): Bluetooth device id
193
+ Returns:
194
+ AsyncGenerator: MAC and State of RuuviTag sensor data (tuple)
195
+ """
196
+ throw_if_not_async_adapter(ble)
185
197
 
186
198
  mac_blacklist = Manager().list()
187
199
  data_iter = ble.get_data(mac_blacklist, bt_device)
@@ -213,6 +225,7 @@ class RuuviTagSensor:
213
225
  run_flag (object): RunFlag object. Function executes while run_flag.running
214
226
  bt_device (string): Bluetooth device id
215
227
  """
228
+ throw_if_not_sync_adapter(ble)
216
229
 
217
230
  log.info("Get latest data for sensors. Stop with Ctrl+C.")
218
231
  log.info("MACs: %s", macs)
@@ -233,6 +246,7 @@ class RuuviTagSensor:
233
246
  Use get_data-method instead.
234
247
  """
235
248
  warn("This method will be removed in a future version, use get_data() instead", FutureWarning)
249
+ throw_if_not_sync_adapter(ble)
236
250
  return RuuviTagSensor.get_data(callback, macs, run_flag, bt_device)
237
251
 
238
252
  @staticmethod
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
- Name: ruuvitag-sensor
3
- Version: 2.2.0
2
+ Name: ruuvitag_sensor
3
+ Version: 2.3.1
4
4
  Summary: Find RuuviTag sensors and get decoded data from selected sensors
5
5
  Author-email: Tomi Tuhkanen <tomi.tuhkanen@iki.fi>
6
6
  License: MIT License
@@ -36,6 +36,7 @@ Classifier: Programming Language :: Python :: 3.8
36
36
  Classifier: Programming Language :: Python :: 3.9
37
37
  Classifier: Programming Language :: Python :: 3.10
38
38
  Classifier: Programming Language :: Python :: 3.11
39
+ Classifier: Programming Language :: Python :: 3.12
39
40
  Classifier: License :: OSI Approved :: MIT License
40
41
  Classifier: Operating System :: OS Independent
41
42
  Classifier: Intended Audience :: Developers
@@ -49,6 +50,7 @@ Requires-Dist: reactivex
49
50
  Requires-Dist: ptyprocess; platform_system == "Linux"
50
51
  Requires-Dist: mypy-extensions; python_version < "3.8"
51
52
  Requires-Dist: importlib-metadata<4.3,>=1.1.0; python_version < "3.8"
53
+ Requires-Dist: bleak; platform_system == "Windows" or platform_system == "Darwin"
52
54
  Provides-Extra: dev
53
55
  Requires-Dist: pytest; extra == "dev"
54
56
  Requires-Dist: pytest-asyncio; extra == "dev"
@@ -56,7 +58,7 @@ Requires-Dist: flake8-pyproject; extra == "dev"
56
58
  Requires-Dist: pylint; extra == "dev"
57
59
  Requires-Dist: mypy; extra == "dev"
58
60
  Requires-Dist: isort; extra == "dev"
59
- Requires-Dist: black; extra == "dev"
61
+ Requires-Dist: black==23.3.0; extra == "dev"
60
62
 
61
63
  RuuviTag Sensor Python Package
62
64
  ---------------------------------
@@ -65,7 +67,7 @@ RuuviTag Sensor Python Package
65
67
  [![License](https://img.shields.io/pypi/l/ruuvitag-sensor.svg)](https://pypi.python.org/pypi/ruuvitag-sensor/)
66
68
  [![PyPI version](https://img.shields.io/pypi/v/ruuvitag-sensor.svg)](https://pypi.python.org/pypi/ruuvitag_-sensor)
67
69
  [![PyPI downloads](https://img.shields.io/pypi/dm/ruuvitag-sensor.svg)](https://pypistats.org/packages/ruuvitag-sensor)
68
- [![Python versions](https://img.shields.io/pypi/pyversions/ruuvitag-sensor.svg)](https://pypi.python.org/pypi/ruuvitag-sensor/)
70
+ [![Python versions](https://img.shields.io/badge/python-3.7+-blue.svg)](https://pypi.python.org/pypi/ruuvitag-sensor/)
69
71
 
70
72
  `ruuvitag-sensor` is a Python package for communicating with [RuuviTag BLE Sensor](https://ruuvi.com/) and for decoding measurement data from broadcasted BLE data.
71
73
 
@@ -73,18 +75,25 @@ RuuviTag Sensor Python Package
73
75
 
74
76
  ## Requirements
75
77
 
76
- * RuuviTag with Weather Station firmware
77
- * Setup [guide](https://lab.ruuvi.com/start/)
78
- * Supports [Data Format 2, 3, 4 and 5](https://github.com/ruuvi/ruuvi-sensor-protocols)
78
+ * RuuviTag sensor
79
+ * Setup [guide](https://ruuvi.com/quick-start/)
80
+ * Supports [Data Format 2, 3, 4 and 5](https://docs.ruuvi.com/)
79
81
  * __NOTE:__ Data Formats 2, 3 and 4 are _deprecated_ and should not be used
82
+ * [Bleak](https://github.com/hbldh/bleak) communication module (Windows, macOS and Linux)
83
+ * Default adapter for Windows and macOS
84
+ * Bleak supports
85
+ * [Async-methods](#usage)
86
+ * [Observable streams](#usage)
87
+ * [Install guide](#Bleak)
80
88
  * Bluez (Linux-only)
81
- * [BlueZ install guide](#BlueZ)
82
- * __BETA:__ Cross-platform BLE adapters
83
- * [Bleak](https://github.com/hbldh/bleak) communication module
84
- * Bleak only supports async methods (work in progress)
85
- * [Bleak install guide](#Bleak)
86
- * [Bleson](https://github.com/TheCellule/python-bleson) communication module
87
- * [Bleson install guide](#Bleson)
89
+ * Default adapter for Linux
90
+ * Bluez supports
91
+ * [Sync-methods](#usage)
92
+ * [Observable streams](#usage)
93
+ * [Install guide](#BlueZ)
94
+ * __NOTE:__ The BlueZ-adapter implementation uses deprecated BlueZ tools that are no longer supported.
95
+ * Even though BlueZ is still the default adapter, it is recommended to use the Bleak-communication adapter with Linux. Bleak will be the default adapter for Linux in the next major release.
96
+ * Bleson-adapter supports sync-methods, but please be aware that it is not fully supported due to the alpha release status of the Bleson communication module. See [Bleson](#Bleson) for more information.
88
97
  * Python 3.7+
89
98
  * For Python 2.x or <3.7 support, check [installation instructions](#python-2x-and-36-and-below) for an older version
90
99
 
@@ -113,8 +122,8 @@ Full installation guide for [Raspberry PI & Raspbian](https://github.com/ttu/ruu
113
122
 
114
123
  The package provides 3 ways to fetch data from sensors:
115
124
 
116
- 1. Synchronously with callback
117
- 2. Asynchronously with async/await (BETA)
125
+ 1. Asynchronously with async/await
126
+ 2. Synchronously with callback
118
127
  3. Observable streams with ReactiveX
119
128
 
120
129
  RuuviTag sensors can be identified using MAC addresses. Methods return a tuple with MAC and sensor data payload.
@@ -123,56 +132,11 @@ RuuviTag sensors can be identified using MAC addresses. Methods return a tuple w
123
132
  ('D2:A3:6E:C8:E0:25', {'data_format': 5, 'humidity': 47.62, 'temperature': 23.58, 'pressure': 1023.68, 'acceleration': 993.2331045630729, 'acceleration_x': -48, 'acceleration_y': -12, 'acceleration_z': 992, 'tx_power': 4, 'battery': 2197, 'movement_counter': 0, 'measurement_sequence_number': 88, 'mac': 'd2a36ec8e025', 'rssi': -80})
124
133
  ```
125
134
 
126
- ### 1. Get sensor data synchronously with callback
135
+ ### 1. Get sensor data asynchronously with async/await
127
136
 
128
- `get_data` calls the callback whenever a RuuviTag sensor broadcasts data. This method is the preferred way to use the library.
137
+ __NOTE:__ Asynchronous functionality works only with `Bleak`-adapter.
129
138
 
130
- ```python
131
- from ruuvitag_sensor.ruuvi import RuuviTagSensor
132
-
133
-
134
- def handle_data(found_data):
135
- print(f"MAC {found_data[0]}")
136
- print(f"Data {found_data[1]}")
137
-
138
-
139
- if __name__ == "__main__":
140
- RuuviTagSensor.get_data(handle_data)
141
- ```
142
-
143
- The line `if __name__ == "__main__":` is required on Windows and macOS due to the way the `multiprocessing` library works. It is not required on Linux, but it is recommended. It is omitted from the rest of the examples below.
144
-
145
- The optional list of MACs and run flag can be passed to the `get_data` function. The callback is called only for MACs in the list and setting the run flag to false will stop execution. If the run flag is not passed, the function will execute forever.
146
-
147
- ```python
148
- from ruuvitag_sensor.ruuvi import RuuviTagSensor, RunFlag
149
-
150
- counter = 10
151
- # RunFlag for stopping execution at desired time
152
- run_flag = RunFlag()
153
-
154
-
155
- def handle_data(found_data):
156
- print(f"MAC: {found_data[0]}")
157
- print(f"Data: {found_data[1]}")
158
-
159
- global counter
160
- counter = counter - 1
161
- if counter < 0:
162
- run_flag.running = False
163
-
164
-
165
- # List of MACs of sensors which will execute callback function
166
- macs = ["AA:2C:6A:1E:59:3D", "CC:2C:6A:1E:59:3D"]
167
-
168
- RuuviTagSensor.get_data(handle_data, macs, run_flag)
169
- ```
170
-
171
- ### 2. Get sensor data asynchronously
172
-
173
- __NOTE:__ Asynchronous functionality is currently in beta state and works only with `Bleak`-adapter.
174
-
175
- `get_data_async` returns the data whenever a RuuviTag sensor broadcasts data. `get_data_async` will exceute until iterator is exited. This method is the preferred way to use the library with async-adapter.
139
+ `get_data_async` returns the data whenever a RuuviTag sensor broadcasts data. `get_data_async` will execute until iterator is exited. This method is the preferred way to use the library with _Bleak_.
176
140
 
177
141
  ```py
178
142
  import asyncio
@@ -213,6 +177,53 @@ if __name__ == "__main__":
213
177
  asyncio.get_event_loop().run_until_complete(main())
214
178
  ```
215
179
 
180
+ The line `if __name__ == "__main__":` is required on Windows and macOS due to the way the `multiprocessing` library works. It is not required on Linux, but it is recommended. It is omitted from the rest of the examples below.
181
+
182
+ ### 2. Get sensor data synchronously with callback
183
+
184
+ __NOTE:__ Asynchronous functionality works only with `BlueZ`-adapter.
185
+
186
+ `get_data` calls the callback whenever a RuuviTag sensor broadcasts data. This method is the preferred way to use the library with _BlueZ_.
187
+
188
+ ```python
189
+ from ruuvitag_sensor.ruuvi import RuuviTagSensor
190
+
191
+
192
+ def handle_data(found_data):
193
+ print(f"MAC {found_data[0]}")
194
+ print(f"Data {found_data[1]}")
195
+
196
+
197
+ if __name__ == "__main__":
198
+ RuuviTagSensor.get_data(handle_data)
199
+ ```
200
+
201
+ The optional list of MACs and run flag can be passed to the `get_data` function. The callback is called only for MACs in the list and setting the run flag to false will stop execution. If the run flag is not passed, the function will execute forever.
202
+
203
+ ```python
204
+ from ruuvitag_sensor.ruuvi import RuuviTagSensor, RunFlag
205
+
206
+ counter = 10
207
+ # RunFlag for stopping execution at desired time
208
+ run_flag = RunFlag()
209
+
210
+
211
+ def handle_data(found_data):
212
+ print(f"MAC: {found_data[0]}")
213
+ print(f"Data: {found_data[1]}")
214
+
215
+ global counter
216
+ counter = counter - 1
217
+ if counter < 0:
218
+ run_flag.running = False
219
+
220
+
221
+ # List of MACs of sensors which will execute callback function
222
+ macs = ["AA:2C:6A:1E:59:3D", "CC:2C:6A:1E:59:3D"]
223
+
224
+ RuuviTagSensor.get_data(handle_data, macs, run_flag)
225
+ ```
226
+
216
227
  ### 3. Get sensor data with observable streams (ReactiveX / RxPY)
217
228
 
218
229
  `RuuviTagReactive` is a reactive wrapper and background process for RuuviTagSensor `get_data`. An optional MAC address list can be passed on the initializer and execution can be stopped with the stop function.
@@ -240,7 +251,7 @@ ruuvi_rx.get_subject().pipe(
240
251
  ops.distinct_until_changed()
241
252
  ).subscribe(lambda x: print(f"Temperature changed: {x}"))
242
253
 
243
- # Close all connections and stop bluetooth communication
254
+ # Close all connections and stop Bluetooth communication
244
255
  ruuvi_rx.stop()
245
256
  ```
246
257
 
@@ -306,7 +317,7 @@ RuuviTagSensor.find_ruuvitags()
306
317
 
307
318
  ### Using different Bluetooth device
308
319
 
309
- If you have multiple Bluetooth devices installed, a device to be used might not be the default (Linux: hci0). The device can be passed with a `bt_device`-parameter.
320
+ If you have multiple Bluetooth devices installed, a device to be used might not be the default (Linux: `hci0`). The device can be passed with a `bt_device`-parameter.
310
321
 
311
322
  ```python
312
323
  from ruuvitag_sensor.ruuvi import RuuviTagSensor
@@ -476,6 +487,18 @@ $ sudo apt-get install bluez bluez-hcidump
476
487
 
477
488
  `ruuvitag-sensor` package uses internally _hciconfig_, _hcitool_ and _hcidump_. These tools are deprecated. In case tools are missing, an older version of BlueZ is required ([Issue](https://github.com/ttu/ruuvitag-sensor/issues/31))
478
489
 
490
+ If you wish to test the library on Windows or macOS, enable it with `RUUVI_BLE_ADAPTER` environment variable.
491
+
492
+ ```sh
493
+ $ export RUUVI_BLE_ADAPTER="bluez"
494
+ ```
495
+
496
+ And install ptyprocess.
497
+
498
+ ```sh
499
+ python -m pip install ptyprocess
500
+ ```
501
+
479
502
  #### BlueZ limitations
480
503
 
481
504
  `ruuvitag-sensor` package uses BlueZ to listen to broadcasted BL information (uses _hciconf_, _hcitool_, _hcidump_). Implementation does not handle well all unexpected errors or changes, e.g. when the adapter is busy, rebooted or powered down.
@@ -484,7 +507,9 @@ In case of errors, the application tries to exit immediately, so it can be autom
484
507
 
485
508
  ### Bleak
486
509
 
487
- Bleak is not installed automatically with `ruuvitag-sensor` package. Install it manually from PyPI.
510
+ On Windows and macOS Bleak is installed and used automatically with `ruuvitag-sensor` package.
511
+
512
+ On Linux install it manually from PyPI and enable it with `RUUVI_BLE_ADAPTER` environment variable.
488
513
 
489
514
  ```sh
490
515
  $ python -m pip install bleak
@@ -495,7 +520,8 @@ Add environment variable RUUVI_BLE_ADAPTER with value Bleak. E.g.
495
520
  ```sh
496
521
  $ export RUUVI_BLE_ADAPTER="bleak"
497
522
  ```
498
- Or use `os.environ`. NOTE: this must be set before importing `ruuvitag_sensor`.
523
+
524
+ Or use `os.environ`. __NOTE:__ this must be set before importing `ruuvitag_sensor`.
499
525
 
500
526
  ```py
501
527
  import os
@@ -529,12 +555,12 @@ Bleak-adapter has a development-time generator for dummy data, which can be usef
529
555
 
530
556
  Current state and known bugs in [issue #78](https://github.com/ttu/ruuvitag-sensor/issues/78).
531
557
 
532
- Bleson works with Linux, macOS and partially with Windows.
558
+ [Bleson](https://github.com/TheCellule/python-bleson) works with Linux, macOS and partially with Windows.
533
559
 
534
560
  Bleson is not installed automatically with `ruuvitag-sensor` package. Install it manually from GitHub.
535
561
 
536
562
  ```sh
537
- $ pip install git+https://github.com/TheCellule/python-bleson
563
+ $ python -m pip install git+https://github.com/TheCellule/python-bleson
538
564
  ```
539
565
 
540
566
  Add environment variable `RUUVI_BLE_ADAPTER` with value `bleson`. E.g.
@@ -22,6 +22,7 @@ ruuvitag_sensor/adapters/bleson.py
22
22
  ruuvitag_sensor/adapters/dummy.py
23
23
  ruuvitag_sensor/adapters/nix_hci.py
24
24
  ruuvitag_sensor/adapters/nix_hci_file.py
25
+ ruuvitag_sensor/adapters/utils.py
25
26
  ruuvitag_sensor/adapters/development/__init__.py
26
27
  ruuvitag_sensor/adapters/development/dev_bleak_scanner.py
27
28
  tests/test_data_formats.py
@@ -3,6 +3,9 @@ reactivex
3
3
  [:platform_system == "Linux"]
4
4
  ptyprocess
5
5
 
6
+ [:platform_system == "Windows" or platform_system == "Darwin"]
7
+ bleak
8
+
6
9
  [:python_version < "3.8"]
7
10
  mypy-extensions
8
11
  importlib-metadata<4.3,>=1.1.0
@@ -14,4 +17,4 @@ flake8-pyproject
14
17
  pylint
15
18
  mypy
16
19
  isort
17
- black
20
+ black==23.3.0
File without changes