owlsensor 0.2__tar.gz → 0.3.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {owlsensor-0.2 → owlsensor-0.3.1}/PKG-INFO +1 -1
- owlsensor-0.3.1/README.rst +42 -0
- owlsensor-0.3.1/owlsensor/__init__.py +13 -0
- {owlsensor-0.2 → owlsensor-0.3.1}/owlsensor/const.py +10 -1
- {owlsensor-0.2 → owlsensor-0.3.1}/owlsensor/serial_cm.py +46 -30
- {owlsensor-0.2 → owlsensor-0.3.1}/owlsensor.egg-info/PKG-INFO +1 -1
- {owlsensor-0.2 → owlsensor-0.3.1}/setup.py +1 -1
- owlsensor-0.2/README.rst +0 -7
- owlsensor-0.2/owlsensor/__init__.py +0 -0
- {owlsensor-0.2 → owlsensor-0.3.1}/LICENSE +0 -0
- {owlsensor-0.2 → owlsensor-0.3.1}/owlsensor.egg-info/SOURCES.txt +0 -0
- {owlsensor-0.2 → owlsensor-0.3.1}/owlsensor.egg-info/dependency_links.txt +0 -0
- {owlsensor-0.2 → owlsensor-0.3.1}/owlsensor.egg-info/not-zip-safe +0 -0
- {owlsensor-0.2 → owlsensor-0.3.1}/owlsensor.egg-info/requires.txt +0 -0
- {owlsensor-0.2 → owlsensor-0.3.1}/owlsensor.egg-info/top_level.txt +0 -0
- {owlsensor-0.2 → owlsensor-0.3.1}/setup.cfg +0 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
owlsensor - Library for OWL CM 160 Energy meter
|
|
2
|
+
================================================
|
|
3
|
+
|
|
4
|
+
This library lets you read sensor data from serial-connected OWL Energy meter.
|
|
5
|
+
It current supports the following model:
|
|
6
|
+
|
|
7
|
+
- CM 160
|
|
8
|
+
|
|
9
|
+
Usage
|
|
10
|
+
=====
|
|
11
|
+
|
|
12
|
+
* Create a CMDataCollector with port name as first argument, and OWL model as second argument,
|
|
13
|
+
|
|
14
|
+
```python
|
|
15
|
+
cm.CMDataCollector("COM4", cm.SUPPORTED_SENSORS["TheOWL,CM160"])
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
```python
|
|
19
|
+
cm.CMDataCollector("/dev/ttyUSB0", cm.SUPPORTED_SENSORS["TheOWL,CM160"])
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
* You can add several devices the same way, on different serial ports.
|
|
23
|
+
|
|
24
|
+
* Connect will be called automatically by read_data.
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
await s.connect()
|
|
28
|
+
await s.read_data()
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
* read_data returns a dict (CMVALS=[CURRENT]) containing the acquired real-time value. Currently only current in ampere is returned.
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
print(await s.read_data())
|
|
35
|
+
|
|
36
|
+
{'Current': 4.1}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Limitations
|
|
40
|
+
===========
|
|
41
|
+
|
|
42
|
+
* Historical data returned by the device at connection is discarded, only realtime transmissions are available
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from .serial_cm import CMDataCollector, SUPPORTED_SENSORS
|
|
2
|
+
|
|
3
|
+
async def get_async_datacollector(port_url: str, model: str, scan_interval_s: int = 30) -> CMDataCollector:
|
|
4
|
+
"""
|
|
5
|
+
Return asynchronous version of CMDataCollector interface
|
|
6
|
+
:param port_url: serial port, i.e. '/dev/ttyUSB0'
|
|
7
|
+
:param model: device type, i.e. "CM160"
|
|
8
|
+
:return: asynchronous implementation of Monoprice interface
|
|
9
|
+
"""
|
|
10
|
+
if not model in SUPPORTED_SENSORS:
|
|
11
|
+
return None
|
|
12
|
+
|
|
13
|
+
return CMDataCollector(port_url, SUPPORTED_SENSORS[model], scan_interval_s)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
""" Constants """
|
|
2
|
-
|
|
2
|
+
|
|
3
3
|
ID_REPLY = "IDTCMV001"
|
|
4
4
|
ID_WAIT_HISTORY = "IDTWAITPC"
|
|
5
5
|
CONTINUE_REQUEST = [0xa5]
|
|
@@ -7,3 +7,12 @@ START_REQUEST = [0x5a]
|
|
|
7
7
|
PACKET_ID_HISTORY = 0xa9
|
|
8
8
|
PACKET_ID_HISTORY_DATA = 0x59
|
|
9
9
|
PACKET_ID_REALTIME = 0x51
|
|
10
|
+
|
|
11
|
+
RECORD_LENGTH = "RL"
|
|
12
|
+
CURRENT = "Current"
|
|
13
|
+
BAUD_RATE = "BAUD"
|
|
14
|
+
BYTE_ORDER = "BO",
|
|
15
|
+
LSB = "lsb"
|
|
16
|
+
MSB = "msb"
|
|
17
|
+
MULTIPLIER = "MP"
|
|
18
|
+
TIMEOUT = "TO"
|
|
@@ -4,27 +4,15 @@ Reading data from particulate matter sensors with a serial interface.
|
|
|
4
4
|
import time
|
|
5
5
|
import logging
|
|
6
6
|
import asyncio
|
|
7
|
+
from serial import SerialException
|
|
7
8
|
import serial_asyncio_fast
|
|
8
9
|
|
|
9
|
-
from .const import
|
|
10
|
-
|
|
11
|
-
STARTBLOCK = "SB"
|
|
12
|
-
RECORD_LENGTH = "RL"
|
|
13
|
-
# Ofsets of the PM data (always 2 byte)
|
|
14
|
-
CURRENT = "Current"
|
|
15
|
-
BAUD_RATE = "BAUD"
|
|
16
|
-
BYTE_ORDER = "BO",
|
|
17
|
-
LSB = "lsb"
|
|
18
|
-
MSB = "msb"
|
|
19
|
-
DTR_ON = "DTR"
|
|
20
|
-
DTR_OFF = "NOT_DTR"
|
|
21
|
-
MULTIPLIER = "MP"
|
|
22
|
-
TIMEOUT = "TO"
|
|
10
|
+
from .const import *
|
|
11
|
+
|
|
23
12
|
|
|
24
13
|
# Owl CM160 settings
|
|
25
14
|
OWL_CM160 = {
|
|
26
15
|
"TheOWL": "CM160",
|
|
27
|
-
STARTBLOCK: bytes([0x42, 0x4d, 0x00, 0x14]),
|
|
28
16
|
RECORD_LENGTH: 11,
|
|
29
17
|
CURRENT: 8,
|
|
30
18
|
BAUD_RATE: 250000,
|
|
@@ -34,7 +22,7 @@ OWL_CM160 = {
|
|
|
34
22
|
}
|
|
35
23
|
|
|
36
24
|
SUPPORTED_SENSORS = {
|
|
37
|
-
"
|
|
25
|
+
"CM160": OWL_CM160
|
|
38
26
|
}
|
|
39
27
|
|
|
40
28
|
DEVICE_STATES = {
|
|
@@ -75,23 +63,41 @@ class CMDataCollector():
|
|
|
75
63
|
self.reader = None
|
|
76
64
|
self.writer = None
|
|
77
65
|
self.baudrate = configuration[BAUD_RATE]
|
|
66
|
+
self.connected = False
|
|
67
|
+
self.updateTask = None
|
|
78
68
|
|
|
79
|
-
async def connect(self):
|
|
69
|
+
async def connect(self) -> bool:
|
|
80
70
|
"""Establish the serial connection asynchronously."""
|
|
81
|
-
self.
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
71
|
+
self.connected = False
|
|
72
|
+
try:
|
|
73
|
+
self.reader, self.writer = await serial_asyncio_fast.open_serial_connection(
|
|
74
|
+
url=self.serialdevice,
|
|
75
|
+
baudrate=self.baudrate
|
|
76
|
+
)
|
|
77
|
+
except SerialException as ex:
|
|
78
|
+
LOGGER.warning("Connect: %s", ex)
|
|
79
|
+
return False
|
|
80
|
+
|
|
81
|
+
self.connected = True
|
|
82
|
+
|
|
83
|
+
if self.updateTask is not None:
|
|
84
|
+
try:
|
|
85
|
+
self.updateTask.cancel()
|
|
86
|
+
self.updateTask = None
|
|
87
|
+
except Exception as e:
|
|
88
|
+
LOGGER.warning("Exception while cancelling update Task: %s", e)
|
|
85
89
|
|
|
86
90
|
if self.scan_interval > 0:
|
|
87
|
-
asyncio.create_task(self.refresh())
|
|
91
|
+
self.updateTask = asyncio.create_task(self.refresh())
|
|
92
|
+
|
|
93
|
+
return True
|
|
88
94
|
|
|
89
95
|
async def refresh(self):
|
|
90
96
|
"""Asynchronous background refreshing task."""
|
|
91
97
|
while True:
|
|
92
98
|
await self.read_data()
|
|
93
99
|
await asyncio.sleep(self.scan_interval)
|
|
94
|
-
|
|
100
|
+
|
|
95
101
|
async def send_data(self, data: bytes):
|
|
96
102
|
LOGGER.debug("-> %s", ''.join(format(x, '02x') for x in data))
|
|
97
103
|
self.writer.write(data)
|
|
@@ -143,8 +149,13 @@ class CMDataCollector():
|
|
|
143
149
|
|
|
144
150
|
return None
|
|
145
151
|
|
|
146
|
-
async def read_data(self):
|
|
152
|
+
async def read_data(self) -> dict | None:
|
|
147
153
|
"""Read data from the serial interface asynchronously."""
|
|
154
|
+
|
|
155
|
+
if not self.connected:
|
|
156
|
+
if not await self.connect():
|
|
157
|
+
return None
|
|
158
|
+
|
|
148
159
|
mytime = asyncio.get_event_loop().time()
|
|
149
160
|
if (self.last_poll is not None) and \
|
|
150
161
|
(mytime - self.last_poll) <= 15 and \
|
|
@@ -155,12 +166,17 @@ class CMDataCollector():
|
|
|
155
166
|
finished = False
|
|
156
167
|
|
|
157
168
|
while not finished:
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
169
|
+
try:
|
|
170
|
+
packet = await self.get_packet()
|
|
171
|
+
if packet:
|
|
172
|
+
result = await self.parse_packet(packet)
|
|
173
|
+
if result is not None:
|
|
174
|
+
res = result
|
|
175
|
+
finished = True
|
|
176
|
+
except SerialException as ex:
|
|
177
|
+
LOGGER.warning(ex)
|
|
178
|
+
self.connected = False
|
|
179
|
+
return None
|
|
164
180
|
|
|
165
181
|
self._data = res
|
|
166
182
|
self.last_poll = asyncio.get_event_loop().time()
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from setuptools import setup, find_packages
|
|
2
2
|
|
|
3
3
|
setup(name='owlsensor',
|
|
4
|
-
version='0.
|
|
4
|
+
version='0.3.1',
|
|
5
5
|
description='Library to read data from OWL Energy meters',
|
|
6
6
|
long_description='This package is designed for integrating into Home Assistant a serial-connected OWL energy meter.',
|
|
7
7
|
long_description_content_type = 'text/x-rst',
|
owlsensor-0.2/README.rst
DELETED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|