Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add dict interface #16

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions examples/telegraf_influxdb.py
Original file line number Diff line number Diff line change
@@ -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 <script location>/telegraf_influxdb.py"]
timeout = "2s"
data_format = "influx"
"""

from pms5003 import PMS5003


# Configure the PMS5003 for Enviro+
pms5003 = PMS5003(
device='/dev/ttyAMA0',
baudrate=9600,
pin_enable=22,
pin_reset=27
)

print(pms5003.read().as_influxdb_line_proto())
54 changes: 53 additions & 1 deletion library/pms5003/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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: 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()
self.timestamp = timestamp # The timestamp in ns

def pm_ug_per_m3(self, size, atmospheric_environment=False):
if atmospheric_environment:
Expand Down Expand Up @@ -63,6 +71,40 @@ 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 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 = ["{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 = ["{} {}".format(x, int(1e9 * self.timestamp)) for x in ret]
return '\n'.join(ret)

def __repr__(self):
return """
PM1.0 ug/m3 (ultrafine particles): {}
Expand All @@ -82,6 +124,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):
Expand Down
2 changes: 1 addition & 1 deletion library/setup.cfg
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[metadata]
name = pms5003
version = 0.0.5
version = 0.0.6
author = Philip Howard
author_email = [email protected]
description = PMS5003 Particulate Sensor
Expand Down
4 changes: 4 additions & 0 deletions library/tests/test_setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ 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)
data.as_influxdb_line_proto()


def test_read_fail():
Expand Down