Skip to content

Commit

Permalink
v0.4.3: homogenise keys for PM values from SPS30
Browse files Browse the repository at this point in the history
By default `aioairq.core.AirQ.get_latest_data` now returns values
from SPS30 PM sensor under the same keys as the values from the sensor
that has been predominantly used so far.
Previous behaviour is still available by setting
`return_original_keys=True`.

Add test and documentation.
  • Loading branch information
Sibgatulin committed Nov 19, 2024
1 parent 760ad27 commit 5a73880
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 1 deletion.
2 changes: 1 addition & 1 deletion aioairq/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
__email__ = "[email protected]"
__url__ = "https://www.air-q.com"
__license__ = "Apache License 2.0"
__version__ = "0.4.2"
__version__ = "0.4.3"
__all__ = [
"AirQ",
"DeviceInfo",
Expand Down
33 changes: 33 additions & 0 deletions aioairq/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,14 +245,47 @@ async def get_latest_data(
return_average=True,
clip_negative_values=True,
return_uncertainties=False,
return_original_keys=False,
):
"""Poll the dictionary with the momentary values from the device.
Parameters
----------
return_average : bool, default is True
The device exposes both the raw momentary data as well as their
rolling / exponential averages. To access the former, set this to False.
clip_negative_values : bool, default is True
For brief periods, mostly during self-calibration, the device may return
negative sensor values. A conventional solution is to clip them to 0.
return_uncertainties : bool, default is False
For certain sensors, the device exposes not only the momentary value
but also an estimate of its uncertainty. If this parameter is set to True,
the values of those sensors will be returned as lists of length 2: [value, uncertainty].
return_original_keys : bool, default is False
Currently, depending on configuration, the device expose the particulate
matter values under two different set of keys: `pm{1,2_5,10}` and `pm{1,2_5,10}_SPS30`.
By default, this method strips the `_SPS30` suffix to offer a more homogineous
structure of the returned dict.
"""

data = await self.get("average" if return_average else "data")
if clip_negative_values:
data = self.clip_negative_values(data)
if not return_uncertainties:
data = self.drop_uncertainties_from_data(data)
if not return_original_keys:
data = {self._homogenise_key(key): value for key, value in data.items()}
return data

def _homogenise_key(self, key: str) -> str:
"""Meant to capture various changes to the original keys.
Currently it only strips `_SPS30` suffix from the keys of the particulate
values from a new sensor, allowing all PM values to appear under the same keys
disregarding the underlying sensor configuration.
"""
return key.replace("_SPS30", "")

async def get(self, subject: str) -> dict:
"""Return the given subject from the air-Q device.
Expand Down
54 changes: 54 additions & 0 deletions tests/test_core.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from unittest.mock import patch

import aiohttp
import pytest
import pytest_asyncio
Expand Down Expand Up @@ -38,3 +40,55 @@ async def test_constructor(valid_address, passw, session):
airq = AirQ(valid_address, passw, session)
assert airq.anchor == "http://" + valid_address
assert not airq._session.closed


@pytest.mark.asyncio
@pytest.mark.parametrize(
"return_original_keys,data,expected",
[
(
True,
{"co2": [604.0, 68.1], "Status": "OK", "pm1": [0, 10], "pm2_5": [0, 10]},
{"co2": [604.0, 68.1], "Status": "OK", "pm1": [0, 10], "pm2_5": [0, 10]},
),
(
False,
{"co2": [604.0, 68.1], "Status": "OK", "pm1": [0, 10], "pm2_5": [0, 10]},
{"co2": [604.0, 68.1], "Status": "OK", "pm1": [0, 10], "pm2_5": [0, 10]},
),
(
True,
{
"co2": [604.0, 68.1],
"Status": "OK",
"pm1_SPS30": [0, 10],
"pm2_5_SPS30": [0, 10],
},
{
"co2": [604.0, 68.1],
"Status": "OK",
"pm1_SPS30": [0, 10],
"pm2_5_SPS30": [0, 10],
},
),
(
False,
{
"co2": [604.0, 68.1],
"Status": "OK",
"pm1_SPS30": [0, 10],
"pm2_5_SPS30": [0, 10],
},
{"co2": [604.0, 68.1], "Status": "OK", "pm1": [0, 10], "pm2_5": [0, 10]},
),
],
)
async def test_data_key_filtering(
return_original_keys, data, expected, valid_address, passw, session
):
airq = AirQ(valid_address, passw, session)
with patch("aioairq.AirQ.get", return_value=data):
actual = await airq.get_latest_data(
return_uncertainties=True, return_original_keys=return_original_keys
)
assert actual == expected

0 comments on commit 5a73880

Please sign in to comment.