From 619178196130d9cf0dcd4a329cf95db26afcc84e Mon Sep 17 00:00:00 2001 From: Christopher Pierce Date: Fri, 4 Feb 2022 16:08:46 -0500 Subject: [PATCH 1/7] adds methods that bulk access data attributes --- library/pms5003/__init__.py | 29 +++++++++++++++++++++++++++++ library/tests/test_setup.py | 3 +++ 2 files changed, 32 insertions(+) diff --git a/library/pms5003/__init__.py b/library/pms5003/__init__.py index 4941016..b25531a 100644 --- a/library/pms5003/__init__.py +++ b/library/pms5003/__init__.py @@ -63,6 +63,25 @@ def pm_per_1l_air(self, size): raise ValueError("Particle size {} measurement not available.".format(size)) + def get_all_pm(self): + """ + Returns all PM measurements as a list of dicts with keys 'size', 'environment', and 'val' which are the + particulate matter size recorded by the measurement, the conditions this was calculated under (atmospheric or + standard) and the actual value of the PM measurement in ug/m^3 + + :return: list of dicts of measurements + """ + vals = [{'size': x, 'environment': y} for y in ['std', 'atm'] for x in [1.0, 2.5, 10.0]] + return [{k: v for d in (x, {'val': y}) for (k, v) in d.items()} for x, y in zip(vals, self.data)] + + def get_all_counts(self): + """ + Returns dict mapping size (float) to number (int) of particles beyond that size in 0.1 L of air. + :return: dict: size -> particle count + """ + sizes = [0.3, 0.5, 1.0, 2.5, 5.0, 10.0] + return {s: v for s, v in zip(sizes, self.data[6:])} + def __repr__(self): return """ PM1.0 ug/m3 (ultrafine particles): {} @@ -82,6 +101,16 @@ def __repr__(self): def __str__(self): return self.__repr__() + def __iter__(self): + """ + Iterator allows conversion of data object into dict for direct access. IE call d = dict(data) + :return: + """ + for x in self.get_all_pm(): + yield "pm{:.1f}_{:s}".format(x['size'], x['environment']), x['val'] + for size, count in self.get_all_counts().items(): + yield "count_{:.1f}".format(size), count + class PMS5003(): def __init__(self, device='/dev/ttyAMA0', baudrate=9600, pin_enable=22, pin_reset=27): diff --git a/library/tests/test_setup.py b/library/tests/test_setup.py index 4c636c6..1d1a5e4 100644 --- a/library/tests/test_setup.py +++ b/library/tests/test_setup.py @@ -57,6 +57,9 @@ def test_read(): sensor._serial = MockSerial() data = sensor.read() data.pm_ug_per_m3(2.5) + data.get_all_pm() + data.get_all_counts() + dict(data) def test_read_fail(): From d969a407e98f2492a0f661052cdeac740f2748e0 Mon Sep 17 00:00:00 2001 From: Christopher Pierce Date: Fri, 4 Feb 2022 16:09:16 -0500 Subject: [PATCH 2/7] version bump --- library/setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/setup.cfg b/library/setup.cfg index 22ce319..0f10a1a 100644 --- a/library/setup.cfg +++ b/library/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = pms5003 -version = 0.0.5 +version = 0.0.6 author = Philip Howard author_email = phil@pimoroni.com description = PMS5003 Particulate Sensor From 0557bd7c9f3696a1e5bab0914661fda1129cb67d Mon Sep 17 00:00:00 2001 From: Christopher Pierce Date: Fri, 4 Feb 2022 16:30:51 -0500 Subject: [PATCH 3/7] adds influxdb line protocol output --- library/pms5003/__init__.py | 21 ++++++++++++++++++++- library/tests/test_setup.py | 1 + 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/library/pms5003/__init__.py b/library/pms5003/__init__.py index b25531a..45872f2 100644 --- a/library/pms5003/__init__.py +++ b/library/pms5003/__init__.py @@ -23,10 +23,18 @@ class SerialTimeoutError(RuntimeError): class PMS5003Data(): - def __init__(self, raw_data): + def __init__(self, raw_data, timestamp=None): + """ + Object to store the output of the PMS5003 sensor + :param raw_data: raw data from the serial output + :param timestamp: timestamp in ns of when the data was collected + """ self.raw_data = raw_data self.data = struct.unpack(">HHHHHHHHHHHHHH", raw_data) self.checksum = self.data[13] + if timestamp is None: + timestamp = time.time_ns() + self.timestamp = timestamp # The timestamp in ns def pm_ug_per_m3(self, size, atmospheric_environment=False): if atmospheric_environment: @@ -82,6 +90,17 @@ def get_all_counts(self): sizes = [0.3, 0.5, 1.0, 2.5, 5.0, 10.0] return {s: v for s, v in zip(sizes, self.data[6:])} + def as_influxdb_line_proto(self, meas_name='pms5003', timestamp=True): + """ + Get the data in the form of influxDB line protocol + :return: str: the formatted data + """ + ret = [f"{meas_name},size={x['size']},environment={'environment'} pm={x['val']}" for x in self.get_all_pm()] + ret.extend([f"{meas_name},size={s} count={c}" for s, c in self.get_all_counts().items()]) + if timestamp: + ret = [x + f' {self.timestamp}' for x in ret] + return '\n'.join(ret) + def __repr__(self): return """ PM1.0 ug/m3 (ultrafine particles): {} diff --git a/library/tests/test_setup.py b/library/tests/test_setup.py index 1d1a5e4..d6e79d8 100644 --- a/library/tests/test_setup.py +++ b/library/tests/test_setup.py @@ -60,6 +60,7 @@ def test_read(): data.get_all_pm() data.get_all_counts() dict(data) + data.as_influxdb_line_proto() def test_read_fail(): From a7bf4d371228755f81d360f242291bee8444f8d4 Mon Sep 17 00:00:00 2001 From: Christopher Pierce Date: Fri, 4 Feb 2022 16:38:24 -0500 Subject: [PATCH 4/7] missed some square brackets --- library/pms5003/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/pms5003/__init__.py b/library/pms5003/__init__.py index 45872f2..3be835b 100644 --- a/library/pms5003/__init__.py +++ b/library/pms5003/__init__.py @@ -95,7 +95,7 @@ def as_influxdb_line_proto(self, meas_name='pms5003', timestamp=True): Get the data in the form of influxDB line protocol :return: str: the formatted data """ - ret = [f"{meas_name},size={x['size']},environment={'environment'} pm={x['val']}" for x in self.get_all_pm()] + ret = [f"{meas_name},size={x['size']},environment={x['environment']} pm={x['val']}" for x in self.get_all_pm()] ret.extend([f"{meas_name},size={s} count={c}" for s, c in self.get_all_counts().items()]) if timestamp: ret = [x + f' {self.timestamp}' for x in ret] From fb111c6b1be526fb08f45cd7fd8a5d96acbaf646 Mon Sep 17 00:00:00 2001 From: Christopher Pierce Date: Fri, 4 Feb 2022 16:58:22 -0500 Subject: [PATCH 5/7] add type to influxdb line protocol --- library/pms5003/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/pms5003/__init__.py b/library/pms5003/__init__.py index 3be835b..ee40d08 100644 --- a/library/pms5003/__init__.py +++ b/library/pms5003/__init__.py @@ -95,8 +95,8 @@ def as_influxdb_line_proto(self, meas_name='pms5003', timestamp=True): Get the data in the form of influxDB line protocol :return: str: the formatted data """ - ret = [f"{meas_name},size={x['size']},environment={x['environment']} pm={x['val']}" for x in self.get_all_pm()] - ret.extend([f"{meas_name},size={s} count={c}" for s, c in self.get_all_counts().items()]) + ret = [f"{meas_name},size={x['size']},environment={x['environment']} pm={x['val']}u" for x in self.get_all_pm()] + ret.extend([f"{meas_name},size={s} count={c}u" for s, c in self.get_all_counts().items()]) if timestamp: ret = [x + f' {self.timestamp}' for x in ret] return '\n'.join(ret) From 3b62d69a302e7cf8d31764bf2ff80c36e3165423 Mon Sep 17 00:00:00 2001 From: "Christopher M. Pierce" Date: Tue, 8 Feb 2022 11:44:09 -0500 Subject: [PATCH 6/7] fixes to support older python versions --- library/pms5003/__init__.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/library/pms5003/__init__.py b/library/pms5003/__init__.py index ee40d08..051872e 100644 --- a/library/pms5003/__init__.py +++ b/library/pms5003/__init__.py @@ -27,13 +27,13 @@ def __init__(self, raw_data, timestamp=None): """ Object to store the output of the PMS5003 sensor :param raw_data: raw data from the serial output - :param timestamp: timestamp in ns of when the data was collected + :param timestamp: float, seconds since epoch in UTC; timestamp of when data was collected """ self.raw_data = raw_data self.data = struct.unpack(">HHHHHHHHHHHHHH", raw_data) self.checksum = self.data[13] if timestamp is None: - timestamp = time.time_ns() + timestamp = time.time() self.timestamp = timestamp # The timestamp in ns def pm_ug_per_m3(self, size, atmospheric_environment=False): @@ -93,12 +93,16 @@ def get_all_counts(self): def as_influxdb_line_proto(self, meas_name='pms5003', timestamp=True): """ Get the data in the form of influxDB line protocol + + :param meas_name: str, the name of the measurement as will show up in influxdb + :param timestamp: bool, include timestamp in the output or not :return: str: the formatted data """ - ret = [f"{meas_name},size={x['size']},environment={x['environment']} pm={x['val']}u" for x in self.get_all_pm()] - ret.extend([f"{meas_name},size={s} count={c}u" for s, c in self.get_all_counts().items()]) + ret = ["{name},size={size},environment={environment} pm={val}u".format(name=meas_name, **x) for x in + self.get_all_pm()] + ret.extend(["{},size={} count={}u".format(meas_name, s, c) for s, c in self.get_all_counts().items()]) if timestamp: - ret = [x + f' {self.timestamp}' for x in ret] + ret = ["{} {}".format(x, int(1e9 * self.timestamp)) for x in ret] return '\n'.join(ret) def __repr__(self): From d942e08aa51d5e201a7007851ef74435b5973122 Mon Sep 17 00:00:00 2001 From: "Christopher M. Pierce" Date: Wed, 9 Feb 2022 01:29:50 -0500 Subject: [PATCH 7/7] add example of using influxdb line protocol --- examples/telegraf_influxdb.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 examples/telegraf_influxdb.py diff --git a/examples/telegraf_influxdb.py b/examples/telegraf_influxdb.py new file mode 100644 index 0000000..a1058c7 --- /dev/null +++ b/examples/telegraf_influxdb.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +""" +Call this script from telegraf by adding the following lines to telegraf.conf + +[[inputs.exec]] + commands = ["python