Skip to content

Commit 48d2622

Browse files
committed
Added some error handling
Minimal error checking, simply that the bytearray is the correct size. Otherwise it is kind of up to the user to decide what a valid looking result is.
1 parent 6855d2e commit 48d2622

4 files changed

Lines changed: 87 additions & 22 deletions

File tree

atmotube/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from .packets import InvalidByteData
12
from .packets import StatusPacket, SPS30Packet, BME280Packet, SGPC3Packet
23
from .uuids import AtmoTube_GATT_UUIDs
34

atmotube/packets.py

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,17 @@
22
import time
33

44

5+
class InvalidByteData(Exception):
6+
pass
7+
8+
59
class AtmoTubePacket(LittleEndianStructure):
10+
_byte_size_ = 0 # To be defined in subclasses
11+
612
def __new__(cls, data, ts=None):
13+
if len(data) != cls._byte_size_:
14+
raise InvalidByteData(f"Expected {cls._byte_size_} bytes, "
15+
f"got {len(data)} bytes")
716
return cls.from_buffer_copy(data)
817

918
def __init__(self, data, ts=None):
@@ -12,6 +21,9 @@ def __init__(self, data, ts=None):
1221
self.timestamp = ts
1322
self._process_bytes()
1423

24+
def __repr__(self):
25+
return str(self)
26+
1527
def _process_bytes(self):
1628
...
1729

@@ -29,6 +41,8 @@ class StatusPacket(AtmoTubePacket):
2941
("_battery", c_ubyte, 8),
3042
]
3143

44+
_byte_size_ = 2
45+
3246
def _process_bytes(self):
3347
self.pm_sensor_status = bool(self._pm_sensor)
3448
self.error_flag = bool(self._error)
@@ -39,7 +53,7 @@ def _process_bytes(self):
3953
self.battery_level = self._battery
4054

4155
def __str__(self):
42-
return (f"StatusPacket("
56+
return (f"StatusPacket(timestamp={time.ctime(self.timestamp)}, "
4357
f"pm_sensor_status={self.pm_sensor_status}, "
4458
f"error_flag={self.error_flag}, "
4559
f"bonding_flag={self.bonding_flag}, "
@@ -56,7 +70,10 @@ class SPS30Packet(AtmoTubePacket):
5670
('_pm10', c_byte*3),
5771
('_pm4', c_byte*3),
5872
]
73+
5974
_pack_ = 1
75+
_layout_ = "ms"
76+
_byte_size_ = 12
6077

6178
def pm_from_bytes(self, byte_array):
6279
res = int.from_bytes(byte_array, byteorder='little', signed=True)
@@ -69,7 +86,8 @@ def _process_bytes(self):
6986
self.pm4 = self.pm_from_bytes(self._pm4)
7087

7188
def __str__(self):
72-
return (f"SPS30Packet(pm1={self.pm1}µg/m³, pm2_5={self.pm2_5}µg/m³, "
89+
return (f"SPS30Packet(timestamp={time.ctime(self.timestamp)}, "
90+
f"pm1={self.pm1}µg/m³, pm2_5={self.pm2_5}µg/m³, "
7391
f"pm10={self.pm10}µg/m³, pm4={self.pm4}µg/m³)")
7492

7593

@@ -80,15 +98,19 @@ class BME280Packet(AtmoTubePacket):
8098
('_P', c_int),
8199
('_T_dec', c_short),
82100
]
101+
83102
_pack_ = 1
103+
_layout_ = "ms"
104+
_byte_size_ = 8
84105

85106
def _process_bytes(self):
86107
self.humidity = self._rh if self._rh > 0 else None
87108
self.temperature = self._T_dec / 100.0
88109
self.pressure = self._P / 100.0 if self._P > 0 else None
89110

90111
def __str__(self):
91-
return (f"BME280Packet(humidity={self.humidity}%, "
112+
return (f"BME280Packet(timestamp={time.ctime(self.timestamp)}, "
113+
f"humidity={self.humidity}%, "
92114
f"temperature={self.temperature}°C, "
93115
f"pressure={self.pressure}mbar)")
94116

@@ -97,10 +119,14 @@ class SGPC3Packet(AtmoTubePacket):
97119
_fields_ = [
98120
('_tvoc', c_short)
99121
]
122+
100123
_pack_ = 1
124+
_layout_ = "ms"
125+
_byte_size_ = 4
101126

102127
def _process_bytes(self):
103128
self.tvoc = self._tvoc/1000.0 if self._tvoc > 0 else None
104129

105130
def __str__(self):
106-
return f"SGPC3Packet(tvoc={self.tvoc}ppb)"
131+
return (f"SGPC3Packet(timestamp={time.ctime(self.timestamp)}, "
132+
f"tvoc={self.tvoc}ppb)")

examples/data_logging_example.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,5 @@ async def runner():
9090

9191

9292
if __name__ == "__main__":
93-
logger = logging.getLogger()
94-
logger.setLevel(logging.INFO)
93+
logging.basicConfig(level=logging.INFO)
9594
main()

tests/test_packets.py

Lines changed: 55 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,85 @@
1+
from atmotube import InvalidByteData
12
from atmotube import StatusPacket, SPS30Packet, BME280Packet, SGPC3Packet
23

4+
import pytest
5+
import time
6+
7+
# Status Packet tests
8+
timestamp = 1766865019.792235
39
status_test_byte = bytearray(b'Ad')
4-
sps30_test_byte = bytearray(b'd\x00\x00\xb9\x00\x00J\x01\x00o\x00\x00')
5-
bme280_test_byte = bytearray(b'\x0e\x17\x8ao\x01\x00\x1a\t')
6-
sgpc3_test_byte = bytearray(b'\x02\x00\x00\x00')
10+
status_test_invalid_byte = bytearray(b'A')
11+
status_packet_str = (f"StatusPacket(timestamp={time.ctime(timestamp)}, "
12+
f"pm_sensor_status=True, "
13+
f"error_flag=False, bonding_flag=False, "
14+
f"charging=False, charging_timer=False, "
15+
f"pre_heating=True, battery_level=100%)")
716

817

918
def test_status_packet():
10-
packet = StatusPacket(status_test_byte)
19+
packet = StatusPacket(status_test_byte, ts=timestamp)
1120
assert packet.pm_sensor_status is True
1221
assert packet.error_flag is False
1322
assert packet.bonding_flag is False
1423
assert packet.charging is False
1524
assert packet.charging_timer is False
1625
assert packet.pre_heating is True
1726
assert packet.battery_level == 100
18-
assert str(packet) == ("StatusPacket(pm_sensor_status=True, "
19-
"error_flag=False, bonding_flag=False, "
20-
"charging=False, charging_timer=False, "
21-
"pre_heating=True, battery_level=100%)")
27+
assert str(packet) == status_packet_str
28+
assert repr(packet) == status_packet_str
29+
with pytest.raises(InvalidByteData):
30+
StatusPacket(status_test_invalid_byte, ts=timestamp)
31+
32+
33+
# SPS30 Packet tests
34+
sps30_test_byte = bytearray(b'd\x00\x00\xb9\x00\x00J\x01\x00o\x00\x00')
35+
sps30_test_invalid_byte = bytearray(b'd\x00\x00\xb9\x00\x00J\x01')
36+
sps30_test_str = (f"SPS30Packet(timestamp={time.ctime(timestamp)}, "
37+
f"pm1=1.0µg/m³, pm2_5=1.85µg/m³, "
38+
f"pm10=3.3µg/m³, pm4=1.11µg/m³)")
2239

2340

2441
def test_sps30_packet():
25-
packet = SPS30Packet(sps30_test_byte)
42+
packet = SPS30Packet(sps30_test_byte, ts=timestamp)
2643
assert packet.pm1 == 1.0
2744
assert packet.pm2_5 == 1.85
2845
assert packet.pm10 == 3.3
2946
assert packet.pm4 == 1.11
30-
assert str(packet) == ("SPS30Packet(pm1=1.0µg/m³, pm2_5=1.85µg/m³, "
31-
"pm10=3.3µg/m³, pm4=1.11µg/m³)")
47+
assert str(packet) == sps30_test_str
48+
assert repr(packet) == sps30_test_str
49+
with pytest.raises(InvalidByteData):
50+
SPS30Packet(sps30_test_invalid_byte, ts=timestamp)
51+
52+
53+
# BME280 Packet tests
54+
bme280_test_byte = bytearray(b'\x0e\x17\x8ao\x01\x00\x1a\t')
55+
bme280_test_invalid_byte = bytearray(b'\x0e\x17\x8ao\x01')
56+
bme280_test_str = (f"BME280Packet(timestamp={time.ctime(timestamp)}, "
57+
f"humidity=14%, temperature=23.3°C, "
58+
f"pressure=940.9mbar)")
3259

3360

3461
def test_bme280_packet():
35-
packet = BME280Packet(bme280_test_byte)
62+
packet = BME280Packet(bme280_test_byte, ts=timestamp)
3663
assert packet.humidity == 14
3764
assert packet.temperature == 23.3
3865
assert packet.pressure == 940.9
39-
assert str(packet) == ("BME280Packet(humidity=14%, temperature=23.3°C, "
40-
"pressure=940.9mbar)")
66+
assert str(packet) == bme280_test_str
67+
assert repr(packet) == bme280_test_str
68+
with pytest.raises(InvalidByteData):
69+
BME280Packet(bme280_test_invalid_byte, ts=timestamp)
70+
71+
72+
# SGPC3 Packet tests
73+
sgpc3_test_byte = bytearray(b'\x02\x00\x00\x00')
74+
sgpc3_test_invalid_byte = bytearray(b'\x02\x00\x00')
75+
sgpc3_test_str = (f"SGPC3Packet(timestamp={time.ctime(timestamp)}, "
76+
f"tvoc=0.002ppb)")
4177

4278

4379
def test_sgpc3_packet():
44-
packet = SGPC3Packet(sgpc3_test_byte)
80+
packet = SGPC3Packet(sgpc3_test_byte, ts=timestamp)
4581
assert packet.tvoc == 0.002
46-
assert str(packet) == "SGPC3Packet(tvoc=0.002ppb)"
82+
assert str(packet) == sgpc3_test_str
83+
assert repr(packet) == sgpc3_test_str
84+
with pytest.raises(InvalidByteData):
85+
SGPC3Packet(sgpc3_test_invalid_byte, ts=timestamp)

0 commit comments

Comments
 (0)