Plugins¶
The application allows you to create and add plugins, hooking on certain events triggered.
Contents
Configuration¶
You can create plugins in their own file in dsmr_plugins/modules/plugin_name.py
,
where plugin_name
is the name of your plugin.
Please make sure the plugin_name
,
- is lowercase (
plugin_name
and notPLUGIN_NAME
), - does not contains spaces or dashes, only use underscores and do not start the name with a digit.
See also
Add the dotted path as DSMRREADER_PLUGINS
env var. For more information see DSMRREADER_PLUGINS in Env Settings.
Your plugin file is imported once, so you should make sure to hook any events you want.
And finally, make sure to restart the application to reflect the changes. Do so by executing the following as root user or sudoer:
sudo supervisorctl restart all
Events / Signals¶
These are either dispatched by the Django framework or the application at some point.
dsmr_backend.signals.backend_called
¶
Called by each iteration of the backend. Please use with caution, as it may block all backend operations when used improperly.
dsmr_pvoutput.signals.pvoutput_upload
¶
Called by dsmr_pvoutput just before uploading data to PVOutput. The data
kwarg contains the data to be sent.
dsmr_datalogger.signals.raw_telegram
¶
Called by dsmr_datalogger when receiving or reading a telegram string. The data
kwarg contains the raw telegram string.
dsmr_notification.signals.notification_sent
¶
Called by dsmr_notification just after dispatching a notification. The title
kwarg contains the notification title, message
contains the message body.
django.db.models.signals.post_save
¶
Called by Django after saving new records to the database. Can be bound to the DayStatistics
model for example, to process daily statistics elsewhere.
Examples:¶
Example #1: Upload data to second PVOutput account¶
This is an example of issue #407, requesting the feature to upload data to a second PVOuput account.
See also
DSMRREADER_PLUGINS configuration:
DSMRREADER_PLUGINS=dsmr_plugins.modules.secondary_pvoutput_upload
Plugin file dsmr_plugins/modules/secondary_pvoutput_upload.py
(new file):
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))
Attention
Note that the XXXXX
and YYYYY
variables should be replace by your second set of PVOutput API credentials.
Example #2: Forwarding raw telegram data to another serial port¶
This is an example of issue #557, allowing raw DSMR telegrams to be forwarded to another serial port.
See also
DSMRREADER_PLUGINS configuration:
DSMRREADER_PLUGINS=dsmr_plugins.modules.forward_raw_telegram_to_serial
Plugin file dsmr_plugins/modules/forward_raw_telegram_to_serial.py
(new file):
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()
Attention
Note that the /dev/ttyUSBvA
variable should be changed to the serial port used in your own situation.
Example #3: Forwarding raw telegram data to another instance by API¶
This can be quite handy if you run multiple instances of DSMR-reader (i.e.: RaspberryPI + somewhere in cloud).
See also
DSMRREADER_PLUGINS configuration:
DSMRREADER_PLUGINS=dsmr_plugins.modules.forward_raw_telegram_to_api
Plugin file dsmr_plugins/modules/forward_raw_telegram_to_api.py
(new file):
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))
Attention
Note that the API_HOST
, API_KEY
and TIMEOUT
variables should be changed to your own preferences.
Example #4: Forwarding DSMR readings in JSON format to some API¶
Use this to send DSMR readings in JSON format to some (arbitrary) API.
See also
DSMRREADER_PLUGINS configuration:
DSMRREADER_PLUGINS=dsmr_plugins.modules.forward_json_dsmrreading_to_api
Plugin file dsmr_plugins/modules/forward_json_dsmrreading_to_api.py
(new file):
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)
Example #5: Read telegrams using DSMRloggerWS API¶
See also
DSMRREADER_PLUGINS configuration:
DSMRREADER_PLUGINS=dsmr_plugins.modules.poll_dsmrloggerws_api
Plugin file dsmr_plugins/modules/poll_dsmrloggerws_api.py
(new file):
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)
Attention
Note that you might need to update the http://localhost
value to your own situation.