From fa70ada0bfee02d33aa5f400672c930bd42ec1d9 Mon Sep 17 00:00:00 2001
From: Erik <erik@montnemery.com>
Date: Sat, 14 Aug 2021 17:08:27 +0200
Subject: [PATCH] Add support for Swedish smart meters

---
 dsmr_parser/__main__.py                |  4 ++--
 dsmr_parser/clients/protocol.py        |  3 +++
 dsmr_parser/obis_name_mapping.py       |  4 +++-
 dsmr_parser/obis_references.py         |  2 ++
 dsmr_parser/telegram_specifications.py | 25 +++++++++++++++++++++++++
 5 files changed, 35 insertions(+), 3 deletions(-)

diff --git a/dsmr_parser/__main__.py b/dsmr_parser/__main__.py
index a9fbaa0..a24a6a2 100644
--- a/dsmr_parser/__main__.py
+++ b/dsmr_parser/__main__.py
@@ -16,8 +16,8 @@ def console():
                         help='alternatively connect using TCP host.')
     parser.add_argument('--port', default=None,
                         help='TCP port to use for connection')
-    parser.add_argument('--version', default='2.2', choices=['2.2', '4', '5', '5B', '5L'],
-                        help='DSMR version (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, 5S)')
     parser.add_argument('--verbose', '-v', action='count')
 
     args = parser.parse_args()
diff --git a/dsmr_parser/clients/protocol.py b/dsmr_parser/clients/protocol.py
index 34a405e..fce549d 100644
--- a/dsmr_parser/clients/protocol.py
+++ b/dsmr_parser/clients/protocol.py
@@ -32,6 +32,9 @@ def create_dsmr_protocol(dsmr_version, telegram_callback, loop=None, **kwargs):
     elif dsmr_version == "5L":
         specification = telegram_specifications.LUXEMBOURG_SMARTY
         serial_settings = SERIAL_SETTINGS_V5
+    elif dsmr_version == "5S":
+        specification = telegram_specifications.SWEDEN
+        serial_settings = SERIAL_SETTINGS_V5
     else:
         raise NotImplementedError("No telegram parser found for version: %s",
                                   dsmr_version)
diff --git a/dsmr_parser/obis_name_mapping.py b/dsmr_parser/obis_name_mapping.py
index bf00f13..4028890 100644
--- a/dsmr_parser/obis_name_mapping.py
+++ b/dsmr_parser/obis_name_mapping.py
@@ -52,7 +52,9 @@ EN = {
     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'
+    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()])
diff --git a/dsmr_parser/obis_references.py b/dsmr_parser/obis_references.py
index fe8952e..5ac3b66 100644
--- a/dsmr_parser/obis_references.py
+++ b/dsmr_parser/obis_references.py
@@ -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_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-)
+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-)
diff --git a/dsmr_parser/telegram_specifications.py b/dsmr_parser/telegram_specifications.py
index b06e4f4..4e59f51 100644
--- a/dsmr_parser/telegram_specifications.py
+++ b/dsmr_parser/telegram_specifications.py
@@ -156,3 +156,28 @@ LUXEMBOURG_SMARTY['objects'].update({
     obis.LUXEMBOURG_ELECTRICITY_USED_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)),
+    }
+}