Coder Social home page Coder Social logo

aranet4-python's Introduction

Aranet4 Python client

Python library and command line interface for Aranet4, Aranet2, Aranet Radiation and Aranet Radon Plus sensors.

Installation

  1. Install aranet4 and its dependencies:
pip3 install aranet4
  1. Pair Aranet4 device
  2. Run aranetctl or use as library

Note: Smart Home Integrations must be enabled in Aranet4 mobile app for full support.

aranetctl usage

$ aranetctl -h
usage: aranetctl.py [-h] [--scan] [-u URL] [-r] [-s DATE] [-e DATE] [-o FILE] [-w] [-l COUNT] [--xt] [--xh] [--xp] [--xc] [--set-interval MINUTES]
                    [--set-integrations {on,off}] [--set-range {normal,extended}]
                    [device_mac]

positional arguments:
  device_mac            Aranet4 Bluetooth Address

options:
  -h, --help            show this help message and exit
  --scan                Scan Aranet4 devices
  -r, --records         Fetch historical log records

Options for current reading:
  -u URL, --url URL     Remote url for current value push

Filter History Log Records:
  -s DATE, --start DATE
                        Records range start (UTC time, example: 2019-09-29T14:00:00
  -e DATE, --end DATE   Records range end (UTC time, example: 2019-09-30T14:00:00
  -o FILE, --output FILE
                        Save records to a file
  -w, --wait            Wait until new data point available
  -l COUNT, --last COUNT
                        Get <COUNT> last records
  --xt                  Don't get temperature records
  --xh                  Don't get humidity records
  --xp                  Don't get pressure records
  --xc                  Don't get co2 records

Change device settings:
  --set-interval MINUTES
                        Change update interval
  --set-integrations {on,off}
                        Toggle Smart Home Integrations
  --set-range {normal,extended}
                        Change bluetooth range

Scan devices

Usage: aranetctl --scan

Output:

=======================================
  Name:     Aranet4 00001
  Address:  AA:BB:CC:DD:EE:FF
  RSSI:     -83 dBm
---------------------------------------
  CO2:            484 ppm
  Temperature:    20.9 °C
  Humidity:       43 %
  Pressure:       1024.8 hPa
  Battery:        3 %
  Status Display: GREEN
  Age:            9/60

Note: To receive current measurements, Smart Home Integrations must be enabled and firmware version must be v1.2.0 or newer.

Current Readings Example

Usage: aranetctl XX:XX:XX:XX:XX:XX

Output:

--------------------------------------
Connected: Aranet4 00000 | v0.3.1
Updated 51 s ago. Intervals: 60 s
5040 total readings
--------------------------------------
CO2:          904 ppm
Temperature:  19.9 C
Humidity:     51 %
Pressure:     997.0 hPa
Battery:      96 %
Status Display: GREEN
--------------------------------------

Get History Example

Write full log to screen:

Usage: aranetctl XX:XX:XX:XX:XX:XX -r

-------------------------------------------------------------
Device Name    :        Aranet4 00000
Device Version :               v0.3.1
-------------------------------------------------------------
 id  |        date         |  co2   | temp | hum | pressure |
-------------------------------------------------------------
   1 | 2022-02-18T14:15:44 |    844 | 21.8 |  50 |    985.6 |
   2 | 2022-02-18T14:20:44 |    846 | 21.8 |  50 |    985.9 |
   3 | 2022-02-18T14:25:44 |    843 | 22.0 |  50 |    986.4 |
   4 | 2022-02-18T14:30:44 |    881 | 22.1 |  50 |    986.4 |
   5 | 2022-02-18T14:35:44 |    854 | 22.1 |  50 |    987.3 |
   6 | 2022-02-18T14:40:44 |    867 | 22.2 |  50 |    987.5 |
   7 | 2022-02-18T14:45:44 |    883 | 22.1 |  50 |    988.1 |
   8 | 2022-02-18T14:50:44 |    921 | 22.1 |  50 |    988.6 |
   9 | 2022-02-18T14:55:44 |    930 | 22.0 |  50 |    989.1 |
  10 | 2022-02-18T15:00:44 |    954 | 22.0 |  50 |    989.5 |
-------------------------------------------------------------

Usage: aranetctl XX:XX:XX:XX:XX:XX -r -o aranet4.csv

Output file format: Date,CO2,Temperature,Humidity,Pressure

Output file example:

date,co2,temperature,humidity,pressure
2022-02-18 10:05:47,1398,23.2,53,986.6
2022-02-18 10:10:47,1155,23.1,50,986.3

Usage of library

Current Readings Example

import aranet4

device_mac = "XX:XX:XX:XX:XX:XX"

current = aranet4.client.get_current_readings(device_mac)

print("co2 reading:", current.co2)
print("Temperature:", current.temperature)
print("Humidity:", current.humidity)
print("Pressure:", current.pressure)

Logged Readings Example

import aranet4

device_mac = "XX:XX:XX:XX:XX:XX"

history = aranet4.client.get_all_records(
    device_mac, entry_filter={"humi": False, "pres": False}
)
print(f"{'Date':^20} | {'CO2':^10} | {'Temperature':^10} ")
for entry in history.value:
    print(f"{entry.date.isoformat():^20} | {entry.co2:^10} | {entry.temperature:^10}")

Library functions

get_current_readings(mac_address: str) -> client.CurrentReading

Get current measurements from device Returns CurrentReading object:

class CurrentReading:
    name: str = ""
    version: str = ""
    temperature: float = -1
    humidity: int = -1
    pressure: float = -1
    co2: int = -1
    battery: int = -1
    status: int = -1
    interval: int = -1
    ago: int = -1
    stored: int = -1

get_all_records(mac_address: str, entry_filter: dict) -> client.Record

Get stored datapoints from device. Apply any filters if required

entry_filter is a dictionary that can have the following values:

  • last: int : Get last n entries
  • start: datetime : Get entries after specified time
  • end: datetime : Get entries before specified time
  • temp: bool : Get temperature data points (default = True)
  • humi: bool : Get humidity data points (default = True)
  • pres: bool : Get pressure data points (default = True)
  • co2: bool : Get co2 data points (default = True)

Returns CurrentReading object:

class Record:
    name: str
    version: str
    records_on_device: int
    filter: Filter
    value: List[RecordItem] = field(default_factory=list)

Which includes these objects:

class RecordItem:
    date: datetime
    temperature: float
    humidity: int
    pressure: float
    co2: int

class Filter:
    begin: int
    end: int
    incl_temperature: bool
    incl_humidity: bool
    incl_pressure: bool
    incl_co2: bool

aranet4-python's People

Contributors

0ki avatar anrijs avatar aschmitz avatar elyscape avatar karlis99 avatar ukbaz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

aranet4-python's Issues

Abort on mac os

Hello,

I'm trying to use aranet4 on my macbook but I can't scan the aranet4 device.

Here the logs :

(base) ➜  ~ pip3 install aranet4
Collecting aranet4
  Downloading aranet4-2.2.2.tar.gz (13 kB)
  Preparing metadata (setup.py) ... done
Collecting bleak (from aranet4)
  Downloading bleak-0.20.2-py3-none-any.whl (135 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 135.6/135.6 kB 2.7 MB/s eta 0:00:00
Requirement already satisfied: requests in ./opt/miniconda3/lib/python3.9/site-packages (from aranet4) (2.29.0)
Requirement already satisfied: async-timeout<5,>=3.0.0 in ./opt/miniconda3/lib/python3.9/site-packages (from bleak->aranet4) (4.0.2)
Collecting pyobjc-core<10.0.0,>=9.0.1 (from bleak->aranet4)
  Downloading pyobjc_core-9.2-cp39-cp39-macosx_10_9_universal2.whl (737 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 737.1/737.1 kB 3.3 MB/s eta 0:00:00
Collecting pyobjc-framework-CoreBluetooth<10.0.0,>=9.0.1 (from bleak->aranet4)
  Downloading pyobjc_framework_CoreBluetooth-9.2-cp36-abi3-macosx_11_0_universal2.whl (13 kB)
Collecting pyobjc-framework-libdispatch<10.0.0,>=9.0.1 (from bleak->aranet4)
  Downloading pyobjc_framework_libdispatch-9.2-cp39-cp39-macosx_10_9_universal2.whl (20 kB)
Requirement already satisfied: charset-normalizer<4,>=2 in ./opt/miniconda3/lib/python3.9/site-packages (from requests->aranet4) (2.0.4)
Requirement already satisfied: idna<4,>=2.5 in ./opt/miniconda3/lib/python3.9/site-packages (from requests->aranet4) (3.4)
Requirement already satisfied: urllib3<1.27,>=1.21.1 in ./opt/miniconda3/lib/python3.9/site-packages (from requests->aranet4) (1.26.14)
Requirement already satisfied: certifi>=2017.4.17 in ./opt/miniconda3/lib/python3.9/site-packages (from requests->aranet4) (2023.5.7)
Collecting pyobjc-framework-Cocoa>=9.2 (from pyobjc-framework-CoreBluetooth<10.0.0,>=9.0.1->bleak->aranet4)
  Downloading pyobjc_framework_Cocoa-9.2-cp39-cp39-macosx_10_9_universal2.whl (390 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 390.5/390.5 kB 3.2 MB/s eta 0:00:00
Building wheels for collected packages: aranet4
  Building wheel for aranet4 (setup.py) ... done
  Created wheel for aranet4: filename=aranet4-2.2.2-py3-none-any.whl size=12940 sha256=fbf6d4d4ab78da8fea5d8ffd850ac7c83e377fdce6f20b7869f35a705c5086c6
  Stored in directory: /Users/nabil_servais/Library/Caches/pip/wheels/0f/23/9f/a72addd44baf97a016e4988280c1e1f1429103d230698e67e6
Successfully built aranet4
Installing collected packages: pyobjc-core, pyobjc-framework-libdispatch, pyobjc-framework-Cocoa, pyobjc-framework-CoreBluetooth, bleak, aranet4
Successfully installed aranet4-2.2.2 bleak-0.20.2 pyobjc-core-9.2 pyobjc-framework-Cocoa-9.2 pyobjc-framework-CoreBluetooth-9.2 pyobjc-framework-libdispatch-9.2
(base) ➜  ~ aranetctl
Device address not specified
(base) ➜  ~ aranetctl --scan
Looking for Aranet devices...
[1]    74329 abort      aranetctl --scan

OS: macOS 13.3.1 22E261 arm64
python version: Python 3.9.16

sys:1: RuntimeWarning: coroutine 'BleakClient.disconnect' was never awaited

With the current commit (bfa3db5, and various ones before it), I get the following error:

$ aranetctl -r CC:47:4C:60:7A:6A
Next data point will be logged in 293 seconds
Traceback (most recent call last):
  File "/usr/local/bin/aranetctl", line 33, in <module>
    sys.exit(load_entry_point('aranet4==2.3.2', 'console_scripts', 'aranetctl')())
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/dist-packages/aranet4-2.3.2-py3.11.egg/aranet4/aranetctl.py", line 247, in entry_point
  File "/usr/local/lib/python3.11/dist-packages/aranet4-2.3.2-py3.11.egg/aranet4/aranetctl.py", line 235, in main
  File "/usr/local/lib/python3.11/dist-packages/aranet4-2.3.2-py3.11.egg/aranet4/client.py", line 990, in get_all_records
  File "/usr/lib/python3.11/asyncio/runners.py", line 190, in run
    return runner.run(main)
           ^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/asyncio/runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/asyncio/base_events.py", line 653, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/dist-packages/aranet4-2.3.2-py3.11.egg/aranet4/client.py", line 969, in _all_records
UnboundLocalError: cannot access local variable 'rad_dose_rate_val' where it is not associated with a value
Exception ignored in: <function Aranet4.__del__ at 0x7fb66f07c0>
Traceback (most recent call last):
  File "/usr/local/lib/python3.11/dist-packages/aranet4-2.3.2-py3.11.egg/aranet4/client.py", line 521, in __del__
  File "/usr/lib/python3.11/asyncio/tasks.py", line 874, in shield
  File "/usr/lib/python3.11/asyncio/tasks.py", line 668, in _ensure_future
  File "/usr/lib/python3.11/asyncio/events.py", line 677, in get_event_loop
RuntimeError: There is no current event loop in thread 'MainThread'.
sys:1: RuntimeWarning: coroutine 'BleakClient.disconnect' was never awaited

With older commits (e.g., 5a88f6e), it returns records as expected.

I haven't (yet) bifurcated revisions to find out where it broke.

Unreliable "start" filter

Hello,

I'm trying to load aranet data points in a database, and I tried to use the "start" filter to not get the already recorded points, but it's not working correctly :

I start by getting the last mesurement :

cur.execute('select max(time) from data')
last = cur.fetchone()[0]
if last != None:
    filter["start"] = last

And then I call aranet4.client.get_all_records(device_mac, entry_filter=filter)

I often get all recorded datapoints in response.

I reproduced this with aranetctl :

(env) bastien@sandworm:~/projects/aracollect$ aranetctl -s 2022-10-07T16:33:51 -r E5:47:86:60:6A:4D
{'device_mac': 'E5:47:86:60:6A:4D', 'scan': False, 'url': None, 'records': True, 'start': datetime.datetime(2022, 10, 7, 16, 33, 51), 'end': None, 'output': None, 'wait': False, 'last': None, 'temp': True, 'humi': True, 'pres': True, 'co2': True}
Next data point will be logged in 91 seconds
-------------------------------------------------------------
Device Name    :        Aranet4 1CCCF
Device Version :              v0.4.14
-------------------------------------------------------------
 id  |        date         |  co2   | temp | hum | pressure |
-------------------------------------------------------------
1637 | 2022-10-07T16:35:51 |    624 | 20.1 |  51 |   1012.5 |
1638 | 2022-10-07T16:37:51 |    639 | 20.0 |  51 |   1012.5 |
-------------------------------------------------------------
(env) bastien@sandworm:~/projects/aracollect$ aranetctl -s 2022-10-07T16:35:51 -r E5:47:86:60:6A:4D
{'device_mac': 'E5:47:86:60:6A:4D', 'scan': False, 'url': None, 'records': True, 'start': datetime.datetime(2022, 10, 7, 16, 35, 51), 'end': None, 'output': None, 'wait': False, 'last': None, 'temp': True, 'humi': True, 'pres': True, 'co2': True}
Next data point will be logged in 75 seconds
-------------------------------------------------------------
Device Name    :        Aranet4 1CCCF
Device Version :              v0.4.14
-------------------------------------------------------------
 id  |        date         |  co2   | temp | hum | pressure |
-------------------------------------------------------------
   1 | 2022-10-05T10:03:50 |   1349 | 20.8 |  58 |   1010.5 |
   2 | 2022-10-05T10:05:50 |   1676 | 21.8 |  54 |   1010.6 |
   3 | 2022-10-05T10:07:50 |   1676 | 21.8 |  54 |   1010.6 |
   4 | 2022-10-05T10:09:50 |   1676 | 21.8 |  54 |   1010.6 |
[...]
1637 | 2022-10-07T16:35:50 |    624 | 20.1 |  51 |   1012.5 |
1638 | 2022-10-07T16:37:50 |    639 | 20.0 |  51 |   1012.5 |
-------------------------------------------------------------

It seems if the result set would contains less than 2 rows, all datapoints are returned

MAC address not found

Although I think this library is written for Linux, I thought I'd try it out on macOS.

It looks like it can't find the unit (the MAC is correct, just redacted below).

Is the intended flow that you connect the OS to the device before running this - or should this both find and connect to it directly?

aranetctl "XX:XX:XX:XX:XX:XX"
Traceback (most recent call last):
  File "/opt/homebrew/bin/aranetctl", line 8, in <module>
    sys.exit(entry_point())
  File "/opt/homebrew/lib/python3.9/site-packages/aranet4/aranetctl.py", line 215, in entry_point
    main(argv=sys.argv[1:])
  File "/opt/homebrew/lib/python3.9/site-packages/aranet4/aranetctl.py", line 207, in main
    current = client.get_current_readings(args.device_mac)
  File "/opt/homebrew/lib/python3.9/site-packages/aranet4/client.py", line 366, in get_current_readings
    return asyncio.run(_current_reading(mac_address))
  File "/opt/homebrew/Cellar/[email protected]/3.9.13_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "/opt/homebrew/Cellar/[email protected]/3.9.13_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/asyncio/base_events.py", line 647, in run_until_complete
    return future.result()
  File "/opt/homebrew/lib/python3.9/site-packages/aranet4/client.py", line 356, in _current_reading
    await monitor.connect()
  File "/opt/homebrew/lib/python3.9/site-packages/aranet4/client.py", line 215, in connect
    await self.device.connect()
  File "/opt/homebrew/lib/python3.9/site-packages/bleak/backends/corebluetooth/client.py", line 86, in connect
    raise BleakError(
bleak.exc.BleakError: Device with address XX:XX:XX:XX:XX:XX was not found
  • macOS 12.5 (latest)
  • Python 3.9
  • aranet4 2.0.0

Test failure

Test failure with 2.3.0

[...]
python3.12-aranet4> Executing pytestCheckPhase
python3.12-aranet4> ============================= test session starts ==============================
python3.12-aranet4> platform linux -- Python 3.12.2, pytest-8.0.2, pluggy-1.4.0
python3.12-aranet4> rootdir: /build/source
python3.12-aranet4> collected 8 items / 1 deselected / 7 selected                                  
python3.12-aranet4> tests/test_csv.py F                                                      [ 14%]
python3.12-aranet4> tests/test_values.py ......                                              [100%]
python3.12-aranet4> =================================== FAILURES ===================================
python3.12-aranet4> ________________________ CSVCreation.test_simple_write _________________________
python3.12-aranet4> self = <test_csv.CSVCreation testMethod=test_simple_write>
python3.12-aranet4>     def setUp(self):
python3.12-aranet4>         # Create data object
python3.12-aranet4> >       self.records = build_data()
python3.12-aranet4> tests/test_csv.py:27:
python3.12-aranet4> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
python3.12-aranet4>     def build_data():
python3.12-aranet4> >       log_filter = client.Filter(1, 14, True, True, True, True)
python3.12-aranet4> E       TypeError: Filter.__init__() missing 3 required positional arguments: 'incl_rad_dose', 'incl_rad_dose_rate', and 'incl_rad_dose_total'
python3.12-aranet4> tests/test_csv.py:15: TypeError
python3.12-aranet4> =========================== short test summary info ============================
python3.12-aranet4> FAILED tests/test_csv.py::CSVCreation::test_simple_write - TypeError: Filter.__init__() missing 3 required positional arguments: 'incl...
python3.12-aranet4> ================== 1 failed, 6 passed, 1 deselected in 0.11s ===================

Client hangs when used on RaspberryPi

the aranet.py program hangs when started on my raspberrypi (version 3, with Stretch)
ar4.getName() and ar4.getVersion() return the name and version ( v0.3.9)
but ar4.currentReadings(False) does not return from bytearray(c[0].read()) in client.py

Not connected error

Problem resolved;

After this session I used bluetoothctl to remove the the device, I scan on, then off, and was able to pair successfully in case anyone else runs into the same problem

I'm getting a Not connected error when I try to read from my aranet4 e.g. aranetctl 60:C0:BF:48:F1:05

Below is the log of connecting and pairing:

bluetoothctl
[NEW] Controller 00:1A:7D:DA:71:15 jetson-dev [default]
[NEW] Device 60:C0:BF:48:F1:05 Aranet4 18645
Agent registered
[bluetooth]# trust 60:C0:BF:48:F1:05
Changing 60:C0:BF:48:F1:05 trust succeeded
[bluetooth]# connect 60:C0:BF:48:F1:05
Attempting to connect to 60:C0:BF:48:F1:05
[CHG] Device 60:C0:BF:48:F1:05 Connected: yes
[CHG] Device 60:C0:BF:48:F1:05 Connected: no
Connection successful
[CHG] Device 60:C0:BF:48:F1:05 Connected: yes
[CHG] Device 60:C0:BF:48:F1:05 Connected: no
[bluetooth]# paired-devices
Device 60:C0:BF:48:F1:05 Aranet4 18645 <-- execute aranetctl 60:C0:BF:48:F1:05
[CHG] Controller 00:1A:7D:DA:71:15 Discovering: yes
[NEW] Device 29:EF:63:AB:22:8D 29-EF-63-AB-22-8D
[NEW] Device 49:7A:DF:38:FB:1D 49-7A-DF-38-FB-1D
[NEW] Device 43:03:A3:92:6B:C7 43-03-A3-92-6B-C7
[CHG] Device 29:EF:63:AB:22:8D RSSI: -48
[CHG] Device 60:C0:BF:48:F1:05 RSSI: -58
[CHG] Device 60:C0:BF:48:F1:05 ManufacturerData Key: 0x0702
[CHG] Device 60:C0:BF:48:F1:05 ManufacturerData Value:
22 01 02 01 00 0e 0f 01 da 01 0a 02 53 27 47 61 "...........S'Ga
01 2c 01 14 01 e2 .,....
[NEW] Device 4D:0C:8C:99:BC:B7 4D-0C-8C-99-BC-B7
[CHG] Device 29:EF:63:AB:22:8D RSSI is nil
[DEL] Device 29:EF:63:AB:22:8D 29-EF-63-AB-22-8D
[CHG] Device 43:03:A3:92:6B:C7 RSSI is nil
[DEL] Device 43:03:A3:92:6B:C7 43-03-A3-92-6B-C7
[CHG] Device 4D:0C:8C:99:BC:B7 RSSI is nil
[DEL] Device 4D:0C:8C:99:BC:B7 4D-0C-8C-99-BC-B7
[CHG] Controller 00:1A:7D:DA:71:15 Discovering: no
[CHG] Device 60:C0:BF:48:F1:05 RSSI is nil
[CHG] Device 49:7A:DF:38:FB:1D TxPower is nil
[CHG] Device 49:7A:DF:38:FB:1D RSSI is nil
[CHG] Device 60:C0:BF:48:F1:05 Connected: yes
[CHG] Device 60:C0:BF:48:F1:05 Connected: no
[CHG] Device 60:C0:BF:48:F1:05 Connected: yes
[CHG] Device 60:C0:BF:48:F1:05 Connected: no

Thanks in advance for any help

is periodic on-demand scanning possible?

Given the issues calling asyncio more than once, is there a recommended way to on-demand scan (i.e., when some other function wants to)?

The aranet4.client.find_nearby() function, as used in the scanner_simple.py example, won't work because of the issues calling asynchio more than once.

Using aranet4.Aranet4Scanner() seems like a good alternative, but the scanner_advanced.py example is built around polling at a common interval. I need to poll periodically, triggered by some other action. If I have it .start() and .stop() every time it needs to poll, I assume I'll run into above-mentioned issues. Is there some way I can have it start, scan as requested (not with a fixed delay), and eventually stop?

Tag the source

Could you please tag the source? This allows distributions to get the complete source from GitHub if they want.

Thanks

Basic Inquiry Not Working

Hi, I'm eager to get the Aranet4 integrated into Home Assistant and I found this project that would be perfect.

My Aranet4 device works properly, and the mobile app functions as expected.
The device firmware is reported as v0.4.10.
"Smart Home" integration is enabled in the mobile app.

aranetctl in installed on Ubuntu 22.04 using Python 3.10
Looks like pip installed version 2.1.3 of Aranet4-Python

ananetctl scan works as expected. I assume the sensor details are not returned during the scan because of the old firmware version.

./aranetctl --scan
Looking for Aranet4 devices...
=======================================
  Name:     Aranet4 12ED5
  Address:  EF:BE:2D:BA:DD:A5
  RSSI:     -43 dBm

However, any attempt to query the device directly never returns a result:
./aranetctl EF:BE:2D:BA:DD:A5
(blocks forever)

I have tried multiple variations with the device paired and unpaired with the mobile phone and computer.
Unfortunately the mobile app will not allow me to update the firmware due to "unable to reach firmware service..."

Any ideas?

asyncio.exceptions.CancelledError

I have Raspberry Pi 4 Model B Rev 1.1 (Linux earth-ha 5.15.61-v7l+ #1579 SMP Fri Aug 26 11:13:03 BST 2022 armv7l GNU/Linux) and Aranet4 (firmware v0.4.10).

After I connected Aranet4 for the first time, everything was fine. With the command sudo aranetctl EF:C1:88:E1:31:D0 I got the actual data, but after a little while the following error appeared:

earth@earth-ha:~ $ sudo aranetctl EF:C1:88:E1:31:D0
Traceback (most recent call last):
File "/usr/local/lib/python3.9/dist-packages/bleak/backends/bluezdbus/client.py", line 171, in connect
reply = await self._bus.call(
File "/usr/local/lib/python3.9/dist-packages/dbus_fast/aio/message_bus.py", line 337, in call
await future
asyncio.exceptions.CancelledError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "/usr/local/bin/aranetctl", line 33, in
sys.exit(load_entry_point('aranet4==2.0.5', 'console_scripts', 'aranetctl')())
File "/usr/local/lib/python3.9/dist-packages/aranet4/aranetctl.py", line 236, in entry_point
main(argv=sys.argv[1:])
File "/usr/local/lib/python3.9/dist-packages/aranet4/aranetctl.py", line 228, in main
current = client.get_current_readings(args.device_mac)
File "/usr/local/lib/python3.9/dist-packages/aranet4/client.py", line 451, in get_current_readings
return asyncio.run(_current_reading(mac_address))
File "/usr/lib/python3.9/asyncio/runners.py", line 44, in run
return loop.run_until_complete(main)
File "/usr/lib/python3.9/asyncio/base_events.py", line 642, in run_until_complete
return future.result()
File "/usr/local/lib/python3.9/dist-packages/aranet4/client.py", line 441, in _current_reading
await monitor.connect()
File "/usr/local/lib/python3.9/dist-packages/aranet4/client.py", line 230, in connect
await self.device.connect()
File "/usr/local/lib/python3.9/dist-packages/bleak/init.py", line 392, in connect
return await self._backend.connect(**kwargs)
File "/usr/local/lib/python3.9/dist-packages/bleak/backends/bluezdbus/client.py", line 171, in connect
reply = await self._bus.call(
File "/usr/local/lib/python3.9/dist-packages/async_timeout/init.py", line 129, in aexit
self._do_exit(exc_type)
File "/usr/local/lib/python3.9/dist-packages/async_timeout/init.py", line 212, in _do_exit
raise asyncio.TimeoutError
asyncio.exceptions.TimeoutError

[bluetooth]# list
Controller DC:A6:32:66:43:DF earth-ha [default]
[bluetooth]# paired-devices
Device EF:C1:88:E1:31:D0 Aranet4 00407

I can also see data coming from the Aranet device:
[CHG] Device EF:C1:88:E1:31:D0 RSSI: -83

When the device is deleted in bluetoothctl and paired again, it is possible to receive data once again. Then the said error appears again.

Additionally and unrelatedly, when I got the data from Aranet, I found that the firmware version does not match what is shown on the phone.

earth@earth-ha:~ $ sudo aranetctl EF:C1:88:E1:31:D0


Connected: Aranet4 00407 | v0.3.1
Updated 12 s ago. Intervals: 120 s
5040 total log_size

CO2: 812 ppm
Temperature: 21.3 °C
Humidity: 56 %
Pressure: 1009.7 hPa
Battery: 76 %
Status Display: GREEN

Exception when running with Aranet2 with a passive scanner that doesn't provide device name

if (not device.name and len(raw_bytes) in [7,22]) or "Aranet4" in device.name:
raw_bytes.insert(0,0)
breaks Aranet2 device without name

  File "/home/vscode/.local/lib/python3.12/site-packages/aranet4/client.py", line 377, in __init__
    if (not device.name and len(raw_bytes) in [7,22]) or "Aranet4" in device.name:
                                                         ^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: argument of type 'NoneType' is not iterable

Maybe change this to something like:

if device.name and "Aranet4" in device.name or len(raw_bytes) in [7,22]:
    raw_bytes.insert(0,0) 

FEATURE REQUEST: mqtt.publish would be a killer!

Anrij, THANK YOU a ton for this.
I had created my own quick&dirty aranet4 reader, but yours is so much better. Having also MQTT publish would be a killer feature, as it would open your project for all of mqtt-friendly smart home systems (like openhab, hass, domoticz, etc).
I could comment with my own code that does through paho-mqtt ... though it is a kindergarten level, comparing to yours ... I'm too shy for a proper pull ;)

"Read not permitted" error when querying data

Hello
An Aranet4 (firmware v0.4.10) is paired with a Linux (kernel 5.15) device. I can connect to this Aranet4 device and read the readable attributes via bluetoothctl (version 5.55) without any issues.
When trying to query the same device with aranetctl, however, I get this:

$> aranetctl <aranet4:mac:address>
Traceback (most recent call last):
  File "/home/username/.local/bin/aranetctl", line 8, in <module>
    sys.exit(entry_point())
  File "/home/username/.local/lib/python3.9/site-packages/aranet4/aranetctl.py", line 236, in entry_point
    main(argv=sys.argv[1:])
  File "/home/username/.local/lib/python3.9/site-packages/aranet4/aranetctl.py", line 228, in main
    current = client.get_current_readings(args.device_mac)
  File "/home/username/.local/lib/python3.9/site-packages/aranet4/client.py", line 451, in get_current_readings
    return asyncio.run(_current_reading(mac_address))
  File "/usr/lib/python3.9/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "/usr/lib/python3.9/asyncio/base_events.py", line 642, in run_until_complete
    return future.result()
  File "/home/username/.local/lib/python3.9/site-packages/aranet4/client.py", line 442, in _current_reading
    readings = await monitor.current_readings(details=True)
  File "/home/username/.local/lib/python3.9/site-packages/aranet4/client.py", line 245, in current_readings
    raw_bytes = await self.device.read_gatt_char(uuid)
  File "/home/username/.local/lib/python3.9/site-packages/bleak/backends/bluezdbus/client.py", line 598, in read_gatt_char
    assert_reply(reply)
  File "/home/username/.local/lib/python3.9/site-packages/bleak/backends/bluezdbus/utils.py", line 22, in assert_reply
    raise BleakDBusError(reply.error_name, reply.body)
bleak.exc.BleakDBusError: [org.bluez.Error.NotPermitted] Read not permitted

I get that this is probably not strictly related exclusively to just this client, since I get pretty much the same error when using Go module for Aranet4 as well. Maybe something has changed after the most recent Aranet4 firmware update or I'm missing something else?

Scan works, but querying with MAC address doesn't

I'm on Arch Linux and the device is paired. aranetctl --scan works:

$ aranetctl --scan
Looking for Aranet devices...
=======================================
  Name:     Aranet4 1BC2D
  Address:  EC:94:30:8D:5A:B9
/usr/lib/python3.11/site-packages/aranet4/aranetctl.py:165: FutureWarning: BLEDevice.rssi is deprecated and will be removed in a future version of Bleak, use AdvertisementData.rssi instead
  print(f"  RSSI:     {advertisement.device.rssi} dBm")
  RSSI:     -64 dBm
--------------------------------------
  CO2:            688 pm
  Temperature:    24.8 °C
  Humidity:       45 %
  Pressure:       1003.5 hPa
  Battery:        36 %
  Status Disp.:   GREEN
  Age:            31/120
  Counter:        -1

Scan finished. Found 1

But trying to connect with the MAC address doesn't:

$ aranetctl EC:94:30:8D:5A:B9
Traceback (most recent call last):
  File "/usr/bin/aranetctl", line 33, in <module>
    sys.exit(load_entry_point('aranet4==2.2.2', 'console_scripts', 'aranetctl')())
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/site-packages/aranet4/aranetctl.py", line 273, in entry_point
    main(argv=sys.argv[1:])
  File "/usr/lib/python3.11/site-packages/aranet4/aranetctl.py", line 264, in main
    current = client.get_current_readings(args.device_mac)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/site-packages/aranet4/client.py", line 643, in get_current_readings
    return asyncio.run(_current_reading(mac_address))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/asyncio/runners.py", line 190, in run
    return runner.run(main)
           ^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/asyncio/runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/asyncio/base_events.py", line 653, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/site-packages/aranet4/client.py", line 633, in _current_reading
    await monitor.connect()
  File "/usr/lib/python3.11/site-packages/aranet4/client.py", line 378, in connect
    await self.device.connect()
  File "/usr/lib/python3.11/site-packages/bleak/__init__.py", line 605, in connect
    return await self._backend.connect(**kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/site-packages/bleak/backends/bluezdbus/client.py", line 268, in connect
    await self.get_services(
  File "/usr/lib/python3.11/site-packages/bleak/backends/bluezdbus/client.py", line 656, in get_services
    self.services = await manager.get_services(
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/site-packages/bleak/backends/bluezdbus/manager.py", line 644, in get_services
    await self._wait_for_services_discovery(device_path)
  File "/usr/lib/python3.11/site-packages/bleak/backends/bluezdbus/manager.py", line 779, in _wait_for_services_discovery
    raise BleakError("failed to discover services, device disconnected")
bleak.exc.BleakError: failed to discover services, device disconnected

The maximum number of active connections has been reached

Hi,

my Python script is reading records (using client.get_all_records()) every few minutes/hours from the device and after some time the connection fails and the system log says

 dbus-daemon[244]: [system] The maximum number of active connections for UID 1000 has been reached (max_connections_per_user=256)

Looking into the code I noticed, that the connection established with Aranet4.connect() is never closed, therefore I assume that after the above mentioned 256 connections the script fails.

Therefore my questions:

  • why does the client.get_all_records() does not use BleakClient.disconnect()? Or do I miss something?
  • is there another way to repeatedly read records without establishing a new connection every time?

"Encryption is insufficient" error

I can successfully scan for devices and find my device's ID, but when running aranetctl <uuid> I get the following error:

Traceback (most recent call last):
  File "/opt/homebrew/bin/aranetctl", line 8, in <module>
    sys.exit(entry_point())
  File "/opt/homebrew/lib/python3.9/site-packages/aranet4/aranetctl.py", line 239, in entry_point
    main(argv=sys.argv[1:])
  File "/opt/homebrew/lib/python3.9/site-packages/aranet4/aranetctl.py", line 231, in main
    current = client.get_current_readings(args.device_mac)
  File "/opt/homebrew/lib/python3.9/site-packages/aranet4/client.py", line 374, in get_current_readings
    return asyncio.run(_current_reading(mac_address))
  File "/opt/homebrew/Cellar/[email protected]/3.9.13_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "/opt/homebrew/Cellar/[email protected]/3.9.13_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/asyncio/base_events.py", line 647, in run_until_complete
    return future.result()
  File "/opt/homebrew/lib/python3.9/site-packages/aranet4/client.py", line 368, in _current_reading
    readings.stored = await monitor.get_total_readings()
  File "/opt/homebrew/lib/python3.9/site-packages/aranet4/client.py", line 268, in get_total_readings
    raw_bytes = await self.device.read_gatt_char(self.AR4_READ_TOTAL_READINGS)
  File "/opt/homebrew/lib/python3.9/site-packages/bleak/backends/corebluetooth/client.py", line 259, in read_gatt_char
    output = await self._delegate.read_characteristic(
  File "/opt/homebrew/lib/python3.9/site-packages/bleak/backends/corebluetooth/PeripheralDelegate.py", line 147, in read_characteristic
    return await asyncio.wait_for(future, timeout=timeout)
  File "/opt/homebrew/Cellar/[email protected]/3.9.13_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/asyncio/tasks.py", line 479, in wait_for
    return fut.result()
bleak.exc.BleakError: Failed to read characteristic 44: Error Domain=CBATTErrorDomain Code=15 "Encryption is insufficient." UserInfo={NSLocalizedDescription=Encryption is insufficient.}

Missing files in sdist

It appears that the manifest is missing at least one file necessary to build
from the sdist for version 1.1.3.post1. You're in good company, about 5% of other
projects updated in the last year are also missing files.

+ /tmp/venv/bin/pip3 wheel --no-binary aranet4 -w /tmp/ext aranet4==1.1.3.post1
Looking in indexes: http://10.10.0.139:9191/root/pypi/+simple/
Collecting aranet4==1.1.3.post1
  Downloading http://10.10.0.139:9191/root/pypi/%2Bf/6c3/42f18b08bd1f8/aranet4-1.1.3.post1.tar.gz (5.6 kB)
    ERROR: Command errored out with exit status 1:
     command: /tmp/venv/bin/python3 -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-wheel-cxjd0a5y/aranet4/setup.py'"'"'; __file__='"'"'/tmp/pip-wheel-cxjd0a5y/aranet4/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' egg_info --egg-base /tmp/pip-wheel-cxjd0a5y/aranet4/pip-egg-info
         cwd: /tmp/pip-wheel-cxjd0a5y/aranet4/
    Complete output (5 lines):
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "/tmp/pip-wheel-cxjd0a5y/aranet4/setup.py", line 3, in <module>
        with open("aranet4/README.md", "r") as fh:
    FileNotFoundError: [Errno 2] No such file or directory: 'aranet4/README.md'
    ----------------------------------------
ERROR: Command errored out with exit status 1: python setup.py egg_info Check the logs for full command output.

history data retrieval fails

Apologies for not creating a proper pull request and making notes here:

-l parameter does not work, because two additional libraries have to be imported:

import time, datetime

Then, in python script lines 114, 117, 120 an 123 need an update - instead of "Aranet4" (e.g. Aranet4.PARAM_CO2) we need to use ar4 (e.g. ar4.PARAM_CO2)

Many thanks for your work, much appreciated and really nice thing to have!

README update

For anyone attempting to use Raspberry Pi boards with Raspbian lite, there are two notes:

  1. another Python library is needed
    sudo pip install requests
  2. Does not work on Raspbian Stretch. Confirmed to work on Buster (2019-07-10)

Descriptions for undocumented characteristics

As part of work on an app I run, I did some spelunking. I have figured out the purposes of the characteristics that are not yet documented:

  • "f0cd1401-95da-4f4b-9ac8-aa55d312af0c" is the sensor settings state, packed into a small struct.
  • "f0cd1502-95da-4f4b-9ac8-aa55d312af0c" is the sensor calibration.
  • "f0cd2003-95da-4f4b-9ac8-aa55d312af0c" appears to be unused - no reference to it in the entire app. Maybe that's why it contains all zeros?
  • "F0CD2005-95DA-4F4B-9AC8-AA55D312AF0C" appears to be the characteristic for the sensor logs.
    I haven't written out the data layouts yet or wrote code to parse them, but thought I'd let you know!

Error in influx example

In the influx example code, when fetching current (non-historical) readings here, the mac_address parameter is not being passed

TypeError: get_current_readings() missing 1 required positional argument: 'mac_address'

License file

In the setup.py file it says that this project is licenses with MIT License.

You might want to also create a text file (typically named LICENSE or LICENSE.txt) in the root of this repository and copy the text of the license into the file. Replace [year] with the current year and [fullname] with the name (or names) of the copyright holders.

The content of that file is available at: https://choosealicense.com/licenses/mit/

Which battery value is supposed to be correct?

This is weird. I'm seeing right now the battery service (0000180f-0000-1000-8000-00805f9b34fb, 00002a19-0000-1000-8000-00805f9b34fb) return a correct value (11%), but the CO2 measurement characteristic (f0cd1503-95da-4f4b-9ac8-aa55d312af0c) is returning an entirely different value (45%).

Any idea what's going on? Not really an issue with your repo here, but might be useful to someone :)

Changing settings ("writes")

With the app you can change some settings, in particular the interval at which the sensor takes readings. The default is 5 or 10 min, but it might be nice to be able to change it to 1min by API.

If someone has reversed this, that would be especially cool. I could help implement the golang variants, if someone has done the python ones.

As an aside, it would be awesome to see some custom firmware for this kind of device. It can't be too complicated, and then we would have more freedom on what we could do to them.

Cheers!

Decode manufacturer-specific data in advertisements

I see that you added some code to process the advertisement data. It would be nice if it also decoded the manufacturer-specific data present in ad_data.manufacturer_data[1794]. It is structured as follows:

┌ A┐B┐ C┐D───┐E┐ F┐
│ 210a 0400 000c 0f
└ │ │  │ │    │  │
  21│──│─│────│──│─── device state
                      └ 0010 0001
                        ││││ │ │└ device is disconnected
                        ││││ │ └  unknown
                        ││││ └    calibration state:
                        ││││          00: not active
                        ││││          01: "end request"
                        ││││          10: in progress
                        ││││          11: error
                        │││└      DFU mode active
                        ││└       "Smart Home Integration" (i.e. 0x0038 handle) is enabled
                        │└        unknown
                        └         unknown
    0a─│─│────│──│─── patch version number
       04│────│──│─── minor version number
          0000│──│─── major version number (uLE16)
              0c─│─── hardware revision
                 0f── unknown

If DFU mode is not active, the version numbers correspond to the firmware version. If DFU mode is active, the version numbers correspond to the bootloader version.

As an aside, the Bleak documentation notes that register_detection_callback is deprecated. As such, this code should be updated:

self.scanner = BleakScanner()
self.scanner.register_detection_callback(self._process_advertisement)

To something like this:

self.scanner = BleakScanner(self._process_advertisement)

As a fun side bonus, you could use the service_uuids parameter to let Bleak do the device filtering for you:

self.scanner = BleakScanner(self._process_advertisement, [Aranet4.AR4_SERVICE, Aranet4.AR4_OLD_SERVICE]))

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.