Skip to content

Commit

Permalink
Added new datalogger configuration option for selecting extra device …
Browse files Browse the repository at this point in the history
…channel for specific vendor(s) #1794
  • Loading branch information
dennissiemensma committed Jan 23, 2023
1 parent 20b1912 commit f8abd4e
Show file tree
Hide file tree
Showing 8 changed files with 246 additions and 13 deletions.
1 change: 1 addition & 0 deletions docs/reference/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ v5.10.0 - January 2023

- ``Added`` [`#1770 <https://github.com/dsmrreader/dsmr-reader/issues/1770>`_] Now tracking meter position timestamps in day statistics. Added them to Archive (day view) and Export.
- ``Added`` [`#1770 <https://github.com/dsmrreader/dsmr-reader/issues/1770>`_] Automatic migrations retroactively reassessing meter positions and timestamps. Additionally checks/fixes gas consumption mismatch.
- ``Added`` [`#1794 <https://github.com/dsmrreader/dsmr-reader/issues/1794>`_] Added new datalogger configuration option for selecting extra device channel for specific vendor(s)
- ``Added`` Old Dashboard notifications can now be viewed and permanently deleted in the Frontend admin section.

- ``Changed`` [`#1725 <https://github.com/dsmrreader/dsmr-reader/issues/1725>`_] The value of ``DSMRREADER_REMOTE_DATALOGGER_INPUT_METHOD`` is now restricted to: ``DEBUG``, ``WARNING`` or ``ERROR``
Expand Down
6 changes: 5 additions & 1 deletion dsmr_datalogger/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,11 @@ class DataloggerSettingsAdmin(SingletonModelAdmin):
(
_("Advanced"),
{
"fields": ["process_sleep", "override_telegram_timestamp"],
"fields": [
"dsmr_extra_device_channel",
"process_sleep",
"override_telegram_timestamp",
],
},
),
(
Expand Down
46 changes: 46 additions & 0 deletions dsmr_datalogger/migrations/0032_dsmr_extra_device_channel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Generated by Django 3.2.16 on 2023-01-23 21:27

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("dsmr_datalogger", "0031_meter_statistics_meta"),
]

operations = [
migrations.AddField(
model_name="dataloggersettings",
name="dsmr_extra_device_channel",
field=models.IntegerField(
blank=True,
choices=[
(None, "Auto (default)"),
(1, "Belgium - Fluvius (channel 1)"),
(2, "Belgium - Fluvius (channel 2)"),
(3, "Belgium - Fluvius (channel 3)"),
(4, "Belgium - Fluvius (channel 4)"),
],
default=None,
help_text="Only use when your extra device is read incorrectly (e.g. gas). Also, only works with specific vendor(s).",
null=True,
verbose_name="Extra device channel",
),
),
migrations.AlterField(
model_name="dataloggersettings",
name="dsmr_version",
field=models.IntegerField(
choices=[
(4, "Netherlands - DSMR version 4/5 (default)"),
(3, "Netherlands - DSMR version 2/3"),
(101, "Belgium - Fluvius (gas meter fix)"),
(102, "Luxembourg - Smarty (single tariff fix)"),
],
default=4,
help_text="The DSMR version your meter supports or the vendor related to it. Version should be printed on meter.",
verbose_name="DSMR version/vendor",
),
),
]
27 changes: 25 additions & 2 deletions dsmr_datalogger/models/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,19 @@ class DataloggerSettings(ModelUpdateMixin, SingletonModel):
(DSMR_LUXEMBOURG_SMARTY, _("Luxembourg - Smarty (single tariff fix)")),
)

DSMR_EXTRA_DEVICE_CHANNEL_AUTO = None
DSMR_EXTRA_DEVICE_CHANNEL_1 = 1
DSMR_EXTRA_DEVICE_CHANNEL_2 = 2
DSMR_EXTRA_DEVICE_CHANNEL_3 = 3
DSMR_EXTRA_DEVICE_CHANNEL_4 = 4
DSMR_EXTRA_DEVICE_CHANNEL_CHOICES = (
(DSMR_EXTRA_DEVICE_CHANNEL_AUTO, _("Auto (default)")),
(DSMR_EXTRA_DEVICE_CHANNEL_1, _("Belgium - Fluvius (channel 1)")),
(DSMR_EXTRA_DEVICE_CHANNEL_2, _("Belgium - Fluvius (channel 2)")),
(DSMR_EXTRA_DEVICE_CHANNEL_3, _("Belgium - Fluvius (channel 3)")),
(DSMR_EXTRA_DEVICE_CHANNEL_4, _("Belgium - Fluvius (channel 4)")),
)

input_method = models.CharField(
max_length=16,
default=INPUT_METHOD_SERIAL,
Expand All @@ -38,9 +51,19 @@ class DataloggerSettings(ModelUpdateMixin, SingletonModel):
dsmr_version = models.IntegerField(
default=DSMR_VERSION_4_PLUS,
choices=DSMR_VERSION_CHOICES,
verbose_name=_("DSMR version"),
verbose_name=_("DSMR version/vendor"),
help_text=_(
"The DSMR version your meter supports or the vendor related to it. Version should be printed on meter."
),
)
dsmr_extra_device_channel = models.IntegerField(
default=None,
blank=True,
null=True,
choices=DSMR_EXTRA_DEVICE_CHANNEL_CHOICES,
verbose_name=_("Extra device channel"),
help_text=_(
"The DSMR version your meter supports. Version should be printed on meter."
"Only use when your extra device is read incorrectly (e.g. gas). Also, only works with specific vendor(s)."
),
)
serial_port = models.CharField(
Expand Down
21 changes: 16 additions & 5 deletions dsmr_datalogger/services/datalogger.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ def _map_telegram_to_model(parsed_telegram: Dict, data: str):

datalogger_settings = DataloggerSettings.get_solo()
model_fields = {k: None for k in READING_FIELDS + STATISTICS_FIELDS}
mapping = _get_dsmrreader_mapping(datalogger_settings.dsmr_version)
mapping = _get_dsmrreader_mapping(datalogger_settings)

for obis_ref, obis_data in parsed_telegram.items():
try:
Expand Down Expand Up @@ -188,7 +188,7 @@ def _map_telegram_to_model(parsed_telegram: Dict, data: str):
return new_instance


def _get_dsmrreader_mapping(version: int) -> Dict:
def _get_dsmrreader_mapping(datalogger_settings: DataloggerSettings) -> Dict:
"""Returns the mapping for OBIS to DSMR-reader (model fields)."""
SPLIT_GAS_FIELD = {
"value": "extra_device_delivered",
Expand Down Expand Up @@ -232,14 +232,25 @@ def _get_dsmrreader_mapping(version: int) -> Dict:
obis_references.VOLTAGE_SWELL_L3_COUNT: "voltage_swell_count_l3",
}

if version == DataloggerSettings.DSMR_BELGIUM_FLUVIUS:
if datalogger_settings.dsmr_version == DataloggerSettings.DSMR_BELGIUM_FLUVIUS:
# Cheap hack for forcing channel selection.
try:
mbus_reference = {
DataloggerSettings.DSMR_EXTRA_DEVICE_CHANNEL_1: obis_references.BELGIUM_MBUS1_METER_READING2,
DataloggerSettings.DSMR_EXTRA_DEVICE_CHANNEL_2: obis_references.BELGIUM_MBUS2_METER_READING2,
DataloggerSettings.DSMR_EXTRA_DEVICE_CHANNEL_3: obis_references.BELGIUM_MBUS3_METER_READING2,
DataloggerSettings.DSMR_EXTRA_DEVICE_CHANNEL_4: obis_references.BELGIUM_MBUS4_METER_READING2,
}[datalogger_settings.dsmr_extra_device_channel]
except KeyError:
mbus_reference = obis_references.BELGIUM_MBUS_WILDCARD_METER_READING2

mapping.update(
{
obis_references.BELGIUM_MBUS_WILDCARD_METER_READING2: SPLIT_GAS_FIELD,
mbus_reference: SPLIT_GAS_FIELD,
}
)

if version == DataloggerSettings.DSMR_LUXEMBOURG_SMARTY:
if datalogger_settings.dsmr_version == DataloggerSettings.DSMR_LUXEMBOURG_SMARTY:
mapping.update(
{
obis_references.ELECTRICITY_IMPORTED_TOTAL: "electricity_delivered_1",
Expand Down
124 changes: 124 additions & 0 deletions dsmr_datalogger/tests/datalogger/test_fluvius_multiple_gas_devices.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
from unittest import mock
from datetime import datetime
from decimal import Decimal

from django.test import TestCase
from django.utils import timezone
import pytz

from dsmr_backend.tests.mixins import InterceptCommandStdoutMixin
from dsmr_datalogger.models.reading import DsmrReading
from dsmr_datalogger.models.statistics import MeterStatistics
from dsmr_datalogger.models.settings import DataloggerSettings
from dsmr_datalogger.tests.datalogger.mixins import FakeDsmrReadingMixin


class TestDatalogger(FakeDsmrReadingMixin, InterceptCommandStdoutMixin, TestCase):
"""Belgium Fluvius meter (polyphase) with two gas devices (one inactive)."""

def setUp(self):
DataloggerSettings.get_solo()
DataloggerSettings.objects.all().update(
dsmr_version=DataloggerSettings.DSMR_BELGIUM_FLUVIUS,
dsmr_extra_device_channel=DataloggerSettings.DSMR_EXTRA_DEVICE_CHANNEL_1,
)

def _dsmr_dummy_data(self):
return [
"/FLU5\253769484_A\r\n",
"\r\n",
"0-0:96.1.4(012345678901234567890123456789)\r\n",
"0-0:96.1.1(012345678901234567890123456789)\r\n",
"0-0:1.0.0(230119124638W)\r\n",
"1-0:1.8.1(004423.770*kWh)\r\n",
"1-0:1.8.2(002607.237*kWh)\r\n",
"1-0:2.8.1(001194.693*kWh)\r\n",
"1-0:2.8.2(000755.554*kWh)\r\n",
"0-0:96.14.0(0001)\r\n",
"1-0:1.4.0(00.000*kW)\r\n",
"1-0:1.6.0(230116090000W)(11.173*kW)\r\n",
"0-0:98.1.0(1)(1-0:1.6.0)(1-0:1.6.0)(230101000000W)(221230114500W)(13.603*kW)\r\n",
"1-0:1.7.0(00.000*kW)\r\n",
"1-0:2.7.0(00.204*kW)\r\n",
"1-0:21.7.0(00.000*kW)\r\n",
"1-0:41.7.0(00.177*kW)\r\n",
"1-0:61.7.0(00.109*kW)\r\n",
"1-0:22.7.0(00.491*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(234.3*V)\r\n",
"1-0:52.7.0(234.2*V)\r\n",
"1-0:72.7.0(234.3*V)\r\n",
"1-0:31.7.0(002.18*A)\r\n",
"1-0:51.7.0(000.93*A)\r\n",
"1-0:71.7.0(000.60*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",
# Active gas device
"0-1:24.1.0(003)\r\n",
"0-1:96.1.1(012345678901234567890123456789)\r\n",
"0-1:24.4.0(1)\r\n",
"0-1:24.2.3(230119124616W)(00734.607*m3)\r\n",
# Inactive/stale gas device
"0-2:24.1.0(003)\r\n",
"0-2:96.1.1(012345678901234567890123456789)\r\n",
"0-2:24.4.0(1)\r\n",
"0-2:24.2.3(230119124005W)(00000.000*m3)\r\n",
"!B315",
]

def test_reading_creation(self):
self.assertFalse(DsmrReading.objects.exists())
self._fake_dsmr_reading()
self.assertTrue(DsmrReading.objects.exists())

@mock.patch("django.utils.timezone.now")
def test_reading_values(self, now_mock):
now_mock.return_value = timezone.make_aware(timezone.datetime(2023, 1, 20))
self._fake_dsmr_reading()
self.assertTrue(DsmrReading.objects.exists())
reading = DsmrReading.objects.get()
self.assertEqual(
reading.timestamp, datetime(2023, 1, 19, 11, 46, 38, tzinfo=pytz.UTC)
)
self.assertEqual(reading.electricity_delivered_1, Decimal("4423.770"))
self.assertEqual(reading.electricity_returned_1, Decimal("1194.693"))
self.assertEqual(reading.electricity_delivered_2, Decimal("2607.237"))
self.assertEqual(reading.electricity_returned_2, Decimal("755.554"))
self.assertEqual(reading.electricity_currently_delivered, Decimal("0"))
self.assertEqual(reading.electricity_currently_returned, Decimal("0.204"))
self.assertEqual(
reading.extra_device_timestamp,
datetime(2023, 1, 19, 11, 46, 16, tzinfo=pytz.UTC),
)
self.assertEqual(reading.extra_device_delivered, Decimal("734.607"))
self.assertEqual(reading.phase_voltage_l1, Decimal("234.3"))
self.assertEqual(reading.phase_voltage_l2, Decimal("234.2"))
self.assertEqual(reading.phase_voltage_l3, Decimal("234.3"))
self.assertEqual(reading.phase_power_current_l1, 2)
self.assertEqual(reading.phase_power_current_l2, 0)
self.assertEqual(reading.phase_power_current_l3, 0)

meter_statistics = MeterStatistics.get_solo()
self.assertIsNone(meter_statistics.dsmr_version)
self.assertEqual(meter_statistics.electricity_tariff, 1)
self.assertEqual(meter_statistics.power_failure_count, None)
self.assertEqual(meter_statistics.long_power_failure_count, None)
self.assertEqual(meter_statistics.voltage_sag_count_l1, None)
self.assertEqual(meter_statistics.voltage_sag_count_l2, None)
self.assertEqual(meter_statistics.voltage_sag_count_l3, None)
self.assertEqual(meter_statistics.voltage_swell_count_l1, None)
self.assertEqual(meter_statistics.voltage_swell_count_l2, None)
self.assertEqual(meter_statistics.voltage_swell_count_l3, None)

# @mock.patch("django.utils.timezone.now")
# def test_telegram_override_timestamp(self, now_mock):
# reading = self._reading_with_override_telegram_timestamp_active(now_mock)
#
# self.assertEqual(
# # CET > UTC. Minute marker rounded to hours. Because Fluvius may or may not communicate DSMR v5 in telegrams
# reading.extra_device_timestamp,
# datetime(2021, 1, 15, 11, 0, 0, 0, tzinfo=pytz.UTC),
# )
Binary file modified dsmrreader/locales/nl/LC_MESSAGES/django.mo
Binary file not shown.
34 changes: 29 additions & 5 deletions dsmrreader/locales/nl/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ msgid ""
msgstr ""
"Project-Id-Version: DSMR-reader\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-01-09 22:12+0100\n"
"POT-Creation-Date: 2023-01-23 22:41+0100\n"
"PO-Revision-Date: \n"
"Last-Translator: Dennis Siemensma <[email protected]>\n"
"Language-Team: Dennis Siemensma <[email protected]>\n"
Expand Down Expand Up @@ -711,17 +711,38 @@ msgstr "België - Fluvius (fix voor gasmeter)"
msgid "Luxembourg - Smarty (single tariff fix)"
msgstr "Luxemburg - Smarty (fix voor enkel tarief)"

msgid "Auto (default)"
msgstr "Automatisch (standaard)"

msgid "Belgium - Fluvius (channel 1)"
msgstr "België - Fluvius (kanaal 1)"

msgid "Belgium - Fluvius (channel 2)"
msgstr "België - Fluvius (kanaal 2)"

msgid "Belgium - Fluvius (channel 3)"
msgstr "België - Fluvius (kanaal 3)"

msgid "Belgium - Fluvius (channel 4)"
msgstr "België - Fluvius (kanaal 4)"

msgid "Input method"
msgstr "Uitleesmethode"

msgid "Whether to read telegrams from a serial port or network socket."
msgstr "Geeft aan of telegrammen uitgelezen worden via een seriële poort of netwerk socket."

msgid "DSMR version"
msgstr "DSMR-versie"
msgid "DSMR version/vendor"
msgstr "DSMR-versie/fabrikant"

msgid "The DSMR version your meter supports or the vendor related to it. Version should be printed on meter."
msgstr "De DSMR-versie ondersteund door je meter, of de fabrikant. Versie staat meestal aangegeven op de meter."

msgid "Extra device channel"
msgstr "Kanaal extra gekoppeld apparaat"

msgid "The DSMR version your meter supports. Version should be printed on meter."
msgstr "De DSMR-versie ondersteund door je meter. Versie staat meestal aangegeven op de meter."
msgid "Only use when your extra device is read incorrectly (e.g. gas). Also, only works with specific vendor(s)."
msgstr "Alleen gebruiken wanneer een extra gekoppeld apparaat (zoals een gasmeter) niet goed uitgelezen wordt. Werkt daarnaast alleen voor specifieke fabrikant(en)."

msgid "For serial input: Serial port connected to smartmeter. E.g.: /dev/ttyUSB0"
msgstr "Voor uitlezen via een seriële poort: De seriële poort verbonden met de slimme meter. Bijvoorbeeld: /dev/ttyUSB0"
Expand Down Expand Up @@ -786,6 +807,9 @@ msgstr "Retentieconfiguratie"
msgid "Timestamp indicating when the reading was taken"
msgstr "Moment waarop de meting is gedaan"

msgid "DSMR version"
msgstr "DSMR-versie"

msgid "Tariff indicator electricity. The tariff indicator can be used to switch tariff dependent loads e.g boilers. This is responsibility of the P1 user."
msgstr "Tariefindicatie. Dit kan gebruikt worden om te wisselen met tariefafhankelijke vraag, zoals bijvoorbeeld een boiler. Verantwoording is voor de gebruiker van de P1-poort."

Expand Down

0 comments on commit f8abd4e

Please sign in to comment.