Compare commits

..
40 Commits
Author SHA1 Message Date
Nigel 47d914edb7 Readded python 3.6. If it works, then there is no harm 2024-12-22 14:36:37 +01:00
Nigel 99ad21c896 added a bit of documentation on how to run tests (#165)
* added a bit of documentation on how to run tests

* formatting

* formatting

* formatting

* formatting

* formatting

* formatting
2024-08-25 14:55:22 +02:00
Nigel 94189b6b63 Preparing release 1.4.2 2024-07-14 14:21:13 +02:00
Dennis Siemensma 8829260b15 Bump Github Actions to latest versions in favor of Node deprecations (#159) 2024-06-15 11:33:15 +02:00
Ido Szargel 55ac551a2a Add socket timeout (#155)
* Add socket timeout
2024-06-10 16:19:01 +02:00
J. Nick Koston fc65326370 Swap pyserial-asyncio for pyserial-asyncio-fast (#158)
* Swap pyserial-asyncio for pyserial-asyncio-fast

fixes #154

* actually commit the import change
2024-06-07 16:08:35 +02:00
Nigel 059c0802ad Fix changelog 2024-06-04 16:06:38 +02:00
Nigel d7f30b5069 Release 1.4.1 2024-06-04 15:59:32 +02:00
Joakim Plate b42ceb6555 Avoid loading timezone at runtime (#157)
* Avoid loading timezone at runtime

Pytz will load timezone info from files in
a lazy fashion on first access. This triggers warnings in HA due to it blocking the event loop.

Pre-load the needed timezone info at module import instead, which will run in
executor in HA.

* Update value_types.py
2024-06-04 15:51:03 +02:00
gigatexel 3cf627eedc Change checksum log level (#151)
* Update filereader.py

* Update socket_.py

* Update serial_.py

* Update protocol.py

* Update socket_.py
2024-03-28 12:40:17 +01:00
Nigel d680d44683 Update CHANGELOG.rst 2024-03-12 13:26:04 +01:00
Nigel 6387cc4440 CHANGELOG formatting 2024-03-12 12:56:13 +01:00
Nigel 61985c5c89 Changelog formatting 2024-03-12 12:54:28 +01:00
Nigel 88aaa2d30f Release 1.4.0 2024-03-12 12:50:36 +01:00
dupondje 1ce0925b61 Copy head_parsers list on construct. (#150)
We need to create a copy of the head_parsers.
Otherwise the self.value_formats contains just a reference to
head_parsers.
But the self.value_formats is modified in the _parse_values, causing
invalid values in the head_parsers list.
2024-03-12 11:14:43 +01:00
dupondje bc35a051f2 Change DSMR 5 and 5B to use DSMR devices (#142)
At least the BE and the NL DSMR devices use MBUS.
Therefore we make the MBUS obis reference generic, and convert the V5
telegram to use MBUS values.

Finally set self.maxDiff = None to have a full test output on some
tests.
2024-03-07 11:21:23 +01:00
Gunnar Klauberg fcbcbb30e0 Q3D add CURRENT_ELECTRICITY_DELIVERY (#149)
add CURRENT_ELECTRICITY_DELIVERY sensor for Q3D
2024-02-19 14:31:09 +01:00
Nigel be61d4aaf9 Prepare release 1.3.2 2024-01-29 14:29:02 +01:00
Nigel 1680cacf49 fix protocol unit test for python3.12 (#148)
* fix protocol unit test for python3.12

* add workflow test for python 3.12
2024-01-29 14:25:39 +01:00
dupondje 739419e3ee Remove BELGIUM_MAX_POWER_PER_PHASE (#144)
This commit removes BELGIUM_MAX_POWER_PER_PHASE and uses
ACTUAL_TRESHOLD_ELECTRICITY as this has same regex. So useless to keep
the BELIUM variant.
2023-11-07 12:10:42 +01:00
Jean-Louis Dupond d2ff6f4bb9 Fix tests after FUSE_TRESHOLD rename 2023-11-06 15:10:36 +01:00
Jean-Louis Dupond a316d93082 Adjust the naming of the MAX_POWER and MAX_CURRENT obis values 2023-11-06 15:10:36 +01:00
Jean-Louis Dupond 01d7622c40 Release 1.3.1 2023-11-06 15:09:47 +01:00
dupondje e8a86272ea Merge pull request #143 from dupondje/fix_invalid_occured
Fix parsing peak usage when value is not set
2023-11-06 14:30:20 +01:00
Jean-Louis Dupond 389bcefb50 Fix parsing peak usage when value is not set
Sometimes peak usage is not yet visible in the 13 months history code.
It gives a 0.0kW value with timestamp (632525252525W).

Also the peak usage value can be invalid, handle this case also.
2023-11-06 09:50:34 +01:00
Nigel 88923622a9 Release 1.3.0 2023-08-01 09:26:40 +02:00
balazs92117 2f1e080df3 Refactor of name mapping, added E.ON Hungary (#137)
* Added EON HUNGARY specification

* refactoring obis name mapping
2023-08-01 09:23:17 +02:00
Nigel af2e6558de Revert "Added E.ON HUNGARY specification (#134)" (#135)
This reverts commit 0752deb58a.
2023-07-26 15:03:29 +02:00
balazs92117 0752deb58a Added E.ON HUNGARY specification (#134)
* Added EON HUNGARY specification
2023-07-26 13:31:59 +02:00
Nigel 3fd032f18f Release 1.2.4 2023-07-11 22:01:00 +02:00
Emilio Jose Palacios-Garcia 4602e8c99f EQUIPMENT IDENTIFIER is wrong for Fluvius meters when other mbus devices are present (#133)
* change obis of fluvius meter ID to BELGIUM_EQUIPMENT_IDENTIFIER

* update test case to use direct access to attributes

* fix test case for pipeline
2023-07-11 21:43:41 +02:00
Emilio Jose Palacios-Garcia f3444a515d EQUIPMENT IDENTIFIER is wrong for Fluvius meters when other mbus devices are present (#133)
* change obis of fluvius meter ID to BELGIUM_EQUIPMENT_IDENTIFIER
2023-07-11 21:43:25 +02:00
Nigel 99c7b0d9f0 Prepare release 1.2.3 2023-04-18 20:48:30 +02:00
dupondje 84973872af Fix parsing tests and line start matching (#132)
* Raise parsing exception and check it in tests

* use multiline regex and match everything at the line start
2023-04-14 16:51:01 +02:00
Nigel ae8a2baaab Merge branch 'master' of github.com:ndokter/dsmr_parser 2023-04-12 10:58:40 +02:00
Nigel d3fab4f105 Release 1.2.2 2023-04-12 10:57:38 +02:00
Nigel 5a59c36646 improve performance parsing speed by about 6% by caching compiled regexes (#131)
* improve performance parsing speed by about 6% by caching compiled regexes
2023-04-12 10:52:11 +02:00
Nigel 9847bdf66b Prepare releasse 1.2.1 2023-04-05 11:19:42 +02:00
Nigel 76da334234 Update release notes for 1.2.0 where not all additions where added 2023-04-05 11:14:43 +02:00
Emilio Jose Palacios-GarciaandEmilio Jose Palacios Garcia c03d2483d7 Bug/duplicate index BELGIUM_MAXIMUM_DEMAND_13_MONTHS (#129)
* fix index offset for month timestamp in BELGIUM_MAXIMUM_DEMAND_13_MONTHS

* add timestamps to test case

---------

Co-authored-by: Emilio Jose Palacios Garcia <epalacio@emilio.esat.kuleuven.be>
2023-04-05 11:06:21 +02:00
28 changed files with 2449 additions and 829 deletions
+6 -4
View File
@@ -12,23 +12,25 @@ jobs:
strategy: strategy:
matrix: matrix:
python-version: python-version:
- '3.6'
- '3.7' - '3.7'
- '3.8' - '3.8'
- '3.9' - '3.9'
- '3.10' - '3.10'
- '3.11' - '3.11'
- '3.12'
name: Python ${{ matrix.python-version }} name: Python ${{ matrix.python-version }}
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Setup Python ${{ matrix.python-version }} - name: Setup Python ${{ matrix.python-version }}
uses: actions/setup-python@v2 uses: actions/setup-python@v5
with: with:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
- name: Cached PIP dependencies - name: Cached PIP dependencies
uses: actions/cache@v3 uses: actions/cache@v4
with: with:
path: | path: |
~/.cache/pip ~/.cache/pip
@@ -43,4 +45,4 @@ jobs:
run: tox run: tox
- name: Code coverage upload - name: Code coverage upload
uses: codecov/codecov-action@v3 uses: codecov/codecov-action@v4
+46
View File
@@ -1,9 +1,55 @@
Change Log Change Log
---------- ----------
**1.4.2** (2024-07-14)
- Bump Github Actions to latest versions in favor of Node deprecations (`PR #159 <https://github.com/ndokter/dsmr_parser/pull/159>`_ by `dennissiemensma <https://github.com/dennissiemensma>`_)
- Swap pyserial-asyncio for pyserial-asyncio-fast (`PR #158 <https://github.com/ndokter/dsmr_parser/pull/158>`_ by `bdraco <https://github.com/bdraco>`_)
**1.4.1** (2024-06-04)
- Avoid loading timezone at runtime (`PR #157 <https://github.com/ndokter/dsmr_parser/pull/157>`_ by `elupus <https://github.com/elupus>`_)
**1.4.0** (2024-03-12)
- Mbus alt (`PR #142 <https://github.com/ndokter/dsmr_parser/pull/142>`_ by `dupondje <https://github.com/dupondje>`_)
- Q3D add CURRENT_ELECTRICITY_DELIVERY (`PR #149 <https://github.com/ndokter/dsmr_parser/pull/149>`_ by `Aeroid <https://github.com/Aeroid>`_)
- Copy head_parsers list on construct. (`PR #150 <https://github.com/ndokter/dsmr_parser/pull/150>`_ by `dupondje <https://github.com/dupondje>`_)
**1.3.2** (2024-01-29)
- Fix unit test for pyton 3.12 (`PR #148 <https://github.com/ndokter/dsmr_parser/pull/148>`_ by `ndokter <https://github.com/ndokter>`_)
**1.3.1** (2023-11-06)
- Fix parsing peak usage with invalid timestamps (`PR #143 <https://github.com/ndokter/dsmr_parser/pull/143>`_ by `dupondje <https://github.com/dupondje>`_)
**1.3.0** (2023-08-01)
- added E.ON Hungary; refactored DSMR specifications to fix obis reference conflicts (`PR #137 <https://github.com/ndokter/dsmr_parser/pull/137>`_ by `balazs92117 <https://github.com/balazs92117>`_)
**1.2.4** (2023-07-11)
- EQUIPMENT IDENTIFIER is wrong for Fluvius meters when other mbus devices are present (`PR #133 <https://github.com/ndokter/dsmr_parser/pull/133>`_ by `ejpalacios <https://github.com/ejpalacios>`_)
**1.2.3** (2023-04-18)
- Fix parsing tests and line start matching (`PR #132 <https://github.com/ndokter/dsmr_parser/pull/132>`_ by `dupondje <https://github.com/dupondje>`_)
**1.2.2** (2023-04-12)
- Improve performance. Thanks to `ejpalacios <https://github.com/bdraco>`_ (`PR #130 <https://github.com/ndokter/dsmr_parser/pull/130>`_ by `ndokter <https://github.com/ndokter>`_)
**1.2.1** (2023-04-05)
- Bug/duplicate index BELGIUM_MAXIMUM_DEMAND_13_MONTHS (`PR #129 <https://github.com/ndokter/dsmr_parser/pull/129>`_ by `ejpalacios <https://github.com/ejpalacios>`_)
**1.2.0** (2023-02-18) **1.2.0** (2023-02-18)
- Improved gas meter (mbus devices) support and replaced Telegram dictionary with backwards compatible object (`PR #121 <https://github.com/ndokter/dsmr_parser/pull/121>`_ by `ndokter <https://github.com/ndokter>`_) - Improved gas meter (mbus devices) support and replaced Telegram dictionary with backwards compatible object (`PR #121 <https://github.com/ndokter/dsmr_parser/pull/121>`_ by `ndokter <https://github.com/ndokter>`_)
- Fix parsing with invalid timestamps (`PR #125 <https://github.com/ndokter/dsmr_parser/pull/125>`_ by `dupondje <https://github.com/dupondje>`_)
- Add Iskra IE.x meters specification (`PR #126 <https://github.com/ndokter/dsmr_parser/pull/126>`_ by `jchevalier7 <https://github.com/jchevalier7>`_)
**1.1.0** (2023-02-08) **1.1.0** (2023-02-08)
+30
View File
@@ -293,6 +293,36 @@ To install DSMR Parser:
$ pip install dsmr-parser $ pip install dsmr-parser
Development
-----------
Create a virtualenv and activate it followed by the installation of the dsmr-parser:
.. code-block:: bash
python3 -m venv venv
source venv/bin/activate
pip install -e .
Install tox and run it:
.. code-block:: bash
pip install tox
tox
You should see that the tests have succeeded:
.. code-block:: text
======================================================================================================== 59 passed in 0.91s ========================================================================================================
py: commands[1]> pylama dsmr_parser test
py: OK (11.55=setup[9.73]+cmd[1.29,0.53] seconds)
congratulations :) (11.69 seconds)
Now you can make changes by editing the code and rerunning tox to verify your changes.
Known issues Known issues
------------ ------------
+1 -1
View File
@@ -73,7 +73,7 @@ class FileReader(object):
try: try:
yield self.telegram_parser.parse(telegram) yield self.telegram_parser.parse(telegram)
except InvalidChecksumError as e: except InvalidChecksumError as e:
logger.warning(str(e)) logger.info(str(e))
except ParseError as e: except ParseError as e:
logger.error('Failed to parse telegram: %s', e) logger.error('Failed to parse telegram: %s', e)
+8 -3
View File
@@ -4,7 +4,7 @@ from functools import partial
import asyncio import asyncio
import logging import logging
from serial_asyncio import create_serial_connection from serial_asyncio_fast import create_serial_connection
from dsmr_parser import telegram_specifications from dsmr_parser import telegram_specifications
from dsmr_parser.clients.telegram_buffer import TelegramBuffer from dsmr_parser.clients.telegram_buffer import TelegramBuffer
@@ -21,7 +21,9 @@ def create_dsmr_protocol(dsmr_version, telegram_callback, loop=None, **kwargs):
return protocol return protocol
def _create_dsmr_protocol(dsmr_version, telegram_callback, protocol, loop=None, **kwargs): # pylama noqa - because of "complex" (too long) if-elif-else.
# Match - case might be a solution but it is not available in <3.10
def _create_dsmr_protocol(dsmr_version, telegram_callback, protocol, loop=None, **kwargs): #noqa
"""Creates a DSMR asyncio protocol.""" """Creates a DSMR asyncio protocol."""
if dsmr_version == '2.2': if dsmr_version == '2.2':
@@ -51,6 +53,9 @@ def _create_dsmr_protocol(dsmr_version, telegram_callback, protocol, loop=None,
elif dsmr_version == 'ISKRA_IE': elif dsmr_version == 'ISKRA_IE':
specification = telegram_specifications.ISKRA_IE specification = telegram_specifications.ISKRA_IE
serial_settings = SERIAL_SETTINGS_V5 serial_settings = SERIAL_SETTINGS_V5
elif dsmr_version == '5EONHU':
specification = telegram_specifications.EON_HUNGARY
serial_settings = SERIAL_SETTINGS_V5
else: else:
raise NotImplementedError("No telegram parser found for version: %s", raise NotImplementedError("No telegram parser found for version: %s",
dsmr_version) dsmr_version)
@@ -153,7 +158,7 @@ class DSMRProtocol(asyncio.Protocol):
try: try:
parsed_telegram = self.telegram_parser.parse(telegram) parsed_telegram = self.telegram_parser.parse(telegram)
except InvalidChecksumError as e: except InvalidChecksumError as e:
self.log.warning(str(e)) self.log.info(str(e))
except ParseError: except ParseError:
self.log.exception("failed to parse telegram") self.log.exception("failed to parse telegram")
else: else:
+1 -1
View File
@@ -2,7 +2,7 @@
import asyncio import asyncio
from serial_asyncio import create_serial_connection from serial_asyncio_fast import create_serial_connection
from .protocol import DSMRProtocol, _create_dsmr_protocol from .protocol import DSMRProtocol, _create_dsmr_protocol
+4 -4
View File
@@ -1,6 +1,6 @@
import logging import logging
import serial import serial
import serial_asyncio import serial_asyncio_fast
from dsmr_parser.clients.telegram_buffer import TelegramBuffer from dsmr_parser.clients.telegram_buffer import TelegramBuffer
from dsmr_parser.exceptions import ParseError, InvalidChecksumError from dsmr_parser.exceptions import ParseError, InvalidChecksumError
@@ -37,7 +37,7 @@ class SerialReader(object):
try: try:
yield self.telegram_parser.parse(telegram) yield self.telegram_parser.parse(telegram)
except InvalidChecksumError as e: except InvalidChecksumError as e:
logger.warning(str(e)) logger.info(str(e))
except ParseError as e: except ParseError as e:
logger.error('Failed to parse telegram: %s', e) logger.error('Failed to parse telegram: %s', e)
@@ -77,7 +77,7 @@ class AsyncSerialReader(SerialReader):
:rtype: None :rtype: None
""" """
# create Serial StreamReader # create Serial StreamReader
conn = serial_asyncio.open_serial_connection(**self.serial_settings) conn = serial_asyncio_fast.open_serial_connection(**self.serial_settings)
reader, _ = await conn reader, _ = await conn
while True: while True:
@@ -107,7 +107,7 @@ class AsyncSerialReader(SerialReader):
""" """
# create Serial StreamReader # create Serial StreamReader
conn = serial_asyncio.open_serial_connection(**self.serial_settings) conn = serial_asyncio_fast.open_serial_connection(**self.serial_settings)
reader, _ = await conn reader, _ = await conn
while True: while True:
+13 -4
View File
@@ -31,11 +31,15 @@ class SocketReader(object):
buffer = b"" buffer = b""
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as socket_handle: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as socket_handle:
socket_handle.settimeout(60)
socket_handle.connect((self.host, self.port)) socket_handle.connect((self.host, self.port))
while True: while True:
buffer += socket_handle.recv(self.BUFFER_SIZE) try:
buffer += socket_handle.recv(self.BUFFER_SIZE)
except socket.timeout:
logger.error("Socket timeout occurred, exiting")
break
lines = buffer.splitlines(keepends=True) lines = buffer.splitlines(keepends=True)
@@ -43,13 +47,18 @@ class SocketReader(object):
continue continue
for data in lines: for data in lines:
self.telegram_buffer.append(data.decode('ascii')) try:
self.telegram_buffer.append(data.decode('ascii'))
except UnicodeDecodeError:
# Some garbage came through the channel
# E.g.: Happens at EON_HUNGARY, but only once at the start of the socket.
logger.error('Failed to parse telegram due to unicode decode error')
for telegram in self.telegram_buffer.get_all(): for telegram in self.telegram_buffer.get_all():
try: try:
yield self.telegram_parser.parse(telegram) yield self.telegram_parser.parse(telegram)
except InvalidChecksumError as e: except InvalidChecksumError as e:
logger.warning(str(e)) logger.info(str(e))
except ParseError as e: except ParseError as e:
logger.error('Failed to parse telegram: %s', e) logger.error('Failed to parse telegram: %s', e)
+10 -20
View File
@@ -1,5 +1,13 @@
import re import re
# - Match all characters after start of telegram except for the start
# itself again '^\/]+', which eliminates incomplete preceding telegrams.
# - Do non greedy match using '?' so start is matched up to the first
# checksum that's found.
# - The checksum is optional '{0,4}' because not all telegram versions
# support it.
_FIND_TELEGRAMS_REGEX = re.compile(r"\/[^\/]+?\![A-F0-9]{0,4}\0?\r\n", re.DOTALL)
class TelegramBuffer(object): class TelegramBuffer(object):
""" """
@@ -8,14 +16,14 @@ class TelegramBuffer(object):
""" """
def __init__(self): def __init__(self):
self._buffer = '' self._buffer = ""
def get_all(self): def get_all(self):
""" """
Remove complete telegrams from buffer and yield them. Remove complete telegrams from buffer and yield them.
:rtype generator: :rtype generator:
""" """
for telegram in self._find_telegrams(): for telegram in _FIND_TELEGRAMS_REGEX.findall(self._buffer):
self._remove(telegram) self._remove(telegram)
yield telegram yield telegram
@@ -37,21 +45,3 @@ class TelegramBuffer(object):
index = self._buffer.index(telegram) + len(telegram) index = self._buffer.index(telegram) + len(telegram)
self._buffer = self._buffer[index:] self._buffer = self._buffer[index:]
def _find_telegrams(self):
"""
Find complete telegrams in buffer from start ('/') till ending
checksum ('!AB12\r\n').
:rtype: list
"""
# - Match all characters after start of telegram except for the start
# itself again '^\/]+', which eliminates incomplete preceding telegrams.
# - Do non greedy match using '?' so start is matched up to the first
# checksum that's found.
# - The checksum is optional '{0,4}' because not all telegram versions
# support it.
return re.findall(
r'\/[^\/]+?\![A-F0-9]{0,4}\0?\r\n',
self._buffer,
re.DOTALL
)
-98
View File
@@ -1,98 +0,0 @@
from dsmr_parser import obis_references as obis
"""
dsmr_parser.obis_name_mapping
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This module contains a mapping of obis references to names.
"""
EN = {
obis.P1_MESSAGE_HEADER: 'P1_MESSAGE_HEADER',
obis.P1_MESSAGE_TIMESTAMP: 'P1_MESSAGE_TIMESTAMP',
obis.ELECTRICITY_IMPORTED_TOTAL: 'ELECTRICITY_IMPORTED_TOTAL',
obis.ELECTRICITY_REACTIVE_IMPORTED_TOTAL: 'ELECTRICITY_REACTIVE_IMPORTED_TOTAL',
obis.ELECTRICITY_USED_TARIFF_1: 'ELECTRICITY_USED_TARIFF_1',
obis.ELECTRICITY_USED_TARIFF_2: 'ELECTRICITY_USED_TARIFF_2',
obis.ELECTRICITY_EXPORTED_TOTAL: 'ELECTRICITY_EXPORTED_TOTAL',
obis.ELECTRICITY_REACTIVE_EXPORTED_TOTAL: 'ELECTRICITY_REACTIVE_EXPORTED_TOTAL',
obis.ELECTRICITY_DELIVERED_TARIFF_1: 'ELECTRICITY_DELIVERED_TARIFF_1',
obis.ELECTRICITY_DELIVERED_TARIFF_2: 'ELECTRICITY_DELIVERED_TARIFF_2',
obis.ELECTRICITY_ACTIVE_TARIFF: 'ELECTRICITY_ACTIVE_TARIFF',
obis.CURRENT_REACTIVE_EXPORTED: 'CURRENT_REACTIVE_EXPORTED',
obis.ELECTRICITY_REACTIVE_IMPORTED_TARIFF_1: 'ELECTRICITY_REACTIVE_IMPORTED_TARIFF_1',
obis.ELECTRICITY_REACTIVE_IMPORTED_TARIFF_2: 'ELECTRICITY_REACTIVE_IMPORTED_TARIFF_2',
obis.ELECTRICITY_REACTIVE_EXPORTED_TARIFF_1: 'ELECTRICITY_REACTIVE_EXPORTED_TARIFF_1',
obis.ELECTRICITY_REACTIVE_EXPORTED_TARIFF_2: 'ELECTRICITY_REACTIVE_EXPORTED_TARIFF_2',
obis.CURRENT_REACTIVE_IMPORTED: 'CURRENT_REACTIVE_IMPORTED',
obis.EQUIPMENT_IDENTIFIER: 'EQUIPMENT_IDENTIFIER',
obis.CURRENT_ELECTRICITY_USAGE: 'CURRENT_ELECTRICITY_USAGE',
obis.CURRENT_ELECTRICITY_DELIVERY: 'CURRENT_ELECTRICITY_DELIVERY',
obis.LONG_POWER_FAILURE_COUNT: 'LONG_POWER_FAILURE_COUNT',
obis.SHORT_POWER_FAILURE_COUNT: 'SHORT_POWER_FAILURE_COUNT',
obis.POWER_EVENT_FAILURE_LOG: 'POWER_EVENT_FAILURE_LOG',
obis.VOLTAGE_SAG_L1_COUNT: 'VOLTAGE_SAG_L1_COUNT',
obis.VOLTAGE_SAG_L2_COUNT: 'VOLTAGE_SAG_L2_COUNT',
obis.VOLTAGE_SAG_L3_COUNT: 'VOLTAGE_SAG_L3_COUNT',
obis.VOLTAGE_SWELL_L1_COUNT: 'VOLTAGE_SWELL_L1_COUNT',
obis.VOLTAGE_SWELL_L2_COUNT: 'VOLTAGE_SWELL_L2_COUNT',
obis.VOLTAGE_SWELL_L3_COUNT: 'VOLTAGE_SWELL_L3_COUNT',
obis.INSTANTANEOUS_VOLTAGE_L1: 'INSTANTANEOUS_VOLTAGE_L1',
obis.INSTANTANEOUS_VOLTAGE_L2: 'INSTANTANEOUS_VOLTAGE_L2',
obis.INSTANTANEOUS_VOLTAGE_L3: 'INSTANTANEOUS_VOLTAGE_L3',
obis.INSTANTANEOUS_CURRENT_L1: 'INSTANTANEOUS_CURRENT_L1',
obis.INSTANTANEOUS_CURRENT_L2: 'INSTANTANEOUS_CURRENT_L2',
obis.INSTANTANEOUS_CURRENT_L3: 'INSTANTANEOUS_CURRENT_L3',
obis.TEXT_MESSAGE_CODE: 'TEXT_MESSAGE_CODE',
obis.TEXT_MESSAGE: 'TEXT_MESSAGE',
obis.DEVICE_TYPE: 'DEVICE_TYPE',
obis.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE: 'INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE',
obis.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE: 'INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE',
obis.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE: 'INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE',
obis.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE: 'INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE',
obis.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE: 'INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE',
obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE: 'INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE',
obis.INSTANTANEOUS_REACTIVE_POWER_L1_POSITIVE: 'INSTANTANEOUS_REACTIVE_POWER_L1_POSITIVE',
obis.INSTANTANEOUS_REACTIVE_POWER_L1_NEGATIVE: 'INSTANTANEOUS_REACTIVE_POWER_L1_NEGATIVE',
obis.INSTANTANEOUS_REACTIVE_POWER_L2_POSITIVE: 'INSTANTANEOUS_REACTIVE_POWER_L2_POSITIVE',
obis.INSTANTANEOUS_REACTIVE_POWER_L2_NEGATIVE: 'INSTANTANEOUS_REACTIVE_POWER_L2_NEGATIVE',
obis.INSTANTANEOUS_REACTIVE_POWER_L3_POSITIVE: 'INSTANTANEOUS_REACTIVE_POWER_L3_POSITIVE',
obis.INSTANTANEOUS_REACTIVE_POWER_L3_NEGATIVE: 'INSTANTANEOUS_REACTIVE_POWER_L3_NEGATIVE',
obis.EQUIPMENT_IDENTIFIER_GAS: 'EQUIPMENT_IDENTIFIER_GAS',
obis.HOURLY_GAS_METER_READING: 'HOURLY_GAS_METER_READING',
obis.GAS_METER_READING: 'GAS_METER_READING',
obis.ACTUAL_TRESHOLD_ELECTRICITY: 'ACTUAL_TRESHOLD_ELECTRICITY',
obis.ACTUAL_SWITCH_POSITION: 'ACTUAL_SWITCH_POSITION',
obis.VALVE_POSITION_GAS: 'VALVE_POSITION_GAS',
obis.BELGIUM_VERSION_INFORMATION: 'BELGIUM_VERSION_INFORMATION',
obis.BELGIUM_EQUIPMENT_IDENTIFIER: 'BELGIUM_EQUIPMENT_IDENTIFIER',
obis.BELGIUM_CURRENT_AVERAGE_DEMAND: 'BELGIUM_CURRENT_AVERAGE_DEMAND',
obis.BELGIUM_MAXIMUM_DEMAND_MONTH: 'BELGIUM_MAXIMUM_DEMAND_MONTH',
obis.BELGIUM_MAXIMUM_DEMAND_13_MONTHS: 'BELGIUM_MAXIMUM_DEMAND_13_MONTHS',
obis.BELGIUM_MAX_POWER_PER_PHASE: 'BELGIUM_MAX_POWER_PER_PHASE',
obis.BELGIUM_MAX_CURRENT_PER_PHASE: 'BELGIUM_MAX_CURRENT_PER_PHASE',
obis.BELGIUM_MBUS1_DEVICE_TYPE: 'BELGIUM_MBUS1_DEVICE_TYPE',
obis.BELGIUM_MBUS1_EQUIPMENT_IDENTIFIER: 'BELGIUM_MBUS1_EQUIPMENT_IDENTIFIER',
obis.BELGIUM_MBUS1_VALVE_POSITION: 'BELGIUM_MBUS1_VALVE_POSITION',
obis.BELGIUM_MBUS1_METER_READING1: 'BELGIUM_MBUS1_METER_READING1',
obis.BELGIUM_MBUS1_METER_READING2: 'BELGIUM_MBUS1_METER_READING2',
obis.BELGIUM_MBUS2_EQUIPMENT_IDENTIFIER: 'BELGIUM_MBUS2_EQUIPMENT_IDENTIFIER',
obis.BELGIUM_MBUS2_VALVE_POSITION: 'BELGIUM_MBUS2_VALVE_POSITION',
obis.BELGIUM_MBUS2_METER_READING1: 'BELGIUM_MBUS2_METER_READING1',
obis.BELGIUM_MBUS2_METER_READING2: 'BELGIUM_MBUS2_METER_READING2',
obis.BELGIUM_MBUS3_EQUIPMENT_IDENTIFIER: 'BELGIUM_MBUS3_EQUIPMENT_IDENTIFIER',
obis.BELGIUM_MBUS3_VALVE_POSITION: 'BELGIUM_MBUS3_VALVE_POSITION',
obis.BELGIUM_MBUS3_METER_READING1: 'BELGIUM_MBUS3_METER_READING1',
obis.BELGIUM_MBUS3_METER_READING2: 'BELGIUM_MBUS3_METER_READING2',
obis.BELGIUM_MBUS4_EQUIPMENT_IDENTIFIER: 'BELGIUM_MBUS4_EQUIPMENT_IDENTIFIER',
obis.BELGIUM_MBUS4_VALVE_POSITION: 'BELGIUM_MBUS4_VALVE_POSITION',
obis.BELGIUM_MBUS4_METER_READING1: 'BELGIUM_MBUS4_METER_READING1',
obis.BELGIUM_MBUS4_METER_READING2: 'BELGIUM_MBUS4_METER_READING2',
obis.LUXEMBOURG_EQUIPMENT_IDENTIFIER: 'LUXEMBOURG_EQUIPMENT_IDENTIFIER',
obis.Q3D_EQUIPMENT_IDENTIFIER: 'Q3D_EQUIPMENT_IDENTIFIER',
obis.Q3D_EQUIPMENT_STATE: 'Q3D_EQUIPMENT_STATE',
obis.Q3D_EQUIPMENT_SERIALNUMBER: 'Q3D_EQUIPMENT_SERIALNUMBER',
obis.BELGIUM_MBUS2_DEVICE_TYPE: 'BELGIUM_MBUS2_DEVICE_TYPE'
}
REVERSE_EN = dict([(v, k) for k, v in EN.items()])
+105 -97
View File
@@ -6,64 +6,88 @@ refactored to full line signatures to maintain backwards compatibility.
Might be refactored in a backwards incompatible way as soon as proper telegram Might be refactored in a backwards incompatible way as soon as proper telegram
objects are introduced. objects are introduced.
""" """
P1_MESSAGE_HEADER = r'\d-\d:0\.2\.8.+?\r\n' P1_MESSAGE_HEADER = r'^\d-\d:0\.2\.8.+?\r\n'
P1_MESSAGE_TIMESTAMP = r'\d-\d:1\.0\.0.+?\r\n' P1_MESSAGE_TIMESTAMP = r'^\d-\d:1\.0\.0.+?\r\n'
ELECTRICITY_USED_TARIFF_1 = r'\d-\d:1\.8\.1.+?\r\n' ELECTRICITY_USED_TARIFF_1 = r'^\d-\d:1\.8\.1.+?\r\n'
ELECTRICITY_USED_TARIFF_2 = r'\d-\d:1\.8\.2.+?\r\n' ELECTRICITY_USED_TARIFF_2 = r'^\d-\d:1\.8\.2.+?\r\n'
ELECTRICITY_DELIVERED_TARIFF_1 = r'\d-\d:2\.8\.1.+?\r\n' ELECTRICITY_USED_TARIFF_3 = r'^\d-\d:1\.8\.3.+?\r\n'
ELECTRICITY_DELIVERED_TARIFF_2 = r'\d-\d:2\.8\.2.+?\r\n' ELECTRICITY_USED_TARIFF_4 = r'^\d-\d:1\.8\.4.+?\r\n'
CURRENT_REACTIVE_IMPORTED = r'\d-\d:3\.7\.0.+?\r\n' ELECTRICITY_DELIVERED_TARIFF_1 = r'^\d-\d:2\.8\.1.+?\r\n'
ELECTRICITY_REACTIVE_IMPORTED_TOTAL = r'\d-\d:3\.8\.0.+?\r\n' ELECTRICITY_DELIVERED_TARIFF_2 = r'^\d-\d:2\.8\.2.+?\r\n'
ELECTRICITY_REACTIVE_IMPORTED_TARIFF_1 = r'\d-\d:3\.8\.1.+?\r\n' ELECTRICITY_DELIVERED_TARIFF_3 = r'^\d-\d:2\.8\.3.+?\r\n'
ELECTRICITY_REACTIVE_IMPORTED_TARIFF_2 = r'\d-\d:3\.8\.2.+?\r\n' ELECTRICITY_DELIVERED_TARIFF_4 = r'^\d-\d:2\.8\.4.+?\r\n'
CURRENT_REACTIVE_EXPORTED = r'\d-\d:4\.7\.0.+?\r\n' CURRENT_REACTIVE_IMPORTED = r'^\d-\d:3\.7\.0.+?\r\n'
ELECTRICITY_REACTIVE_EXPORTED_TOTAL = r'\d-\d:4\.8\.0.+?\r\n' ELECTRICITY_REACTIVE_IMPORTED_TOTAL = r'^\d-\d:3\.8\.0.+?\r\n'
ELECTRICITY_REACTIVE_EXPORTED_TARIFF_1 = r'\d-\d:4\.8\.1.+?\r\n' ELECTRICITY_REACTIVE_IMPORTED_TARIFF_1 = r'^\d-\d:3\.8\.1.+?\r\n'
ELECTRICITY_REACTIVE_EXPORTED_TARIFF_2 = r'\d-\d:4\.8\.2.+?\r\n' ELECTRICITY_REACTIVE_IMPORTED_TARIFF_2 = r'^\d-\d:3\.8\.2.+?\r\n'
ELECTRICITY_ACTIVE_TARIFF = r'\d-\d:96\.14\.0.+?\r\n' CURRENT_REACTIVE_EXPORTED = r'^\d-\d:4\.7\.0.+?\r\n'
EQUIPMENT_IDENTIFIER = r'\d-\d:96\.1\.1.+?\r\n' ELECTRICITY_REACTIVE_EXPORTED_TOTAL = r'^\d-\d:4\.8\.0.+?\r\n'
CURRENT_ELECTRICITY_USAGE = r'\d-\d:1\.7\.0.+?\r\n' ELECTRICITY_REACTIVE_EXPORTED_TARIFF_1 = r'^\d-\d:4\.8\.1.+?\r\n'
CURRENT_ELECTRICITY_DELIVERY = r'\d-\d:2\.7\.0.+?\r\n' ELECTRICITY_REACTIVE_EXPORTED_TARIFF_2 = r'^\d-\d:4\.8\.2.+?\r\n'
LONG_POWER_FAILURE_COUNT = r'\d-\d:96\.7\.9.+?\r\n' ELECTRICITY_ACTIVE_TARIFF = r'^\d-\d:96\.14\.0.+?\r\n'
SHORT_POWER_FAILURE_COUNT = r'\d-\d:96\.7\.21.+?\r\n' EQUIPMENT_IDENTIFIER = r'^\d-\d:96\.1\.1.+?\r\n'
POWER_EVENT_FAILURE_LOG = r'\d-\d:99\.97\.0.+?\r\n' CURRENT_ELECTRICITY_USAGE = r'^\d-\d:1\.7\.0.+?\r\n'
VOLTAGE_SAG_L1_COUNT = r'\d-\d:32\.32\.0.+?\r\n' CURRENT_ELECTRICITY_DELIVERY = r'^\d-\d:2\.7\.0.+?\r\n'
VOLTAGE_SAG_L2_COUNT = r'\d-\d:52\.32\.0.+?\r\n' LONG_POWER_FAILURE_COUNT = r'^\d-\d:96\.7\.9.+?\r\n'
VOLTAGE_SAG_L3_COUNT = r'\d-\d:72\.32\.0.+?\r\n' SHORT_POWER_FAILURE_COUNT = r'^\d-\d:96\.7\.21.+?\r\n'
VOLTAGE_SWELL_L1_COUNT = r'\d-\d:32\.36\.0.+?\r\n' POWER_EVENT_FAILURE_LOG = r'^\d-\d:99\.97\.0.+?\r\n'
VOLTAGE_SWELL_L2_COUNT = r'\d-\d:52\.36\.0.+?\r\n' VOLTAGE_SAG_L1_COUNT = r'^\d-\d:32\.32\.0.+?\r\n'
VOLTAGE_SWELL_L3_COUNT = r'\d-\d:72\.36\.0.+?\r\n' VOLTAGE_SAG_L2_COUNT = r'^\d-\d:52\.32\.0.+?\r\n'
INSTANTANEOUS_VOLTAGE_L1 = r'\d-\d:32\.7\.0.+?\r\n' VOLTAGE_SAG_L3_COUNT = r'^\d-\d:72\.32\.0.+?\r\n'
INSTANTANEOUS_VOLTAGE_L2 = r'\d-\d:52\.7\.0.+?\r\n' VOLTAGE_SWELL_L1_COUNT = r'^\d-\d:32\.36\.0.+?\r\n'
INSTANTANEOUS_VOLTAGE_L3 = r'\d-\d:72\.7\.0.+?\r\n' VOLTAGE_SWELL_L2_COUNT = r'^\d-\d:52\.36\.0.+?\r\n'
INSTANTANEOUS_CURRENT_L1 = r'\d-\d:31\.7\.0.+?\r\n' VOLTAGE_SWELL_L3_COUNT = r'^\d-\d:72\.36\.0.+?\r\n'
INSTANTANEOUS_CURRENT_L2 = r'\d-\d:51\.7\.0.+?\r\n' INSTANTANEOUS_VOLTAGE_L1 = r'^\d-\d:32\.7\.0.+?\r\n'
INSTANTANEOUS_CURRENT_L3 = r'\d-\d:71\.7\.0.+?\r\n' INSTANTANEOUS_VOLTAGE_L2 = r'^\d-\d:52\.7\.0.+?\r\n'
TEXT_MESSAGE_CODE = r'\d-\d:96\.13\.1.+?\r\n' INSTANTANEOUS_VOLTAGE_L3 = r'^\d-\d:72\.7\.0.+?\r\n'
TEXT_MESSAGE = r'\d-\d:96\.13\.0.+?\r\n' INSTANTANEOUS_CURRENT_L1 = r'^\d-\d:31\.7\.0.+?\r\n'
DEVICE_TYPE = r'\d-\d:24\.1\.0.+?\r\n' INSTANTANEOUS_CURRENT_L2 = r'^\d-\d:51\.7\.0.+?\r\n'
INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE = r'\d-\d:21\.7\.0.+?\r\n' INSTANTANEOUS_CURRENT_L3 = r'^\d-\d:71\.7\.0.+?\r\n'
INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE = r'\d-\d:41\.7\.0.+?\r\n' FUSE_THRESHOLD_L1 = r'^\d-\d:31\.4\.0.+?\r\n' # Applicable when current limitation is active
INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE = r'\d-\d:61\.7\.0.+?\r\n' FUSE_THRESHOLD_L2 = r'^\d-\d:51\.4\.0.+?\r\n' # Applicable when current limitation is active
INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE = r'\d-\d:22\.7\.0.+?\r\n' FUSE_THRESHOLD_L3 = r'^\d-\d:71\.4\.0.+?\r\n' # Applicable when current limitation is active
INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE = r'\d-\d:42\.7\.0.+?\r\n' TEXT_MESSAGE_CODE = r'^\d-\d:96\.13\.1.+?\r\n'
INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE = r'\d-\d:62\.7\.0.+?\r\n' TEXT_MESSAGE = r'^\d-\d:96\.13\.0.+?\r\n'
INSTANTANEOUS_REACTIVE_POWER_L1_POSITIVE = r'\d-\d:23\.7\.0.+?\r\n' DEVICE_TYPE = r'^\d-\d:24\.1\.0.+?\r\n'
INSTANTANEOUS_REACTIVE_POWER_L1_NEGATIVE = r'\d-\d:24\.7\.0.+?\r\n' INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE = r'^\d-\d:21\.7\.0.+?\r\n'
INSTANTANEOUS_REACTIVE_POWER_L2_POSITIVE = r'\d-\d:43\.7\.0.+?\r\n' INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE = r'^\d-\d:41\.7\.0.+?\r\n'
INSTANTANEOUS_REACTIVE_POWER_L2_NEGATIVE = r'\d-\d:44\.7\.0.+?\r\n' INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE = r'^\d-\d:61\.7\.0.+?\r\n'
INSTANTANEOUS_REACTIVE_POWER_L3_POSITIVE = r'\d-\d:63\.7\.0.+?\r\n' INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE = r'^\d-\d:22\.7\.0.+?\r\n'
INSTANTANEOUS_REACTIVE_POWER_L3_NEGATIVE = r'\d-\d:64\.7\.0.+?\r\n' INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE = r'^\d-\d:42\.7\.0.+?\r\n'
EQUIPMENT_IDENTIFIER_GAS = r'\d-\d:96\.1\.0.+?\r\n' INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE = r'^\d-\d:62\.7\.0.+?\r\n'
INSTANTANEOUS_REACTIVE_POWER_L1_POSITIVE = r'^\d-\d:23\.7\.0.+?\r\n'
INSTANTANEOUS_REACTIVE_POWER_L1_NEGATIVE = r'^\d-\d:24\.7\.0.+?\r\n'
INSTANTANEOUS_REACTIVE_POWER_L2_POSITIVE = r'^\d-\d:43\.7\.0.+?\r\n'
INSTANTANEOUS_REACTIVE_POWER_L2_NEGATIVE = r'^\d-\d:44\.7\.0.+?\r\n'
INSTANTANEOUS_REACTIVE_POWER_L3_POSITIVE = r'^\d-\d:63\.7\.0.+?\r\n'
INSTANTANEOUS_REACTIVE_POWER_L3_NEGATIVE = r'^\d-\d:64\.7\.0.+?\r\n'
EQUIPMENT_IDENTIFIER_GAS = r'^\d-\d:96\.1\.0.+?\r\n'
# TODO differences between gas meter readings in v3 and lower and v4 and up # TODO differences between gas meter readings in v3 and lower and v4 and up
HOURLY_GAS_METER_READING = r'\d-\d:24\.2\.1.+?\r\n' HOURLY_GAS_METER_READING = r'^\d-\d:24\.2\.1.+?\r\n'
GAS_METER_READING = r'\d-\d:24\.3\.0.+?\r\n.+?\r\n' GAS_METER_READING = r'^\d-\d:24\.3\.0.+?\r\n.+?\r\n'
ACTUAL_TRESHOLD_ELECTRICITY = r'\d-\d:17\.0\.0.+?\r\n' ACTUAL_TRESHOLD_ELECTRICITY = r'^\d-\d:17\.0\.0.+?\r\n'
ACTUAL_SWITCH_POSITION = r'\d-\d:96\.3\.10.+?\r\n' ACTUAL_SWITCH_POSITION = r'^\d-\d:96\.3\.10.+?\r\n'
VALVE_POSITION_GAS = r'\d-\d:24\.4\.0.+?\r\n' VALVE_POSITION_GAS = r'^\d-\d:24\.4\.0.+?\r\n'
# Multiple 'slaves' can be linked to the main device.
# The type is reported on 24.1.0
# Specifications are in EN 13757-3
# For example: Water mater = 7, Gas meter = 3
# Identifier is on 96.1.0 (in NL for ex) or
# on 96.1.1 (in BE for ex)
# The values are reported on 24.2.1
# With an exception in Belgium for the GAS meter
# Be aware that for the gas volume, another OBIS-code is published
# than the one listed in section 7 of DSMR P1.
# This is due to the fact that in Belgium the not-temperature
# corrected gas volume is used while in the Netherlands,
# the temperature corrected gas volume is used.
MBUS_DEVICE_TYPE = r'^\d-[1-9]:24\.1\.0.+?\r\n'
MBUS_EQUIPMENT_IDENTIFIER = r'^\d-[1-9]:96\.1\.[01].+?\r\n'
MBUS_VALVE_POSITION = r'^\d-[1-9]:24\.4\.0.+?\r\n'
MBUS_METER_READING = r'^\d-[1-9]:24\.2\.[13].+?\r\n'
# TODO 17.0.0 # TODO 17.0.0
# TODO 96.3.10
ELECTRICITY_USED_TARIFF_ALL = ( ELECTRICITY_USED_TARIFF_ALL = (
ELECTRICITY_USED_TARIFF_1, ELECTRICITY_USED_TARIFF_1,
@@ -75,50 +99,34 @@ ELECTRICITY_DELIVERED_TARIFF_ALL = (
) )
# International generalized additions # International generalized additions
ELECTRICITY_IMPORTED_TOTAL = r'\d-\d:1\.8\.0.+?\r\n' # Total imported energy register (P+) ELECTRICITY_IMPORTED_TOTAL = r'^\d-\d:1\.8\.0.+?\r\n' # Total imported energy register (P+)
ELECTRICITY_EXPORTED_TOTAL = r'\d-\d:2\.8\.0.+?\r\n' # Total exported energy register (P-) ELECTRICITY_EXPORTED_TOTAL = r'^\d-\d:2\.8\.0.+?\r\n' # Total exported energy register (P-)
# International non generalized additions (country specific) / risk for necessary refactoring # International non generalized additions (country specific) / risk for necessary refactoring
BELGIUM_VERSION_INFORMATION = r'\d-\d:96\.1\.4.+?\r\n' BELGIUM_VERSION_INFORMATION = r'^\d-\d:96\.1\.4.+?\r\n'
BELGIUM_EQUIPMENT_IDENTIFIER = r'\d-0:96\.1\.1.+?\r\n' BELGIUM_EQUIPMENT_IDENTIFIER = r'^\d-0:96\.1\.1.+?\r\n'
BELGIUM_CURRENT_AVERAGE_DEMAND = r'\d-\d:1\.4\.0.+?\r\n' BELGIUM_CURRENT_AVERAGE_DEMAND = r'^\d-\d:1\.4\.0.+?\r\n'
BELGIUM_MAXIMUM_DEMAND_MONTH = r'\d-\d:1\.6\.0.+?\r\n' BELGIUM_MAXIMUM_DEMAND_MONTH = r'^\d-\d:1\.6\.0.+?\r\n'
BELGIUM_MAXIMUM_DEMAND_13_MONTHS = r'\d-\d:98\.1\.0.+?\r\n' BELGIUM_MAXIMUM_DEMAND_13_MONTHS = r'^\d-\d:98\.1\.0.+?\r\n'
BELGIUM_MAX_POWER_PER_PHASE = r'\d-\d:17\.0\.0.+?\r\n' # Applicable when power limitation is active
BELGIUM_MAX_CURRENT_PER_PHASE = r'\d-\d:31\.4\.0.+?\r\n' # Applicable when current limitation is active
# Multiple 'slaves' can be linked to the main device. LUXEMBOURG_EQUIPMENT_IDENTIFIER = r'^\d-\d:42\.0\.0.+?\r\n' # Logical device name
# Mostly MBUS1 = GAS METER with values on 24.2.3
# While WATER METER reports it's values on 24.2.1
# The GAS METER also reports its valve state on 24.4.0
# Dev type for gas = 7 and water = 8
BELGIUM_MBUS1_DEVICE_TYPE = r'\d-1:24\.1\.0.+?\r\n'
BELGIUM_MBUS1_EQUIPMENT_IDENTIFIER = r'\d-1:96\.1\.1.+?\r\n'
BELGIUM_MBUS1_VALVE_POSITION = r'\d-1:24\.4\.0.+?\r\n'
BELGIUM_MBUS1_METER_READING1 = r'\d-1:24\.2\.1.+?\r\n'
BELGIUM_MBUS1_METER_READING2 = r'\d-1:24\.2\.3.+?\r\n'
BELGIUM_MBUS2_DEVICE_TYPE = r'\d-2:24\.1\.0.+?\r\n' Q3D_EQUIPMENT_IDENTIFIER = r'^\d-\d:0\.0\.0.+?\r\n' # Logical device name
BELGIUM_MBUS2_EQUIPMENT_IDENTIFIER = r'\d-2:96\.1\.1.+?\r\n' Q3D_EQUIPMENT_STATE = r'^\d-\d:96\.5\.5.+?\r\n' # Device state (hexadecimal)
BELGIUM_MBUS2_VALVE_POSITION = r'\d-2:24\.4\.0.+?\r\n' Q3D_EQUIPMENT_SERIALNUMBER = r'^\d-\d:96\.1\.255.+?\r\n' # Device Serialnumber
BELGIUM_MBUS2_METER_READING1 = r'\d-2:24\.2\.1.+?\r\n'
BELGIUM_MBUS2_METER_READING2 = r'\d-2:24\.2\.3.+?\r\n'
BELGIUM_MBUS3_DEVICE_TYPE = r'\d-3:24\.1\.0.+?\r\n' # EON Hungary
BELGIUM_MBUS3_EQUIPMENT_IDENTIFIER = r'\d-3:96\.1\.1.+?\r\n' EON_HU_ELECTRICITY_REACTIVE_TOTAL_Q1 = r'^\d-\d:5\.8\.0.+?\r\n'
BELGIUM_MBUS3_VALVE_POSITION = r'\d-3:24\.4\.0.+?\r\n' EON_HU_ELECTRICITY_REACTIVE_TOTAL_Q2 = r'^\d-\d:6\.8\.0.+?\r\n'
BELGIUM_MBUS3_METER_READING1 = r'\d-3:24\.2\.1.+?\r\n' EON_HU_ELECTRICITY_REACTIVE_TOTAL_Q3 = r'^\d-\d:7\.8\.0.+?\r\n'
BELGIUM_MBUS3_METER_READING2 = r'\d-3:24\.2\.3.+?\r\n' EON_HU_ELECTRICITY_REACTIVE_TOTAL_Q4 = r'^\d-\d:8\.8\.0.+?\r\n'
EON_HU_ELECTRICITY_COMBINED = r'^\d-\d:15\.8\.0.+?\r\n'
BELGIUM_MBUS4_DEVICE_TYPE = r'\d-4:24\.1\.0.+?\r\n' EON_HU_INSTANTANEOUS_POWER_FACTOR_TOTAL = r'^\d-\d:13\.7\.0.+?\r\n'
BELGIUM_MBUS4_EQUIPMENT_IDENTIFIER = r'\d-4:96\.1\.1.+?\r\n' EON_HU_INSTANTANEOUS_POWER_FACTOR_L1 = r'^\d-\d:33\.7\.0.+?\r\n'
BELGIUM_MBUS4_VALVE_POSITION = r'\d-4:24\.4\.0.+?\r\n' EON_HU_INSTANTANEOUS_POWER_FACTOR_L2 = r'^\d-\d:53\.7\.0.+?\r\n'
BELGIUM_MBUS4_METER_READING1 = r'\d-4:24\.2\.1.+?\r\n' EON_HU_INSTANTANEOUS_POWER_FACTOR_L3 = r'^\d-\d:73\.7\.0.+?\r\n'
BELGIUM_MBUS4_METER_READING2 = r'\d-4:24\.2\.3.+?\r\n' EON_HU_FREQUENCY = r'^\d-\d:14\.7\.0.+?\r\n'
EON_HU_INSTANTANEOUS_REACTIVE_POWER_Q1 = r'^\d-\d:5\.7\.0.+?\r\n'
EON_HU_INSTANTANEOUS_REACTIVE_POWER_Q2 = r'^\d-\d:6\.7\.0.+?\r\n'
LUXEMBOURG_EQUIPMENT_IDENTIFIER = r'\d-\d:42\.0\.0.+?\r\n' # Logical device name EON_HU_INSTANTANEOUS_REACTIVE_POWER_Q3 = r'^\d-\d:7\.7\.0.+?\r\n'
EON_HU_INSTANTANEOUS_REACTIVE_POWER_Q4 = r'^\d-\d:8\.7\.0.+?\r\n'
Q3D_EQUIPMENT_IDENTIFIER = r'\d-\d:0\.0\.0.+?\r\n' # Logical device name
Q3D_EQUIPMENT_STATE = r'\d-\d:96\.5\.5.+?\r\n' # Device state (hexadecimal)
Q3D_EQUIPMENT_SERIALNUMBER = r'\d-\d:96\.1\.255.+?\r\n' # Device Serialnumber
+37 -28
View File
@@ -5,8 +5,6 @@ import json
import pytz import pytz
from dsmr_parser import obis_name_mapping
class Telegram(dict): class Telegram(dict):
""" """
@@ -27,22 +25,21 @@ class Telegram(dict):
self._mbus_devices = [] self._mbus_devices = []
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
def add(self, obis_reference, dsmr_object): def add(self, obis_reference, dsmr_object, obis_name):
# Update name mapping used to get value by attribute. Example: telegram.P1_MESSAGE_HEADER # Update name mapping used to get value by attribute. Example: telegram.P1_MESSAGE_HEADER
obis_name = obis_name_mapping.EN[obis_reference]
setattr(self, obis_name, dsmr_object) setattr(self, obis_name, dsmr_object)
if obis_name not in self._item_names: # TODO repeating obis references
self._item_names.append(obis_name)
# TODO isinstance check: MaxDemandParser (BELGIUM_MAXIMUM_DEMAND_13_MONTHS) returns a list # TODO isinstance check: MaxDemandParser (BELGIUM_MAXIMUM_DEMAND_13_MONTHS) returns a list
if isinstance(dsmr_object, DSMRObject) and dsmr_object.is_mbus_reading: if isinstance(dsmr_object, DSMRObject) and dsmr_object.is_mbus_reading:
self._add_mbus(obis_reference, dsmr_object) self._add_mbus(obis_reference, dsmr_object, obis_name)
elif obis_name not in self._item_names: # TODO repeating obis references
self._item_names.append(obis_name)
# Fill dict which is only used for backwards compatibility # Fill dict which is only used for backwards compatibility
if obis_reference not in self: if obis_reference not in self:
self[obis_reference] = dsmr_object self[obis_reference] = dsmr_object
def _add_mbus(self, obis_reference, dsmr_object): def _add_mbus(self, obis_reference, dsmr_object, obis_name):
""" """
The given DsmrObject is assumed to be Mbus related and will be grouped into a MbusDevice. The given DsmrObject is assumed to be Mbus related and will be grouped into a MbusDevice.
Grouping is done by the DsmrObject channel ID. Grouping is done by the DsmrObject channel ID.
@@ -55,7 +52,7 @@ class Telegram(dict):
mbus_device = MbusDevice(channel_id=channel_id) mbus_device = MbusDevice(channel_id=channel_id)
self._mbus_devices.append(mbus_device) self._mbus_devices.append(mbus_device)
mbus_device.add(obis_reference, dsmr_object) mbus_device.add(obis_reference, dsmr_object, obis_name)
if not hasattr(self, 'MBUS_DEVICES'): if not hasattr(self, 'MBUS_DEVICES'):
setattr(self, 'MBUS_DEVICES', self._mbus_devices) setattr(self, 'MBUS_DEVICES', self._mbus_devices)
@@ -143,22 +140,25 @@ class MBusObject(DSMRObject):
return self.values[1]['unit'] return self.values[1]['unit']
def __str__(self): def __str__(self):
timestamp = self.datetime
if isinstance(timestamp, datetime.datetime):
timestamp = timestamp.astimezone().astimezone(pytz.utc).isoformat()
output = "{}\t[{}] at {}".format( output = "{}\t[{}] at {}".format(
str(self.value), str(self.value),
str(self.unit), str(self.unit),
str(self.datetime.astimezone().astimezone(pytz.utc).isoformat()) str(timestamp)
) )
return output return output
def to_json(self): def to_json(self):
timestamp = self.datetime timestamp = self.datetime
if isinstance(self.datetime, datetime.datetime): if isinstance(timestamp, datetime.datetime):
timestamp = self.datetime.astimezone().astimezone(pytz.utc).isoformat() timestamp = timestamp.astimezone().astimezone(pytz.utc).isoformat()
value = self.value value = self.value
if isinstance(self.value, datetime.datetime): if isinstance(value, datetime.datetime):
value = self.value.astimezone().astimezone(pytz.utc).isoformat() value = value.astimezone().astimezone(pytz.utc).isoformat()
if isinstance(self.value, Decimal): if isinstance(value, Decimal):
value = float(self.value) value = float(value)
output = { output = {
'datetime': timestamp, 'datetime': timestamp,
'value': value, 'value': value,
@@ -186,23 +186,33 @@ class MBusObjectPeak(DSMRObject):
return self.values[2]['unit'] return self.values[2]['unit']
def __str__(self): def __str__(self):
timestamp = self.datetime
if isinstance(timestamp, datetime.datetime):
timestamp = timestamp.astimezone().astimezone(pytz.utc).isoformat()
timestamp_occurred = self.occurred
if isinstance(timestamp_occurred, datetime.datetime):
timestamp_occurred = timestamp_occurred.astimezone().astimezone(pytz.utc).isoformat()
value = self.value
if isinstance(value, datetime.datetime):
value = value.astimezone().astimezone(pytz.utc).isoformat()
if isinstance(value, Decimal):
value = float(value)
output = "{}\t[{}] at {} occurred {}"\ output = "{}\t[{}] at {} occurred {}"\
.format(str(self.value), str(self.unit), str(self.datetime.astimezone().astimezone(pytz.utc).isoformat()), .format(str(value), str(self.unit), str(timestamp), str(timestamp_occurred))
str(self.occurred.astimezone().astimezone(pytz.utc).isoformat()))
return output return output
def to_json(self): def to_json(self):
timestamp = self.datetime timestamp = self.datetime
if isinstance(self.datetime, datetime.datetime): if isinstance(timestamp, datetime.datetime):
timestamp = self.datetime.astimezone().astimezone(pytz.utc).isoformat() timestamp = timestamp.astimezone().astimezone(pytz.utc).isoformat()
timestamp_occurred = self.occurred timestamp_occurred = self.occurred
if isinstance(self.occurred, datetime.datetime): if isinstance(timestamp_occurred, datetime.datetime):
timestamp_occurred = self.occurred.astimezone().astimezone(pytz.utc).isoformat() timestamp_occurred = timestamp_occurred.astimezone().astimezone(pytz.utc).isoformat()
value = self.value value = self.value
if isinstance(self.value, datetime.datetime): if isinstance(value, datetime.datetime):
value = self.value.astimezone().astimezone(pytz.utc).isoformat() value = value.astimezone().astimezone(pytz.utc).isoformat()
if isinstance(self.value, Decimal): if isinstance(value, Decimal):
value = float(self.value) value = float(value)
output = { output = {
'datetime': timestamp, 'datetime': timestamp,
'occurred': timestamp_occurred, 'occurred': timestamp_occurred,
@@ -331,10 +341,9 @@ class MbusDevice:
self.channel_id = channel_id self.channel_id = channel_id
self._item_names = [] self._item_names = []
def add(self, obis_reference, dsmr_object): def add(self, obis_reference, dsmr_object, obis_name):
# Update name mapping used to get value by attribute. Example: telegram.P1_MESSAGE_HEADER # Update name mapping used to get value by attribute. Example: telegram.P1_MESSAGE_HEADER
# Also keep track of the added names internally # Also keep track of the added names internally
obis_name = obis_name_mapping.EN[obis_reference]
setattr(self, obis_name, dsmr_object) setattr(self, obis_name, dsmr_object)
self._item_names.append(obis_name) self._item_names.append(obis_name)
+27 -11
View File
@@ -25,10 +25,15 @@ class TelegramParser(object):
telegram DSMR version (v4 and up). telegram DSMR version (v4 and up).
:type telegram_specification: dict :type telegram_specification: dict
""" """
self.telegram_specification = telegram_specification
self.apply_checksum_validation = apply_checksum_validation self.apply_checksum_validation = apply_checksum_validation
self.telegram_specification = telegram_specification
# Regexes are compiled once to improve performance
self.telegram_specification_regexes = {
object["obis_reference"]: re.compile(object["obis_reference"], re.DOTALL | re.MULTILINE)
for object in self.telegram_specification['objects']
}
def parse(self, telegram_data, encryption_key="", authentication_key=""): # noqa: C901 def parse(self, telegram_data, encryption_key="", authentication_key="", throw_ex=False): # noqa: C901
""" """
Parse telegram from string to dict. Parse telegram from string to dict.
The telegram str type makes python 2.x integration easier. The telegram str type makes python 2.x integration easier.
@@ -79,20 +84,31 @@ class TelegramParser(object):
telegram = Telegram() telegram = Telegram()
for signature, parser in self.telegram_specification['objects'].items(): for object in self.telegram_specification['objects']:
pattern = re.compile(signature, re.DOTALL) pattern = self.telegram_specification_regexes[object["obis_reference"]]
matches = pattern.findall(telegram_data) matches = pattern.findall(telegram_data)
# Some signatures are optional and may not be present, # Some signatures are optional and may not be present,
# so only parse lines that match # so only parse lines that match
for match in matches: for match in matches:
try: try:
dsmr_object = parser.parse(match) dsmr_object = object["value_parser"].parse(match)
except Exception: except ParseError:
logger.error("ignore line with signature {}, because parsing failed.".format(signature), logger.error(
exc_info=True) "ignore line with signature {}, because parsing failed.".format(object["obis_reference"]),
exc_info=True
)
if throw_ex:
raise
except Exception as err:
logger.error("Unexpected {}: {}".format(type(err), err))
raise
else: else:
telegram.add(obis_reference=signature, dsmr_object=dsmr_object) telegram.add(
obis_reference=object["obis_reference"],
dsmr_object=dsmr_object,
obis_name=object["value_name"]
)
return telegram return telegram
@@ -258,7 +274,7 @@ class MaxDemandParser(DSMRObjectParser):
count = int(values[0]) count = int(values[0])
for i in range(1, count + 1): for i in range(1, count + 1):
timestamp_month = ValueParser(timestamp).parse(values[i * 3 + 1]) timestamp_month = ValueParser(timestamp).parse(values[i * 3 + 0])
timestamp_occurred = ValueParser(timestamp).parse(values[i * 3 + 1]) timestamp_occurred = ValueParser(timestamp).parse(values[i * 3 + 1])
value = ValueParser(Decimal).parse(values[i * 3 + 2]) value = ValueParser(Decimal).parse(values[i * 3 + 2])
objects.append(MBusObjectPeak( objects.append(MBusObjectPeak(
@@ -319,7 +335,7 @@ class ProfileGenericParser(DSMRObjectParser):
""" """
def __init__(self, buffer_types, head_parsers, parsers_for_unidentified): def __init__(self, buffer_types, head_parsers, parsers_for_unidentified):
self.value_formats = head_parsers self.value_formats = head_parsers.copy()
self.buffer_types = buffer_types self.buffer_types = buffer_types
self.parsers_for_unidentified = parsers_for_unidentified self.parsers_for_unidentified = parsers_for_unidentified
File diff suppressed because it is too large Load Diff
+4 -2
View File
@@ -2,6 +2,10 @@ import datetime
import pytz import pytz
# TODO : Use system timezone
# Preload timezone to avoid loading in event loop later
local_tz = pytz.timezone('Europe/Amsterdam')
def timestamp(value): def timestamp(value):
try: try:
@@ -20,8 +24,6 @@ def timestamp(value):
else: else:
is_dst = False is_dst = False
# TODO : Use system timezone
local_tz = pytz.timezone('Europe/Amsterdam')
localized_datetime = local_tz.localize(naive_datetime, is_dst=is_dst) localized_datetime = local_tz.localize(naive_datetime, is_dst=is_dst)
return localized_datetime.astimezone(pytz.utc) return localized_datetime.astimezone(pytz.utc)
+2 -2
View File
@@ -7,11 +7,11 @@ setup(
author_email='mail@nldr.net', author_email='mail@nldr.net',
license='MIT', license='MIT',
url='https://github.com/ndokter/dsmr_parser', url='https://github.com/ndokter/dsmr_parser',
version='1.2.0', version='1.4.2',
packages=find_packages(exclude=('test', 'test.*')), packages=find_packages(exclude=('test', 'test.*')),
install_requires=[ install_requires=[
'pyserial>=3,<4', 'pyserial>=3,<4',
'pyserial-asyncio<1', 'pyserial-asyncio-fast>=0.11',
'pytz', 'pytz',
'Tailer==0.4.1', 'Tailer==0.4.1',
'dlms_cosem==21.3.2' 'dlms_cosem==21.3.2'
+96 -2
View File
@@ -175,7 +175,7 @@ TELEGRAM_V5_TWO_MBUS = (
) )
TELEGRAM_FLUVIUS_V171 = ( TELEGRAM_FLUVIUS_V171 = (
'/FLU5\253769484_A\r\n' '/FLU5\\253769484_A\r\n'
'\r\n' '\r\n'
'0-0:96.1.4(50217)\r\n' '0-0:96.1.4(50217)\r\n'
'0-0:96.1.1(3153414733313031303231363035)\r\n' '0-0:96.1.1(3153414733313031303231363035)\r\n'
@@ -213,7 +213,49 @@ TELEGRAM_FLUVIUS_V171 = (
'0-2:24.1.0(007)\r\n' '0-2:24.1.0(007)\r\n'
'0-2:96.1.1(3853414731323334353637383930)\r\n' '0-2:96.1.1(3853414731323334353637383930)\r\n'
'0-2:24.2.1(200512134558S)(00872.234*m3)\r\n' '0-2:24.2.1(200512134558S)(00872.234*m3)\r\n'
'!911C\r\n' '!3AD7\r\n'
)
TELEGRAM_FLUVIUS_V171_ALT = (
'/FLU5\\253769484_A\r\n'
'\r\n'
'0-0:96.1.4(50217)\r\n'
'0-0:96.1.1(3153414733313030373231333236)\r\n'
'0-0:1.0.0(231102121548W)\r\n'
'1-0:1.8.1(000301.548*kWh)\r\n'
'1-0:1.8.2(000270.014*kWh)\r\n'
'1-0:2.8.1(000000.005*kWh)\r\n'
'1-0:2.8.2(000000.000*kWh)\r\n'
'0-0:96.14.0(0001)\r\n'
'1-0:1.4.0(00.052*kW)\r\n'
'1-0:1.6.0(231102114500W)(03.064*kW)\r\n'
'0-0:98.1.0(4)(1-0:1.6.0)(1-0:1.6.0)(230801000000S)(632525252525W)(00.000*kW)(230901000000S)(230831181500S)(01.862*kW)(231001000000S)(230910183000S)(04.229*kW)(231101000000W)(231016130000S)(04.927*kW)\r\n'
'1-0:1.7.0(00.338*kW)\r\n'
'1-0:2.7.0(00.000*kW)\r\n'
'1-0:21.7.0(00.047*kW)\r\n'
'1-0:41.7.0(00.179*kW)\r\n'
'1-0:61.7.0(00.111*kW)\r\n'
'1-0:22.7.0(00.000*kW)\r\n'
'1-0:42.7.0(00.000*kW)\r\n'
'1-0:62.7.0(00.000*kW)\r\n'
'1-0:32.7.0(232.9*V)\r\n'
'1-0:52.7.0(228.1*V)\r\n'
'1-0:72.7.0(228.1*V)\r\n'
'1-0:31.7.0(000.27*A)\r\n'
'1-0:51.7.0(000.88*A)\r\n'
'1-0:71.7.0(000.52*A)\r\n'
'0-0:96.3.10(1)\r\n'
'0-0:17.0.0(999.9*kW)\r\n'
'1-0:31.4.0(999*A)\r\n'
'0-0:96.13.0()\r\n'
'0-1:24.1.0(003)\r\n'
'0-1:96.1.1(37464C4F32313233303838303237)\r\n'
'0-1:24.4.0(1)\r\n'
'0-1:24.2.3(231102121002W)(00092.287*m3)\r\n'
'0-2:24.1.0(007)\r\n'
'0-2:96.1.1(3853455430303030393631313733)\r\n'
'0-2:24.2.1(231102121532W)(00008.579*m3)\r\n'
'!C4B0\r\n'
) )
# EasyMeter via COM-1 Ethernet Gateway # EasyMeter via COM-1 Ethernet Gateway
@@ -311,3 +353,55 @@ TELEGRAM_ISKRA_IE = (
'0-1:96.1.1()\r\n' '0-1:96.1.1()\r\n'
'!AD3B\r\n' '!AD3B\r\n'
) )
# V5 telegram of EON in Hungary
TELEGRAM_V5_EON_HU = (
'/SAG5SAG-METER\r\n'
'\r\n'
'0-0:1.0.0(230724150730S)\r\n'
'0-0:42.0.0(53414733303832323030303032313630)\r\n'
'0-0:96.1.0(383930303832323030303032313630)\r\n'
'0-0:96.14.0(0001)\r\n'
'0-0:96.3.10(1)\r\n'
'0-0:17.0.0(90.000*kW)\r\n'
'1-0:1.8.0(000173.640*kWh)\r\n'
'1-0:1.8.1(000047.719*kWh)\r\n'
'1-0:1.8.2(000125.921*kWh)\r\n'
'1-0:1.8.3(000000.000*kWh)\r\n'
'1-0:1.8.4(000000.000*kWh)\r\n'
'1-0:2.8.0(000627.177*kWh)\r\n'
'1-0:2.8.1(000401.829*kWh)\r\n'
'1-0:2.8.2(000225.348*kWh)\r\n'
'1-0:2.8.3(000000.000*kWh)\r\n'
'1-0:2.8.4(000000.000*kWh)\r\n'
'1-0:3.8.0(000000.123*kvarh)\r\n'
'1-0:4.8.0(000303.131*kvarh)\r\n'
'1-0:5.8.0(000000.668*kvarh)\r\n'
'1-0:6.8.0(000000.071*kvarh)\r\n'
'1-0:7.8.0(000160.487*kvarh)\r\n'
'1-0:8.8.0(000143.346*kvarh)\r\n'
'1-0:15.8.0(000800.817*kWh)\r\n'
'1-0:32.7.0(240.4*V)\r\n'
'1-0:52.7.0(239.1*V)\r\n'
'1-0:72.7.0(241.2*V)\r\n'
'1-0:31.7.0(003*A)\r\n'
'1-0:51.7.0(004*A)\r\n'
'1-0:71.7.0(003*A)\r\n'
'1-0:13.7.0(4.556)\r\n'
'1-0:33.7.0(4.591)\r\n'
'1-0:53.7.0(4.542)\r\n'
'1-0:73.7.0(4.552)\r\n'
'1-0:14.7.0(50.00*Hz)\r\n'
'1-0:1.7.0(00.000*kW)\r\n'
'1-0:2.7.0(02.601*kW)\r\n'
'1-0:5.7.0(00.000*kvar)\r\n'
'1-0:6.7.0(00.000*kvar)\r\n'
'1-0:7.7.0(00.504*kvar)\r\n'
'1-0:8.7.0(00.000*kvar)\r\n'
'1-0:31.4.0(200.00*A)\r\n'
'1-0:51.4.0(200.00*A)\r\n'
'1-0:71.4.0(200.00*A)\r\n'
'0-0:98.1.0(230701000000S)(000040.777*kWh)(000008.950*kWh)(000031.827*kWh)(000142.250*kWh)(000111.164*kWh)(000031.086*kWh)(000000.030*kvarh)(000073.988*kvarh)(000000.205*kvarh)(000000.048*kvarh)(000039.199*kvarh)(000035.020*kvarh)(000183.027*kWh)(03.564*kW)(02.156*kW)(03.564*kW)(04.104*kW)(04.104*kW)(03.400*kW)\r\n'
'0-0:96.13.0()\r\n'
'!99DA\r\n'
)
+33 -21
View File
@@ -12,50 +12,62 @@ class MbusDeviceTest(unittest.TestCase):
def setUp(self): def setUp(self):
v5_objects = telegram_specifications.V5['objects'] v5_objects = telegram_specifications.V5['objects']
device_type_parser = v5_objects[obis_references.DEVICE_TYPE] device_type_parser = [
object["value_parser"]
for object in v5_objects
if object["obis_reference"] == obis_references.MBUS_DEVICE_TYPE
][0]
device_type = device_type_parser.parse('0-2:24.1.0(003)\r\n') device_type = device_type_parser.parse('0-2:24.1.0(003)\r\n')
equipment_parser = v5_objects[obis_references.EQUIPMENT_IDENTIFIER_GAS] equipment_parser = [
object["value_parser"]
for object in v5_objects
if object["obis_reference"] == obis_references.MBUS_EQUIPMENT_IDENTIFIER
][0]
equipment = equipment_parser.parse('0-2:96.1.0(4730303339303031393336393930363139)\r\n') equipment = equipment_parser.parse('0-2:96.1.0(4730303339303031393336393930363139)\r\n')
gas_reading_parser = v5_objects[obis_references.HOURLY_GAS_METER_READING] gas_reading_parser = [
object["value_parser"]
for object in v5_objects
if object["obis_reference"] == obis_references.MBUS_METER_READING
][0]
gas_reading = gas_reading_parser.parse('0-2:24.2.1(200426223001S)(00246.138*m3)\r\n') gas_reading = gas_reading_parser.parse('0-2:24.2.1(200426223001S)(00246.138*m3)\r\n')
mbus_device = MbusDevice(channel_id=1) mbus_device = MbusDevice(channel_id=2)
mbus_device.add(obis_references.DEVICE_TYPE, device_type) mbus_device.add(obis_references.MBUS_DEVICE_TYPE, device_type, "MBUS_DEVICE_TYPE")
mbus_device.add(obis_references.EQUIPMENT_IDENTIFIER_GAS, equipment) mbus_device.add(obis_references.MBUS_EQUIPMENT_IDENTIFIER, equipment, "MBUS_EQUIPMENT_IDENTIFIER")
mbus_device.add(obis_references.HOURLY_GAS_METER_READING, gas_reading) mbus_device.add(obis_references.MBUS_METER_READING, gas_reading, "MBUS_METER_READING")
self.mbus_device = mbus_device self.mbus_device = mbus_device
def test_attributes(self): def test_attributes(self):
self.assertEqual(self.mbus_device.DEVICE_TYPE.value, 3) self.assertEqual(self.mbus_device.MBUS_DEVICE_TYPE.value, 3)
self.assertEqual(self.mbus_device.DEVICE_TYPE.unit, None) self.assertEqual(self.mbus_device.MBUS_DEVICE_TYPE.unit, None)
self.assertEqual(self.mbus_device.EQUIPMENT_IDENTIFIER_GAS.value, self.assertEqual(self.mbus_device.MBUS_EQUIPMENT_IDENTIFIER.value,
'4730303339303031393336393930363139') '4730303339303031393336393930363139')
self.assertEqual(self.mbus_device.EQUIPMENT_IDENTIFIER_GAS.unit, None) self.assertEqual(self.mbus_device.MBUS_EQUIPMENT_IDENTIFIER.unit, None)
self.assertEqual(self.mbus_device.HOURLY_GAS_METER_READING.value, Decimal('246.138')) self.assertEqual(self.mbus_device.MBUS_METER_READING.value, Decimal('246.138'))
self.assertEqual(self.mbus_device.HOURLY_GAS_METER_READING.unit, 'm3') self.assertEqual(self.mbus_device.MBUS_METER_READING.unit, 'm3')
def test_to_json(self): def test_to_json(self):
self.assertEqual( self.assertEqual(
json.loads(self.mbus_device.to_json()), json.loads(self.mbus_device.to_json()),
{ {
'CHANNEL_ID': 1, 'CHANNEL_ID': 2,
'DEVICE_TYPE': {'value': 3, 'unit': None}, 'MBUS_DEVICE_TYPE': {'value': 3, 'unit': None},
'EQUIPMENT_IDENTIFIER_GAS': {'value': '4730303339303031393336393930363139', 'unit': None}, 'MBUS_EQUIPMENT_IDENTIFIER': {'value': '4730303339303031393336393930363139', 'unit': None},
'HOURLY_GAS_METER_READING': {'datetime': '2020-04-26T20:30:01+00:00', 'value': 246.138, 'unit': 'm3'}} 'MBUS_METER_READING': {'datetime': '2020-04-26T20:30:01+00:00', 'value': 246.138, 'unit': 'm3'}}
) )
def test_str(self): def test_str(self):
self.assertEqual( self.assertEqual(
str(self.mbus_device), str(self.mbus_device),
( (
'MBUS DEVICE (channel 1)\n' 'MBUS DEVICE (channel 2)\n'
'\tDEVICE_TYPE: 3 [None]\n' '\tMBUS_DEVICE_TYPE: 3 [None]\n'
'\tEQUIPMENT_IDENTIFIER_GAS: 4730303339303031393336393930363139 [None]\n' '\tMBUS_EQUIPMENT_IDENTIFIER: 4730303339303031393336393930363139 [None]\n'
'\tHOURLY_GAS_METER_READING: 246.138 [m3] at 2020-04-26T20:30:01+00:00\n' '\tMBUS_METER_READING: 246.138 [m3] at 2020-04-26T20:30:01+00:00\n'
) )
) )
+45 -48
View File
@@ -4,7 +4,7 @@ import datetime
import pytz import pytz
from dsmr_parser import telegram_specifications, obis_references from dsmr_parser import telegram_specifications, obis_references
from dsmr_parser import obis_name_mapping
from dsmr_parser.objects import CosemObject from dsmr_parser.objects import CosemObject
from dsmr_parser.objects import MBusObject from dsmr_parser.objects import MBusObject
from dsmr_parser.objects import ProfileGenericObject from dsmr_parser.objects import ProfileGenericObject
@@ -216,14 +216,6 @@ class TelegramTest(unittest.TestCase):
value_type=Decimal, value_type=Decimal,
value_val=Decimal('2')) value_val=Decimal('2'))
# DEVICE_TYPE (0-x:24.1.0)
self.verify_telegram_item(telegram,
'DEVICE_TYPE',
object_type=CosemObject,
unit_val=None,
value_type=int,
value_val=3)
# INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE (1-0:21.7.0) # INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE (1-0:21.7.0)
self.verify_telegram_item(telegram, self.verify_telegram_item(telegram,
'INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE', 'INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE',
@@ -272,7 +264,15 @@ class TelegramTest(unittest.TestCase):
value_type=Decimal, value_type=Decimal,
value_val=Decimal('0')) value_val=Decimal('0'))
# EQUIPMENT_IDENTIFIER_GAS (0-x:96.1.0) # DEVICE_TYPE (0-1:24.1.0)
self.verify_telegram_item(telegram,
'DEVICE_TYPE',
object_type=CosemObject,
unit_val=None,
value_type=int,
value_val=3)
# EQUIPMENT_IDENTIFIER_GAS (0-1:96.1.0)
self.verify_telegram_item(telegram, self.verify_telegram_item(telegram,
'EQUIPMENT_IDENTIFIER_GAS', 'EQUIPMENT_IDENTIFIER_GAS',
object_type=CosemObject, object_type=CosemObject,
@@ -314,8 +314,8 @@ class TelegramTest(unittest.TestCase):
self.item_names_tested.append(testitem_name) self.item_names_tested.append(testitem_name)
# check if all items in telegram V4 specification are covered # check if all items in telegram V4 specification are covered
V4_name_list = [obis_name_mapping.EN[signature] for signature, parser in V4_name_list = [object["value_name"] for object in
telegram_specifications.V4['objects'].items()] telegram_specifications.V4['objects']]
V4_name_set = set(V4_name_list) V4_name_set = set(V4_name_list)
item_names_tested_set = set(self.item_names_tested) item_names_tested_set = set(self.item_names_tested)
@@ -329,7 +329,7 @@ class TelegramTest(unittest.TestCase):
break break
# Verify that the iterator works for at least one value # Verify that the iterator works for at least one value
self.assertEqual(obis_name, obis_name_mapping.EN[obis_references.P1_MESSAGE_HEADER]) self.assertEqual(obis_name, "P1_MESSAGE_HEADER")
self.assertEqual(dsmr_object.value, '50') self.assertEqual(dsmr_object.value, '50')
def test_mbus_devices(self): def test_mbus_devices(self):
@@ -340,28 +340,28 @@ class TelegramTest(unittest.TestCase):
self.assertEqual(len(mbus_devices), 2) self.assertEqual(len(mbus_devices), 2)
mbus_device_1 = mbus_devices[0] mbus_device_1 = mbus_devices[0]
self.assertEqual(mbus_device_1.DEVICE_TYPE.value, 3) self.assertEqual(mbus_device_1.MBUS_DEVICE_TYPE.value, 3)
self.assertEqual(mbus_device_1.EQUIPMENT_IDENTIFIER_GAS.value, None) self.assertEqual(mbus_device_1.MBUS_EQUIPMENT_IDENTIFIER.value, None)
self.assertEqual(mbus_device_1.HOURLY_GAS_METER_READING.value, Decimal('0')) self.assertEqual(mbus_device_1.MBUS_METER_READING.value, Decimal('0'))
mbus_device_2 = mbus_devices[1] mbus_device_2 = mbus_devices[1]
self.assertEqual(mbus_device_2.DEVICE_TYPE.value, 3) self.assertEqual(mbus_device_2.MBUS_DEVICE_TYPE.value, 3)
self.assertEqual(mbus_device_2.EQUIPMENT_IDENTIFIER_GAS.value, '4730303339303031393336393930363139') self.assertEqual(mbus_device_2.MBUS_EQUIPMENT_IDENTIFIER.value, '4730303339303031393336393930363139')
self.assertEqual(mbus_device_2.HOURLY_GAS_METER_READING.value, Decimal('246.138')) self.assertEqual(mbus_device_2.MBUS_METER_READING.value, Decimal('246.138'))
def test_get_mbus_device_by_channel(self): def test_get_mbus_device_by_channel(self):
parser = TelegramParser(telegram_specifications.V5) parser = TelegramParser(telegram_specifications.V5)
telegram = parser.parse(TELEGRAM_V5_TWO_MBUS) telegram = parser.parse(TELEGRAM_V5_TWO_MBUS)
mbus_device_1 = telegram.get_mbus_device_by_channel(1) mbus_device_1 = telegram.get_mbus_device_by_channel(1)
self.assertEqual(mbus_device_1.DEVICE_TYPE.value, 3) self.assertEqual(mbus_device_1.MBUS_DEVICE_TYPE.value, 3)
self.assertEqual(mbus_device_1.EQUIPMENT_IDENTIFIER_GAS.value, None) self.assertEqual(mbus_device_1.MBUS_EQUIPMENT_IDENTIFIER.value, None)
self.assertEqual(mbus_device_1.HOURLY_GAS_METER_READING.value, Decimal('0')) self.assertEqual(mbus_device_1.MBUS_METER_READING.value, Decimal('0'))
mbus_device_2 = telegram.get_mbus_device_by_channel(2) mbus_device_2 = telegram.get_mbus_device_by_channel(2)
self.assertEqual(mbus_device_2.DEVICE_TYPE.value, 3) self.assertEqual(mbus_device_2.MBUS_DEVICE_TYPE.value, 3)
self.assertEqual(mbus_device_2.EQUIPMENT_IDENTIFIER_GAS.value, '4730303339303031393336393930363139') self.assertEqual(mbus_device_2.MBUS_EQUIPMENT_IDENTIFIER.value, '4730303339303031393336393930363139')
self.assertEqual(mbus_device_2.HOURLY_GAS_METER_READING.value, Decimal('246.138')) self.assertEqual(mbus_device_2.MBUS_METER_READING.value, Decimal('246.138'))
def test_without_mbus_devices(self): def test_without_mbus_devices(self):
parser = TelegramParser(telegram_specifications.V5, apply_checksum_validation=False) parser = TelegramParser(telegram_specifications.V5, apply_checksum_validation=False)
@@ -375,11 +375,12 @@ class TelegramTest(unittest.TestCase):
telegram = parser.parse(TELEGRAM_V5) telegram = parser.parse(TELEGRAM_V5)
json_data = json.loads(telegram.to_json()) json_data = json.loads(telegram.to_json())
self.maxDiff = None
self.assertEqual( self.assertEqual(
json_data, json_data,
{'CURRENT_ELECTRICITY_DELIVERY': {'unit': 'kW', 'value': 0.0}, {'CURRENT_ELECTRICITY_DELIVERY': {'unit': 'kW', 'value': 0.0},
'CURRENT_ELECTRICITY_USAGE': {'unit': 'kW', 'value': 0.244}, 'CURRENT_ELECTRICITY_USAGE': {'unit': 'kW', 'value': 0.244},
'DEVICE_TYPE': {'unit': None, 'value': 3},
'ELECTRICITY_ACTIVE_TARIFF': {'unit': None, 'value': '0002'}, 'ELECTRICITY_ACTIVE_TARIFF': {'unit': None, 'value': '0002'},
'ELECTRICITY_DELIVERED_TARIFF_1': {'unit': 'kWh', 'value': 2.444}, 'ELECTRICITY_DELIVERED_TARIFF_1': {'unit': 'kWh', 'value': 2.444},
'ELECTRICITY_DELIVERED_TARIFF_2': {'unit': 'kWh', 'value': 0.0}, 'ELECTRICITY_DELIVERED_TARIFF_2': {'unit': 'kWh', 'value': 0.0},
@@ -387,10 +388,6 @@ class TelegramTest(unittest.TestCase):
'ELECTRICITY_USED_TARIFF_2': {'unit': 'kWh', 'value': 2.399}, 'ELECTRICITY_USED_TARIFF_2': {'unit': 'kWh', 'value': 2.399},
'EQUIPMENT_IDENTIFIER': {'unit': None, 'EQUIPMENT_IDENTIFIER': {'unit': None,
'value': '4B384547303034303436333935353037'}, 'value': '4B384547303034303436333935353037'},
'EQUIPMENT_IDENTIFIER_GAS': {'unit': None, 'value': None},
'HOURLY_GAS_METER_READING': {'datetime': '2017-01-02T15:10:05+00:00',
'unit': 'm3',
'value': 0.107},
'INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE': {'unit': 'kW', 'value': 0.0}, 'INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE': {'unit': 'kW', 'value': 0.0},
'INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE': {'unit': 'kW', 'value': 0.07}, 'INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE': {'unit': 'kW', 'value': 0.07},
'INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE': {'unit': 'kW', 'value': 0.0}, 'INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE': {'unit': 'kW', 'value': 0.0},
@@ -405,15 +402,16 @@ class TelegramTest(unittest.TestCase):
'INSTANTANEOUS_VOLTAGE_L3': {'unit': 'V', 'value': 229.0}, 'INSTANTANEOUS_VOLTAGE_L3': {'unit': 'V', 'value': 229.0},
'LONG_POWER_FAILURE_COUNT': {'unit': None, 'value': 0}, 'LONG_POWER_FAILURE_COUNT': {'unit': None, 'value': 0},
'MBUS_DEVICES': [{'CHANNEL_ID': 1, 'MBUS_DEVICES': [{'CHANNEL_ID': 1,
'DEVICE_TYPE': {'unit': None, 'value': 3}, 'MBUS_DEVICE_TYPE': {'unit': None, 'value': 3},
'EQUIPMENT_IDENTIFIER_GAS': {'unit': None, 'MBUS_EQUIPMENT_IDENTIFIER': {'unit': None,
'value': '3232323241424344313233343536373839'}, 'value': '3232323241424344313233343536373839'},
'HOURLY_GAS_METER_READING': {'datetime': '2017-01-02T15:10:05+00:00', 'MBUS_METER_READING': {'datetime': '2017-01-02T15:10:05+00:00',
'unit': 'm3', 'unit': 'm3',
'value': 0.107}}, 'value': 0.107}},
{'CHANNEL_ID': 2, {'CHANNEL_ID': 2,
'DEVICE_TYPE': {'unit': None, 'value': 3}, 'MBUS_DEVICE_TYPE': {'unit': None, 'value': 3},
'EQUIPMENT_IDENTIFIER_GAS': {'unit': None, 'value': None}}], 'MBUS_EQUIPMENT_IDENTIFIER': {'unit': None,
'value': None}}],
'P1_MESSAGE_HEADER': {'unit': None, 'value': '50'}, 'P1_MESSAGE_HEADER': {'unit': None, 'value': '50'},
'P1_MESSAGE_TIMESTAMP': {'unit': None, 'value': '2017-01-02T18:20:02+00:00'}, 'P1_MESSAGE_TIMESTAMP': {'unit': None, 'value': '2017-01-02T18:20:02+00:00'},
'POWER_EVENT_FAILURE_LOG': {'buffer': [], 'POWER_EVENT_FAILURE_LOG': {'buffer': [],
@@ -433,6 +431,8 @@ class TelegramTest(unittest.TestCase):
parser = TelegramParser(telegram_specifications.V5) parser = TelegramParser(telegram_specifications.V5)
telegram = parser.parse(TELEGRAM_V5) telegram = parser.parse(TELEGRAM_V5)
self.maxDiff = None
self.assertEqual( self.assertEqual(
str(telegram), str(telegram),
( (
@@ -463,22 +463,19 @@ class TelegramTest(unittest.TestCase):
'INSTANTANEOUS_CURRENT_L2: 0.44 [A]\n' 'INSTANTANEOUS_CURRENT_L2: 0.44 [A]\n'
'INSTANTANEOUS_CURRENT_L3: 0.86 [A]\n' 'INSTANTANEOUS_CURRENT_L3: 0.86 [A]\n'
'TEXT_MESSAGE: None [None]\n' 'TEXT_MESSAGE: None [None]\n'
'DEVICE_TYPE: 3 [None]\n'
'MBUS DEVICE (channel 1)\n'
' DEVICE_TYPE: 3 [None]\n'
' EQUIPMENT_IDENTIFIER_GAS: 3232323241424344313233343536373839 [None]\n'
' HOURLY_GAS_METER_READING: 0.107 [m3] at 2017-01-02T15:10:05+00:00\n'
'MBUS DEVICE (channel 2)\n'
' DEVICE_TYPE: 3 [None]\n'
' EQUIPMENT_IDENTIFIER_GAS: None [None]\n'
'INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE: 0.070 [kW]\n' 'INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE: 0.070 [kW]\n'
'INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE: 0.032 [kW]\n' 'INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE: 0.032 [kW]\n'
'INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE: 0.142 [kW]\n' 'INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE: 0.142 [kW]\n'
'INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE: 0.000 [kW]\n' 'INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE: 0.000 [kW]\n'
'INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE: 0.000 [kW]\n' 'INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE: 0.000 [kW]\n'
'INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE: 0.000 [kW]\n' 'INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE: 0.000 [kW]\n'
'EQUIPMENT_IDENTIFIER_GAS: None [None]\n' 'MBUS DEVICE (channel 1)\n'
'HOURLY_GAS_METER_READING: 0.107 [m3] at 2017-01-02T15:10:05+00:00\n' ' MBUS_DEVICE_TYPE: 3 [None]\n'
' MBUS_EQUIPMENT_IDENTIFIER: 3232323241424344313233343536373839 [None]\n'
' MBUS_METER_READING: 0.107 [m3] at 2017-01-02T15:10:05+00:00\n'
'MBUS DEVICE (channel 2)\n'
' MBUS_DEVICE_TYPE: 3 [None]\n'
' MBUS_EQUIPMENT_IDENTIFIER: None [None]\n'
) )
) )
+310 -165
View File
@@ -1,16 +1,16 @@
from decimal import Decimal from decimal import Decimal
import datetime import datetime
import json
import unittest import unittest
import pytz import pytz
from dsmr_parser import obis_references as obis
from dsmr_parser import telegram_specifications from dsmr_parser import telegram_specifications
from dsmr_parser.exceptions import InvalidChecksumError, ParseError from dsmr_parser.exceptions import InvalidChecksumError, ParseError
from dsmr_parser.objects import CosemObject, MBusObject, MBusObjectPeak from dsmr_parser.objects import CosemObject, MBusObject, MBusObjectPeak
from dsmr_parser.parsers import TelegramParser from dsmr_parser.parsers import TelegramParser
from test.example_telegrams import TELEGRAM_FLUVIUS_V171 from test.example_telegrams import TELEGRAM_FLUVIUS_V171, TELEGRAM_FLUVIUS_V171_ALT
class TelegramParserFluviusTest(unittest.TestCase): class TelegramParserFluviusTest(unittest.TestCase):
@@ -18,233 +18,263 @@ class TelegramParserFluviusTest(unittest.TestCase):
def test_parse(self): def test_parse(self):
parser = TelegramParser(telegram_specifications.BELGIUM_FLUVIUS) parser = TelegramParser(telegram_specifications.BELGIUM_FLUVIUS)
result = parser.parse(TELEGRAM_FLUVIUS_V171) try:
result = parser.parse(TELEGRAM_FLUVIUS_V171, throw_ex=True)
except Exception as ex:
assert False, f"parse trigged an exception {ex}"
# BELGIUM_VERSION_INFORMATION (0-0:96.1.4) # BELGIUM_VERSION_INFORMATION (0-0:96.1.4)
assert isinstance(result[obis.BELGIUM_VERSION_INFORMATION], CosemObject) assert isinstance(result.BELGIUM_VERSION_INFORMATION, CosemObject)
assert result[obis.BELGIUM_VERSION_INFORMATION].unit is None assert result.BELGIUM_VERSION_INFORMATION.unit is None
assert isinstance(result[obis.BELGIUM_VERSION_INFORMATION].value, str) assert isinstance(result.BELGIUM_VERSION_INFORMATION.value, str)
assert result[obis.BELGIUM_VERSION_INFORMATION].value == '50217' assert result.BELGIUM_VERSION_INFORMATION.value == '50217'
# EQUIPMENT_IDENTIFIER (0-0:96.1.1) # EQUIPMENT_IDENTIFIER (0-0:96.1.1)
assert isinstance(result[obis.EQUIPMENT_IDENTIFIER], CosemObject) assert isinstance(result.BELGIUM_EQUIPMENT_IDENTIFIER, CosemObject)
assert result[obis.EQUIPMENT_IDENTIFIER].unit is None assert result.BELGIUM_EQUIPMENT_IDENTIFIER.unit is None
assert isinstance(result[obis.EQUIPMENT_IDENTIFIER].value, str) assert isinstance(result.BELGIUM_EQUIPMENT_IDENTIFIER.value, str)
assert result[obis.EQUIPMENT_IDENTIFIER].value == '3153414733313031303231363035' assert result.BELGIUM_EQUIPMENT_IDENTIFIER.value == '3153414733313031303231363035'
# P1_MESSAGE_TIMESTAMP (0-0:1.0.0) # P1_MESSAGE_TIMESTAMP (0-0:1.0.0)
assert isinstance(result[obis.P1_MESSAGE_TIMESTAMP], CosemObject) assert isinstance(result.P1_MESSAGE_TIMESTAMP, CosemObject)
assert result[obis.P1_MESSAGE_TIMESTAMP].unit is None assert result.P1_MESSAGE_TIMESTAMP.unit is None
assert isinstance(result[obis.P1_MESSAGE_TIMESTAMP].value, datetime.datetime) assert isinstance(result.P1_MESSAGE_TIMESTAMP.value, datetime.datetime)
assert result[obis.P1_MESSAGE_TIMESTAMP].value == \ assert result.P1_MESSAGE_TIMESTAMP.value == \
datetime.datetime(2020, 5, 12, 11, 54, 9, tzinfo=pytz.UTC) pytz.timezone("Europe/Brussels").localize(datetime.datetime(2020, 5, 12, 13, 54, 9))
# ELECTRICITY_USED_TARIFF_1 (1-0:1.8.1) # ELECTRICITY_USED_TARIFF_1 (1-0:1.8.1)
assert isinstance(result[obis.ELECTRICITY_USED_TARIFF_1], CosemObject) assert isinstance(result.ELECTRICITY_USED_TARIFF_1, CosemObject)
assert result[obis.ELECTRICITY_USED_TARIFF_1].unit == 'kWh' assert result.ELECTRICITY_USED_TARIFF_1.unit == 'kWh'
assert isinstance(result[obis.ELECTRICITY_USED_TARIFF_1].value, Decimal) assert isinstance(result.ELECTRICITY_USED_TARIFF_1.value, Decimal)
assert result[obis.ELECTRICITY_USED_TARIFF_1].value == Decimal('0.034') assert result.ELECTRICITY_USED_TARIFF_1.value == Decimal('0.034')
# ELECTRICITY_USED_TARIFF_2 (1-0:1.8.2) # ELECTRICITY_USED_TARIFF_2 (1-0:1.8.2)
assert isinstance(result[obis.ELECTRICITY_USED_TARIFF_2], CosemObject) assert isinstance(result.ELECTRICITY_USED_TARIFF_2, CosemObject)
assert result[obis.ELECTRICITY_USED_TARIFF_2].unit == 'kWh' assert result.ELECTRICITY_USED_TARIFF_2.unit == 'kWh'
assert isinstance(result[obis.ELECTRICITY_USED_TARIFF_2].value, Decimal) assert isinstance(result.ELECTRICITY_USED_TARIFF_2.value, Decimal)
assert result[obis.ELECTRICITY_USED_TARIFF_2].value == Decimal('15.758') assert result.ELECTRICITY_USED_TARIFF_2.value == Decimal('15.758')
# ELECTRICITY_DELIVERED_TARIFF_1 (1-0:2.8.1) # ELECTRICITY_DELIVERED_TARIFF_1 (1-0:2.8.1)
assert isinstance(result[obis.ELECTRICITY_DELIVERED_TARIFF_1], CosemObject) assert isinstance(result.ELECTRICITY_DELIVERED_TARIFF_1, CosemObject)
assert result[obis.ELECTRICITY_DELIVERED_TARIFF_1].unit == 'kWh' assert result.ELECTRICITY_DELIVERED_TARIFF_1.unit == 'kWh'
assert isinstance(result[obis.ELECTRICITY_DELIVERED_TARIFF_1].value, Decimal) assert isinstance(result.ELECTRICITY_DELIVERED_TARIFF_1.value, Decimal)
assert result[obis.ELECTRICITY_DELIVERED_TARIFF_1].value == Decimal('0.000') assert result.ELECTRICITY_DELIVERED_TARIFF_1.value == Decimal('0.000')
# ELECTRICITY_DELIVERED_TARIFF_2 (1-0:2.8.2) # ELECTRICITY_DELIVERED_TARIFF_2 (1-0:2.8.2)
assert isinstance(result[obis.ELECTRICITY_DELIVERED_TARIFF_2], CosemObject) assert isinstance(result.ELECTRICITY_DELIVERED_TARIFF_2, CosemObject)
assert result[obis.ELECTRICITY_DELIVERED_TARIFF_2].unit == 'kWh' assert result.ELECTRICITY_DELIVERED_TARIFF_2.unit == 'kWh'
assert isinstance(result[obis.ELECTRICITY_DELIVERED_TARIFF_2].value, Decimal) assert isinstance(result.ELECTRICITY_DELIVERED_TARIFF_2.value, Decimal)
assert result[obis.ELECTRICITY_DELIVERED_TARIFF_2].value == Decimal('0.011') assert result.ELECTRICITY_DELIVERED_TARIFF_2.value == Decimal('0.011')
# ELECTRICITY_ACTIVE_TARIFF (0-0:96.14.0) # ELECTRICITY_ACTIVE_TARIFF (0-0:96.14.0)
assert isinstance(result[obis.ELECTRICITY_ACTIVE_TARIFF], CosemObject) assert isinstance(result.ELECTRICITY_ACTIVE_TARIFF, CosemObject)
assert result[obis.ELECTRICITY_ACTIVE_TARIFF].unit is None assert result.ELECTRICITY_ACTIVE_TARIFF.unit is None
assert isinstance(result[obis.ELECTRICITY_ACTIVE_TARIFF].value, str) assert isinstance(result.ELECTRICITY_ACTIVE_TARIFF.value, str)
assert result[obis.ELECTRICITY_ACTIVE_TARIFF].value == '0001' assert result.ELECTRICITY_ACTIVE_TARIFF.value == '0001'
# BELGIUM_CURRENT_AVERAGE_DEMAND (1-0:1.4.0) # BELGIUM_CURRENT_AVERAGE_DEMAND (1-0:1.4.0)
assert isinstance(result[obis.BELGIUM_CURRENT_AVERAGE_DEMAND], CosemObject) assert isinstance(result.BELGIUM_CURRENT_AVERAGE_DEMAND, CosemObject)
assert result[obis.BELGIUM_CURRENT_AVERAGE_DEMAND].unit == 'kW' assert result.BELGIUM_CURRENT_AVERAGE_DEMAND.unit == 'kW'
assert isinstance(result[obis.BELGIUM_CURRENT_AVERAGE_DEMAND].value, Decimal) assert isinstance(result.BELGIUM_CURRENT_AVERAGE_DEMAND.value, Decimal)
assert result[obis.BELGIUM_CURRENT_AVERAGE_DEMAND].value == Decimal('2.351') assert result.BELGIUM_CURRENT_AVERAGE_DEMAND.value == Decimal('2.351')
# BELGIUM_MAXIMUM_DEMAND_MONTH (1-0:1.6.0) # BELGIUM_MAXIMUM_DEMAND_MONTH (1-0:1.6.0)
assert isinstance(result[obis.BELGIUM_MAXIMUM_DEMAND_MONTH], MBusObject) assert isinstance(result.BELGIUM_MAXIMUM_DEMAND_MONTH, MBusObject)
assert result[obis.BELGIUM_MAXIMUM_DEMAND_MONTH].unit == 'kW' assert result.BELGIUM_MAXIMUM_DEMAND_MONTH.unit == 'kW'
assert isinstance(result[obis.BELGIUM_MAXIMUM_DEMAND_MONTH].value, Decimal) assert isinstance(result.BELGIUM_MAXIMUM_DEMAND_MONTH.value, Decimal)
assert result[obis.BELGIUM_MAXIMUM_DEMAND_MONTH].value == Decimal('2.589') assert result.BELGIUM_MAXIMUM_DEMAND_MONTH.value == Decimal('2.589')
assert isinstance(result.BELGIUM_MAXIMUM_DEMAND_MONTH.datetime, datetime.datetime)
assert result.BELGIUM_MAXIMUM_DEMAND_MONTH.datetime == \
pytz.timezone("Europe/Brussels").localize(datetime.datetime(2020, 5, 9, 13, 45, 58))
# BELGIUM_MAXIMUM_DEMAND_13_MONTHS (0-0:98.1.0) Value 0 # BELGIUM_MAXIMUM_DEMAND_13_MONTHS (0-0:98.1.0) Value 0
assert isinstance(result[obis.BELGIUM_MAXIMUM_DEMAND_13_MONTHS][0], MBusObjectPeak) assert isinstance(result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[0], MBusObjectPeak)
assert result[obis.BELGIUM_MAXIMUM_DEMAND_13_MONTHS][0].unit == 'kW' assert result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[0].unit == 'kW'
assert isinstance(result[obis.BELGIUM_MAXIMUM_DEMAND_13_MONTHS][0].value, Decimal) assert isinstance(result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[0].value, Decimal)
assert result[obis.BELGIUM_MAXIMUM_DEMAND_13_MONTHS][0].value == Decimal('3.695') assert result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[0].value == Decimal('3.695')
assert isinstance(result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[0].datetime, datetime.datetime)
assert result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[0].datetime == \
pytz.timezone("Europe/Brussels").localize(datetime.datetime(2020, 5, 1, 0, 0, 0))
assert isinstance(result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[0].occurred, datetime.datetime)
assert result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[0].occurred == \
pytz.timezone("Europe/Brussels").localize(datetime.datetime(2020, 4, 23, 19, 25, 38))
# BELGIUM_MAXIMUM_DEMAND_13_MONTHS (0-0:98.1.0) Value 1 # BELGIUM_MAXIMUM_DEMAND_13_MONTHS (0-0:98.1.0) Value 1
assert isinstance(result[obis.BELGIUM_MAXIMUM_DEMAND_13_MONTHS][1], MBusObjectPeak) assert isinstance(result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[1], MBusObjectPeak)
assert result[obis.BELGIUM_MAXIMUM_DEMAND_13_MONTHS][1].unit == 'kW' assert result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[1].unit == 'kW'
assert isinstance(result[obis.BELGIUM_MAXIMUM_DEMAND_13_MONTHS][1].value, Decimal) assert isinstance(result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[1].value, Decimal)
assert result[obis.BELGIUM_MAXIMUM_DEMAND_13_MONTHS][1].value == Decimal('5.980') assert result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[1].value == Decimal('5.980')
assert isinstance(result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[1].datetime, datetime.datetime)
assert result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[1].datetime == \
pytz.timezone("Europe/Brussels").localize(datetime.datetime(2020, 4, 1, 0, 0, 0))
assert isinstance(result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[1].occurred, datetime.datetime)
assert result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[1].occurred == \
pytz.timezone("Europe/Brussels").localize(datetime.datetime(2020, 3, 5, 12, 21, 39))
# BELGIUM_MAXIMUM_DEMAND_13_MONTHS (0-0:98.1.0) Value 2 # BELGIUM_MAXIMUM_DEMAND_13_MONTHS (0-0:98.1.0) Value 2
assert isinstance(result[obis.BELGIUM_MAXIMUM_DEMAND_13_MONTHS][2], MBusObjectPeak) assert isinstance(result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[2], MBusObjectPeak)
assert result[obis.BELGIUM_MAXIMUM_DEMAND_13_MONTHS][2].unit == 'kW' assert result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[2].unit == 'kW'
assert isinstance(result[obis.BELGIUM_MAXIMUM_DEMAND_13_MONTHS][2].value, Decimal) assert isinstance(result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[2].value, Decimal)
assert result[obis.BELGIUM_MAXIMUM_DEMAND_13_MONTHS][2].value == Decimal('4.318') assert result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[2].value == Decimal('4.318')
assert isinstance(result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[2].datetime, datetime.datetime)
assert result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[2].datetime == \
pytz.timezone("Europe/Brussels").localize(datetime.datetime(2020, 3, 1, 0, 0, 0))
assert isinstance(result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[2].occurred, datetime.datetime)
assert result.BELGIUM_MAXIMUM_DEMAND_13_MONTHS[2].occurred == \
pytz.timezone("Europe/Brussels").localize(datetime.datetime(2020, 2, 10, 3, 54, 21))
# CURRENT_ELECTRICITY_USAGE (1-0:1.7.0) # CURRENT_ELECTRICITY_USAGE (1-0:1.7.0)
assert isinstance(result[obis.CURRENT_ELECTRICITY_USAGE], CosemObject) assert isinstance(result.CURRENT_ELECTRICITY_USAGE, CosemObject)
assert result[obis.CURRENT_ELECTRICITY_USAGE].unit == 'kW' assert result.CURRENT_ELECTRICITY_USAGE.unit == 'kW'
assert isinstance(result[obis.CURRENT_ELECTRICITY_USAGE].value, Decimal) assert isinstance(result.CURRENT_ELECTRICITY_USAGE.value, Decimal)
assert result[obis.CURRENT_ELECTRICITY_USAGE].value == Decimal('0.000') assert result.CURRENT_ELECTRICITY_USAGE.value == Decimal('0.000')
# CURRENT_ELECTRICITY_DELIVERY (1-0:2.7.0) # CURRENT_ELECTRICITY_DELIVERY (1-0:2.7.0)
assert isinstance(result[obis.CURRENT_ELECTRICITY_DELIVERY], CosemObject) assert isinstance(result.CURRENT_ELECTRICITY_DELIVERY, CosemObject)
assert result[obis.CURRENT_ELECTRICITY_DELIVERY].unit == 'kW' assert result.CURRENT_ELECTRICITY_DELIVERY.unit == 'kW'
assert isinstance(result[obis.CURRENT_ELECTRICITY_DELIVERY].value, Decimal) assert isinstance(result.CURRENT_ELECTRICITY_DELIVERY.value, Decimal)
assert result[obis.CURRENT_ELECTRICITY_DELIVERY].value == Decimal('0.000') assert result.CURRENT_ELECTRICITY_DELIVERY.value == Decimal('0.000')
# INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE (1-0:21.7.0) # INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE (1-0:21.7.0)
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE], CosemObject) assert isinstance(result.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE, CosemObject)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE].unit == 'kW' assert result.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE.unit == 'kW'
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE].value, Decimal) assert isinstance(result.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE.value, Decimal)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE].value == Decimal('0.000') assert result.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE.value == Decimal('0.000')
# INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE (1-0:41.7.0) # INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE (1-0:41.7.0)
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE], CosemObject) assert isinstance(result.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE, CosemObject)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE].unit == 'kW' assert result.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE.unit == 'kW'
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE].value, Decimal) assert isinstance(result.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE.value, Decimal)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE].value == Decimal('0.000') assert result.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE.value == Decimal('0.000')
# INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE (1-0:61.7.0) # INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE (1-0:61.7.0)
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE], CosemObject) assert isinstance(result.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE, CosemObject)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE].unit == 'kW' assert result.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE.unit == 'kW'
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE].value, Decimal) assert isinstance(result.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE.value, Decimal)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE].value == Decimal('0.000') assert result.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE.value == Decimal('0.000')
# INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE (1-0:22.7.0) # INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE (1-0:22.7.0)
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE], CosemObject) assert isinstance(result.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE, CosemObject)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE].unit == 'kW' assert result.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE.unit == 'kW'
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE].value, Decimal) assert isinstance(result.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE.value, Decimal)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE].value == Decimal('0.000') assert result.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE.value == Decimal('0.000')
# INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE (1-0:42.7.0) # INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE (1-0:42.7.0)
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE], CosemObject) assert isinstance(result.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE, CosemObject)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE].unit == 'kW' assert result.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE.unit == 'kW'
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE].value, Decimal) assert isinstance(result.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE.value, Decimal)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE].value == Decimal('0.000') assert result.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE.value == Decimal('0.000')
# INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE (1-0:62.7.0) # INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE (1-0:62.7.0)
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE], CosemObject) assert isinstance(result.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE, CosemObject)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE].unit == 'kW' assert result.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE.unit == 'kW'
assert isinstance(result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE].value, Decimal) assert isinstance(result.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE.value, Decimal)
assert result[obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE].value == Decimal('0.000') assert result.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE.value == Decimal('0.000')
# INSTANTANEOUS_VOLTAGE_L1 (1-0:32.7.0) # INSTANTANEOUS_VOLTAGE_L1 (1-0:32.7.0)
assert isinstance(result[obis.INSTANTANEOUS_VOLTAGE_L1], CosemObject) assert isinstance(result.INSTANTANEOUS_VOLTAGE_L1, CosemObject)
assert result[obis.INSTANTANEOUS_VOLTAGE_L1].unit == 'V' assert result.INSTANTANEOUS_VOLTAGE_L1.unit == 'V'
assert isinstance(result[obis.INSTANTANEOUS_VOLTAGE_L1].value, Decimal) assert isinstance(result.INSTANTANEOUS_VOLTAGE_L1.value, Decimal)
assert result[obis.INSTANTANEOUS_VOLTAGE_L1].value == Decimal('234.7') assert result.INSTANTANEOUS_VOLTAGE_L1.value == Decimal('234.7')
# INSTANTANEOUS_VOLTAGE_L2 (1-0:52.7.0) # INSTANTANEOUS_VOLTAGE_L2 (1-0:52.7.0)
assert isinstance(result[obis.INSTANTANEOUS_VOLTAGE_L2], CosemObject) assert isinstance(result.INSTANTANEOUS_VOLTAGE_L2, CosemObject)
assert result[obis.INSTANTANEOUS_VOLTAGE_L2].unit == 'V' assert result.INSTANTANEOUS_VOLTAGE_L2.unit == 'V'
assert isinstance(result[obis.INSTANTANEOUS_VOLTAGE_L2].value, Decimal) assert isinstance(result.INSTANTANEOUS_VOLTAGE_L2.value, Decimal)
assert result[obis.INSTANTANEOUS_VOLTAGE_L2].value == Decimal('234.7') assert result.INSTANTANEOUS_VOLTAGE_L2.value == Decimal('234.7')
# INSTANTANEOUS_VOLTAGE_L3 (1-0:72.7.0) # INSTANTANEOUS_VOLTAGE_L3 (1-0:72.7.0)
assert isinstance(result[obis.INSTANTANEOUS_VOLTAGE_L3], CosemObject) assert isinstance(result.INSTANTANEOUS_VOLTAGE_L3, CosemObject)
assert result[obis.INSTANTANEOUS_VOLTAGE_L3].unit == 'V' assert result.INSTANTANEOUS_VOLTAGE_L3.unit == 'V'
assert isinstance(result[obis.INSTANTANEOUS_VOLTAGE_L3].value, Decimal) assert isinstance(result.INSTANTANEOUS_VOLTAGE_L3.value, Decimal)
assert result[obis.INSTANTANEOUS_VOLTAGE_L3].value == Decimal('234.7') assert result.INSTANTANEOUS_VOLTAGE_L3.value == Decimal('234.7')
# INSTANTANEOUS_CURRENT_L1 (1-0:31.7.0) # INSTANTANEOUS_CURRENT_L1 (1-0:31.7.0)
assert isinstance(result[obis.INSTANTANEOUS_CURRENT_L1], CosemObject) assert isinstance(result.INSTANTANEOUS_CURRENT_L1, CosemObject)
assert result[obis.INSTANTANEOUS_CURRENT_L1].unit == 'A' assert result.INSTANTANEOUS_CURRENT_L1.unit == 'A'
assert isinstance(result[obis.INSTANTANEOUS_CURRENT_L1].value, Decimal) assert isinstance(result.INSTANTANEOUS_CURRENT_L1.value, Decimal)
assert result[obis.INSTANTANEOUS_CURRENT_L1].value == Decimal('0.000') assert result.INSTANTANEOUS_CURRENT_L1.value == Decimal('0.000')
# INSTANTANEOUS_CURRENT_L2 (1-0:51.7.0) # INSTANTANEOUS_CURRENT_L2 (1-0:51.7.0)
assert isinstance(result[obis.INSTANTANEOUS_CURRENT_L2], CosemObject) assert isinstance(result.INSTANTANEOUS_CURRENT_L2, CosemObject)
assert result[obis.INSTANTANEOUS_CURRENT_L2].unit == 'A' assert result.INSTANTANEOUS_CURRENT_L2.unit == 'A'
assert isinstance(result[obis.INSTANTANEOUS_CURRENT_L2].value, Decimal) assert isinstance(result.INSTANTANEOUS_CURRENT_L2.value, Decimal)
assert result[obis.INSTANTANEOUS_CURRENT_L2].value == Decimal('0.000') assert result.INSTANTANEOUS_CURRENT_L2.value == Decimal('0.000')
# INSTANTANEOUS_CURRENT_L3 (1-0:71.7.0) # INSTANTANEOUS_CURRENT_L3 (1-0:71.7.0)
assert isinstance(result[obis.INSTANTANEOUS_CURRENT_L3], CosemObject) assert isinstance(result.INSTANTANEOUS_CURRENT_L3, CosemObject)
assert result[obis.INSTANTANEOUS_CURRENT_L3].unit == 'A' assert result.INSTANTANEOUS_CURRENT_L3.unit == 'A'
assert isinstance(result[obis.INSTANTANEOUS_CURRENT_L3].value, Decimal) assert isinstance(result.INSTANTANEOUS_CURRENT_L3.value, Decimal)
assert result[obis.INSTANTANEOUS_CURRENT_L3].value == Decimal('0.000') assert result.INSTANTANEOUS_CURRENT_L3.value == Decimal('0.000')
# ACTUAL_SWITCH_POSITION (0-0:96.3.10) # ACTUAL_SWITCH_POSITION (0-0:96.3.10)
assert isinstance(result[obis.ACTUAL_SWITCH_POSITION], CosemObject) assert isinstance(result.ACTUAL_SWITCH_POSITION, CosemObject)
assert result[obis.ACTUAL_SWITCH_POSITION].unit is None assert result.ACTUAL_SWITCH_POSITION.unit is None
assert isinstance(result[obis.ACTUAL_SWITCH_POSITION].value, int) assert isinstance(result.ACTUAL_SWITCH_POSITION.value, int)
assert result[obis.ACTUAL_SWITCH_POSITION].value == 1 assert result.ACTUAL_SWITCH_POSITION.value == 1
# BELGIUM_MAX_POWER_PER_PHASE (0-0:17.0.0) # ACTUAL_TRESHOLD_ELECTRICITY (0-0:17.0.0)
assert isinstance(result[obis.BELGIUM_MAX_POWER_PER_PHASE], CosemObject) assert isinstance(result.ACTUAL_TRESHOLD_ELECTRICITY, CosemObject)
assert result[obis.BELGIUM_MAX_POWER_PER_PHASE].unit == 'kW' assert result.ACTUAL_TRESHOLD_ELECTRICITY.unit == 'kW'
assert isinstance(result[obis.BELGIUM_MAX_POWER_PER_PHASE].value, Decimal) assert isinstance(result.ACTUAL_TRESHOLD_ELECTRICITY.value, Decimal)
assert result[obis.BELGIUM_MAX_POWER_PER_PHASE].value == Decimal('999.9') assert result.ACTUAL_TRESHOLD_ELECTRICITY.value == Decimal('999.9')
# BELGIUM_MAX_POWER_PER_PHASE (1-0:31.4.0) # FUSE_THRESHOLD_L1 (1-0:31.4.0)
assert isinstance(result[obis.BELGIUM_MAX_CURRENT_PER_PHASE], CosemObject) assert isinstance(result.FUSE_THRESHOLD_L1, CosemObject)
assert result[obis.BELGIUM_MAX_CURRENT_PER_PHASE].unit == 'A' assert result.FUSE_THRESHOLD_L1.unit == 'A'
assert isinstance(result[obis.BELGIUM_MAX_CURRENT_PER_PHASE].value, Decimal) assert isinstance(result.FUSE_THRESHOLD_L1.value, Decimal)
assert result[obis.BELGIUM_MAX_CURRENT_PER_PHASE].value == Decimal('999') assert result.FUSE_THRESHOLD_L1.value == Decimal('999')
# TEXT_MESSAGE (0-0:96.13.0) # TEXT_MESSAGE (0-0:96.13.0)
assert isinstance(result[obis.TEXT_MESSAGE], CosemObject) assert isinstance(result.TEXT_MESSAGE, CosemObject)
assert result[obis.TEXT_MESSAGE].unit is None assert result.TEXT_MESSAGE.unit is None
assert result[obis.TEXT_MESSAGE].value is None assert result.TEXT_MESSAGE.value is None
# BELGIUM_MBUS1_DEVICE_TYPE (0-1:24.1.0) # MBUS DEVICE 1
assert isinstance(result[obis.BELGIUM_MBUS1_DEVICE_TYPE], CosemObject) mbus1 = result.get_mbus_device_by_channel(1)
assert result[obis.BELGIUM_MBUS1_DEVICE_TYPE].unit is None
assert isinstance(result[obis.BELGIUM_MBUS1_DEVICE_TYPE].value, int)
assert result[obis.BELGIUM_MBUS1_DEVICE_TYPE].value == 3
# BELGIUM_MBUS1_EQUIPMENT_IDENTIFIER (0-1:96.1.1) # MBUS_DEVICE_TYPE (0-1:24.1.0)
assert isinstance(result[obis.BELGIUM_MBUS1_EQUIPMENT_IDENTIFIER], CosemObject) assert isinstance(mbus1.MBUS_DEVICE_TYPE, CosemObject)
assert result[obis.BELGIUM_MBUS1_EQUIPMENT_IDENTIFIER].unit is None assert mbus1.MBUS_DEVICE_TYPE.unit is None
assert isinstance(result[obis.BELGIUM_MBUS1_EQUIPMENT_IDENTIFIER].value, str) assert isinstance(mbus1.MBUS_DEVICE_TYPE.value, int)
assert result[obis.BELGIUM_MBUS1_EQUIPMENT_IDENTIFIER].value == '37464C4F32313139303333373333' assert mbus1.MBUS_DEVICE_TYPE.value == 3
# BELGIUM_MBUS1_VALVE_POSITION (0-1:24.4.0) # MBUS_EQUIPMENT_IDENTIFIER (0-1:96.1.1)
assert isinstance(result[obis.BELGIUM_MBUS1_VALVE_POSITION], CosemObject) assert isinstance(mbus1.MBUS_EQUIPMENT_IDENTIFIER, CosemObject)
assert result[obis.BELGIUM_MBUS1_VALVE_POSITION].unit is None assert mbus1.MBUS_EQUIPMENT_IDENTIFIER.unit is None
assert isinstance(result[obis.BELGIUM_MBUS1_VALVE_POSITION].value, int) assert isinstance(mbus1.MBUS_EQUIPMENT_IDENTIFIER.value, str)
assert result[obis.BELGIUM_MBUS1_VALVE_POSITION].value == 1 assert mbus1.MBUS_EQUIPMENT_IDENTIFIER.value == '37464C4F32313139303333373333'
# BELGIUM_MBUS1_METER_READING2 (0-1:24.2.3) # MBUS_VALVE_POSITION (0-1:24.4.0)
assert isinstance(result[obis.BELGIUM_MBUS1_METER_READING2], MBusObject) assert isinstance(result.MBUS_VALVE_POSITION, CosemObject)
assert result[obis.BELGIUM_MBUS1_METER_READING2].unit == 'm3' assert result.MBUS_VALVE_POSITION.unit is None
assert isinstance(result[obis.BELGIUM_MBUS1_METER_READING2].value, Decimal) assert isinstance(result.MBUS_VALVE_POSITION.value, int)
assert result[obis.BELGIUM_MBUS1_METER_READING2].value == Decimal('112.384') assert result.MBUS_VALVE_POSITION.value == 1
# BELGIUM_MBUS2_DEVICE_TYPE (0-2:24.1.0) # MBUS_METER_READING (0-1:24.2.3)
assert isinstance(result[obis.BELGIUM_MBUS2_DEVICE_TYPE], CosemObject) assert isinstance(mbus1.MBUS_METER_READING, MBusObject)
assert result[obis.BELGIUM_MBUS2_DEVICE_TYPE].unit is None assert mbus1.MBUS_METER_READING.unit == 'm3'
assert isinstance(result[obis.BELGIUM_MBUS2_DEVICE_TYPE].value, int) assert isinstance(mbus1.MBUS_METER_READING.value, Decimal)
assert result[obis.BELGIUM_MBUS2_DEVICE_TYPE].value == 7 assert mbus1.MBUS_METER_READING.value == Decimal('112.384')
# BELGIUM_MBUS2_EQUIPMENT_IDENTIFIER (0-2:96.1.1) # MBUS DEVICE 2
assert isinstance(result[obis.BELGIUM_MBUS2_EQUIPMENT_IDENTIFIER], CosemObject) mbus2 = result.get_mbus_device_by_channel(2)
assert result[obis.BELGIUM_MBUS2_EQUIPMENT_IDENTIFIER].unit is None
assert isinstance(result[obis.BELGIUM_MBUS2_EQUIPMENT_IDENTIFIER].value, str)
assert result[obis.BELGIUM_MBUS2_EQUIPMENT_IDENTIFIER].value == '3853414731323334353637383930'
# BELGIUM_MBUS2_METER_READING1 (0-1:24.2.1) # MBUS_DEVICE_TYPE (0-2:24.1.0)
assert isinstance(result[obis.BELGIUM_MBUS2_METER_READING1], MBusObject) assert isinstance(mbus2.MBUS_DEVICE_TYPE, CosemObject)
assert result[obis.BELGIUM_MBUS2_METER_READING1].unit == 'm3' assert mbus2.MBUS_DEVICE_TYPE.unit is None
assert isinstance(result[obis.BELGIUM_MBUS2_METER_READING1].value, Decimal) assert isinstance(mbus2.MBUS_DEVICE_TYPE.value, int)
assert result[obis.BELGIUM_MBUS2_METER_READING1].value == Decimal('872.234') assert mbus2.MBUS_DEVICE_TYPE.value == 7
# MBUS_EQUIPMENT_IDENTIFIER (0-2:96.1.1)
assert isinstance(mbus2.MBUS_EQUIPMENT_IDENTIFIER, CosemObject)
assert mbus2.MBUS_EQUIPMENT_IDENTIFIER.unit is None
assert isinstance(mbus2.MBUS_EQUIPMENT_IDENTIFIER.value, str)
assert mbus2.MBUS_EQUIPMENT_IDENTIFIER.value == '3853414731323334353637383930'
# MBUS_METER_READING (0-1:24.2.1)
assert isinstance(mbus2.MBUS_METER_READING, MBusObject)
assert mbus2.MBUS_METER_READING.unit == 'm3'
assert isinstance(mbus2.MBUS_METER_READING.value, Decimal)
assert mbus2.MBUS_METER_READING.value == Decimal('872.234')
def test_checksum_valid(self): def test_checksum_valid(self):
# No exception is raised. # No exception is raised.
@@ -263,6 +293,121 @@ class TelegramParserFluviusTest(unittest.TestCase):
def test_checksum_missing(self): def test_checksum_missing(self):
# Remove the checksum value causing a ParseError. # Remove the checksum value causing a ParseError.
corrupted_telegram = TELEGRAM_FLUVIUS_V171.replace('!911C\r\n', '') corrupted_telegram = TELEGRAM_FLUVIUS_V171.replace('!3AD7\r\n', '')
with self.assertRaises(ParseError): with self.assertRaises(ParseError):
TelegramParser.validate_checksum(corrupted_telegram) TelegramParser.validate_checksum(corrupted_telegram)
def test_to_json(self):
parser = TelegramParser(telegram_specifications.BELGIUM_FLUVIUS)
telegram = parser.parse(TELEGRAM_FLUVIUS_V171_ALT)
json_data = json.loads(telegram.to_json())
self.maxDiff = None
self.assertEqual(
json_data,
{'BELGIUM_VERSION_INFORMATION': {'value': '50217', 'unit': None},
'BELGIUM_EQUIPMENT_IDENTIFIER': {'value': '3153414733313030373231333236', 'unit': None},
'P1_MESSAGE_TIMESTAMP': {'value': '2023-11-02T11:15:48+00:00', 'unit': None},
'ELECTRICITY_USED_TARIFF_1': {'value': 301.548, 'unit': 'kWh'},
'ELECTRICITY_USED_TARIFF_2': {'value': 270.014, 'unit': 'kWh'},
'ELECTRICITY_DELIVERED_TARIFF_1': {'value': 0.005, 'unit': 'kWh'},
'ELECTRICITY_DELIVERED_TARIFF_2': {'value': 0.0, 'unit': 'kWh'},
'ELECTRICITY_ACTIVE_TARIFF': {'value': '0001', 'unit': None},
'BELGIUM_CURRENT_AVERAGE_DEMAND': {'value': 0.052, 'unit': 'kW'},
'BELGIUM_MAXIMUM_DEMAND_MONTH': {'datetime': '2023-11-02T10:45:00+00:00',
'value': 3.064, 'unit': 'kW'},
'BELGIUM_MAXIMUM_DEMAND_13_MONTHS': [{'datetime': '2023-07-31T22:00:00+00:00',
'occurred': None, 'value': 0.0, 'unit': 'kW'},
{'datetime': '2023-08-31T22:00:00+00:00',
'occurred': '2023-08-31T16:15:00+00:00',
'value': 1.862, 'unit': 'kW'},
{'datetime': '2023-09-30T22:00:00+00:00',
'occurred': '2023-09-10T16:30:00+00:00',
'value': 4.229, 'unit': 'kW'},
{'datetime': '2023-10-31T23:00:00+00:00',
'occurred': '2023-10-16T11:00:00+00:00',
'value': 4.927, 'unit': 'kW'}],
'CURRENT_ELECTRICITY_USAGE': {'value': 0.338, 'unit': 'kW'},
'CURRENT_ELECTRICITY_DELIVERY': {'value': 0.0, 'unit': 'kW'},
'INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE': {'value': 0.047, 'unit': 'kW'},
'INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE': {'value': 0.179, 'unit': 'kW'},
'INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE': {'value': 0.111, 'unit': 'kW'},
'INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE': {'value': 0.0, 'unit': 'kW'},
'INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE': {'value': 0.0, 'unit': 'kW'},
'INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE': {'value': 0.0, 'unit': 'kW'},
'INSTANTANEOUS_VOLTAGE_L1': {'value': 232.9, 'unit': 'V'},
'INSTANTANEOUS_VOLTAGE_L2': {'value': 228.1, 'unit': 'V'},
'INSTANTANEOUS_VOLTAGE_L3': {'value': 228.1, 'unit': 'V'},
'INSTANTANEOUS_CURRENT_L1': {'value': 0.27, 'unit': 'A'},
'INSTANTANEOUS_CURRENT_L2': {'value': 0.88, 'unit': 'A'},
'INSTANTANEOUS_CURRENT_L3': {'value': 0.52, 'unit': 'A'},
'ACTUAL_SWITCH_POSITION': {'value': 1, 'unit': None},
'ACTUAL_TRESHOLD_ELECTRICITY': {'value': 999.9, 'unit': 'kW'},
'FUSE_THRESHOLD_L1': {'value': 999.0, 'unit': 'A'},
'TEXT_MESSAGE': {'value': None, 'unit': None},
'MBUS_DEVICES': [{'MBUS_DEVICE_TYPE': {'value': 3, 'unit': None},
'MBUS_EQUIPMENT_IDENTIFIER': {'value': '37464C4F32313233303838303237',
'unit': None},
'MBUS_VALVE_POSITION': {'value': 1, 'unit': None},
'MBUS_METER_READING': {'datetime': '2023-11-02T11:10:02+00:00',
'value': 92.287, 'unit': 'm3'},
'CHANNEL_ID': 1},
{'MBUS_DEVICE_TYPE': {'value': 7, 'unit': None},
'MBUS_EQUIPMENT_IDENTIFIER': {'value': '3853455430303030393631313733',
'unit': None},
'MBUS_METER_READING': {'datetime': '2023-11-02T11:15:32+00:00',
'value': 8.579, 'unit': 'm3'},
'CHANNEL_ID': 2}]}
)
def test_to_str(self):
parser = TelegramParser(telegram_specifications.BELGIUM_FLUVIUS)
telegram = parser.parse(TELEGRAM_FLUVIUS_V171_ALT)
self.assertEqual(
str(telegram),
(
'BELGIUM_VERSION_INFORMATION: 50217 [None]\n'
'BELGIUM_EQUIPMENT_IDENTIFIER: 3153414733313030373231333236 [None]\n'
'P1_MESSAGE_TIMESTAMP: 2023-11-02T11:15:48+00:00 [None]\n'
'ELECTRICITY_USED_TARIFF_1: 301.548 [kWh]\n'
'ELECTRICITY_USED_TARIFF_2: 270.014 [kWh]\n'
'ELECTRICITY_DELIVERED_TARIFF_1: 0.005 [kWh]\n'
'ELECTRICITY_DELIVERED_TARIFF_2: 0.000 [kWh]\n'
'ELECTRICITY_ACTIVE_TARIFF: 0001 [None]\n'
'BELGIUM_CURRENT_AVERAGE_DEMAND: 0.052 [kW]\n'
'BELGIUM_MAXIMUM_DEMAND_MONTH: 3.064 [kW] at 2023-11-02T10:45:00+00:00\n'
'0.0 [kW] at 2023-07-31T22:00:00+00:00 occurred None'
'1.862 [kW] at 2023-08-31T22:00:00+00:00 occurred 2023-08-31T16:15:00+00:00'
'4.229 [kW] at 2023-09-30T22:00:00+00:00 occurred 2023-09-10T16:30:00+00:00'
'4.927 [kW] at 2023-10-31T23:00:00+00:00 occurred 2023-10-16T11:00:00+00:00'
'CURRENT_ELECTRICITY_USAGE: 0.338 [kW]\n'
'CURRENT_ELECTRICITY_DELIVERY: 0.000 [kW]\n'
'INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE: 0.047 [kW]\n'
'INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE: 0.179 [kW]\n'
'INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE: 0.111 [kW]\n'
'INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE: 0.000 [kW]\n'
'INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE: 0.000 [kW]\n'
'INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE: 0.000 [kW]\n'
'INSTANTANEOUS_VOLTAGE_L1: 232.9 [V]\n'
'INSTANTANEOUS_VOLTAGE_L2: 228.1 [V]\n'
'INSTANTANEOUS_VOLTAGE_L3: 228.1 [V]\n'
'INSTANTANEOUS_CURRENT_L1: 0.27 [A]\n'
'INSTANTANEOUS_CURRENT_L2: 0.88 [A]\n'
'INSTANTANEOUS_CURRENT_L3: 0.52 [A]\n'
'ACTUAL_SWITCH_POSITION: 1 [None]\n'
'ACTUAL_TRESHOLD_ELECTRICITY: 999.9 [kW]\n'
'FUSE_THRESHOLD_L1: 999 [A]\n'
'TEXT_MESSAGE: None [None]\n'
'MBUS DEVICE (channel 1)\n'
' MBUS_DEVICE_TYPE: 3 [None]\n'
' MBUS_EQUIPMENT_IDENTIFIER: 37464C4F32313233303838303237 [None]\n'
' MBUS_VALVE_POSITION: 1 [None]\n'
' MBUS_METER_READING: 92.287 [m3] at 2023-11-02T11:10:02+00:00\n'
'MBUS DEVICE (channel 2)\n'
' MBUS_DEVICE_TYPE: 7 [None]\n'
' MBUS_EQUIPMENT_IDENTIFIER: 3853455430303030393631313733 [None]\n'
' MBUS_METER_READING: 8.579 [m3] at 2023-11-02T11:15:32+00:00\n'
)
)
+4 -1
View File
@@ -15,7 +15,10 @@ class TelegramParserIskraIETest(unittest.TestCase):
def test_parse(self): def test_parse(self):
parser = TelegramParser(telegram_specifications.ISKRA_IE) parser = TelegramParser(telegram_specifications.ISKRA_IE)
result = parser.parse(TELEGRAM_ISKRA_IE) try:
result = parser.parse(TELEGRAM_ISKRA_IE, throw_ex=True)
except Exception as ex:
assert False, f"parse trigged an exception {ex}"
# EQUIPMENT_IDENTIFIER_GAS (0-0:96.1.0) # EQUIPMENT_IDENTIFIER_GAS (0-0:96.1.0)
assert isinstance(result[obis.EQUIPMENT_IDENTIFIER_GAS], CosemObject) assert isinstance(result[obis.EQUIPMENT_IDENTIFIER_GAS], CosemObject)
+4 -1
View File
@@ -14,7 +14,10 @@ class TelegramParserV2_2Test(unittest.TestCase):
def test_parse(self): def test_parse(self):
parser = TelegramParser(telegram_specifications.V2_2) parser = TelegramParser(telegram_specifications.V2_2)
result = parser.parse(TELEGRAM_V2_2) try:
result = parser.parse(TELEGRAM_V2_2, throw_ex=True)
except Exception as ex:
assert False, f"parse trigged an exception {ex}"
# ELECTRICITY_USED_TARIFF_1 (1-0:1.8.1) # ELECTRICITY_USED_TARIFF_1 (1-0:1.8.1)
assert isinstance(result[obis.ELECTRICITY_USED_TARIFF_1], CosemObject) assert isinstance(result[obis.ELECTRICITY_USED_TARIFF_1], CosemObject)
+4 -1
View File
@@ -14,7 +14,10 @@ class TelegramParserV3Test(unittest.TestCase):
def test_parse(self): def test_parse(self):
parser = TelegramParser(telegram_specifications.V3) parser = TelegramParser(telegram_specifications.V3)
result = parser.parse(TELEGRAM_V3) try:
result = parser.parse(TELEGRAM_V3, throw_ex=True)
except Exception as ex:
assert False, f"parse trigged an exception {ex}"
# ELECTRICITY_USED_TARIFF_1 (1-0:1.8.1) # ELECTRICITY_USED_TARIFF_1 (1-0:1.8.1)
assert isinstance(result[obis.ELECTRICITY_USED_TARIFF_1], CosemObject) assert isinstance(result[obis.ELECTRICITY_USED_TARIFF_1], CosemObject)
+4 -1
View File
@@ -17,7 +17,10 @@ class TelegramParserV4_2Test(unittest.TestCase):
def test_parse(self): def test_parse(self):
parser = TelegramParser(telegram_specifications.V4) parser = TelegramParser(telegram_specifications.V4)
result = parser.parse(TELEGRAM_V4_2) try:
result = parser.parse(TELEGRAM_V4_2, throw_ex=True)
except Exception as ex:
assert False, f"parse trigged an exception {ex}"
# P1_MESSAGE_HEADER (1-3:0.2.8) # P1_MESSAGE_HEADER (1-3:0.2.8)
assert isinstance(result[obis.P1_MESSAGE_HEADER], CosemObject) assert isinstance(result[obis.P1_MESSAGE_HEADER], CosemObject)
+29 -29
View File
@@ -17,8 +17,11 @@ class TelegramParserV5Test(unittest.TestCase):
def test_parse(self): def test_parse(self):
parser = TelegramParser(telegram_specifications.V5) parser = TelegramParser(telegram_specifications.V5)
telegram = parser.parse(TELEGRAM_V5) try:
telegram = parser.parse(TELEGRAM_V5, throw_ex=True)
except Exception as ex:
assert False, f"parse trigged an exception {ex}"
print('test: ', type(telegram.P1_MESSAGE_HEADER), telegram.P1_MESSAGE_HEADER.__dict__)
# P1_MESSAGE_HEADER (1-3:0.2.8) # P1_MESSAGE_HEADER (1-3:0.2.8)
assert isinstance(telegram.P1_MESSAGE_HEADER, CosemObject) assert isinstance(telegram.P1_MESSAGE_HEADER, CosemObject)
assert telegram.P1_MESSAGE_HEADER.unit is None assert telegram.P1_MESSAGE_HEADER.unit is None
@@ -169,12 +172,6 @@ class TelegramParserV5Test(unittest.TestCase):
assert telegram.TEXT_MESSAGE.unit is None assert telegram.TEXT_MESSAGE.unit is None
assert telegram.TEXT_MESSAGE.value is None assert telegram.TEXT_MESSAGE.value is None
# DEVICE_TYPE (0-x:24.1.0)
assert isinstance(telegram.DEVICE_TYPE, CosemObject)
assert telegram.DEVICE_TYPE.unit is None
assert isinstance(telegram.DEVICE_TYPE.value, int)
assert telegram.DEVICE_TYPE.value == 3
# INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE (1-0:21.7.0) # INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE (1-0:21.7.0)
assert isinstance(telegram.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE, CosemObject) assert isinstance(telegram.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE, CosemObject)
assert telegram.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE.unit == 'kW' assert telegram.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE.unit == 'kW'
@@ -212,27 +209,27 @@ class TelegramParserV5Test(unittest.TestCase):
assert telegram.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE.value == Decimal('0') assert telegram.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE.value == Decimal('0')
# There's only one Mbus device (gas meter) in this case. Alternatively # There's only one Mbus device (gas meter) in this case. Alternatively
# use get_mbget_mbus_device_by_channel # use get_mbus_device_by_channel
gas_meter_devices = telegram.MBUS_DEVICES gas_meter_devices = telegram.MBUS_DEVICES
gas_meter_device = gas_meter_devices[0] gas_meter_device = gas_meter_devices[0]
# EQUIPMENT_IDENTIFIER_GAS (0-x:96.1.0) # MBUS_DEVICE_TYPE (0-1:96.1.0)
assert isinstance(gas_meter_device.DEVICE_TYPE, CosemObject) assert isinstance(gas_meter_device.MBUS_DEVICE_TYPE, CosemObject)
assert gas_meter_device.DEVICE_TYPE.unit is None assert gas_meter_device.MBUS_DEVICE_TYPE.unit is None
assert isinstance(gas_meter_device.DEVICE_TYPE.value, int) assert isinstance(gas_meter_device.MBUS_DEVICE_TYPE.value, int)
assert gas_meter_device.DEVICE_TYPE.value == 3 assert gas_meter_device.MBUS_DEVICE_TYPE.value == 3
# EQUIPMENT_IDENTIFIER_GAS (0-x:96.1.0) # MBUS_EQUIPMENT_IDENTIFIER (0-1:96.1.0)
assert isinstance(gas_meter_device.EQUIPMENT_IDENTIFIER_GAS, CosemObject) assert isinstance(gas_meter_device.MBUS_EQUIPMENT_IDENTIFIER, CosemObject)
assert gas_meter_device.EQUIPMENT_IDENTIFIER_GAS.unit is None assert gas_meter_device.MBUS_EQUIPMENT_IDENTIFIER.unit is None
assert isinstance(gas_meter_device.EQUIPMENT_IDENTIFIER_GAS.value, str) assert isinstance(gas_meter_device.MBUS_EQUIPMENT_IDENTIFIER.value, str)
assert gas_meter_device.EQUIPMENT_IDENTIFIER_GAS.value == '3232323241424344313233343536373839' assert gas_meter_device.MBUS_EQUIPMENT_IDENTIFIER.value == '3232323241424344313233343536373839'
# HOURLY_GAS_METER_READING (0-1:24.2.1) # MBUS_METER_READING (0-1:24.2.1)
assert isinstance(gas_meter_device.HOURLY_GAS_METER_READING, MBusObject) assert isinstance(gas_meter_device.MBUS_METER_READING, MBusObject)
assert gas_meter_device.HOURLY_GAS_METER_READING.unit == 'm3' assert gas_meter_device.MBUS_METER_READING.unit == 'm3'
assert isinstance(telegram.HOURLY_GAS_METER_READING.value, Decimal) assert isinstance(telegram.MBUS_METER_READING.value, Decimal)
assert gas_meter_device.HOURLY_GAS_METER_READING.value == Decimal('0.107') assert gas_meter_device.MBUS_METER_READING.value == Decimal('0.107')
def test_checksum_valid(self): def test_checksum_valid(self):
# No exception is raised. # No exception is raised.
@@ -267,8 +264,11 @@ class TelegramParserV5Test(unittest.TestCase):
parser = TelegramParser(telegram_specifications.V5) parser = TelegramParser(telegram_specifications.V5)
telegram = parser.parse(invalid_date_telegram) telegram = parser.parse(invalid_date_telegram)
# HOURLY_GAS_METER_READING (0-1:24.2.1) # MBUS DEVICE 1
assert isinstance(telegram.HOURLY_GAS_METER_READING, MBusObject) mbus1 = telegram.get_mbus_device_by_channel(1)
assert telegram.HOURLY_GAS_METER_READING.unit is None
assert isinstance(telegram.HOURLY_GAS_METER_READING.value, Decimal) # MBUS_METER_READING (0-1:24.2.1)
assert telegram.HOURLY_GAS_METER_READING.value == Decimal('0.000') assert isinstance(mbus1.MBUS_METER_READING, MBusObject)
assert mbus1.MBUS_METER_READING.unit is None
assert isinstance(mbus1.MBUS_METER_READING.value, Decimal)
assert mbus1.MBUS_METER_READING.value == Decimal('0.000')
+308
View File
@@ -0,0 +1,308 @@
from decimal import Decimal
import datetime
import unittest
import pytz
from dsmr_parser import telegram_specifications
from dsmr_parser.exceptions import InvalidChecksumError, ParseError
from dsmr_parser.objects import CosemObject
from dsmr_parser.parsers import TelegramParser
from test.example_telegrams import TELEGRAM_V5_EON_HU
class TelegramParserV5EONHUTest(unittest.TestCase):
""" Test parsing of a DSMR v5 EON Hungary telegram. """
def test_parse(self):
parser = TelegramParser(telegram_specifications.EON_HUNGARY)
try:
telegram = parser.parse(TELEGRAM_V5_EON_HU, throw_ex=True)
except Exception as ex:
assert False, f"parse trigged an exception {ex}"
# P1_MESSAGE_TIMESTAMP (0-0:1.0.0)
assert isinstance(telegram.P1_MESSAGE_TIMESTAMP, CosemObject)
assert telegram.P1_MESSAGE_TIMESTAMP.unit is None
assert isinstance(telegram.P1_MESSAGE_TIMESTAMP.value, datetime.datetime)
assert telegram.P1_MESSAGE_TIMESTAMP.value == \
pytz.timezone("Europe/Budapest").localize(datetime.datetime(2023, 7, 24, 15, 7, 30))
# EON_HU_COSEM_LOGICAL_DEVICE_NAME (0-0:42.0.0)
assert isinstance(telegram.COSEM_LOGICAL_DEVICE_NAME, CosemObject)
assert telegram.COSEM_LOGICAL_DEVICE_NAME.unit is None
assert isinstance(telegram.COSEM_LOGICAL_DEVICE_NAME.value, str)
assert telegram.COSEM_LOGICAL_DEVICE_NAME.value == '53414733303832323030303032313630'
# EON_HU_EQUIPMENT_SERIAL_NUMBER (0-0:96.1.0)
assert isinstance(telegram.EQUIPMENT_SERIAL_NUMBER, CosemObject)
assert telegram.EQUIPMENT_SERIAL_NUMBER.unit is None
assert isinstance(telegram.EQUIPMENT_SERIAL_NUMBER.value, str)
assert telegram.EQUIPMENT_SERIAL_NUMBER.value == '383930303832323030303032313630'
# ELECTRICITY_ACTIVE_TARIFF (0-0:96.14.0)
assert isinstance(telegram.ELECTRICITY_ACTIVE_TARIFF, CosemObject)
assert telegram.ELECTRICITY_ACTIVE_TARIFF.unit is None
assert isinstance(telegram.ELECTRICITY_ACTIVE_TARIFF.value, str)
assert telegram.ELECTRICITY_ACTIVE_TARIFF.value == '0001'
# ACTUAL_SWITCH_POSITION (0-0:96.3.10)
assert isinstance(telegram.ACTUAL_SWITCH_POSITION, CosemObject)
assert telegram.ACTUAL_SWITCH_POSITION.unit is None
assert isinstance(telegram.ACTUAL_SWITCH_POSITION.value, str)
assert telegram.ACTUAL_SWITCH_POSITION.value == '1'
# ACTUAL_TRESHOLD_ELECTRICITY (0-0:17.0.0)
assert isinstance(telegram.ACTUAL_TRESHOLD_ELECTRICITY, CosemObject)
assert telegram.ACTUAL_TRESHOLD_ELECTRICITY.unit == 'kW'
assert isinstance(telegram.ACTUAL_TRESHOLD_ELECTRICITY.value, Decimal)
assert telegram.ACTUAL_TRESHOLD_ELECTRICITY.value == Decimal('90.000')
# ELECTRICITY_IMPORTED_TOTAL (1-0:1.8.0)
assert isinstance(telegram.ELECTRICITY_IMPORTED_TOTAL, CosemObject)
assert telegram.ELECTRICITY_IMPORTED_TOTAL.unit == 'kWh'
assert isinstance(telegram.ELECTRICITY_IMPORTED_TOTAL.value, Decimal)
assert telegram.ELECTRICITY_IMPORTED_TOTAL.value == Decimal('000173.640')
# ELECTRICITY_USED_TARIFF_1 (1-0:1.8.1)
assert isinstance(telegram.ELECTRICITY_USED_TARIFF_1, CosemObject)
assert telegram.ELECTRICITY_USED_TARIFF_1.unit == 'kWh'
assert isinstance(telegram.ELECTRICITY_USED_TARIFF_1.value, Decimal)
assert telegram.ELECTRICITY_USED_TARIFF_1.value == Decimal('000047.719')
# ELECTRICITY_USED_TARIFF_2 (1-0:1.8.2)
assert isinstance(telegram.ELECTRICITY_USED_TARIFF_2, CosemObject)
assert telegram.ELECTRICITY_USED_TARIFF_2.unit == 'kWh'
assert isinstance(telegram.ELECTRICITY_USED_TARIFF_2.value, Decimal)
assert telegram.ELECTRICITY_USED_TARIFF_2.value == Decimal('000125.921')
# EON_HU_ELECTRICITY_USED_TARIFF_3 (1-0:1.8.3)
assert isinstance(telegram.ELECTRICITY_USED_TARIFF_3, CosemObject)
assert telegram.ELECTRICITY_USED_TARIFF_3.unit == 'kWh'
assert isinstance(telegram.ELECTRICITY_USED_TARIFF_3.value, Decimal)
assert telegram.ELECTRICITY_USED_TARIFF_3.value == Decimal('000000.000')
# EON_HU_ELECTRICITY_USED_TARIFF_4 (1-0:1.8.4)
assert isinstance(telegram.ELECTRICITY_USED_TARIFF_4, CosemObject)
assert telegram.ELECTRICITY_USED_TARIFF_4.unit == 'kWh'
assert isinstance(telegram.ELECTRICITY_USED_TARIFF_4.value, Decimal)
assert telegram.ELECTRICITY_USED_TARIFF_4.value == Decimal('000000.000')
# ELECTRICITY_EXPORTED_TOTAL (1-0:2.8.0)
assert isinstance(telegram.ELECTRICITY_EXPORTED_TOTAL, CosemObject)
assert telegram.ELECTRICITY_EXPORTED_TOTAL.unit == 'kWh'
assert isinstance(telegram.ELECTRICITY_EXPORTED_TOTAL.value, Decimal)
assert telegram.ELECTRICITY_EXPORTED_TOTAL.value == Decimal('000627.177')
# ELECTRICITY_DELIVERED_TARIFF_1 (1-0:2.8.1)
assert isinstance(telegram.ELECTRICITY_DELIVERED_TARIFF_1, CosemObject)
assert telegram.ELECTRICITY_DELIVERED_TARIFF_1.unit == 'kWh'
assert isinstance(telegram.ELECTRICITY_DELIVERED_TARIFF_1.value, Decimal)
assert telegram.ELECTRICITY_DELIVERED_TARIFF_1.value == Decimal('000401.829')
# ELECTRICITY_DELIVERED_TARIFF_2 (1-0:2.8.2)
assert isinstance(telegram.ELECTRICITY_DELIVERED_TARIFF_2, CosemObject)
assert telegram.ELECTRICITY_DELIVERED_TARIFF_2.unit == 'kWh'
assert isinstance(telegram.ELECTRICITY_DELIVERED_TARIFF_2.value, Decimal)
assert telegram.ELECTRICITY_DELIVERED_TARIFF_2.value == Decimal('000225.348')
# EON_HU_ELECTRICITY_DELIVERED_TARIFF_3 (1-0:2.8.3)
assert isinstance(telegram.ELECTRICITY_DELIVERED_TARIFF_3, CosemObject)
assert telegram.ELECTRICITY_DELIVERED_TARIFF_3.unit == 'kWh'
assert isinstance(telegram.ELECTRICITY_DELIVERED_TARIFF_3.value, Decimal)
assert telegram.ELECTRICITY_DELIVERED_TARIFF_3.value == Decimal('000000.000')
# EON_HU_ELECTRICITY_DELIVERED_TARIFF_4 (1-0:2.8.4)
assert isinstance(telegram.ELECTRICITY_DELIVERED_TARIFF_4, CosemObject)
assert telegram.ELECTRICITY_DELIVERED_TARIFF_4.unit == 'kWh'
assert isinstance(telegram.ELECTRICITY_DELIVERED_TARIFF_4.value, Decimal)
assert telegram.ELECTRICITY_DELIVERED_TARIFF_4.value == Decimal('000000.000')
# ELECTRICITY_REACTIVE_IMPORTED_TOTAL (1-0:3.8.0)
assert isinstance(telegram.ELECTRICITY_REACTIVE_IMPORTED_TOTAL, CosemObject)
assert telegram.ELECTRICITY_REACTIVE_IMPORTED_TOTAL.unit == 'kvarh'
assert isinstance(telegram.ELECTRICITY_REACTIVE_IMPORTED_TOTAL.value, Decimal)
assert telegram.ELECTRICITY_REACTIVE_IMPORTED_TOTAL.value == Decimal('000000.123')
# ELECTRICITY_REACTIVE_EXPORTED_TOTAL (1-0:4.8.0)
assert isinstance(telegram.ELECTRICITY_REACTIVE_EXPORTED_TOTAL, CosemObject)
assert telegram.ELECTRICITY_REACTIVE_EXPORTED_TOTAL.unit == 'kvarh'
assert isinstance(telegram.ELECTRICITY_REACTIVE_EXPORTED_TOTAL.value, Decimal)
assert telegram.ELECTRICITY_REACTIVE_EXPORTED_TOTAL.value == Decimal('000303.131')
# EON_HU_ELECTRICITY_REACTIVE_TOTAL_Q1 (1-0:5.8.0)
assert isinstance(telegram.ELECTRICITY_REACTIVE_TOTAL_Q1, CosemObject)
assert telegram.ELECTRICITY_REACTIVE_TOTAL_Q1.unit == 'kvarh'
assert isinstance(telegram.ELECTRICITY_REACTIVE_TOTAL_Q1.value, Decimal)
assert telegram.ELECTRICITY_REACTIVE_TOTAL_Q1.value == Decimal('000000.668')
# EON_HU_ELECTRICITY_REACTIVE_TOTAL_Q2 (1-0:6.8.0)
assert isinstance(telegram.ELECTRICITY_REACTIVE_TOTAL_Q2, CosemObject)
assert telegram.ELECTRICITY_REACTIVE_TOTAL_Q2.unit == 'kvarh'
assert isinstance(telegram.ELECTRICITY_REACTIVE_TOTAL_Q2.value, Decimal)
assert telegram.ELECTRICITY_REACTIVE_TOTAL_Q2.value == Decimal('000000.071')
# EON_HU_ELECTRICITY_REACTIVE_TOTAL_Q3 (1-0:7.8.0)
assert isinstance(telegram.ELECTRICITY_REACTIVE_TOTAL_Q3, CosemObject)
assert telegram.ELECTRICITY_REACTIVE_TOTAL_Q3.unit == 'kvarh'
assert isinstance(telegram.ELECTRICITY_REACTIVE_TOTAL_Q3.value, Decimal)
assert telegram.ELECTRICITY_REACTIVE_TOTAL_Q3.value == Decimal('000160.487')
# EON_HU_ELECTRICITY_REACTIVE_TOTAL_Q4 (1-0:8.8.0)
assert isinstance(telegram.ELECTRICITY_REACTIVE_TOTAL_Q4, CosemObject)
assert telegram.ELECTRICITY_REACTIVE_TOTAL_Q4.unit == 'kvarh'
assert isinstance(telegram.ELECTRICITY_REACTIVE_TOTAL_Q4.value, Decimal)
assert telegram.ELECTRICITY_REACTIVE_TOTAL_Q4.value == Decimal('000143.346')
# EON_HU_ELECTRICITY_COMBINED (1-0:15.8.0)
assert isinstance(telegram.ELECTRICITY_COMBINED, CosemObject)
assert telegram.ELECTRICITY_COMBINED.unit == 'kWh'
assert isinstance(telegram.ELECTRICITY_COMBINED.value, Decimal)
assert telegram.ELECTRICITY_COMBINED.value == Decimal('000800.817')
# INSTANTANEOUS_VOLTAGE_L2 (1-0:32.7.0)
assert isinstance(telegram.INSTANTANEOUS_VOLTAGE_L1, CosemObject)
assert telegram.INSTANTANEOUS_VOLTAGE_L1.unit == 'V'
assert isinstance(telegram.INSTANTANEOUS_VOLTAGE_L1.value, Decimal)
assert telegram.INSTANTANEOUS_VOLTAGE_L1.value == Decimal('240.4')
# INSTANTANEOUS_VOLTAGE_L2 (1-0:52.7.0)
assert isinstance(telegram.INSTANTANEOUS_VOLTAGE_L2, CosemObject)
assert telegram.INSTANTANEOUS_VOLTAGE_L2.unit == 'V'
assert isinstance(telegram.INSTANTANEOUS_VOLTAGE_L2.value, Decimal)
assert telegram.INSTANTANEOUS_VOLTAGE_L2.value == Decimal('239.1')
# INSTANTANEOUS_VOLTAGE_L3 (1-0:72.7.0)
assert isinstance(telegram.INSTANTANEOUS_VOLTAGE_L3, CosemObject)
assert telegram.INSTANTANEOUS_VOLTAGE_L3.unit == 'V'
assert isinstance(telegram.INSTANTANEOUS_VOLTAGE_L3.value, Decimal)
assert telegram.INSTANTANEOUS_VOLTAGE_L3.value == Decimal('241.2')
# INSTANTANEOUS_CURRENT_L1 (1-0:31.7.0)
assert isinstance(telegram.INSTANTANEOUS_CURRENT_L1, CosemObject)
assert telegram.INSTANTANEOUS_CURRENT_L1.unit == 'A'
assert isinstance(telegram.INSTANTANEOUS_CURRENT_L1.value, Decimal)
assert telegram.INSTANTANEOUS_CURRENT_L1.value == Decimal('003')
# INSTANTANEOUS_CURRENT_L2 (1-0:51.7.0)
assert isinstance(telegram.INSTANTANEOUS_CURRENT_L2, CosemObject)
assert telegram.INSTANTANEOUS_CURRENT_L2.unit == 'A'
assert isinstance(telegram.INSTANTANEOUS_CURRENT_L2.value, Decimal)
assert telegram.INSTANTANEOUS_CURRENT_L2.value == Decimal('004')
# INSTANTANEOUS_CURRENT_L3 (1-0:71.7.0)
assert isinstance(telegram.INSTANTANEOUS_CURRENT_L3, CosemObject)
assert telegram.INSTANTANEOUS_CURRENT_L3.unit == 'A'
assert isinstance(telegram.INSTANTANEOUS_CURRENT_L3.value, Decimal)
assert telegram.INSTANTANEOUS_CURRENT_L3.value == Decimal('003')
# EON_HU_INSTANTANEOUS_POWER_FACTOR_TOTAL (1-0:13.7.0)
assert isinstance(telegram.INSTANTANEOUS_POWER_FACTOR_TOTAL, CosemObject)
assert telegram.INSTANTANEOUS_POWER_FACTOR_TOTAL.unit is None
assert isinstance(telegram.INSTANTANEOUS_POWER_FACTOR_TOTAL.value, Decimal)
assert telegram.INSTANTANEOUS_POWER_FACTOR_TOTAL.value == Decimal('4.556')
# EON_HU_INSTANTANEOUS_POWER_FACTOR_L1 (1-0:33.7.0)
assert isinstance(telegram.INSTANTANEOUS_POWER_FACTOR_L1, CosemObject)
assert telegram.INSTANTANEOUS_POWER_FACTOR_L1.unit is None
assert isinstance(telegram.INSTANTANEOUS_POWER_FACTOR_L1.value, Decimal)
assert telegram.INSTANTANEOUS_POWER_FACTOR_L1.value == Decimal('4.591')
# EON_HU_INSTANTANEOUS_POWER_FACTOR_L2 (1-0:53.7.0)
assert isinstance(telegram.INSTANTANEOUS_POWER_FACTOR_L2, CosemObject)
assert telegram.INSTANTANEOUS_POWER_FACTOR_L2.unit is None
assert isinstance(telegram.INSTANTANEOUS_POWER_FACTOR_L2.value, Decimal)
assert telegram.INSTANTANEOUS_POWER_FACTOR_L2.value == Decimal('4.542')
# EON_HU_INSTANTANEOUS_POWER_FACTOR_L3 (1-0:73.7.0)
assert isinstance(telegram.INSTANTANEOUS_POWER_FACTOR_L3, CosemObject)
assert telegram.INSTANTANEOUS_POWER_FACTOR_L3.unit is None
assert isinstance(telegram.INSTANTANEOUS_POWER_FACTOR_L3.value, Decimal)
assert telegram.INSTANTANEOUS_POWER_FACTOR_L3.value == Decimal('4.552')
# EON_HU_FREQUENCY (1-0:14.7.0)
assert isinstance(telegram.FREQUENCY, CosemObject)
assert telegram.FREQUENCY.unit == "Hz"
assert isinstance(telegram.FREQUENCY.value, Decimal)
assert telegram.FREQUENCY.value == Decimal('50.00')
# CURRENT_ELECTRICITY_USAGE (1-0:1.7.0)
assert isinstance(telegram.CURRENT_ELECTRICITY_USAGE, CosemObject)
assert telegram.CURRENT_ELECTRICITY_USAGE.unit == 'kW'
assert isinstance(telegram.CURRENT_ELECTRICITY_USAGE.value, Decimal)
assert telegram.CURRENT_ELECTRICITY_USAGE.value == Decimal('00.000')
# CURRENT_ELECTRICITY_DELIVERY (1-0:2.7.0)
assert isinstance(telegram.CURRENT_ELECTRICITY_DELIVERY, CosemObject)
assert telegram.CURRENT_ELECTRICITY_DELIVERY.unit == 'kW'
assert isinstance(telegram.CURRENT_ELECTRICITY_DELIVERY.value, Decimal)
assert telegram.CURRENT_ELECTRICITY_DELIVERY.value == Decimal('02.601')
# EON_HU_INSTANTANEOUS_REACTIVE_POWER_Q1 (1-0:5.7.0)
assert isinstance(telegram.INSTANTANEOUS_REACTIVE_POWER_Q1, CosemObject)
assert telegram.INSTANTANEOUS_REACTIVE_POWER_Q1.unit == 'kvar'
assert isinstance(telegram.INSTANTANEOUS_REACTIVE_POWER_Q1.value, Decimal)
assert telegram.INSTANTANEOUS_REACTIVE_POWER_Q1.value == Decimal('00.000')
# EON_HU_INSTANTANEOUS_REACTIVE_POWER_Q2 (1-0:6.7.0)
assert isinstance(telegram.INSTANTANEOUS_REACTIVE_POWER_Q2, CosemObject)
assert telegram.INSTANTANEOUS_REACTIVE_POWER_Q2.unit == 'kvar'
assert isinstance(telegram.INSTANTANEOUS_REACTIVE_POWER_Q2.value, Decimal)
assert telegram.INSTANTANEOUS_REACTIVE_POWER_Q2.value == Decimal('00.000')
# EON_HU_INSTANTANEOUS_REACTIVE_POWER_Q3 (1-0:7.7.0)
assert isinstance(telegram.INSTANTANEOUS_REACTIVE_POWER_Q3, CosemObject)
assert telegram.INSTANTANEOUS_REACTIVE_POWER_Q3.unit == 'kvar'
assert isinstance(telegram.INSTANTANEOUS_REACTIVE_POWER_Q3.value, Decimal)
assert telegram.INSTANTANEOUS_REACTIVE_POWER_Q3.value == Decimal('00.504')
# EON_HU_INSTANTANEOUS_REACTIVE_POWER_Q4 (1-0:8.7.0)
assert isinstance(telegram.INSTANTANEOUS_REACTIVE_POWER_Q4, CosemObject)
assert telegram.INSTANTANEOUS_REACTIVE_POWER_Q4.unit == 'kvar'
assert isinstance(telegram.INSTANTANEOUS_REACTIVE_POWER_Q4.value, Decimal)
assert telegram.INSTANTANEOUS_REACTIVE_POWER_Q4.value == Decimal('00.000')
# FUSE_THRESHOLD_L1 (1-0:31.4.0)
assert isinstance(telegram.FUSE_THRESHOLD_L1, CosemObject)
assert telegram.FUSE_THRESHOLD_L1.unit == 'A'
assert isinstance(telegram.FUSE_THRESHOLD_L1.value, Decimal)
assert telegram.FUSE_THRESHOLD_L1.value == Decimal('200.00')
# FUSE_THRESHOLD_L2 (1-0:31.4.0)
assert isinstance(telegram.FUSE_THRESHOLD_L2, CosemObject)
assert telegram.FUSE_THRESHOLD_L2.unit == 'A'
assert isinstance(telegram.FUSE_THRESHOLD_L2.value, Decimal)
assert telegram.FUSE_THRESHOLD_L2.value == Decimal('200.00')
# FUSE_THRESHOLD_L3 (1-0:31.4.0)
assert isinstance(telegram.FUSE_THRESHOLD_L3, CosemObject)
assert telegram.FUSE_THRESHOLD_L3.unit == 'A'
assert isinstance(telegram.FUSE_THRESHOLD_L3.value, Decimal)
assert telegram.FUSE_THRESHOLD_L3.value == Decimal('200.00')
# TEXT_MESSAGE (0-0:96.13.0)
assert isinstance(telegram.TEXT_MESSAGE, CosemObject)
assert telegram.TEXT_MESSAGE.unit is None
assert telegram.TEXT_MESSAGE.value is None
def test_checksum_valid(self):
# No exception is raised.
TelegramParser.validate_checksum(TELEGRAM_V5_EON_HU)
def test_checksum_invalid(self):
# Remove the electricty used data value. This causes the checksum to
# not match anymore.
corrupted_telegram = TELEGRAM_V5_EON_HU.replace(
'1-0:1.8.1(000047.719*kWh)\r\n',
''
)
with self.assertRaises(InvalidChecksumError):
TelegramParser.validate_checksum(corrupted_telegram)
def test_checksum_missing(self):
# Remove the checksum value causing a ParseError.
corrupted_telegram = TELEGRAM_V5_EON_HU.replace('!99DA\r\n', '')
with self.assertRaises(ParseError):
TelegramParser.validate_checksum(corrupted_telegram)
+1 -1
View File
@@ -68,6 +68,6 @@ class ProtocolTest(unittest.TestCase):
# 2nd call of keep_alive should close the transport # 2nd call of keep_alive should close the transport
self.protocol.keep_alive() self.protocol.keep_alive()
assert mock_transport.close.called_once() mock_transport.close.assert_called_once()
self.protocol.connection_lost(None) self.protocol.connection_lost(None)
+4
View File
@@ -6,6 +6,7 @@ deps=
pytest-asyncio pytest-asyncio
pytest-mock pytest-mock
dlms_cosem dlms_cosem
setuptools
commands= commands=
py.test --cov=dsmr_parser test {posargs} py.test --cov=dsmr_parser test {posargs}
pylama dsmr_parser test pylama dsmr_parser test
@@ -13,6 +14,9 @@ commands=
[pylama:dsmr_parser/clients/__init__.py] [pylama:dsmr_parser/clients/__init__.py]
ignore = W0611 ignore = W0611
[pylama:dsmr_parser/clients/socket_.py]
ignore = C901
[pylama:dsmr_parser/parsers.py] [pylama:dsmr_parser/parsers.py]
ignore = W605 ignore = W605