Service Reference
APIs
Django API
This is the reference implementation for an BEMCom API service.
Configuration
Ports
This service serves plain HTTP only.
Note: If the API service should be exposed on the public it is absolutely important to place a reverse proxy like nginx in front of the service to secure the connection with HTTPS.
Port |
Usage/Remarks |
|---|---|
8080 |
REST interface and admin user interface on plain HTTP |
Environment Variables
Enironment Variable |
Example Value |
Usage/Remarks |
|---|---|---|
MQTT_BROKER_HOST |
broker.domain.de |
The DNS name or IP address of the MQTT broker. |
MQTT_BROKER_HOST |
1883 |
The port of the MQTT broker. Defaults to |
ACTIVATE_CONTROL_EXTENSION |
TRUE |
If set to |
ACTIVATE_HISTORY_EXTENSION |
TRUE |
If set to |
LOGLEVEL |
INFO |
Defines the log level. Should be one of the following strings: |
DJANGO_DEBUG |
FALSE |
If set to |
DJANGO_ADMINS |
‘[“John”, “john@example.com”]’ |
Must be a valid JSON string. Is set to ADMINS setting of Django. Defaults to an empty list. |
DJANGO_SECRET_KEY |
oTg2aWkM… |
Can be used to specify the SECRET_KEY setting of Django. Defaults to a random sequence of 64 chars generated on container startup. Note that changing the secret key will invalidate all cookies and thus force all user to login again. |
DJANGO_ALLOWED_HOSTS |
[“bemcom.domain.com”] |
A list of fully qualified hostnames the API service should be hosted on. Must be encoded as JSON string. See the ALLOWED_HOSTS setting of Django for details. Defaults to |
DJANGO_SUPERUSER_USERNAME |
admin |
If username, password and email is provided will attempt to create a superuser account with these credentials. |
DJANGO_SUPERUSER_PASSWORD |
pass |
See above. |
DJANGO_SUPERUSER_EMAIL |
admin@example.com |
See above. |
DJANGOAPIDB_HOST |
timescaledb.domain.de |
The DNS name or IP address of the TimescaleDB used to persist data. Note that |
DJANGOAPIDB_PORT |
5433 |
The port of the TimescaleDB. Defaults to |
DJANGOAPIDB_USER |
johndoe |
The username used for authentication at TimescaleDB. Defaults to |
DJANGOAPIDB_PASSWORD |
VerySecret123 |
The password used for authentication at TimescaleDB. Defaults to |
DJANGOAPIDB_DBNAME |
bemcom |
The name of the of the database inside TimescaleDB to store the data in. Defaults to |
N_MTD_WRITE_THREADS |
1 |
The number of parallel threads the api_main/mqtt_integration.py MqttToDb class uses to push incomming MQTT messages into the Database. This must be an integer. Defaults to 1 as SQLite DBs don’t support parallel read or write operations. For TimescaleDBs Values like 32 or above give a significant increase in write throughput. |
N_WORKER_PROCESSES |
16 |
The number of parallel worker processes that are used by the production server (UVicorn) to run the application. A sane number may be roughly 2-4 times the number of cores. Defaults to 1. |
ROOT_PATH |
bemcom/ |
Use this if BEMCom is served on a subpath behind a reverse proxy. |
HTTPS_ONLY |
TRUE |
If set to |
Volumes
None for most scenarios. Eventually a volume may be used to persist the SQLite database file. See below.
Database Setup
The Django API service is intended to use a TimescaleDB to persist data, which allows good performance even if larger number of datapoint value/setpoint/schedule messages are stored.
TimescaleDB
No special configuration of TimescaleDB is necessary to use it with the Django API service. See the docker-compose.yml file of the service for an example how to start a Timescale Container. See also the TimescaleDB documentation for further details.
SQLite
SQLite database are not recommended for production use. No setup is required for just testing the container.
Please note: Some features of the Django API do not work while using SQLite. In particular this is holds for the interval parameter of the GET /datapoint/{dp-id}/value/ of the REST API endpoint, as well as any other features that uses TimescaleDBs time_bucket to aggregate data to desired intervals.
If the SQLite file should be persisted beyond the container life it is necessary to carry out the following steps:
# Create an empty file so docker has something to mount touch /source/db.sqlite3
Add the following line to the docker-compose file:
volumes: - ./source/db.sqlite3:/source/db.sqlite3
Initialize the Database
If you have added a clean new database you need to initialize the database.
If you develop locally you need to apply the migrations to create the required tables and layout. To do so execute:
source/api/manage.py migrateNote: This is done automatically if you start up the API container.
To create the Admin user (that you need to login into the AdminUI) use:
For local development:
source/api/manage.py createsuperuserWhile running in the container:
docker exec -it django-api /source/api/manage.py createsuperuser
Backup & Restore
Starting from version 0.2.7 this container integrates a script to backup datapoint metadata and datapoint messages stored in the metadata database. The process uses the REST API and can thus be carried out from remote and is independent of the actual database used.
The command reference of the corresponding script can be displayed with (on bash):
docker run bemcom/django-api:<tag_of_target_django-api> python /source/api/ems_utils/simple_db_backup.py --help
An example that backs up the data of 2021-08-01 to the current working directory is the following command (on bash):
docker run --rm -v ${PWD}:/data -u "$(id -u):$(id -g)" --name django-db-backup bemcom/django-api:<tag_of_target_django-api> python /source/api/ems_utils/simple_db_backup.py -b -t <URL_of_target_django-api> -s 2021-08-01 -e 2021-08-01 -d /data/ -u <username_at_target_django-api> -p <username_at_target_django-api>
Development Checklist
Follow the following steps while contributing to the connector:
Create a
.envfile with suitable configuration for your local setup. It is usually a good idea to set theDEBUG=TRUEandLOGLEVEL=DEBUGfor developing.If you have no Database set up yet, follow the steps of Database Setup above.
For local development (the code is executed in a python environment on the machine):
Install dependencies with:
pip install -r ./source/requirements.txt
Define a an export temporary directory for Prometheus exporter (see here for details).
export PROMETHEUS_MULTIPROC_DIR="$(mktemp -d)"
Start the MqttToDb process if needed with:
source/api/manage.py mqtttodbStart the development server if needed with:
source/api/manage.py runserverImplement your changes as well all tests to cover your changes.
To run the tests use (check the pytest docs for more information):
pytest source/api/
For development in the container (use
MODE=DEVLin.envfile for debug log output and to enable nice features of Django):Start the container with:
docker-compose upImplement your changes as well all tests to cover your changes.
To run the tests use the following line in a second terminal (check the pytest docs for more information):
docker exec django-api pytest /source/api/
After everything is ready check that service also works in production mode by executing:
DJANGO_DEBUG=FALSE docker-compose up --build
Update the tag (version) in source/api/api_main/settings.py and execute the shell script to build an updated image.
# This will fail if not all tests are passed. bash build_docker_image.sh
Document your changes and new tag by appending the list below.
git add, commit and push.
Changelog
Tag |
Changes |
|---|---|
0.1.0 |
Initial functional version |
0.2.0 |
Massive performance improvement for handling MQTT messages. |
0.4.0 |
Datapoint Value and Available Datapoint messages and a can now contain any JSON data type as value. |
0.5.0 |
Transition to TimescaleDB. Datapoint value messages can now be resampled to intervals (operation happens in DB) with REST parameter. Restore function implemented. |
0.6.0 |
Django-API service now exposes Prometheus metrics to support monitoring of BEMCom applications. |
0.7.0 |
Django-API service can now be run with multiple worker processes. Added dedicated process to write MQTT messages to DB. Removed support for direct HTTPS endpoint (not supported by Gunicorn). |
0.8.0 |
Added ACTIVATE_CONTROL_EXTENSION and ACTIVATE_HISTORY_EXTENSION flags. |
Connectors
aquametro AMBUS Net Connector
This connector allows linking metering devices connected to aquametro’s AMBUS Net device.
Supported Gateways
Manufacturer |
Model |
Tested?/Remarks? |
|---|---|---|
aquametro |
AMBUS Net |
Tested. The device should in theory be able to communicate with any SOAP client. However the SOAP configuration of our test device was slightly broken, which required some tweaking of the requests. This is incorporated in the connector. |
Supported Devices
The connector should be able to process sensor datapoints of any meter that is supported by the AMBUS Net device.
Manufacturer |
Model |
Tested?/Remarks? |
|---|---|---|
aquametro |
CALEC ST computation unit |
Tested. |
aquametro |
AMFLO SONIC UFA-113 ultrasonic volume flow sensor |
Tested in combination with the CALEC ST device. |
Configuration
Ports
Port |
Usage/Remarks |
|---|---|
1880 |
Node-RED development user interface. |
Environment Variables
Enironment Variable |
Example Value |
Usage/Remarks |
|---|---|---|
AMBUSNETWS_SOAP_URL |
http://127.0.0.1/AmbusNetWS/Service1.asmx |
The Soap URL of the Ambus Net device. |
METER_IDS |
[1, 2] |
The ids of the meters connected to the Ambus Net device that should be read out. |
POLL_SECONDS |
30 |
Configures how often the meters should be polled. |
MAX_POLLS_PER_PERIOD |
15 |
Configure rate limit, that is the delay between polling one and the next meter to prevent something DDOS like. This is the maximum number of requests per POLL_TIME that are sent to Ambus Net, which yields to POLL_SECONDS / MAX_POLLS_PER_PERIOD delay between two requests to Ambus Net. MAX_POLLS_PER_PERIOD should be significantly larger then number of entries in METER_IDS. |
Volumes
None.
Development Checklist
Follow the following steps while contributing to the connector:
Create a
.envfile with suitable configuration for your local setup.Optional: Update the image of the node-red-connector-template by editing source/Dockerfile
Start the development instance with
docker-compose up -dEdit the flows, ensure everything works as expected.
Export the changed flows and update/create the files in ./source/flows/. The filenames should be the flows ids.
Update the image tag in ./build_docker_image.sh and execute the shell script to build an updated image.
Run the new image and check once more everything works as expected.
Document your changes and new tag by appending the list below.
git add, commit and push.
Changelog
Tag |
Changes |
|---|---|
0.1.0 |
Initial version. |
0.1.1 |
Minor bug fix. |
0.2.0 |
Update to connector template 0.3.0 |
Fronius Modbus Connector
This is a Modbus connector specifically for Fronius Symo GEN24 inverters. Manufacturer’s information can be found here.
The reason for this special connector is the requirement to write two registers in order to change the power limit of the inverter (see below).
TODO
This connector contains a lot of code duplicated from modbus-tcp-connector. It would be better to build the image based on the modbus-tcp-connector image and import these code pieces.
Supported Devices/Gateways
This connector is made for Fronius Symo GEN24 devices.
Manufacturer |
Model |
Tested/Remarks |
|---|---|---|
Fronius |
Symo Gen24 |
Tested. |
Configuration
To set the power limit, the following registers have to be written:
Name |
Type |
Unit |
Register |
|---|---|---|---|
WMaxLimPct |
uint16 |
% of max power (10 kW) |
40242 |
WMaxLim_Ena |
enum16 |
40246 |
When WmaxLimPct is to be written, the given value is multiplied by the scaling factor of 100 and WmaxLim_Ena is set to 1 automatically so you only have to set the desired percentage of the power. If you’d like to control those Modbus registers directly, it is recommended to use the generic Modbus TCP Connector instead.
Environment Variables
Enironment Variable |
Example Value |
Usage/Remarks |
|---|---|---|
CONNECTOR_NAME |
modbus-tcp-connector-device-name |
The name of the connector. Must be unique and is used to compute the MQTT topics. Use all lowercase chars and only dashes for separation to prevent clashes with Dockers internal name resolution system. |
MQTT_BROKER_HOST |
broker.domain.de |
The DNS name or IP address of the MQTT broker. |
MQTT_BROKER_HOST |
1883 |
The port of the MQTT broker. |
SEND_RAW_MESSAGE_TO_DB |
TRUE |
If set to |
DEBUG |
TRUE |
If == “TRUE” (i.e. the string) will set the loglevel of the connector the logging.DEBUG. Else is logging.INFO. |
POLL_SECONDS |
30 |
The period of polling the Modbus device/gateway for sensor datapoints. |
MODBUS_MASTER_IP |
192.168.0.1 |
The ip adress or DNS name of the Modbus master device which we want to connect to. |
MODBUS_MASTER_PORT |
502 |
The port on which the master device awaits Modbus communication. |
MODBUS_CONFIG |
see below. |
see below. |
MODBUS_MAX_RETRIES |
3 |
Maximum number of retrying a failed read/write operation before the connector shuts down with an error. Default value is 3. |
MODBUS_RETRY_WAIT_SECONDS |
15 |
Wait time in seconds after a failed read/write operation before trying again. Defaults to 15 seconds. |
MODBUS_POLL_BREAK |
0.1 |
Wait time in seconds between two consecutive requests. Some devices react with errors if getting polled too often. Defaults to 0.0 |
MODBUS_DISCONNECT_BETWEEN_POLLS |
TRUE |
If == “TRUE” (i.e. the string) will disconnect from Modbus master device between polls. This is useful for devices that can only handle a single connection or that react with errors if POLL_SECONDS is larger then a few seconds. Defaults to FALSE. |
MODBUS_W_MAX_LIM_PCT |
40242 |
Register of “WmaxLimPct”. Change it if your inverter uses a different one than 40242. Otherwise it can beleft blank |
MODBUS_W_MAX_LIM_ENA |
40246 |
Register of “WmaxLim_Ena”. Change it if your inverter uses a different one than 40246. Otherwise it can beleft blank |
The MODBUS_CONFIG allows to specify which registers should be read out by the connector as well as how to parse the values. |
A JSON string is expected that is is structured like this:
{
"read_coils": [],
"read_discrete_inputs": [],
"read_holding_registers": [
{
"address": 40071,
"count": 46,
"unit": 1,
"datatypes":">fffffffffffffffffffffff"
},
{
"address": 40242,
"count": 1,
"unit":1,
"datatypes":">H"
}
],
"read_input_registers": [],
"write_coil": [],
"write_register": [
{
"address": 40242,
"unit": 1,
"datatypes": ">H",
"scaling_factor": 100
},
{
"address": 40246,
"unit": 1,
"datatypes": ">H",
"scaling_factor": 1
}
]
}
The keys on the first level (e.g. read_holding_registers) specify the Modbus functions that should be used to interact with the device. Modbus functions that are not used at all (e.g. read_holding_registers here) can be omitted.
It is possible to define several ranges per function, here only one range is defined for read_input_registers. Each range item is an object containing the keys address, count and unit. These keywords are forwarded to the corresponding pymodbus method used for communication using the appropriate Modbus function. Here address is the start address, i.e. the first Modbus address that is requested. count specifies how many registers/coils are requested. unit corresponds to the Modbus unit number, which allows to communicate with several devices through a Modbus master gateway. Finally, the datatypes string is only applicable to registers and specifies how the bytes should be parsed, it must be a valid struct format string.
In the example above 20 input registers starting at address 19000 are read out by the connector from a Modbus device that has the id 1. From the device manual it is known that the registers of interest hold 10 32bit float values in big-endian encoding, the datatypes format string is set accordingly. Furthermore the value on register number 19002 is scaled (i.e. multiplied with) a factor of 0.1 before it is published on the broker.
The read_coils example above requests 8 bits starting (and including) address 10.
Volumes
None.
Development Checklist
Follow the following steps while contributing to the connector:
Update the
.envfile with suitable configuration for your local setup.Specify all required dependencies in source/requirements.txt.
Optional: Install a local version of dependencies for local development:
# in service_templates/connectors/Python/ pip install ./source/
# in the folder of your connector pip install -r ./source/requirements.txt
Place your code under source/connector but keep the source/connector/main.py as entrypoint. Place your tests in source/connector/tests.
Ensure all relevant tests exist and all of those are passed before preceding.
Update the image name and tag in ./build_docker_image.sh and execute the shell script to build an updated image.
# This will fail if not all tests are passed. bash build_docker_image.sh
Document your changes and new tag by appending the list below.
git add, commit and push.
Changelog
Tag |
Changes |
|---|---|
0.1.0 |
First productive version. |
Fronius Solar API Connector
This is a connector that allows reading datapoint values from the Powerflow REST API of Fronius PV inverters.
Supported Devices
Some remarks about the devices or just nothing.
Manufacturer |
Model |
Tested?/Remarks? |
|---|---|---|
Fronius |
Symo GEN24 |
Tested. |
Configuration
Ports
None.
Environment Variables
Enironment Variable |
Example Value |
Usage/Remarks |
|---|---|---|
CONNECTOR_NAME |
brand-new-connector |
The name of the connector. Must be unique and is used to compute the MQTT topics. Use all lowercase chars and only dashes for separation to prevent clashes with Dockers internal name resolution system. |
MQTT_BROKER_HOST |
broker.domain.de |
The DNS name or IP address of the MQTT broker. |
MQTT_BROKER_HOST |
1883 |
The port of the MQTT broker. |
SEND_RAW_MESSAGE_TO_DB |
TRUE |
If set to |
DEBUG |
TRUE |
If == “TRUE” (i.e. the string) will set the loglevel of the connector the logging.DEBUG. Else is logging.INFO. |
POLL_SECONDS |
60 |
The period of polling the inverter. |
INVERTER_POWERFLOW_URL |
http://192.168.0.44:80/solar_api/v1/GetPowerFlowRealtimeData.fcgi |
The URL of the Powerflow API, including IP address (or DNS name), port and HTTP path. |
Volumes
None.
Development Checklist
Follow the following steps while contributing to the connector:
Update the
.envfile with suitable configuration for your local setup.Specify all required dependencies in source/requirements.txt.
Optional: Install a local version of dependencies for local development:
# in service_templates/connectors/Python/ pip install -e ./source/
# in the folder of your connector pip install -r ./source/requirements.txt
Place your code under source/connector but keep the source/connector/main.py as entrypoint. Place your tests in source/connector/tests.
Ensure all relevant tests exist and all of those are passed before preceding.
Update the version number (aka. the tag) in source/connector/main.py before building the docker image.
Execute ./build_docker_image.sh to build an updated image.
# This will fail if not all tests are passed. bash build_docker_image.sh
Document your changes and new tag by appending the list below.
git add, commit and push.
Changelog
Tag |
Changes |
|---|---|
0.1.0 |
First productive version. |
0.2.0 |
Clean up environment variables and improve docs. |
0.4.0 |
Update to python connector template 0.4.0 (Datapoint Value and Available Datapoint messages and a can now contain any JSON data type as value.) |
Homematic Connector
This is a connector to integrate eQ-3 Homematic devices.
Supported Gateways
Manufacturer |
Model |
Tested?/Remarks? |
|---|---|---|
eQ-3 |
Homematic CCU3 (HmIP-CCU3) |
Tested. |
eQ-3 |
Homematic CCU2 (HM-Cen-O-TW-x-x-2) |
Not tested, should nevertheless work. |
Supported Devices
The connector should be able to process sensor datapoints of all existing Homematic devices. Currently only actuator datapoints of parameter type SET_TEMPERATURE are recognized automatically and exposed as available datapoints. The following devices have been tested
Manufacturer |
Model |
Tested?/Remarks? |
|---|---|---|
eQ-3 |
Wall Thermostat (HM-TC-IT-WM-W-EU) |
Tested, also writing the |
eQ-3 |
Window Handle Sensor (HM-Sec-RHS) |
Tested. |
eQ-3 |
Heating Actuator (HM-CC-RT-DN) |
Tested. |
Configuration
Ports
Port |
Usage/Remarks |
|---|---|
1880 |
Node-RED development user interface. |
${CALLBACK_BINRPC_PORT} |
The external port the CCU will try to connect to for the BINRPC protocol. Should be identical for the host as internal for the container. |
${CALLBACK_XMLRPC_PORT} |
The external port the CCU will try to connect to for the XMLRPC protocol. Should be identical for the host as internal for the container. |
Environment Variables
Enironment Variable |
Example Value |
Usage/Remarks |
|---|---|---|
CONNECTOR_NAME |
brand-new-connector |
The name of the connector. Must be unique and is used to compute the MQTT topics. Use all lowercase chars and only dashes for separation to prevent clashes with Dockers internal name resolution system. |
MQTT_BROKER_HOST |
broker.domain.de |
The DNS name or IP address of the MQTT broker. |
MQTT_BROKER_HOST |
1883 |
The port of the MQTT broker. |
SEND_RAW_MESSAGE_TO_DB |
TRUE |
If set to |
CCU_DNS_NAME |
ccu.domain.de |
The DNS name or IP address of the CCU to connect to. |
CALLBACK_DNS_NAME |
hostname.domain.de |
The DNS name or IP of the machine the connector is run on. Is used by the CCU to connect and push updates. |
CALLBACK_BINRPC_PORT |
2069 |
See ports. Must be identical to the port exposed on the host to allow the Node-RED flow to send the correct port value to the CCU for callback. |
CALLBACK_XMLRPC_PORT |
2070 |
See ports. Must be identical to the port exposed on the host to allow the Node-RED flow to send the correct port value to the CCU for callback. |
Volumes
None.
Development Checklist
Follow the following steps while contributing to the connector:
Create a
.envfile with suitable configuration for your local setup.Optional: Update the image of the node-red-connector-template by editing source/Dockerfile
Start the development instance with
docker-compose up -dEdit the flows, ensure everything works as expected.
Export the changed flows and update/create the files in ./source/flows/. The filenames should be the flows ids.
Update the image tag in ./build_docker_image.sh and execute the shell script to build an updated image.
Run the new image and check once more everything works as expected.
Document your changes and new tag by appending the list below.
git add, commit and push.
Changelog
Tag |
Changes |
|---|---|
0.1.0 |
Initial version |
0.1.1 |
Fix bug in send command flow |
KEBA P30 Connector
This is a connector to integrate the KEBA P30 charge station.
The Connector uses the UDP interface specified here.
This connector allows to integrate multiple P30 charge stations with one connector, which is in contrast to the general BEMCom design principle to use one connector per network address. However, as this connector must bind port 7090 to receive responses there can be only one connector at a time.
Supported Gateways
No gateway, the charge station communicated directly via Ethernet.
Supported Devices
The following devices have been tested
Manufacturer |
Model |
Tested?/Remarks? |
|---|---|---|
KEBA |
P30 charge station. |
Tested. |
Configuration
Ports
Port |
Usage/Remarks |
|---|---|
7090 |
UDP Port used by the charge station to receive and send messages. The charge station insists on sending the messages back on exactly this port. |
Environment Variables
Enironment Variable |
Example Value |
Usage/Remarks |
|---|---|---|
CONNECTOR_NAME |
brand-new-connector |
The name of the connector. Must be unique and is used to compute the MQTT topics. Use all lowercase chars and only dashes for separation to prevent clashes with Dockers internal name resolution system. |
MQTT_BROKER_HOST |
broker.domain.de |
The DNS name or IP address of the MQTT broker. |
MQTT_BROKER_HOST |
1883 |
The port of the MQTT broker. |
SEND_RAW_MESSAGE_TO_DB |
TRUE |
If set to |
DEBUG |
TRUE |
If == “TRUE” (i.e. the string) will set the loglevel of the connector the logging.DEBUG. Else is logging.INFO. |
POLL_SECONDS |
30 |
The period of polling the P30 charge stations. |
KEBA_P30_CHARGE_STATIONS |
‘{“p30_garage”: “192.168.0.99”}’ |
A JSON dictionary containing names and ip addresses (or DNS names) of P30 charge stations. This defines which charge stations should be polled for data. Must be set. |
Volumes
None.
Development Checklist
Follow the following steps while contributing to the connector:
Update the
.envfile with suitable configuration for your local setup.Specify all required dependencies in source/requirements.txt.
Optional: Install a local version of dependencies for local development:
# in service_templates/connectors/Python/ pip install -e ./source/
# in the folder of your connector pip install -r ./source/requirements.txt
Place your code under source/connector but keep the source/connector/main.py as entrypoint. Place your tests in source/connector/tests.
Ensure all relevant tests exist and all of those are passed before preceding.
Update the image name and tag in ./build_docker_image.sh and execute the shell script to build an updated image.
# This will fail if not all tests are passed. bash build_docker_image.sh
Document your changes and new tag by appending the list below.
git add, commit and push.
Changelog
Tag |
Changes |
|---|---|
0.1.0 |
Initial version |
0.2.0 |
Switch from Node-RED template to Python template to frequent connection losses due to the charge station ignoring the source port of the UDP message. |
0.3.0 |
Add functionality to write actuator values to charge station. |
0.4.0 |
Update to python connector template 0.4.0 (Datapoint Value and Available Datapoint messages and a can now contain any JSON data type as value.) |
0.5.0 |
Add automatic shutdown of connector if connection to charge station times out 5 times. |
KNX Connector
This is a connector to integrate KNX devices.
Supported Gateways
Note that currently only KNX IP gateways are supported that allow a TCP tunnel connection.
Manufacturer |
Model |
Tested?/Remarks? |
|---|---|---|
JUNG |
Tested. |
Supported Devices
Any KNX device should work in theory. It might happen that some KNX datapoint types are not supported yet. Check the definition of transcoder_by_dpt_number in transcoder.py.
Configuration
Ports
None.
Environment Variables
Enironment Variable |
Example Value |
Usage/Remarks |
|---|---|---|
CONNECTOR_NAME |
brand-new-connector |
The name of the connector. Must be unique and is used to compute the MQTT topics. Use all lowercase chars and only dashes for separation to prevent clashes with Dockers internal name resolution system. Beware: This is not the name of the image of the connector but of the created container. |
MQTT_BROKER_HOST |
broker.domain.de |
The DNS name or IP address of the MQTT broker. |
MQTT_BROKER_HOST |
1883 |
The port of the MQTT broker. |
SEND_RAW_MESSAGE_TO_DB |
TRUE |
If set to |
DEBUG |
TRUE |
If == “TRUE” (i.e. the string) will set the loglevel of the connector the logging.DEBUG. Else is logging.INFO. |
KNX_GATEWAY_HOST |
knx_gw.example.com |
The DNS name or IP address of the KNX gateway to connect to. |
KNX_GATEWAY_PORT |
3672 |
The corresponding port for the gateway. Defaults to |
KNX_DATAPOINTS |
see below |
see below |
KNX_DATAPOINTS is a JSON object mapping from KNX group addresses to KNX datapoint types. It is necessary to tell the connector how to interpret the raw bytes received from the KNX bus.
The KNX_DATAPOINTS object must contain a sensors and actuators entry and could look like follows:
{
"sensor": {
"2/3/111": "DPST-3-7",
"2/3/112": "DPST-5-1",
"2/3/113": "DPST-1-1"
},
"actuator": {
"2/3/113": "DPST-1-1"
}
}
You may want to create the mapping from group address to datapoint type from an ETS CSV export with the following lines:
import json
import pandas as pd
knx_datapoints = pd.read_csv(file_name, sep="\t", encoding="latin-1")
json.dumps({r.Address: r.DatapointType for _, r in knx_datapoints.iterrows()}, indent=4)
Volumes
None preferably. You should only add volumes, especially file mounts if it is really necessary. It introduces a lot of trouble with user id and file ownerships stuff.
Development Checklist
Follow the following steps while contributing to the connector:
Update the
.envfile with suitable configuration for your local setup.Specify all required dependencies in source/requirements.txt.
Optional: Install a local version of dependencies for local development:
# in service_templates/connectors/Python/ pip install -e ./source/
# in the folder of your connector pip install -r ./source/requirements.txt
Place your code under source/connector but keep the source/connector/main.py as entrypoint. Place your tests in source/connector/tests.
Ensure all relevant tests exist and all of those are passed before preceding.
Update the version number (aka. the tag) in source/connector/main.py before building the docker image.
Execute ./build_docker_image.sh to build an updated image.
# This will fail if not all tests are passed. bash build_docker_image.sh
Document your changes and new tag by appending the list below.
git add, commit and push.
Changelog
Tag |
Changes |
|---|---|
0.1.0 |
First productive version. |
Modbus/TCP Connector
This is a connector to communicate with Devices over Modbus/TCP. A short introduction to Modbus can e.g. be found here.
The following Modbus function codes are supported:
Modbus Function Code |
Name |
|---|---|
1 |
Read coil |
2 |
Read discrete input |
3 |
Read holding register |
4 |
Read input register |
5 |
Write single coil |
6 |
Write single holding register |
16 |
Write multiple holding registers |
Supported Devices/Gateways
This connector should be able to communicate with any Modbus Device or Gateway. The following devices have been explicitly tested:
Manufacturer |
Model |
Tested?/Remarks? |
|---|---|---|
Janitza |
UMG-96RM-E |
Tested. |
Configuration
Environment Variables
Enironment Variable |
Example Value |
Usage/Remarks |
|---|---|---|
CONNECTOR_NAME |
modbus-tcp-connector-device-name |
The name of the connector. Must be unique and is used to compute the MQTT topics. Use all lowercase chars and only dashes for separation to prevent clashes with Dockers internal name resolution system. |
MQTT_BROKER_HOST |
broker.domain.de |
The DNS name or IP address of the MQTT broker. |
MQTT_BROKER_HOST |
1883 |
The port of the MQTT broker. |
SEND_RAW_MESSAGE_TO_DB |
TRUE |
If set to |
DEBUG |
TRUE |
If == “TRUE” (i.e. the string) will set the loglevel of the connector the logging.DEBUG. Else is logging.INFO. |
POLL_SECONDS |
30 |
The period of polling the Modbus device/gateway for sensor datapoints. |
MODBUS_MASTER_IP |
192.168.0.1 |
The ip adress or DNS name of the Modbus master device which we want to connect to. |
MODBUS_MASTER_PORT |
502 |
The port on which the master device awaits Modbus communication. |
MODBUS_CONFIG |
see below. |
see below. |
MODBUS_MAX_RETRIES |
3 |
Maximum number of retrying a failed read/write operation before the connector shuts down with an error. Default value is 3. |
MODBUS_RETRY_WAIT_SECONDS |
15 |
Wait time in seconds after a failed read/write operation before trying again. Defaults to 15 seconds. |
MODBUS_POLL_BREAK |
0.1 |
Wait time in seconds between two consecutive requests. Some devices react with errors if getting polled too often. Defaults to 0.0 |
MODBUS_DISCONNECT_BETWEEN_POLLS |
TRUE |
If == “TRUE” (i.e. the string) will disconnect from Modbus master device between polls. This is useful for devices that can only handle a single connection or that react with errors if POLL_SECONDS is larger then a few seconds. Defaults to FALSE. |
MODBUS_LSRF_WORD_ORDER |
TRUE |
If == “TRUE” (i.e. the string) will swap the registers of floats before parsing. This allows data exchange with systems that follow the least significant register first convention. |
The MODBUS_CONFIG allows to specify which registers should be read out or written to by the connector as well as how to parse the values.
A JSON string is expected that is is structured like this:
{
"read_coils": [
{
"address": 10,
"count": 8,
"unit": 1,
}
],
"read_discrete_inputs": [], # Similar to "read_coils"
"read_holding_registers": [
{
"address": 19000,
"count": 20,
"unit": 1,
"datatypes": ">ffffffffff",
"scaling_factors": {
"19002": 0.1
}
}
],
"read_input_registers": [], # Similar to "read_coils"
"write_coil": [
{
"address": 10,
"unit": 1,
"example_value": "true"
}
],
"write_register": [
{
"address": 19000,
"unit": 1,
"datatypes": ">h",
"example_value": "22"
}
],
"write_registers": [
{
"address": 19001,
"unit": 1,
"datatypes": ">f",
"example_value": "21.0"
}
]
}
The keys on the first level (e.g. read_holding_registers) specify the Modbus functions that should be used to interact with the device. Modbus functions that are not used at all (e.g. read_input_registers here) can be omitted.
It is possible to define several ranges per function, here only one range is defined for read_input_registers. Each range item is an object containing keys. The following table defines which keys must be provided per modbus function.
Key |
Applicable to functions |
Explanation |
|---|---|---|
|
all |
Specifies the start address, i.e. the first Modbus address that is requested. |
|
|
Specifies how many registers/coils are requested. |
|
all |
Corresponds to the Modbus unit number, which allows to communicate with several devices through a Modbus master gateway. |
|
All that interact with registers |
Specifies how values are parsed from/to bytes , it must be a valid struct format string. |
|
|
Allows scaling the values received from the Modbus device before publishing on BEMCom (i.e. multipling the value with the factor). |
|
|
Defines an example value for display in the API service. |
The read_coils example above requests 8 bits starting (and including) address 10.
In the example above, the range defined under read_coils requests 8 bits starting (and including) address 10 from a Modbus device that has the id 1.
The range under read_holding_registers configures the connector to read 20 registers starting at address 19000 from a Modbus device that has the id 1. From the device manual it is known that the registers of interest hold 10 32bit float values in big-endian encoding, the datatypes format string is set accordingly. Furthermore the value on register number 19002 is scaled (i.e. multiplied with) a factor of 0.1 before it is published on the broker.
The range under write_coil specifies that a single Bool can be written to coil 10 of Modbus device with id 1. The example value is the boolean value True.
The range under write_register specifies that a single integer can be written to register 19000 of Modbus device with id 1. The example value is 22.
The range under write_registers specifies that a single float can be written to registers 19001 and 19002. This is as the value of datatypes defines a float 32 in big-endian encoding, which will thus span two registers. How many registers are affected is determined automatically based on the data type. Again the value is written a Modbus device with id 1 and the example value is 21.0.
Volumes
None.
Development Checklist
Follow the following steps while contributing to the connector:
Update the
.envfile with suitable configuration for your local setup.Specify all required dependencies in source/requirements.txt.
Optional: Install a local version of dependencies for local development:
# in service_templates/connectors/Python/ pip install -e ./source/
# in the folder of your connector pip install -r ./source/requirements.txt
Place your code under source/connector but keep the source/connector/main.py as entrypoint. Place your tests in source/connector/tests.
Ensure all relevant tests exist and all of those are passed before preceding.
Update the version number (aka. the tag) in source/connector/main.py before building the docker image.
Execute ./build_docker_image.sh to build an updated image.
# This will fail if not all tests are passed. bash build_docker_image.sh
Document your changes and new tag by appending the list below.
git add, commit and push.
Changelog
Tag |
Changes |
|---|---|
0.1.0 |
First productive version. |
0.2.0 |
Can read coils and discrete inputs now and allows application of scaling factors. |
0.3.0 |
Allow disconnecting between polls and breaks between requests in one poll run. |
0.4.0 |
Update to python connector template 0.4.0 (Datapoint Value and Available Datapoint messages and a can now contain any JSON data type as value.) |
0.5.0 |
Connector can now write to coils and registers. Unit id is now part of the internal datapoint id to prevent collisions while interacting with the same addresses from different devices. |
0.6.0 |
Update to python connector template 0.5.0 (It is now possible to stop processing messages in run_sensor_flow.) |
0.7.0 |
Update to python connector template 0.8.0 with reduced image size and enabled ARM builds. |
0.8.0 |
Add option for handling the least significant register first convention for floats. |
MQTT Connector
This is a connector to integrate data that is published on some MQTT broker (not the BEMCom app internal message broker.)
Sending values to actuator datapoints, i.e. to publish on the broker is currently not implemented.
Supported Devices/Gateways
Any broker that implemented MQTT version 3.1.1 should work.
Configuration
Ports
None.
Environment Variables
Enironment Variable |
Example Value |
Usage/Remarks |
|---|---|---|
CONNECTOR_NAME |
brand-new-connector |
The name of the connector. Must be unique and is used to compute the MQTT topics. Use all lowercase chars and only dashes for separation to prevent clashes with Dockers internal name resolution system. |
MQTT_BROKER_HOST |
broker.domain.de |
The DNS name or IP address of the MQTT broker. |
MQTT_BROKER_HOST |
1883 |
The port of the MQTT broker. |
SEND_RAW_MESSAGE_TO_DB |
TRUE |
If set to |
DEBUG |
TRUE |
If == “TRUE” (i.e. the string) will set the loglevel of the connector the logging.DEBUG. Else is logging.INFO. |
REMOTE_MQTT_BROKER_HOST |
broker.domain.de |
The DNS name or IP address of the remote MQTT broker. |
REMOTE_MQTT_BROKER_PORT |
1883 |
The port of the remote MQTT broker. |
REMOTE_MQTT_BROKER_USE_TLS |
FALSE |
If == “TRUE” (i.e. the string), will use TLS to encrypt the connection. |
REMOTE_MQTT_BROKER_CA_FILE |
Wrap the multiline ca file with quotes. |
A CA certificate (full chain) in pem format. If provided will use this CA certificate to verify that server certificate. |
REMOTE_MQTT_BROKER_USERNAME |
john-doe |
If not empty will try to login at the remote broker with this username. |
REMOTE_MQTT_BROKER_PASSWORD |
very-secret |
If not empty (and username not empty) will try to login at the remote broker with this password. |
REMOTE_MQTT_BROKER_TOPIC_MAPPING |
see below |
A json string defining which topics should be forwarded to which datapoints. See Readme.md for details. |
REMOTE_MQTT_BROKER_PARSE_JSON |
FALSE |
If == “TRUE” (i.e. the string), will try to parse the payload of the message received from the remote broker as JSON. Defaults to FALSE |
The content of REMOTE_MQTT_BROKER_TOPIC_MAPPING should be JSON string structured like this:
{
"sensor_topics": {
"sensor/topic/1": {},
"sensor/topic/with/single/+/wildcard": {},
"sensor/topic/with/mulitlevel/wildcard/#": {}
},
"actuator_topics": {
"actuator/topic/1": {
"example_value": "22.0"
}
}
}
For "sensor_topics" just the topic strings (i.e. the keys) are relevant for now (this may change in future), wildcards are allowed. The example values are taken from the first message arriving per topic. The connector will try to parse any incoming payload as JSON.
For "actuator_topics" the keys also specify the topics on which the value message should be published. Note that wildcards are not allowed for the actuator datapoints, as it would not be clear on which topic to publish in these cases.
Volumes
None.
Development Checklist
Follow the following steps while contributing to the connector:
Update the
.envfile with suitable configuration for your local setup.Specify all required dependencies in source/requirements.txt.
Optional: Install a local version of dependencies for local development:
# in service_templates/connectors/Python/ pip install -e ./source/
# in the folder of your connector pip install -r ./source/requirements.txt
Place your code under source/connector but keep the source/connector/main.py as entrypoint. Place your tests in source/connector/tests.
Ensure all relevant tests exist and all of those are passed before preceding.
Update the version number (aka. the tag) in source/connector/main.py before building the docker image.
Execute ./build_docker_image.sh to build an updated image.
# This will fail if not all tests are passed. bash build_docker_image.sh
Document your changes and new tag by appending the list below.
git add, commit and push.
Changelog
Tag |
Changes |
|---|---|
0.0.1 |
Some work in progress development version. |
0.1.0 |
First productive version. |
0.4.0 |
Update to python connector template 0.4.0 (Datapoint Value and Available Datapoint messages and a can now contain any JSON data type as value.) |
0.6.0 |
Update to python connector template 0.6.0 |
OCPP Connector
This is a connector to communicate with ocpp-capable charging stations using OCPP 1.6. The library used is provided by The Mobility House (https://github.com/mobilityhouse/ocpp). The Open ChargePoint Protocol (OCPP) defines the central system (here BEMCom) as a server, while the ChargePoint acts as a client. This is different to many hardware communication protocols.
Implementation
Besides the classes ActuatorFlow, SensorFlow and Connector there is a class called ChargePoint that inherits from the corresponding class in the ocpp module from The Mobility House. This class contains the protocol-specific messages. OCPP uses websockets. The messages from the charge point are routed to the corresponding method where the response to the charge point is defined. If there are values to be sent to the BEMCom message broker, a callback method with the name “sensor_flow_handler” is executed. Messages passed to it will be made available in the BEMCom framework by using the method “run_sensor_flow” of the Connector class. Once the connector starts, the synchronous DeviceDispatcher starts a sever using asyncio. Once the charging station connects to the server, the ChargePoint is instantiated and the communication begins.
Configuration
OCPP_CONFIG |
Usage/Remarks |
|---|---|
execute_send_charging_profile |
No parameters, defines the actuator datapoint so that (constant) charging profiles can be sent. Interpreted as maximum power values in W. |
execute_trigger_message |
No parameters, defines the actuator datapoint to trigger the chargepoint to send specific messages like meter values. |
Ports
Port |
Usage/Remarks |
|---|---|
9000 |
Port of the BEMCom Host (server), not the charging station. Has to be configured in the charging station as well if it is desired to use another one. |
Environment Variables
Enironment Variable |
Example Value |
Usage/Remarks |
|---|---|---|
CONNECTOR_NAME |
ocpp-connector |
The name of the connector. Must be unique and is used to compute the MQTT topics. Use all lowercase chars and only dashes for separation to prevent clashes with Dockers internal name resolution system. |
MQTT_BROKER_HOST |
broker.domain.de |
The DNS name or IP address of the MQTT broker. |
MQTT_BROKER_HOST |
1883 |
The port of the MQTT broker. |
SEND_RAW_MESSAGE_TO_DB |
TRUE |
If set to |
DEBUG |
TRUE |
If == “TRUE” (i.e. the string) will set the loglevel of the connector the logging.DEBUG. Else is logging.INFO. |
OCPP_CONFIG |
“execute_send_charging_profile” |
list of actuator datapoints. Have to begin with “execute_” |
OCPP_PORT |
9000 |
Port of the Server (BEMCom host) |
Volumes
None preferably. You should only add volumes, especially file mounts if it is really necessary. It introduces a lot of trouble with user id and file ownerships stuff.
Usage
Charging profiles consist of a single power value (in Watt) that remain active until the next value is sent. The messages sent from the chargepoint, like meter values, can be triggered using the datapoint “execute_trigger_message”. There, the following vales are allowed:
Value in API |
Message |
|---|---|
0 |
“BootNotification” |
1 |
“DiagnosticsStatusNotification” |
2 |
“FirmwareStatusNotification” |
3 |
“Heartbeat” |
4 |
“MeterValues” |
5 |
“StatusNotification” |
For more information according OCPP, you can download the specification here: (https://www.openchargealliance.org/downloads/)
Development Checklist
Follow the following steps while contributing to the connector:
Update the
.envfile with suitable configuration for your local setup.Specify all required dependencies in source/requirements.txt.
Optional: Install a local version of dependencies for local development:
# in service_templates/connectors/Python/ pip install -e ./source/
# in the folder of your connector pip install -r ./source/requirements.txt
Place your code under source/connector but keep the source/connector/main.py as entrypoint. Place your tests in source/connector/tests.
Ensure all relevant tests exist and all of those are passed before preceding.
Update the image name and tag in ./build_docker_image.sh and execute the shell script to build an updated image.
# This will fail if not all tests are passed. bash build_docker_image.sh
Document your changes and new tag by appending the list below.
git add, commit and push.
Changelog
Tag |
Changes |
|---|---|
0.1.0 |
First successful communication. |
0.2.0 |
First successfully sent charging profile |
0.2.1 |
Possible to trigger and receive meter values |
0.2.2 |
Bugfix logs, increasing robustnes. |
Senertec Dachs Connector
This is a connector to integrate the Senertec Dach combined heat and power plant. It uses the REST interface of the Dachs for communication.
Please refer to the official users manuel “Beschreibung der GLT-Schnittstelle - Dachs Ethernet” for detailed information about the available Datapoints.
Supported Devices
Manufacturer |
Model |
Tested?/Remarks? |
|---|---|---|
Senertec |
Dachs 5.5 |
Tested. |
Configuration
Ports
Port |
Usage/Remarks |
|---|---|
1880 |
Node-RED development user interface. |
Environment Variables
Enironment Variable |
Example Value |
Usage/Remarks |
|---|---|---|
CONNECTOR_NAME |
brand-new-connector |
The name of the connector. Must be unique and is used to compute the MQTT topics. Use all lowercase chars and only dashes for separation to prevent clashes with Dockers internal name resolution system. |
MQTT_BROKER_HOST |
broker.domain.de |
The DNS name or IP address of the MQTT broker. |
MQTT_BROKER_HOST |
1883 |
The port of the MQTT broker. |
SEND_RAW_MESSAGE_TO_DB |
TRUE |
If set to |
POLL_SECONDS |
30 |
Wait time in seconds between polling the CHP for sensor data. |
DACHS_IP |
dachs.example.com |
IP address or DNS name of the Dachs CHP that should be communicated with. We expect the REST API exposed on port 8080 of that address. |
Volumes
None.
Development Checklist
Follow the following steps while contributing to the connector:
Create a
.envfile with suitable configuration for your local setup.Optional: Update the image of the node-red-connector-template by editing source/Dockerfile
Start the development instance with
docker-compose up -dEdit the flows, ensure everything works as expected.
Export the changed flows and update/create the files in ./source/flows/. The filenames should be the flows ids.
If you have added functionality that needs usernames and passwords, you also need to export the flow_cred.json file from the container, as the credentials are not exported with the flows. You might do this with something like:
# Execute this in the root directory of the connector. CONTAINER_NAME=$(cat docker-compose.yml | grep container_name | cut -d : -f 2 | xargs ) docker cp $CONTAINER_NAME:/data/flows_cred.json ./source/
Please note: The credentials are stored unencrypted. If the connector needs credentials that are not well known (e.g. provided in the user manual of the device), it is better set them at runtime via environment variable. This way the credentials will never appear in the repository and can be set by each user to the required values.
Update the image tag in ./build_docker_image.sh and execute the shell script to build an updated image.
Run the new image and check once more everything works as expected.
Document your changes and new tag by appending the list below.
git add, commit and push.
Changelog
Tag |
Changes |
|---|---|
0.1.0 |
First productive version. Read only yet. |
0.2.1 |
Added support for requesting Dachs operation via actuator datapoint. |
0.3.0 |
Update to connector template 0.3.0 |
Socket Connector
This is a connector that allows receiving messages that are published by a device or gateway on a socket. It is not designed to pull information, as the latter usually requires more device/gateway specific interaction for which it very likely more simple to to develop a custom connector then handling every special case here. Currently only TCP is implemented.
Supported Devices/Gateways
This should work with any device or machine that implements standard TCP and uses it to publish information.
Configuration
Ports
None.
Environment Variables
Enironment Variable |
Example Value |
Usage/Remarks |
|---|---|---|
CONNECTOR_NAME |
brand-new-connector |
The name of the connector. Must be unique and is used to compute the MQTT topics. Use all lowercase chars and only dashes for separation to prevent clashes with Dockers internal name resolution system. |
MQTT_BROKER_HOST |
broker.domain.de |
The DNS name or IP address of the MQTT broker. |
MQTT_BROKER_HOST |
1883 |
The port of the MQTT broker. |
SEND_RAW_MESSAGE_TO_DB |
TRUE |
If set to |
DEBUG |
TRUE |
If == “TRUE” (i.e. the string) will set the loglevel of the connector the logging.DEBUG. Else is logging.INFO. |
SERVER_IP |
192.168.0.55 |
The DNS name or IP address of the target device or gateway. |
SERVER_PORT |
587 |
The port which the connector should connect to on the target device or gateway. |
RECV_BUFSIZE |
1024 |
The buffersize used while receiving information. The data from the device or gateway should not exceed this many bytes as the raw message will else be split up and may end up unreadable. See also the socket docs for more information. Defaults to 4096. |
RECV_TIMEOUT |
15 |
Max wait time before a message is expected. Will shutdown connector if no message is received after that time (to allow a restart and reconnection). 0 means we expect a message immediately (non-blocking mode). Negative values wait as long as possible (blocking mode). See here for details. |
PARSE_AS |
YAML |
Defines how the raw data should be parsed as objects. Either YAML or JSON is supported. Defaults to JSON. |
Volumes
None.
Development Checklist
Follow the following steps while contributing to the connector:
Update the
.envfile with suitable configuration for your local setup.Specify all required dependencies in source/requirements.txt.
Optional: Install a local version of dependencies for local development:
# in service_templates/connectors/Python/ pip install -e ./source/
# in the folder of your connector pip install -r ./source/requirements.txt
Place your code under source/connector but keep the source/connector/main.py as entrypoint. Place your tests in source/connector/tests.
Ensure all relevant tests exist and all of those are passed before preceding.
Update the version number (aka. the tag) in source/connector/main.py before building the docker image.
Execute ./build_docker_image.sh to build an updated image.
# This will fail if not all tests are passed. bash build_docker_image.sh
Document your changes and new tag by appending the list below.
git add, commit and push.
Changelog
Tag |
Changes |
|---|---|
0.1.0 |
First productive version. |
0.4.0 |
Update to python connector template 0.4.0 (Datapoint Value and Available Datapoint messages and a can now contain any JSON data type as value.) |
0.5.0 |
Add timeout to allow automatic restarts if connection is lost. |
Controllers
Python Controller
This is a Python based implementation of a Controller service. It listens to all setpoint and schedule messages (see the message format documentation for details) published on the broker, whereby the setpoint reflects the users desired value and the schedule the optimal value computed by an optimization process. Based on these messages the controller computes the value for the actuator datapoint.
The controller ensures the following points:
That each item in a setpoint message is executed, as long as the value for
to_timestampis not in the past. Executed means thereby that the fieldspreferred_value, and if existingacceptable_valuesormin_valueandmax_valueare loaded and used while computing the value for the actuator datapoint.That each item in a schedule message is executed, as long as the value for
to_timestampis not in the past. Executed means thereby that the fieldvalue, is loaded and used while computing the value for the actuator datapoint.valuesfor actuators are only sent if the value changes, to prevent permanently sending the same information to the devices.That logic for computing the actuator value at any time are as follows:
If neither a setpoint nor a schedule exist the
valueof the actuator will not be changed at all.If no setpoint exists but a schedule, the
valuedefined in the schedule will be sent to the actuator.If no schedule exists but a setpoint is, the
preferred_valueof the setpoint message is used asvaluefor the actuator.If setpoint and schedule exist but the datapoint is not of type continuous_numeric or discrete_text/numeric (i.e. the setpoint message contains no flexibility as neither
acceptable_valuesormin_valueandmax_valueare defined) the schedule message is ignored.If setpoint and schedule exist and the datapoint is of type continuous_numeric (i.e. the fields
min_valueandmax_valueexist) the thevalueof the schedule is sent to the actuator, as least as long as the corresponding sensor datapoint is with the range specified bymin_valueandmax_value. If the sensor value leaves the allowed range thepreferred_valueis sent asvalueto the actuator.If setpoint and schedule exist and the datapoint is of type discrete (i.e. the field
acceptable_valuesexists) the thevalueof the schedule is sent to the actuator, as least as long as the corresponding sensor datapoint is with the range specified byacceptable_values. If the sensor value leaves the allowed range thepreferred_valueis sent asvalueto the actuator.
Configuration
Ports
None.
Environment Variables
Enironment Variable |
Example Value |
Usage/Remarks |
|---|---|---|
MQTT_BROKER_HOST |
broker.domain.de |
The DNS name or IP address of the MQTT broker. |
MQTT_BROKER_HOST |
1883 |
The port of the MQTT broker. |
MQTT_TOPIC_CONTROLLED_DATAPOINTS |
python_controller/controlled_datapoints |
The topic on which the controller listens for configuration data. |
Volumes
None.
Changelog
Tag |
Changes |
|---|---|
0.1.0 |
Initial version |
MQTT Brokers
Mosquitto MQTT Broker
MQTT message broker service based on the Mosquitto docker image, see https://hub.docker.com/_/eclipse-mosquitto for details. This provides currently no additional value to using the official Mosquitto image directly, but is here for consistency. However the latter may change in future.
Changelog
Tag |
Changes |
|---|---|
0.1.0 |
Initial version, uses Mosquitto 1.6.10 |
Raw Message DBs
Mongo Raw Message DB
This is a MongoDB based Raw Message DB for BEMCom.
Configuration
Ports
Port |
Usage/Remarks |
|---|---|
27017 |
Port to directly connect to MongoDB. Opening this port is not necessary for storing raw messages by connectors, only for access with external tools. You should definitely configure the passwords before opening this port. |
Environment Variables
Enironment Variable |
Example Value |
Usage/Remarks |
|---|---|---|
MQTT_BROKER_HOST |
broker.domain.de |
The DNS name or IP address of the MQTT broker. |
MQTT_BROKER_PORT |
1883 |
The port of the MQTT broker. |
MONGO_INITDB_ROOT_USERNAME |
mongo-admin |
If set, will automatically create an admin account on the very first startup. See here (Section Environment Variables) for details. |
MONGO_INITDB_ROOT_PASSWORD |
very!secret& |
See above. |
MONGO_USERNAME |
mongo-user |
Starts the MongoDB with access control if not left blank. |
MONGO_PASSWORD |
very!secret& |
The password of the account that is used by the mqtt-integration.py script to connect to the MongoDB. |
MONGO_LOGIN_DB |
admin |
The Login DB of the account that is used by the mqtt-integration.py script to connect to the MongoDB. Should be set to |
MQTT_TOPIC_ALL_RAW_MESSAGES |
+/raw_message_to_db |
The topic on the broker on which the raw messages will be published. Stick to the convention of the example value given here to prevent messy surprises. |
MQTT_INTEGRATION_LOG_LEVEL |
INFO |
The level of log messages that will be logged to stdout of the container for the mqtt-integration.py script. Can be any of |
Volumes
Path in Container |
Usage/Remarks |
|---|---|
/data/db |
Folder in which MongoDB stores its DB. Is required to persist the Database. The folder on the host machine must exist before the container is run for the first time and the container must be started with the user id of the user who own the folder on the host machine. |
Inital Startup Checklist
This creates a non-superuser account for the mqtt integration script to use.
Note: If you have populated MONGO_INITDB_ROOT_USERNAME and MONGO_INITDB_ROOT_USERNAME on the first startup you may need to extend these steps with a login.
Create an empty directory for the containers persistent files. Set USER_ID and GROUP_ID to the ids of the user who owns the directory. Set MONGODB_VOLUME to that directory.
Spin up the container with blank value for MONGO_USERNAME.
Create a user for the Python MQTT Integration script. See https://docs.mongodb.com/manual/tutorial/create-users/ for details. Give the user read and write permissions for the database
bemcom_db. E.g. with this:docker exec -it your-bemcom-app-mongo-raw-message-db mongo use admin db.createUser( { user: "bemcom", pwd: passwordPrompt(), roles: [ { role: "readWrite", db: "bemcom_db" } ] } )
Stop the container.
Set the remaining environment variables (MONGO_USERNAME, MONGO_PASSWORD, MONGO_LOGIN_DB) according to the values you defined previously.
Restart the container, inspect the logs to verify that everything works as expected.
Changelog
Tag |
Changes |
|---|---|
0.0.1 |
Initial version, some bug while shutting down container. |
0.1.0 |
Should be fully functional now. Container shutdown stops everything gracefully now. |
0.1.1 |
Improve credentials handling. |
0.2.0 |
Update Mongo to 5.0, and allow automatic admin account creation. |
Tools
Demo Device
A service that should behave like a simple Modbus device. This tool is solely intended for demonstration of BEMCom. It has no practical value besides this function.
The demo can be interpreted as as a single room for which the temperature is measured and an actor (say an AC system that can heat and cool) exists to manipulate the room temperature.
The demo device exposes the following registers:
Type |
Register |
Data Type |
Description |
|---|---|---|---|
Input Register |
1 |
16bit float |
The current room temperature in °C. |
Holding Register |
1 |
16bit float |
The setpoint for the room temperature in °C. Values below 15°C and above 30° are clipped to that range. |
Configuration
Ports
Port |
Usage/Remarks |
|---|---|
502 |
Container accepts ModbusTCP connections on this port. Use Modbus unit ID 1. |
Environment Variables
Enironment Variable |
Example Value |
Usage/Remarks |
|---|---|---|
LOG_VALUES |
FALSE |
If set to |
Volumes
None.
Changelog
Tag |
Changes |
|---|---|
0.1.0 |
First productive version. |
EMP Link
This is a simple tool that automatically pushes new data to the FZI Energy Management Panel.
Configuration
Ports
None.
Environment Variables
Enironment Variable |
Example Value |
Usage/Remarks |
|---|---|---|
ORIGIN |
EmpLinkTest |
The value for the |
AUTO_RELOAD |
TRUE |
If |
MQTT_BROKER_HOST |
broker.domain.de |
The DNS name or IP address of the MQTT broker. |
MQTT_BROKER_PORT |
18830 |
The port of the MQTT broker. Defaults to |
BEMCOM_API_URL |
https://bemcom.example.com |
The URL of the BEMCom API. Used for fetching the datapoint metadata. |
BEMCOM_API_VERIFY_SSL |
FALSE |
If == “FALSE” (i.e. the string) will disable certificate checking for the BEMCom API connection. Useful if self signed certificates are used but a potential security risk. See also the requests docs on the topic. Defaults to TRUE. |
BEMCOM_API_USERNAME |
jon_doe |
The username (HTTP basic auth) to use for connecting to BEMCom API. |
BEMCOM_API_PASSWORD |
verySecret123 |
The corresponding password. |
EMP_API_URL |
https://emp.example.com |
The URL of the EMP API. All new data is pushed there. |
EMP_API_VERIFY_SSL |
TRUE |
Like |
EMP_API_USERNAME |
jon_doe |
The username (HTTP basic auth) to use for connecting to EMP API. |
EMP_API_PASSWORD |
verySecret123 |
The corresponding password. |
Volumes
None.
Development Quick Start
On a Linux system you can start developing on the code quickly by following these steps.
In a terminal execute:
docker-compose down -v && USER_ID=$(id -u) GROUP_ID=$(id -g) docker-compose up --build
For automatic execution of tests run the following in a second terminal:
docker exec -it emp-devl auto-pytest /source/emp/
For an interactive python terminal execute:
docker exec -it emp-devl /opt/conda/bin/python /source/emp/manage.py shell
Grafana Tool
This service provides a Grafana instance with custom plug-ins that allow:
bemcom-django-api: Data retrieval from the REST interface of the BEMCom Django-API service.prediction-service-api: Request and display data provided via the prediction service REST API.nwpdata-service-api: Request and display of data provided via the nwpdata service REST API.
Please note that the prediction service and nwpdata service REST API have not been published to the public yet. You can simple ignore these plugins thus.
Configuration
Ports
Port |
Usage/Remarks |
|---|---|
3000 |
Default Grafana Ui port |
Environment Variables
Enironment Variable |
Example Value |
Usage/Remarks |
|---|---|---|
GF_SECURITY_ADMIN_USER |
admin |
The default admin user. Defaults to |
GF_SECURITY_ADMIN_PASSWORD |
very!secret& |
The default admin password. Defaults to |
Volumes
Path in Container |
Usage/Remarks |
|---|---|
/var/lib/grafana/grafana.db |
Grafana SQLite database file. Store on local file system to persist grafan settings. |
/var/lib/grafana-plugins/bemcom-django-api |
Allows mounting in the custom plugin. Use for development only. |
/var/lib/grafana-plugins/nwpdata-service-api |
Allows mounting in the custom plugin. Use for development only. |
/var/lib/grafana-plugins/prediction-service-api |
Allows mounting in the custom plugin. Use for development only. |
Hint: Add custom volumes to persist changes in Grafana.
Usage Instructions
Initial Setup
Ensure that the
${GRAFANA_DB_FILE}exists and has read/write permissions for the user running the container.
Data source configuration
The datasources are configured by providing the following settings:
required:
Nameof the datasource inside grafanaurlto the APIs root. For examplehttp://django-api.example.com:8000/
optional:
use basic authenticationbasicAuth userbasicAuth passwordskip TLS verification- check this to ignore self signed certificates. important this option renders https insecure. To enable verification and security a custom CA Authority for verification of certificates is needed.
Optional settings are initally only of importance to the BEMCom API.
Query configuration
BEMCOM API plugin
A query can either display meta data on the API or timeseries data.
Meta data is toggled by a switch. The received table-like data gives information on all available datapoints.
Timeseries data can be of the above described data types.
Simply choose the datapoint by its short name and the datatype. The dropdown also features an autoselection when typing.
Optionally you can define a frequency of the queried entries and an offset of the frequency to define the time range over which the average is taken. Warning: This option is not implemented in BEMCom, yet (Juli 2021).
A custom name and scaling factor can optionally be defined. Click “apply” or press enter to commit changes here.
Nwpdata and stochastic prediction service API plugin
Use the textfield to define the parameters needed for the request as JSON string. The actual format depends on the service queried and can be found on the Swagger UI at the root URL of the respective service. The following settings work well with the FZI internal DWD MOSMIX Data Service for example expects parameters as followed:
{
"station": "Q712",
"start_timestamp": $from,
"end_timestamp": $to,
"updates": "latest",
"mosmix_parameters": ["TTT"]
}
Development
The plugin is written in typescript and based on the grafana plugin creator and tutorial.
You need to have node.js and yarn installed for the following commands. If you have conda installed you can install these with:
conda create -n node -c conda-forge nodejs==12.* yarn
To see your changes in a local grafana instance, change the docker-compose.yml file to development mode (see comments in the file) and start grafana as docker container.
Then, to actually work on the plugin:
Install all needed modules for development from within the
bemcom-django-apifolder with
yarn installMake your changes to the source files under
./source/grafana_plugins/bemcom-django-api/srcHot build the plugin to see changes in the browser on reload with
yarn dev --watchRun the tests with
yarn testBuild the plugin for production with
yarn build
Once finished with developing do:
Update the image tag in ./build_docker_image.sh and execute the shell script to build an updated image.
Document your changes and new tag by appending the list below.
git add, commit and push.
Further instructions about working with Grafana:
Grafana Tutorials - Grafana Tutorials are step-by-step guides that help you make the most of Grafana
Grafana UI Library - UI components to help you build interfaces using Grafana Design System
Changelog
Tag |
Changes |
|---|---|
0.1.0 |
Initial version |
0.1.1 |
Simpler setpoint selection |
0.1.2 |
Basic authentication enabled. Skipping TLS verification enabled for self signed certificates. |
0.1.3 |
Additional fields for custom name, scaling factor and datapoint description |
0.1.4 |
Restructured query editor. Optional field to define an offset. Integrate grafana’s query option ‘interval’ |
0.2.0 |
Added additional plugins for querying nwpdata and stochastic prediction services |
0.3.0 |
Update to Grafana 8.3.* due to a zero day exploit in Grafana. |
0.4.0 |
Update to Grafana 9.0. |
pgAdmin4 Tool
This is a wrapper around the pgAdmin4 Docker image. The image here adds automatic HTTPs certificate generation.
ToDo
This image does not support running as arbitrary user
Configuration
Ports
Port |
Usage/Remarks |
|---|---|
443 |
HTTPs port for pgAdmin user interface. |
Environment Variables
The most essential settings are these:
Enironment Variable |
Example Value |
Usage/Remarks |
|---|---|---|
SSL_CERT_PEM |
—–BEGIN CERTIFICATE—– |
The certificate to use for HTTPS. Will generate a self signed certificate if SSL_CERT_PEM or SSL_KEY_PEM are empty. The self signed certificate will make HTTPS work, but browsers will issue a warning. |
SSL_KEY_PEM |
—–BEGIN PRIVATE KEY—– |
Similar to SSL_CERT_PEM but should hold the private key of the certificate. |
PGADMIN_DEFAULT_EMAIL |
user@example.com |
The username for logging in. |
PGADMIN_DEFAULT_PASSWORD |
VerySecret! |
The password for logging in. |
PGADMIN_ENABLE_TLS |
TRUE |
Should always be set as we have certificates and there seems to be no point on serving plain HTTP. |
See also the official docs for additional configuration possibilities.
Volumes
Path in Container |
Usage/Remarks |
|---|---|
/var/lib/pgadmin |
Folder in which pgAdmin stores its files and configuration. Mount these to host file system if settings should be persisted. |
Changelog
Tag |
Changes |
|---|---|
0.1.0 |
Initial version. Uses pgAdmin4:5.0 docker image. |