Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d7e1f41162 | ||
|
|
45de349062 | ||
|
|
f46f88bdb8 | ||
|
|
fa70ada0bf | ||
|
|
d989cfd0e6 | ||
|
|
ada02bf993 | ||
|
|
e607b62fa2 | ||
|
|
247860be16 | ||
|
|
c590109a1d | ||
|
|
adaa2dcad5 | ||
|
|
8c861ee308 | ||
|
|
804747c370 |
@@ -1,6 +1,13 @@
|
|||||||
Change Log
|
Change Log
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
**0.30** (2021-08-18)
|
||||||
|
- Add support for Swedish smart meters (`pull request #86 <https://github.com/ndokter/dsmr_parser/pull/86>`_).
|
||||||
|
|
||||||
|
**0.29** (2021-04-18)
|
||||||
|
- Add value and unit properties to ProfileGenericObject to make sure that code like iterators that rely on that do not break (`pull request #71 <https://github.com/ndokter/dsmr_parser/pull/71>`_).
|
||||||
|
Remove deprecated asyncio coroutine decorator (`pull request #76 <https://github.com/ndokter/dsmr_parser/pull/76>`_).
|
||||||
|
|
||||||
**0.28** (2021-02-21)
|
**0.28** (2021-02-21)
|
||||||
- Optional keep alive monitoring for TCP/IP connections (`pull request #73 <https://github.com/ndokter/dsmr_parser/pull/73>`_).
|
- Optional keep alive monitoring for TCP/IP connections (`pull request #73 <https://github.com/ndokter/dsmr_parser/pull/73>`_).
|
||||||
- Catch parse errors in TelegramParser, ignore lines that can not be parsed (`pull request #74 <https://github.com/ndokter/dsmr_parser/pull/74>`_).
|
- Catch parse errors in TelegramParser, ignore lines that can not be parsed (`pull request #74 <https://github.com/ndokter/dsmr_parser/pull/74>`_).
|
||||||
|
|||||||
+1
-1
@@ -14,7 +14,7 @@ also includes client implementation to directly read and parse smart meter data.
|
|||||||
Features
|
Features
|
||||||
--------
|
--------
|
||||||
|
|
||||||
DSMR Parser supports DSMR versions 2, 3, 4 and 5. It has been tested with Python 3.4, 3.5 and 3.6.
|
DSMR Parser supports DSMR versions 2, 3, 4 and 5. It has been tested with Python 3.5, 3.6, 3.7, 3.8 and 3.9.
|
||||||
|
|
||||||
|
|
||||||
Client module usage
|
Client module usage
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ def console():
|
|||||||
help='alternatively connect using TCP host.')
|
help='alternatively connect using TCP host.')
|
||||||
parser.add_argument('--port', default=None,
|
parser.add_argument('--port', default=None,
|
||||||
help='TCP port to use for connection')
|
help='TCP port to use for connection')
|
||||||
parser.add_argument('--version', default='2.2', choices=['2.2', '4', '5', '5B', '5L'],
|
parser.add_argument('--version', default='2.2', choices=['2.2', '4', '5', '5B', '5L', '5S'],
|
||||||
help='DSMR version (2.2, 4, 5, 5B, 5L)')
|
help='DSMR version (2.2, 4, 5, 5B, 5L, 5S)')
|
||||||
parser.add_argument('--verbose', '-v', action='count')
|
parser.add_argument('--verbose', '-v', action='count')
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|||||||
@@ -32,6 +32,9 @@ def create_dsmr_protocol(dsmr_version, telegram_callback, loop=None, **kwargs):
|
|||||||
elif dsmr_version == "5L":
|
elif dsmr_version == "5L":
|
||||||
specification = telegram_specifications.LUXEMBOURG_SMARTY
|
specification = telegram_specifications.LUXEMBOURG_SMARTY
|
||||||
serial_settings = SERIAL_SETTINGS_V5
|
serial_settings = SERIAL_SETTINGS_V5
|
||||||
|
elif dsmr_version == "5S":
|
||||||
|
specification = telegram_specifications.SWEDEN
|
||||||
|
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)
|
||||||
@@ -136,7 +139,6 @@ class DSMRProtocol(asyncio.Protocol):
|
|||||||
else:
|
else:
|
||||||
self.telegram_callback(parsed_telegram)
|
self.telegram_callback(parsed_telegram)
|
||||||
|
|
||||||
@asyncio.coroutine
|
async def wait_closed(self):
|
||||||
def wait_closed(self):
|
|
||||||
"""Wait until connection is closed."""
|
"""Wait until connection is closed."""
|
||||||
yield from self._closed.wait()
|
await self._closed.wait()
|
||||||
|
|||||||
@@ -68,8 +68,7 @@ class AsyncSerialReader(SerialReader):
|
|||||||
|
|
||||||
PORT_KEY = 'url'
|
PORT_KEY = 'url'
|
||||||
|
|
||||||
@asyncio.coroutine
|
async def read(self, queue):
|
||||||
def read(self, queue):
|
|
||||||
"""
|
"""
|
||||||
Read complete DSMR telegram's from the serial interface and parse it
|
Read complete DSMR telegram's from the serial interface and parse it
|
||||||
into CosemObject's and MbusObject's.
|
into CosemObject's and MbusObject's.
|
||||||
@@ -81,12 +80,12 @@ class AsyncSerialReader(SerialReader):
|
|||||||
"""
|
"""
|
||||||
# create Serial StreamReader
|
# create Serial StreamReader
|
||||||
conn = serial_asyncio.open_serial_connection(**self.serial_settings)
|
conn = serial_asyncio.open_serial_connection(**self.serial_settings)
|
||||||
reader, _ = yield from conn
|
reader, _ = await conn
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
# Read line if available or give control back to loop until new
|
# Read line if available or give control back to loop until new
|
||||||
# data has arrived.
|
# data has arrived.
|
||||||
data = yield from reader.readline()
|
data = await reader.readline()
|
||||||
self.telegram_buffer.append(data.decode('ascii'))
|
self.telegram_buffer.append(data.decode('ascii'))
|
||||||
|
|
||||||
for telegram in self.telegram_buffer.get_all():
|
for telegram in self.telegram_buffer.get_all():
|
||||||
|
|||||||
@@ -52,7 +52,9 @@ EN = {
|
|||||||
obis.BELGIUM_HOURLY_GAS_METER_READING: 'BELGIUM_HOURLY_GAS_METER_READING',
|
obis.BELGIUM_HOURLY_GAS_METER_READING: 'BELGIUM_HOURLY_GAS_METER_READING',
|
||||||
obis.LUXEMBOURG_EQUIPMENT_IDENTIFIER: 'LUXEMBOURG_EQUIPMENT_IDENTIFIER',
|
obis.LUXEMBOURG_EQUIPMENT_IDENTIFIER: 'LUXEMBOURG_EQUIPMENT_IDENTIFIER',
|
||||||
obis.LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL: 'LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL',
|
obis.LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL: 'LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL',
|
||||||
obis.LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL: 'LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL'
|
obis.LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL: 'LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL',
|
||||||
|
obis.SWEDEN_ELECTRICITY_USED_TARIFF_GLOBAL: 'SWEDEN_ELECTRICITY_USED_TARIFF_GLOBAL',
|
||||||
|
obis.SWEDEN_ELECTRICITY_DELIVERED_TARIFF_GLOBAL: 'SWEDEN_ELECTRICITY_DELIVERED_TARIFF_GLOBAL',
|
||||||
}
|
}
|
||||||
|
|
||||||
REVERSE_EN = dict([(v, k) for k, v in EN.items()])
|
REVERSE_EN = dict([(v, k) for k, v in EN.items()])
|
||||||
|
|||||||
@@ -66,3 +66,5 @@ BELGIUM_HOURLY_GAS_METER_READING = r'\d-\d:24\.2\.3.+?\r\n' # Different code, s
|
|||||||
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_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-)
|
LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL = r'\d-\d:2\.8\.0.+?\r\n' # Total exported energy register (P-)
|
||||||
|
SWEDEN_ELECTRICITY_USED_TARIFF_GLOBAL = r'\d-\d:1\.8\.0.+?\r\n' # Total imported energy register (P+)
|
||||||
|
SWEDEN_ELECTRICITY_DELIVERED_TARIFF_GLOBAL = r'\d-\d:2\.8\.0.+?\r\n' # Total exported energy register (P-)
|
||||||
|
|||||||
@@ -155,6 +155,16 @@ class ProfileGenericObject(DSMRObject):
|
|||||||
super().__init__(values)
|
super().__init__(values)
|
||||||
self._buffer_list = None
|
self._buffer_list = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def value(self):
|
||||||
|
# value is added to make sure the telegram iterator does not break
|
||||||
|
return self.values
|
||||||
|
|
||||||
|
@property
|
||||||
|
def unit(self):
|
||||||
|
# value is added to make sure all items have a unit so code that relies on that does not break
|
||||||
|
return None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def buffer_length(self):
|
def buffer_length(self):
|
||||||
return self.values[0]['value']
|
return self.values[0]['value']
|
||||||
|
|||||||
@@ -156,3 +156,28 @@ LUXEMBOURG_SMARTY['objects'].update({
|
|||||||
obis.LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL: CosemParser(ValueParser(Decimal)),
|
obis.LUXEMBOURG_ELECTRICITY_USED_TARIFF_GLOBAL: CosemParser(ValueParser(Decimal)),
|
||||||
obis.LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL: CosemParser(ValueParser(Decimal)),
|
obis.LUXEMBOURG_ELECTRICITY_DELIVERED_TARIFF_GLOBAL: CosemParser(ValueParser(Decimal)),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
# Source: https://www.energiforetagen.se/globalassets/energiforetagen/det-erbjuder-vi/kurser-och-konferenser/elnat/branschrekommendation-lokalt-granssnitt-v2_0-201912.pdf
|
||||||
|
SWEDEN = {
|
||||||
|
'checksum_support': True,
|
||||||
|
'objects': {
|
||||||
|
obis.P1_MESSAGE_HEADER: CosemParser(ValueParser(str)),
|
||||||
|
obis.P1_MESSAGE_TIMESTAMP: CosemParser(ValueParser(timestamp)),
|
||||||
|
obis.SWEDEN_ELECTRICITY_USED_TARIFF_GLOBAL: CosemParser(ValueParser(Decimal)),
|
||||||
|
obis.SWEDEN_ELECTRICITY_DELIVERED_TARIFF_GLOBAL: CosemParser(ValueParser(Decimal)),
|
||||||
|
obis.CURRENT_ELECTRICITY_USAGE: CosemParser(ValueParser(Decimal)),
|
||||||
|
obis.CURRENT_ELECTRICITY_DELIVERY: CosemParser(ValueParser(Decimal)),
|
||||||
|
obis.INSTANTANEOUS_ACTIVE_POWER_L1_POSITIVE: CosemParser(ValueParser(Decimal)),
|
||||||
|
obis.INSTANTANEOUS_ACTIVE_POWER_L2_POSITIVE: CosemParser(ValueParser(Decimal)),
|
||||||
|
obis.INSTANTANEOUS_ACTIVE_POWER_L3_POSITIVE: CosemParser(ValueParser(Decimal)),
|
||||||
|
obis.INSTANTANEOUS_ACTIVE_POWER_L1_NEGATIVE: CosemParser(ValueParser(Decimal)),
|
||||||
|
obis.INSTANTANEOUS_ACTIVE_POWER_L2_NEGATIVE: CosemParser(ValueParser(Decimal)),
|
||||||
|
obis.INSTANTANEOUS_ACTIVE_POWER_L3_NEGATIVE: CosemParser(ValueParser(Decimal)),
|
||||||
|
obis.INSTANTANEOUS_VOLTAGE_L1: CosemParser(ValueParser(Decimal)),
|
||||||
|
obis.INSTANTANEOUS_VOLTAGE_L2: CosemParser(ValueParser(Decimal)),
|
||||||
|
obis.INSTANTANEOUS_VOLTAGE_L3: CosemParser(ValueParser(Decimal)),
|
||||||
|
obis.INSTANTANEOUS_CURRENT_L1: CosemParser(ValueParser(Decimal)),
|
||||||
|
obis.INSTANTANEOUS_CURRENT_L2: CosemParser(ValueParser(Decimal)),
|
||||||
|
obis.INSTANTANEOUS_CURRENT_L3: CosemParser(ValueParser(Decimal)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,8 +5,9 @@ setup(
|
|||||||
description='Library to parse Dutch Smart Meter Requirements (DSMR)',
|
description='Library to parse Dutch Smart Meter Requirements (DSMR)',
|
||||||
author='Nigel Dokter and many others',
|
author='Nigel Dokter and many others',
|
||||||
author_email='nigel@nldr.net',
|
author_email='nigel@nldr.net',
|
||||||
|
license='MIT',
|
||||||
url='https://github.com/ndokter/dsmr_parser',
|
url='https://github.com/ndokter/dsmr_parser',
|
||||||
version='0.28',
|
version='0.30',
|
||||||
packages=find_packages(exclude=('test', 'test.*')),
|
packages=find_packages(exclude=('test', 'test.*')),
|
||||||
install_requires=[
|
install_requires=[
|
||||||
'pyserial>=3,<4',
|
'pyserial>=3,<4',
|
||||||
|
|||||||
Reference in New Issue
Block a user