Plugins

De applicatie staat je toe om plugins te maken en te koppelen, op bepaalde gebeurtenissen.

Configuratie

Je kan plugins maken in hun eigen bestand in dsmr_plugins/modules/plugin_naam.py, waar plugin_naam de naam van je plugin is.

Zorg er voor dat plugin_naam,

  • zonder hoofdletters is (plugin_naam en niet PLUGIN_NAAM),

  • geen spaties of koppeltekens bevat, gebruik daarvoor underscores en begin de naam niet met een cijfer.

Zie ook

Voeg het dotted pad in als DSMRREADER_PLUGINS env var. Voor meer informatie, zie DSMRREADER_PLUGINS in Env-instellingen.

Je plugin-bestand wordt eenmalig geimporteerd, dus zorg ervoor dat je gebeurtenissen koppelt die je wilt volgen.

Tot slot, zorg ervoor dat je de applicatie herstart om de wijzigingen door te voeren. Voer als root of sudo-gebruiker dit uit:

sudo supervisorctl restart all

Gebeurtenissen / Signalen

Deze worden verstuurd door ofwel het Django framework ofwel de applicatie zelf.

dsmr_backend.signals.backend_called

Aangeroepen bij elke iteratie van de backend. Gebruik dit terughoudend, gezien het alle backend-acties kan blokkeren wanneer het verkeerd gebruikt wordt.

dsmr_pvoutput.signals.pvoutput_upload

Aangeroepen door dsmr_pvoutput vlak voor het uploaden van gegevens naar PVOutput. De data kwarg bevat de gegevens die verstuurd worden.

dsmr_datalogger.signals.raw_telegram

Aangeroepen door dsmr_datalogger wanneer er een telegram ontvangen of uitgelezen wordt. De data kwarg bevat de ruwe string met het telegram.

dsmr_notification.signals.notification_sent

Aangeroepen door dsmr_notification vlak nadat een notificatie verstuurd wordt. De title kwarg bevat de titel van de notificatie, message bevat het inhoudelijke bericht.

django.db.models.signals.post_save

Aangeroepen door Django bij het opslaan van nieuwe databaserecords. Kan gekoppeld worden aan bijvoorbeeld het DayStatistics model, om dagelijkse statistieken ergens anders te verwerken.

Overige

Meer signalen/gebeurtenissen zijn beschikbaar voor gebruik, echter wees voorzichtig bij het gebruiken van Django save-signalen, aangezien het de performance kan beïnvloeden.

Voorbeelden:

Voorbeeld #1: Upload gegevens naar een tweede PVOutput-account

Dit is een voorbeeld van issue #407, met het verzoek om gegevens te uploaden naar een tweede PVOutput-account.

Zie ook

DSMRREADER_PLUGINS configuratie:

DSMRREADER_PLUGINS=dsmr_plugins.modules.secondary_pvoutput_upload

Plugin bestand dsmr_plugins/modules/secondary_pvoutput_upload.py (nieuw bestand):

import requests

from django.dispatch import receiver

from dsmr_pvoutput.models.settings import PVOutputAddStatusSettings
from dsmr_pvoutput.signals import pvoutput_upload


@receiver(pvoutput_upload)
def handle_secondary_pvoutput_upload(**kwargs):
    print(' - Uploading the same data to PVOutput using plugin: {}'.format(kwargs['data']))

    response = requests.post(
        PVOutputAddStatusSettings.API_URL,
        headers={
            'X-Pvoutput-Apikey': 'XXXXX',
            'X-Pvoutput-SystemId': 'YYYYY',
        },
        data=kwargs['data']
    )

    if response.status_code != 200:
        print(' [!] PVOutput upload failed (HTTP {}): {}'.format(response.status_code, response.text))

Let op

N.B.: De variablen XXXXX en YYYYY vervang je door de API-credentials van je tweede PVOutput-account.

Voorbeeld #2: Doorsturen van ruwe telegram-gegevens naar een andere seriële poort

Dit is een voorbeeld van issue #557, wat ervoor zorgt dat je DSMR-telegrammen kunt doorsturen naar een andere seriële poort.

Zie ook

DSMRREADER_PLUGINS configuratie:

DSMRREADER_PLUGINS=dsmr_plugins.modules.forward_raw_telegram_to_serial

Plugin bestand dsmr_plugins/modules/forward_raw_telegram_to_serial.py (nieuw bestand):

import serial

from django.dispatch import receiver

from dsmr_datalogger.signals import raw_telegram
import dsmr_datalogger.services.datalogger


@receiver(raw_telegram)
def handle_forward_raw_telegram_to_serial(**kwargs):
    DEST_PORT = '/dev/ttyUSBvA'
    connection_parameters = dsmr_datalogger.services.datalogger.get_dsmr_connection_parameters()

    serial_handle = serial.Serial()
    serial_handle.port = DEST_PORT
    serial_handle.baudrate = connection_parameters['baudrate']
    serial_handle.bytesize = connection_parameters['bytesize']
    serial_handle.parity = connection_parameters['parity']
    serial_handle.stopbits = serial.STOPBITS_ONE
    serial_handle.xonxoff = 1
    serial_handle.rtscts = 0
    serial_handle.timeout = 1
    serial_handle.write_timeout = 0.2

    try:
        serial_handle.open()
        bytes_sent = serial_handle.write(bytes(kwargs['data'], 'utf-8'))
    except Exception as error:
        print(error)
    else:
        print(' >>> Sent {} bytes to {}'.format(bytes_sent, DEST_PORT))

    serial_handle.close()

Let op

N.B.: Je moet de /dev/ttyUSBvA variabele nog wijzigen naar de juiste seriële poort in je eigen situatie.

Voorbeeld #3: Doorsturen van ruwe telegram-gegevens naar een andere instantie van DSMR-reader, via de API

Dit kan behoorlijk handig zijn wanneer je meerdere instanties van DSMR-reader draait (bijvoorbeeld op een Raspberry en gespiegeld in de cloud).

Zie ook

DSMRREADER_PLUGINS configuratie:

DSMRREADER_PLUGINS=dsmr_plugins.modules.forward_raw_telegram_to_api

Plugin bestand dsmr_plugins/modules/forward_raw_telegram_to_api.py (nieuw bestand):

import requests
import logging

from django.dispatch import receiver

from dsmr_datalogger.signals import raw_telegram


@receiver(raw_telegram)
def handle_forward_raw_telegram_to_api(**kwargs):
    API_HOST = 'https://YOUR-DSMR-HOST'  # Note: Check whether you use HTTP or SSL (HTTPS).
    API_KEY = 'YOUR-API-KEY'
    TIMEOUT = 5  # A low timeout prevents the application from hanging, when the server is unavailable.

    try:
        # Register telegram by simply sending it to the application with a POST request.
        response = requests.post(
            '{}/api/v1/datalogger/dsmrreading'.format(API_HOST),
            headers={'X-AUTHKEY': API_KEY},
            data={'telegram': kwargs['data']},
            timeout=TIMEOUT
        )
    except Exception as error:
        return logging.error(error)

    if response.status_code != 201:
        logging.error('Server Error forwarding telegram: {}'.format(response.text))

Let op

N.B.: De API_HOST, API_KEY en TIMEOUT variabelen moet je nog wijzigen naar je eigen voorkeuren.

Voorbeeld #4: Doorsturen van DSMR-metingen in JSON-formaat naar een API

Gebruik dit om DSMR-metingen in JSON-formaat naar een (willekeurige) API door te sturen.

Zie ook

DSMRREADER_PLUGINS configuratie:

DSMRREADER_PLUGINS=dsmr_plugins.modules.forward_json_dsmrreading_to_api

Plugin bestand dsmr_plugins/modules/forward_json_dsmrreading_to_api.py (nieuw bestand):

import requests
import json

from django.dispatch import receiver
from django.core import serializers
from django.utils import timezone
import django.db.models.signals

from dsmr_datalogger.models.reading import DsmrReading

@receiver(django.db.models.signals.post_save, sender=DsmrReading)
def handle_forward_json_dsmrreading_to_api(sender, instance, created, raw, **kwargs):
    if not created or raw:
        return

    instance.timestamp = timezone.localtime(instance.timestamp)

    if instance.extra_device_timestamp:
        instance.extra_device_timestamp = timezone.localtime(instance.extra_device_timestamp)

    serialized = json.loads(serializers.serialize('json', [instance]))
    json_string = json.dumps(serialized[0]['fields'])

    try:
        requests.post(
            'https://YOUR-DSMR-HOST/api/endpoint/',
            data=json_string,
            # A low timeout prevents DSMR-reader from hanging, when the remote server is unreachable.
            timeout=5
        )
    except Exception as error:
        print('forward_json_dsmrreading_to_api:', error)

Voorbeeld #5: Lees telegrammen uit de DSMRloggerWS API

Zie ook

DSMRREADER_PLUGINS configuratie:

DSMRREADER_PLUGINS=dsmr_plugins.modules.poll_dsmrloggerws_api

Plugin bestand dsmr_plugins/modules/poll_dsmrloggerws_api.py (nieuw bestand):

import requests

from django.dispatch import receiver

from dsmr_backend.signals import backend_called
import dsmr_datalogger.services.datalogger


# Preverve a low timeout to prevent the entire backend process from hanging too long.
DSMRLOGGERWS_ENDPOINT = 'http://localhost/api/v1/sm/telegram'
DSMRLOGGERWS_TIMEOUT = 5


@receiver(backend_called)
def handle_backend_called(**kwargs):
    response = requests.get(DSMRLOGGERWS_ENDPOINT,
                            timeout=DSMRLOGGERWS_TIMEOUT)

    if response.status_code != 200:
        print(' [!] DSMRloggerWS plugin: Telegram endpoint failed (HTTP {}): {}'.format(
            response.status_code,
            response.text
        ))
        return

    dsmr_datalogger.services.datalogger.telegram_to_reading(data=response.text)

Let op

Let op dat je mogelijk de waarde van http://localhost moet bijwerken naar je eigen situatie.