Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3dc77a8231 | ||
|
|
d61f2229c8 | ||
|
|
3ddf0366e6 | ||
|
|
629767590b | ||
|
|
81cccbd228 | ||
|
|
5b1e830018 | ||
|
|
2d712b506d | ||
|
|
1318204d0c | ||
|
|
feb0f88ddc | ||
|
|
602129a665 | ||
|
|
af9a99d995 | ||
|
|
a7b4929eab | ||
|
|
8999b1c924 | ||
|
|
2391f38c4a | ||
|
|
d1a6e15c68 | ||
|
|
b7c8626d0c | ||
|
|
dc902a83e9 | ||
|
|
be4386bc59 |
@@ -1,6 +1,15 @@
|
||||
Change Log
|
||||
----------
|
||||
|
||||
**0.27** (2020-12-24)
|
||||
- fix for empty parentheses in ProfileGenericParser (redone) (`pull request #69 <https://github.com/ndokter/dsmr_parser/pull/69>`_).
|
||||
|
||||
**0.26** (2020-12-15)
|
||||
- reverted fix for empty parentheses in ProfileGenericParser (`pull request #68 <https://github.com/ndokter/dsmr_parser/pull/68>`_).
|
||||
|
||||
**0.25** (2020-12-14)
|
||||
- fix for empty parentheses in ProfileGenericParser (`pull request #57 <https://github.com/ndokter/dsmr_parser/pull/57>`_).
|
||||
|
||||
**0.24** (2020-11-27)
|
||||
- Add Luxembourg equipment identifier (`pull request #62 <https://github.com/ndokter/dsmr_parser/pull/62>`_).
|
||||
|
||||
|
||||
@@ -48,7 +48,11 @@ EN = {
|
||||
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.VALVE_POSITION_GAS: 'VALVE_POSITION_GAS',
|
||||
obis.BELGIUM_HOURLY_GAS_METER_READING: 'BELGIUM_HOURLY_GAS_METER_READING',
|
||||
obis.LUXEMBOURG_EQUIPMENT_IDENTIFIER: 'LUXEMBOURG_EQUIPMENT_IDENTIFIER',
|
||||
obis.LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL: 'LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL',
|
||||
obis.LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL: 'LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL'
|
||||
}
|
||||
|
||||
REVERSE_EN = dict([(v, k) for k, v in EN.items()])
|
||||
|
||||
@@ -63,6 +63,6 @@ ELECTRICITY_DELIVERED_TARIFF_ALL = (
|
||||
|
||||
# Alternate codes for foreign countries.
|
||||
BELGIUM_HOURLY_GAS_METER_READING = r'\d-\d:24\.2\.3.+?\r\n' # Different code, same format.
|
||||
LUXEMBOURG_EQUIPMENT_IDENTIFIER = r'\d-\d:42\.0\.0.+?\r\n' # Logical device name
|
||||
LUXEMBOURG_EQUIPMENT_IDENTIFIER = r'\d-\d:42\.0\.0.+?\r\n' # Logical device name
|
||||
LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL = r'\d-\d:1\.8\.0.+?\r\n' # Total imported energy register (P+)
|
||||
LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL = r'\d-\d:2\.8\.0.+?\r\n' # Total exported energy register (P-)
|
||||
|
||||
@@ -225,6 +225,10 @@ class ProfileGenericParser(DSMRObjectParser):
|
||||
self.parsers_for_unidentified = parsers_for_unidentified
|
||||
|
||||
def _is_line_wellformed(self, line, values):
|
||||
if values and (len(values) == 1) and (values[0] == ''):
|
||||
# special case: single empty parentheses (indicated by empty string)
|
||||
return True
|
||||
|
||||
if values and (len(values) >= 2) and (values[0].isdigit()):
|
||||
buffer_length = int(values[0])
|
||||
return (buffer_length <= 10) and (len(values) == (buffer_length * 2 + 2))
|
||||
@@ -232,6 +236,9 @@ class ProfileGenericParser(DSMRObjectParser):
|
||||
return False
|
||||
|
||||
def _parse_values(self, values):
|
||||
if values and (len(values) == 1) and (values[0] is None):
|
||||
# special case: single empty parentheses; make sure empty ProfileGenericObject is created
|
||||
values = [0, None] # buffer_length=0, buffer_value_obis_ID=None
|
||||
buffer_length = int(values[0])
|
||||
buffer_value_obis_ID = values[1]
|
||||
if (buffer_length > 0):
|
||||
|
||||
@@ -6,7 +6,7 @@ setup(
|
||||
author='Nigel Dokter',
|
||||
author_email='nigel@nldr.net',
|
||||
url='https://github.com/ndokter/dsmr_parser',
|
||||
version='0.24',
|
||||
version='0.27',
|
||||
packages=find_packages(exclude=('test', 'test.*')),
|
||||
install_requires=[
|
||||
'pyserial>=3,<4',
|
||||
|
||||
@@ -127,4 +127,4 @@ TELEGRAM_V5 = (
|
||||
'0-2:24.1.0(003)\r\n'
|
||||
'0-2:96.1.0()\r\n'
|
||||
'!6EEE\r\n'
|
||||
)
|
||||
)
|
||||
|
||||
@@ -241,7 +241,6 @@ class TelegramParserV5Test(unittest.TestCase):
|
||||
|
||||
def test_checksum_missing(self):
|
||||
# Remove the checksum value causing a ParseError.
|
||||
corrupted_telegram = TELEGRAM_V5.replace('!87B3\r\n', '')
|
||||
|
||||
corrupted_telegram = TELEGRAM_V5.replace('!6EEE\r\n', '')
|
||||
with self.assertRaises(ParseError):
|
||||
TelegramParser.validate_checksum(corrupted_telegram)
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
import unittest
|
||||
|
||||
from dsmr_parser import telegram_specifications
|
||||
|
||||
from dsmr_parser.objects import Telegram
|
||||
from dsmr_parser.objects import ProfileGenericObject
|
||||
from dsmr_parser.parsers import TelegramParser
|
||||
from dsmr_parser.parsers import ProfileGenericParser
|
||||
from dsmr_parser.profile_generic_specifications import BUFFER_TYPES
|
||||
from dsmr_parser.profile_generic_specifications import PG_HEAD_PARSERS
|
||||
from dsmr_parser.profile_generic_specifications import PG_UNIDENTIFIED_BUFFERTYPE_PARSERS
|
||||
from test.example_telegrams import TELEGRAM_V5
|
||||
|
||||
|
||||
class TestParserCornerCases(unittest.TestCase):
|
||||
""" Test instantiation of Telegram object """
|
||||
|
||||
def test_power_event_log_empty_1(self):
|
||||
# POWER_EVENT_FAILURE_LOG (1-0:99.97.0)
|
||||
parser = TelegramParser(telegram_specifications.V5)
|
||||
telegram = Telegram(TELEGRAM_V5, parser, telegram_specifications.V5)
|
||||
|
||||
object_type = ProfileGenericObject
|
||||
testitem = telegram.POWER_EVENT_FAILURE_LOG
|
||||
assert isinstance(testitem, object_type)
|
||||
assert testitem.buffer_length == 0
|
||||
assert testitem.buffer_type == '0-0:96.7.19'
|
||||
buffer = testitem.buffer
|
||||
assert isinstance(testitem.buffer, list)
|
||||
assert len(buffer) == 0
|
||||
|
||||
def test_power_event_log_empty_2(self):
|
||||
pef_parser = ProfileGenericParser(BUFFER_TYPES, PG_HEAD_PARSERS, PG_UNIDENTIFIED_BUFFERTYPE_PARSERS)
|
||||
object_type = ProfileGenericObject
|
||||
|
||||
# Power Event Log with 0 items and no object type
|
||||
pefl_line = r'1-0:99.97.0(0)()\r\n'
|
||||
testitem = pef_parser.parse(pefl_line)
|
||||
|
||||
assert isinstance(testitem, object_type)
|
||||
assert testitem.buffer_length == 0
|
||||
assert testitem.buffer_type is None
|
||||
buffer = testitem.buffer
|
||||
assert isinstance(testitem.buffer, list)
|
||||
assert len(buffer) == 0
|
||||
assert testitem.values == [{'value': 0, 'unit': None}, {'value': None, 'unit': None}]
|
||||
json = testitem.to_json()
|
||||
assert json == '{"buffer_length": 0, "buffer_type": null, "buffer": []}'
|
||||
|
||||
def test_power_event_log_null_values(self):
|
||||
pef_parser = ProfileGenericParser(BUFFER_TYPES, PG_HEAD_PARSERS, PG_UNIDENTIFIED_BUFFERTYPE_PARSERS)
|
||||
object_type = ProfileGenericObject
|
||||
|
||||
# Power Event Log with 1 item and no object type and nno values for the item
|
||||
pefl_line = r'1-0:99.97.0(1)()()()\r\n'
|
||||
testitem = pef_parser.parse(pefl_line)
|
||||
|
||||
assert isinstance(testitem, object_type)
|
||||
assert testitem.buffer_length == 1
|
||||
assert testitem.buffer_type is None
|
||||
buffer = testitem.buffer
|
||||
assert isinstance(testitem.buffer, list)
|
||||
assert len(buffer) == 1
|
||||
assert testitem.values == [{'value': 1, 'unit': None}, {'value': None, 'unit': None},
|
||||
{'value': None, 'unit': None}, {'value': None, 'unit': None}]
|
||||
json = testitem.to_json()
|
||||
assert json == \
|
||||
'{"buffer_length": 1, "buffer_type": null, "buffer": [{"datetime": null, "value": null, "unit": null}]}'
|
||||
|
||||
def test_power_event_log_brackets_only(self):
|
||||
# POWER_EVENT_FAILURE_LOG (1-0:99.97.0)
|
||||
# Issue 57
|
||||
# Test of an ill formatted empty POWER_EVENT_FAILURE_LOG, observed on some smartmeters
|
||||
# The idea is that instead of failing, the parser converts it to an empty POWER_EVENT_FAILURE_LOG
|
||||
pef_parser = ProfileGenericParser(BUFFER_TYPES, PG_HEAD_PARSERS, PG_UNIDENTIFIED_BUFFERTYPE_PARSERS)
|
||||
object_type = ProfileGenericObject
|
||||
|
||||
pefl_line = r'1-0:99.97.0()\r\n'
|
||||
testitem = pef_parser.parse(pefl_line)
|
||||
|
||||
assert isinstance(testitem, object_type)
|
||||
assert testitem.buffer_length == 0
|
||||
assert testitem.buffer_type is None
|
||||
buffer = testitem.buffer
|
||||
assert isinstance(testitem.buffer, list)
|
||||
assert len(buffer) == 0
|
||||
assert testitem.values == [{'value': 0, 'unit': None}, {'value': None, 'unit': None}]
|
||||
json = testitem.to_json()
|
||||
assert json == '{"buffer_length": 0, "buffer_type": null, "buffer": []}'
|
||||
Reference in New Issue
Block a user