Compare commits

..
16 Commits
Author SHA1 Message Date
Nigel Dokter 819d0d0696 updated version number 2016-11-22 19:54:19 +01:00
Nigel Dokter a3685f0310 added Travis CI badge 2016-11-21 22:31:22 +01:00
Nigel Dokter 5ae6ad4156 typo 2016-11-21 21:49:17 +01:00
Nigel Dokter e330e9db21 updated changelog 2016-11-21 21:48:17 +01:00
Nigel Dokter 0ac2990df2 accepted changed from pull request instead 2016-11-21 21:46:21 +01:00
Nigel Dokter e0d91f1ec4 Merge pull request #5 from aequitas/master
Even parity offers better compatibility for v2.2.
2016-11-21 21:45:38 +01:00
Nigel Dokter c058feca5f make serial.EVEN the default parity for DSMR v2.2 2016-11-21 21:30:56 +01:00
Nigel Dokter 6f3c74ce7c Merge pull request #8 from aequitas/async_protocol
Async protocol
2016-11-21 20:39:57 +01:00
Nigel Dokter 43a3cb7a96 Merge pull request #7 from aequitas/error_handling
Add error handling to async serial.
2016-11-21 20:28:11 +01:00
Johan Bloemberg 5e88182301 Complete telegram handling logic. 2016-11-21 17:02:32 +01:00
Johan Bloemberg e3569e0719 Add asyncio protocol implementation. 2016-11-21 15:48:29 +01:00
Johan Bloemberg f8a3c76c68 wip async test 2016-11-21 14:35:35 +01:00
Johan Bloemberg a1d077d6f2 Fix style. 2016-11-21 14:19:12 +01:00
Johan Bloemberg 616db8b1cc Add error handling to async serial. 2016-11-21 14:11:30 +01:00
Nigel Dokter 927a4bc8e7 added DSMR v4 parsing unit test; added alternative serial settings for DSMR v2 and v4; 2016-11-20 12:44:45 +01:00
Johan Bloemberg e7ff8f2444 Even parity offers better compatibility for v2.2. 2016-11-15 23:00:59 +01:00
12 changed files with 441 additions and 36 deletions
+9 -3
View File
@@ -1,13 +1,19 @@
Change Log
----------
**0.4** (2016-11-21)
- DSMR v2.2 serial settings now uses parity serial.EVEN by default (`pull request #5 <https://github.com/ndokter/dsmr_parser/pull/5>`_)
- improved asyncio reader and improve it's error handling (`pull request #8 <https://github.com/ndokter/dsmr_parser/pull/8>`_)
**0.3** (2016-11-12)
- Added asyncio reader for non-blocking reads. (thanks to https://github.com/aequitas)
- asyncio reader for non-blocking reads. (`pull request #3 <https://github.com/ndokter/dsmr_parser/pull/3>`_)
**0.2** (2016-11-08)
- Added support for DMSR version 2.2 (thanks to https://github.com/aequitas)
- support for DMSR version 2.2 (`pull request #2 <https://github.com/ndokter/dsmr_parser/pull/2>`_)
**0.1** (2016-08-22)
- Initial version with a serial reader and support for DSMR version 4.x
- initial version with a serial reader and support for DSMR version 4.x
+17 -8
View File
@@ -4,6 +4,9 @@ DSMR Parser
.. image:: https://img.shields.io/pypi/v/dsmr-parser.svg
:target: https://pypi.python.org/pypi/dsmr-parser
.. image:: https://travis-ci.org/ndokter/dsmr_parser.svg?branch=master
:target: https://travis-ci.org/ndokter/dsmr_parser
A library for parsing Dutch Smart Meter Requirements (DSMR) telegram data. It
also includes a serial client to directly read and parse smart meter data.
@@ -11,7 +14,7 @@ also includes a serial client to directly read and parse smart meter data.
Features
--------
DSMR Parser currently supports DSMR versions 2.2 and 4.x. It has been tested with Python 3.5 and 3.4.
DSMR Parser currently supports DSMR versions 2.2 and 4.x. It has been tested with Python 3.4 and 3.5.
Examples
@@ -22,7 +25,7 @@ Using the serial reader to connect to your smart meter and parse it's telegrams:
.. code-block:: python
from dsmr_parser import telegram_specifications
from dsmr_parser.obis_references import P1_MESSAGE_TIMESTAMP
from dsmr_parser import obis_references
from dsmr_parser.serial import SerialReader, SERIAL_SETTINGS_V4
serial_reader = SerialReader(
@@ -34,19 +37,19 @@ Using the serial reader to connect to your smart meter and parse it's telegrams:
for telegram in serial_reader.read():
# The telegram message timestamp.
message_datetime = telegram[P1_MESSAGE_TIMESTAMP]
message_datetime = telegram[obis_references.P1_MESSAGE_TIMESTAMP]
# Using the active tariff to determine the electricity being used and
# delivered for the right tariff.
tariff = telegram[ELECTRICITY_ACTIVE_TARIFF]
tariff = telegram[obis_references.ELECTRICITY_ACTIVE_TARIFF]
tariff = int(tariff.value)
electricity_used_total \
= telegram[ELECTRICITY_USED_TARIFF_ALL[tariff - 1]]
= telegram[obis_references.ELECTRICITY_USED_TARIFF_ALL[tariff - 1]]
electricity_delivered_total = \
telegram[ELECTRICITY_DELIVERED_TARIFF_ALL[tariff - 1]]
telegram[obis_referencesELECTRICITY_DELIVERED_TARIFF_ALL[tariff - 1]]
gas_reading = telegram[HOURLY_GAS_METER_READING]
gas_reading = telegram[obis_references.HOURLY_GAS_METER_READING]
# See dsmr_reader.obis_references for all readable telegram values.
@@ -60,10 +63,16 @@ To install DSMR Parser:
$ pip install dsmr-parser
Known issues
------------
If the serial settings SERIAL_SETTINGS_V2_2 or SERIAL_SETTINGS_V4 don't work.
Make sure to try and replace the parity settings to EVEN or NONE.
It's possible that alternative settings will be added in the future if these
settings don't work for the majority of meters.
TODO
----
- add unit tests
- verify telegram checksum
- improve ease of use
+18 -12
View File
@@ -1,6 +1,8 @@
import argparse
from dsmr_parser.serial import SERIAL_SETTINGS_V2_2, SERIAL_SETTINGS_V4, SerialReader
from dsmr_parser import telegram_specifications
import asyncio
import logging
from .protocol import create_dsmr_reader
def console():
@@ -11,22 +13,26 @@ def console():
help='port to read DSMR data from')
parser.add_argument('--version', default='2.2', choices=['2.2', '4'],
help='DSMR version (2.2, 4)')
parser.add_argument('--verbose', '-v', action='count')
args = parser.parse_args()
settings = {
'2.2': (SERIAL_SETTINGS_V2_2, telegram_specifications.V2_2),
'4': (SERIAL_SETTINGS_V4, telegram_specifications.V4),
}
if args.verbose:
level = logging.DEBUG
else:
level = logging.ERROR
logging.basicConfig(level=level)
serial_reader = SerialReader(
device=args.device,
serial_settings=settings[args.version][0],
telegram_specification=settings[args.version][1],
)
loop = asyncio.get_event_loop()
for telegram in serial_reader.read():
def print_callback(telegram):
"""Callback that prints telegram values."""
for obiref, obj in telegram.items():
if obj:
print(obj.value, obj.unit)
print()
conn = create_dsmr_reader(args.device, args.version, print_callback, loop=loop)
loop.create_task(conn)
loop.run_forever()
+1 -1
View File
@@ -36,7 +36,7 @@ class TelegramParser(object):
return telegram
def parse_line(self, line_value):
logger.debug('Parsing line\'%s\'', line_value)
logger.debug('Parsing line \'%s\'', line_value)
obis_reference, parser = self._find_line_parser(line_value)
+97
View File
@@ -0,0 +1,97 @@
"""Asyncio protocol implementation for handling telegrams."""
import asyncio
import logging
from functools import partial
from serial_asyncio import create_serial_connection
from . import telegram_specifications
from .exceptions import ParseError
from .parsers import TelegramParser, TelegramParserV2_2
from .serial import (SERIAL_SETTINGS_V2_2, SERIAL_SETTINGS_V4,
is_end_of_telegram, is_start_of_telegram)
def create_dsmr_reader(port, dsmr_version, telegram_callback, loop=None):
"""Creates a DSMR asyncio protocol coroutine."""
if dsmr_version == '2.2':
specifications = telegram_specifications.V2_2
telegram_parser = TelegramParserV2_2
serial_settings = SERIAL_SETTINGS_V2_2
elif dsmr_version == '4':
specifications = telegram_specifications.V4
telegram_parser = TelegramParser
serial_settings = SERIAL_SETTINGS_V4
serial_settings['url'] = port
protocol = partial(DSMRProtocol, loop, telegram_parser(specifications),
telegram_callback=telegram_callback)
conn = create_serial_connection(loop, protocol, **serial_settings)
return conn
class DSMRProtocol(asyncio.Protocol):
"""Assemble and handle incoming data into complete DSM telegrams."""
transport = None
telegram_callback = None
def __init__(self, loop, telegram_parser, telegram_callback=None):
"""Initialize class."""
self.loop = loop
self.log = logging.getLogger(__name__)
self.telegram_parser = telegram_parser
# callback to call on complete telegram
self.telegram_callback = telegram_callback
# buffer to keep incoming telegram lines
self.telegram = []
# buffer to keep incomplete incoming data
self.buffer = ''
def connection_made(self, transport):
"""Just logging for now."""
self.transport = transport
self.log.debug('connected')
def data_received(self, data):
"""Add incoming data to buffer."""
data = data.decode()
self.log.debug('received data: %s', data.strip())
self.buffer += data
self.handle_lines()
def handle_lines(self):
"""Assemble incoming data into single lines."""
while "\r\n" in self.buffer:
line, self.buffer = self.buffer.split("\r\n", 1)
self.log.debug('got line: %s', line)
# Telegrams need to be complete because the values belong to a
# particular reading and can also be related to eachother.
if not self.telegram and not is_start_of_telegram(line):
continue
self.telegram.append(line)
if is_end_of_telegram(line):
try:
parsed_telegram = self.telegram_parser.parse(self.telegram)
self.handle_telegram(parsed_telegram)
except ParseError:
self.log.exception("failed to parse telegram")
self.telegram = []
def connection_lost(self, exc):
"""Stop when connection is lost."""
self.log.error('disconnected')
def handle_telegram(self, telegram):
"""Send off parsed telegram to handling callback."""
self.log.debug('got telegram: %s', telegram)
if self.telegram_callback:
self.telegram_callback(telegram)
+16 -4
View File
@@ -1,12 +1,19 @@
import serial
import asyncio
import logging
import serial
import serial_asyncio
from dsmr_parser.exceptions import ParseError
from dsmr_parser.parsers import TelegramParser, TelegramParserV2_2
logger = logging.getLogger(__name__)
SERIAL_SETTINGS_V2_2 = {
'baudrate': 9600,
'bytesize': serial.SEVENBITS,
'parity': serial.PARITY_NONE,
'parity': serial.PARITY_EVEN,
'stopbits': serial.STOPBITS_ONE,
'xonxoff': 0,
'rtscts': 0,
@@ -108,6 +115,11 @@ class AsyncSerialReader(SerialReader):
telegram.append(line)
if is_end_of_telegram(line):
# push new parsed telegram onto queue
queue.put_nowait(self.telegram_parser.parse(telegram))
try:
parsed_telegram = self.telegram_parser.parse(telegram)
# push new parsed telegram onto queue
queue.put_nowait(parsed_telegram)
except ParseError:
logger.exception("failed to parse telegram")
telegram = []
+1 -1
View File
@@ -6,7 +6,7 @@ setup(
author='Nigel Dokter',
author_email='nigeldokter@gmail.com',
url='https://github.com/ndokter/dsmr_parser',
version='0.3',
version='0.4',
packages=find_packages(),
install_requires=[
'pyserial>=3,<4',
View File
@@ -1,8 +1,8 @@
"""Test telegram parsing."""
"""Test parsing of a DSMR v2.2 telegram."""
from dsmr_parser.parsers import TelegramParserV2_2
from dsmr_parser import telegram_specifications
from dsmr_parser.obis_references import CURRENT_ELECTRICITY_USAGE, GAS_METER_READING
from dsmr_parser import obis_references as obis
TELEGRAM_V2_2 = [
"/ISk5\2MT382-1004",
@@ -28,13 +28,14 @@ TELEGRAM_V2_2 = [
]
def test_parse_v2_2():
def test_parse():
"""Test if telegram parsing results in correct results."""
parser = TelegramParserV2_2(telegram_specifications.V2_2)
result = parser.parse(TELEGRAM_V2_2)
assert float(result[CURRENT_ELECTRICITY_USAGE].value) == 1.01
assert result[CURRENT_ELECTRICITY_USAGE].unit == 'kW'
assert float(result[GAS_METER_READING].value) == 1.001
assert result[GAS_METER_READING].unit == 'm3'
assert float(result[obis.CURRENT_ELECTRICITY_USAGE].value) == 1.01
assert result[obis.CURRENT_ELECTRICITY_USAGE].unit == 'kW'
assert float(result[obis.GAS_METER_READING].value) == 1.001
assert result[obis.GAS_METER_READING].unit == 'm3'
+232
View File
@@ -0,0 +1,232 @@
"""Test parsing of a DSMR v4.2 telegram."""
import datetime
from decimal import Decimal
import pytz
from dsmr_parser import obis_references as obis
from dsmr_parser import telegram_specifications
from dsmr_parser.objects import CosemObject, MBusObject
from dsmr_parser.parsers import TelegramParser
TELEGRAM_V4_2 = [
'1-3:0.2.8(42)',
'0-0:1.0.0(161113205757W)',
'0-0:96.1.1(1231231231231231231231231231231231)',
'1-0:1.8.1(001511.267*kWh)',
'1-0:1.8.2(001265.173*kWh)',
'1-0:2.8.1(000000.000*kWh)',
'1-0:2.8.2(000000.000*kWh)',
'0-0:96.14.0(0001)',
'1-0:1.7.0(00.235*kW)',
'1-0:2.7.0(00.000*kW)',
'0-0:96.7.21(00015)',
'0-0:96.7.9(00007)',
('1-0:99.97.0(3)(0-0:96.7.19)(000103180420W)(0000237126*s)'
'(000101000001W)(2147483647*s)(000101000001W)(2147483647*s)'),
'1-0:32.32.0(00000)',
'1-0:52.32.0(00000)',
'1-0:72.32.0(00000)',
'1-0:32.36.0(00000)',
'1-0:52.36.0(00000)',
'1-0:72.36.0(00000)',
'0-0:96.13.1()',
'0-0:96.13.0()',
'1-0:31.7.0(000*A)',
'1-0:51.7.0(000*A)',
'1-0:71.7.0(000*A)',
'1-0:21.7.0(00.095*kW)',
'1-0:22.7.0(00.000*kW)',
'1-0:41.7.0(00.025*kW)',
'1-0:42.7.0(00.000*kW)',
'1-0:61.7.0(00.115*kW)',
'1-0:62.7.0(00.000*kW)',
'0-1:24.1.0(003)',
'0-1:96.1.0(3404856892390357246729543587524029)',
'0-1:24.2.1(161113200000W)(00915.219*m3)',
'!5D83',
]
def test_parse():
parser = TelegramParser(telegram_specifications.V4)
result = parser.parse(TELEGRAM_V4_2)
# P1_MESSAGE_HEADER (1-3:0.2.8)
assert isinstance(result[obis.P1_MESSAGE_HEADER], CosemObject)
assert result[obis.P1_MESSAGE_HEADER].unit is None
assert isinstance(result[obis.P1_MESSAGE_HEADER].value, str)
assert result[obis.P1_MESSAGE_HEADER].value == '42'
# P1_MESSAGE_TIMESTAMP (0-0:1.0.0)
assert isinstance(result[obis.P1_MESSAGE_TIMESTAMP], CosemObject)
assert result[obis.P1_MESSAGE_TIMESTAMP].unit is None
assert isinstance(result[obis.P1_MESSAGE_TIMESTAMP].value, datetime.datetime)
assert result[obis.P1_MESSAGE_TIMESTAMP].value == \
datetime.datetime(2016, 11, 13, 19, 57, 57, tzinfo=pytz.UTC)
# ELECTRICITY_USED_TARIFF_1 (1-0:1.8.1)
assert isinstance(result[obis.ELECTRICITY_USED_TARIFF_1], CosemObject)
assert result[obis.ELECTRICITY_USED_TARIFF_1].unit == 'kWh'
assert isinstance(result[obis.ELECTRICITY_USED_TARIFF_1].value, Decimal)
assert result[obis.ELECTRICITY_USED_TARIFF_1].value == Decimal('1511.267')
# ELECTRICITY_USED_TARIFF_2 (1-0:1.8.2)
assert isinstance(result[obis.ELECTRICITY_USED_TARIFF_2], CosemObject)
assert result[obis.ELECTRICITY_USED_TARIFF_2].unit == 'kWh'
assert isinstance(result[obis.ELECTRICITY_USED_TARIFF_2].value, Decimal)
assert result[obis.ELECTRICITY_USED_TARIFF_2].value == Decimal('1265.173')
# ELECTRICITY_DELIVERED_TARIFF_1 (1-0:2.8.1)
assert isinstance(result[obis.ELECTRICITY_DELIVERED_TARIFF_1], CosemObject)
assert result[obis.ELECTRICITY_DELIVERED_TARIFF_1].unit == 'kWh'
assert isinstance(result[obis.ELECTRICITY_DELIVERED_TARIFF_1].value, Decimal)
assert result[obis.ELECTRICITY_DELIVERED_TARIFF_1].value == Decimal('0')
# ELECTRICITY_DELIVERED_TARIFF_2 (1-0:2.8.2)
assert isinstance(result[obis.ELECTRICITY_DELIVERED_TARIFF_2], CosemObject)
assert result[obis.ELECTRICITY_DELIVERED_TARIFF_2].unit == 'kWh'
assert isinstance(result[obis.ELECTRICITY_DELIVERED_TARIFF_2].value, Decimal)
assert result[obis.ELECTRICITY_DELIVERED_TARIFF_2].value == Decimal('0')
# ELECTRICITY_ACTIVE_TARIFF (0-0:96.14.0)
assert isinstance(result[obis.ELECTRICITY_ACTIVE_TARIFF], CosemObject)
assert result[obis.ELECTRICITY_ACTIVE_TARIFF].unit is None
assert isinstance(result[obis.ELECTRICITY_ACTIVE_TARIFF].value, str)
assert result[obis.ELECTRICITY_ACTIVE_TARIFF].value == '0001'
# EQUIPMENT_IDENTIFIER (0-0:96.1.1)
assert isinstance(result[obis.EQUIPMENT_IDENTIFIER], CosemObject)
assert result[obis.EQUIPMENT_IDENTIFIER].unit is None
assert isinstance(result[obis.EQUIPMENT_IDENTIFIER].value, str)
assert result[obis.EQUIPMENT_IDENTIFIER].value == '1231231231231231231231231231231231'
# CURRENT_ELECTRICITY_USAGE (1-0:1.7.0)
assert isinstance(result[obis.CURRENT_ELECTRICITY_USAGE], CosemObject)
assert result[obis.CURRENT_ELECTRICITY_USAGE].unit == 'kW'
assert isinstance(result[obis.CURRENT_ELECTRICITY_USAGE].value, Decimal)
assert result[obis.CURRENT_ELECTRICITY_USAGE].value == Decimal('0.235')
# CURRENT_ELECTRICITY_DELIVERY (1-0:2.7.0)
assert isinstance(result[obis.CURRENT_ELECTRICITY_DELIVERY], CosemObject)
assert result[obis.CURRENT_ELECTRICITY_DELIVERY].unit == 'kW'
assert isinstance(result[obis.CURRENT_ELECTRICITY_DELIVERY].value, Decimal)
assert result[obis.CURRENT_ELECTRICITY_DELIVERY].value == Decimal('0')
# LONG_POWER_FAILURE_COUNT (96.7.9)
assert isinstance(result[obis.LONG_POWER_FAILURE_COUNT], CosemObject)
assert result[obis.LONG_POWER_FAILURE_COUNT].unit is None
assert isinstance(result[obis.LONG_POWER_FAILURE_COUNT].value, int)
assert result[obis.LONG_POWER_FAILURE_COUNT].value == 7
# VOLTAGE_SAG_L1_COUNT (1-0:32.32.0)
assert isinstance(result[obis.VOLTAGE_SAG_L1_COUNT], CosemObject)
assert result[obis.VOLTAGE_SAG_L1_COUNT].unit is None
assert isinstance(result[obis.VOLTAGE_SAG_L1_COUNT].value, int)
assert result[obis.VOLTAGE_SAG_L1_COUNT].value == 0
# VOLTAGE_SAG_L2_COUNT (1-0:52.32.0)
assert isinstance(result[obis.VOLTAGE_SAG_L2_COUNT], CosemObject)
assert result[obis.VOLTAGE_SAG_L2_COUNT].unit is None
assert isinstance(result[obis.VOLTAGE_SAG_L2_COUNT].value, int)
assert result[obis.VOLTAGE_SAG_L2_COUNT].value == 0
# VOLTAGE_SAG_L3_COUNT (1-0:72.32.0)
assert isinstance(result[obis.VOLTAGE_SAG_L3_COUNT], CosemObject)
assert result[obis.VOLTAGE_SAG_L3_COUNT].unit is None
assert isinstance(result[obis.VOLTAGE_SAG_L3_COUNT].value, int)
assert result[obis.VOLTAGE_SAG_L3_COUNT].value == 0
# VOLTAGE_SWELL_L1_COUNT (1-0:32.36.0)
assert isinstance(result[obis.VOLTAGE_SWELL_L1_COUNT], CosemObject)
assert result[obis.VOLTAGE_SWELL_L1_COUNT].unit is None
assert isinstance(result[obis.VOLTAGE_SWELL_L1_COUNT].value, int)
assert result[obis.VOLTAGE_SWELL_L1_COUNT].value == 0
# VOLTAGE_SWELL_L2_COUNT (1-0:52.36.0)
assert isinstance(result[obis.VOLTAGE_SWELL_L2_COUNT], CosemObject)
assert result[obis.VOLTAGE_SWELL_L2_COUNT].unit is None
assert isinstance(result[obis.VOLTAGE_SWELL_L2_COUNT].value, int)
assert result[obis.VOLTAGE_SWELL_L2_COUNT].value == 0
# VOLTAGE_SWELL_L3_COUNT (1-0:72.36.0)
assert isinstance(result[obis.VOLTAGE_SWELL_L3_COUNT], CosemObject)
assert result[obis.VOLTAGE_SWELL_L3_COUNT].unit is None
assert isinstance(result[obis.VOLTAGE_SWELL_L3_COUNT].value, int)
assert result[obis.VOLTAGE_SWELL_L3_COUNT].value == 0
# TEXT_MESSAGE_CODE (0-0:96.13.1)
assert isinstance(result[obis.TEXT_MESSAGE_CODE], CosemObject)
assert result[obis.TEXT_MESSAGE_CODE].unit is None
assert result[obis.TEXT_MESSAGE_CODE].value is None
# TEXT_MESSAGE (0-0:96.13.0)
assert isinstance(result[obis.TEXT_MESSAGE], CosemObject)
assert result[obis.TEXT_MESSAGE].unit is None
assert result[obis.TEXT_MESSAGE].value is None
# DEVICE_TYPE (0-x:24.1.0)
assert isinstance(result[obis.TEXT_MESSAGE], CosemObject)
assert result[obis.DEVICE_TYPE].unit is None
assert isinstance(result[obis.DEVICE_TYPE].value, int)
assert result[obis.DEVICE_TYPE].value == 3
# INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE (1-0:21.7.0)
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE], CosemObject)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE].unit == 'kW'
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE].value, Decimal)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE].value == Decimal('0.095')
# INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE (1-0:41.7.0)
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE], CosemObject)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE].unit == 'kW'
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE].value, Decimal)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE].value == Decimal('0.025')
# INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE (1-0:61.7.0)
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE], CosemObject)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE].unit == 'kW'
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE].value, Decimal)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE].value == Decimal('0.115')
# INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE (1-0:22.7.0)
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE], CosemObject)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE].unit == 'kW'
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE].value, Decimal)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE].value == Decimal('0')
# INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE (1-0:42.7.0)
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE], CosemObject)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE].unit == 'kW'
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE].value, Decimal)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE].value == Decimal('0')
# INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE (1-0:62.7.0)
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE], CosemObject)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE].unit == 'kW'
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE].value, Decimal)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE].value == Decimal('0')
# EQUIPMENT_IDENTIFIER_GAS (0-x:96.1.0)
assert isinstance(result[obis.EQUIPMENT_IDENTIFIER_GAS], CosemObject)
assert result[obis.EQUIPMENT_IDENTIFIER_GAS].unit is None
assert isinstance(result[obis.EQUIPMENT_IDENTIFIER_GAS].value, str)
assert result[obis.EQUIPMENT_IDENTIFIER_GAS].value == '3404856892390357246729543587524029'
# HOURLY_GAS_METER_READING (0-1:24.2.1)
assert isinstance(result[obis.HOURLY_GAS_METER_READING], MBusObject)
assert result[obis.HOURLY_GAS_METER_READING].unit == 'm3'
assert isinstance(result[obis.HOURLY_GAS_METER_READING].value, Decimal)
assert result[obis.HOURLY_GAS_METER_READING].value == Decimal('915.219')
# POWER_EVENT_FAILURE_LOG (99.97.0)
# TODO to be implemented
# ACTUAL_TRESHOLD_ELECTRICITY (0-0:17.0.0)
# TODO to be implemented
# ACTUAL_SWITCH_POSITION (0-0:96.3.10)
# TODO to be implemented
# VALVE_POSITION_GAS (0-x:24.4.0)
# TODO to be implemented
+39
View File
@@ -0,0 +1,39 @@
"""Test DSMR serial protocol."""
from unittest.mock import Mock
import pytest
from dsmr_parser import obis_references as obis
from dsmr_parser import telegram_specifications
from dsmr_parser.parsers import TelegramParserV2_2
from dsmr_parser.protocol import DSMRProtocol
from .test_parse_v2_2 import TELEGRAM_V2_2
@pytest.fixture
def protocol():
"""DSMRprotocol instance with mocked telegram_callback."""
parser = TelegramParserV2_2
specification = telegram_specifications.V2_2
telegram_parser = parser(specification)
return DSMRProtocol(None, telegram_parser,
telegram_callback=Mock())
def test_complete_packet(protocol):
"""Protocol should assemble incoming lines into complete packet."""
for line in TELEGRAM_V2_2:
protocol.data_received(bytes(line + '\r\n', 'ascii'))
telegram = protocol.telegram_callback.call_args_list[0][0][0]
assert isinstance(telegram, dict)
assert float(telegram[obis.CURRENT_ELECTRICITY_USAGE].value) == 1.01
assert telegram[obis.CURRENT_ELECTRICITY_USAGE].unit == 'kW'
assert float(telegram[obis.GAS_METER_READING].value) == 1.001
assert telegram[obis.GAS_METER_READING].unit == 'm3'
+3
View File
@@ -5,6 +5,9 @@ envlist = py34,py35
deps=
pytest
pylama
pytest-asyncio
pytest-catchlog
pytest-mock
commands=
py.test test {posargs}
pylama dsmr_parser test