A tool for bidirectional communication between serial devices and MQTT brokers.
Project description
A tool for bidirectional communication between serial devices and MQTT brokers.
1 Features
Data flow is fully defined in a configuration file.
Built-in support for the Modbus protocol.
Adding support for custom serial protocols possible by extending code.
Ability to send user-specific initialization MQTT messages on startup.
One of the design goals is to enable regular users to get desired data from any Modbus device merely by editing a configuration file.
2 How it works
2.1 Reading from serial devices
Configured serial devices are periodically polled for data.
These data are then converted to usable values according to the configured rules.
The obtained values are then sent to the configured MQTT brokers.
2.2 Writing to serial devices
Subscribtions to the configured MQTT topics are created.
The configured handlers are run when a matching MQTT message is received.
Handlers extract values from the incoming MQTT messages and convert them to protocol-specific data according to the configured rules.
The obtained data are then written to the configured serial devices.
3 Installation
$ pip install serial-jobs
4 Usage
Edit the provided YAML configuration file stubs in the config_stubs directory or create new ones as desired.
Merge the relevant YAML configuration file stubs into a single file.
$ merge_config config_stubs/* INFO serial_jobs.config loading stub configuration file config_stubs/epever-charge-controller.yaml INFO serial_jobs.config loading stub configuration file config_stubs/jbd-battery-management-system.yaml INFO serial_jobs.config loading stub configuration file config_stubs/mqtt-brokers.yaml INFO serial_jobs.config loading stub configuration file config_stubs/orno-energy-meter.yaml Overwrite configuration file ./configuration.yaml? (y/n) y INFO serial_jobs.config saving configuration to file ./configuration.yaml INFO serial_jobs.config configuration file saved
Convert the configuration file from YAML format to JSON format for faster parsing at runtime.
$ convert_config INFO serial_jobs.config loading configuration file ./configuration.yaml INFO serial_jobs.config configuration file loaded Overwrite configuration file ./configuration.json? (y/n) y INFO serial_jobs.config saving configuration to file ./configuration.json INFO serial_jobs.config configuration file saved
Run the application.
$ serial_jobs INFO serial_jobs.config loading configuration file ./configuration.json INFO serial_jobs.config configuration file loaded INFO serial_jobs.device.base creating device lock /dev/ttyUSB0 ...
5 Configuration
The application is configured via a configuration file in either YAML or JSON format.
Below is an example of a simple configuration file which uses the YAML format.
mqtt_brokers:
- id: local
host: 127.0.0.1
port: 1883
username:
password:
devices:
- id: orno-or-we-514
name: ORNO OR-WE-514 single phase energy meter
serial:
port: /dev/ttyUSB0
baud_rate: 9600
data_bits: 8
stop_bits: 1
parity: E
timeout: 0.1
protocol:
modbus_address: 0x13
tasks:
- id: o-active-power
name: active power in W
device: orno-or-we-514
mqtt_topic: orno-or-we-514/active-power
value:
data:
- signed_long:
register_type: holding
register_count: 2
address: 0x140
jobs:
- id: power-meter
mqtt_messages:
- homeassistant/sensor/default/power-meter-active-power/config:
device:
name: ORNO energy meter
manufacturer: ORNO
model: OR-WE-514
identifiers: orno-or-we-514
device_class: power
name: Active Power
state_class: measurement
state_topic: orno-or-we-514/active-power
object_id: orno-or-we-514-active-power
unique_id: orno-or-we-514-active-power
unit_of_measurement: W
sleep: 10
tasks:
- o-active-power
It instructs serial-jobs to poll an energy meter for the current active power value every 10 seconds and publish it to an MQTT broker.
Configuration files for some real devices and real use cases are available in the config_stubs directory.
The data structures used within the configuration file are described below.
5.1 Configuration data structures
Valid configuration data structures are: a string, a number, a boolean, a sequence or an object.
A sequence is a list-like data structure. It must contain items of the same type.
An object is a dictionary-like data structure. It consists of fields.
A field is represented by a key and a value. A key is a string. A value can be any valid configuration data structure, i.e. a string, a number, a boolean, a sequence or an object.
There can be at most one field with a particular key within a specific object.
5.1.1 Terminology
This section explains the terminology used within this documentation, the configuration files and the source code.
- data part
Partially meaningful piece of information that can be converted to protocol-specific sequence of bytes suitable for a particular serial device.
- value
A meaningful piece of information represented by data parts on a serial device.
- task
A routine which retrieves a particular value from a particular serial device and sends it within an MQTT message.
- job
A group of tasks performed periodically.
- handler
A routine which extracts a particular value from an incoming MQTT message with a particular MQTT topic and writes it to a particular serial device.
- service
A group of handlers to run upon receiving messages from a particular MQTT broker.
5.1.2 Top-level configuration structures
This section describes configuration data structures that might be present at the top-level of the configuration file.
5.1.2.1 Common
- mqtt_brokers
A part of configuration which specifies how to communicate with MQTT brokers.
It consists of a sequence of MQTT broker specifications. Each specification might contain the fields defined below.
- id:
Unique ID of the defined MQTT broker. It is used for referring to a particular MQTT broker within this configuration.
- name:
(optional) Human-readable name of the defined MQTT broker. It might be used to make the configuration file less ambiguous.
- host:
Hostname of the defined MQTT broker.
- port:
Port of the defined MQTT broker.
- username:
Username for connecting to the defined MQTT broker.
- password:
Password for the defined username.
Example:
mqtt_brokers: - id: local host: 127.0.0.1 port: 1883 username: password:
- devices
A part of configuration which specifies how to communicate with serial devices.
It consists of a sequence of serial device specifications. Each specification might contain fields defined below.
- id:
Unique ID of the defined serial device. It is used for referring to a particular serial device within this configuration.
- name:
(optional) Human-readable name of the defined serial device. It might be used to make the configuration file less ambiguous.
- type:
(optional) Type of the defined serial device. Defaults to ModbusDevice.
Available device types:
- serial:
Specification of the parameters for serial communication with the defined device.
- protocol:
(optional) Device-type-specific protocol details needed for communicating with the device.
It might contain fields defined below.
- modbus_address:
(optional) Modbus device ID (or Modbus device address) used for communicating with the defined Modbus device.
In case of ModbusDevice device type, this field is mandatory.
Example:
devices: - id: orno-or-we-514 name: ORNO OR-WE-514 single phase energy meter serial: port: /dev/ttyUSB0 baud_rate: 9600 data_bits: 8 stop_bits: 1 parity: E timeout: 0.1 protocol: modbus_address: 0x13
5.1.3 Lower-level configuration structures
This section describes configuration data structures that might only be present within certain other configuration data structures.
- serial
Specification of the parameters for serial communication with the defined device.
The specified values must be accepted by the serial.Serial class from the pyserial module. It might contain fields defined below.
- port:
Name of the hardware device (or port) used for serial communication with the defined device.
- baud_rate:
Baud rate used for serial communication with the defined device.
- data_bits:
Number of data bits used for serial communication with the defined device.
- stop_bits:
Number of stop bits used for serial communication with the defined device.
- parity:
Parity used for serial communication with the defined device.
- timeout:
Timeout in seconds used for serial communication with the defined device.
Example:
serial: port: /dev/ttyUSB0 baud_rate: 9600 data_bits: 8 stop_bits: 1 parity: E timeout: 0.1
- value (when used within tasks as MQTT output value)
A part of configuration which specifies how to map simple data parts obtained from serial devices to a serializable value that can be used when communicating with an MQTT broker.
It consists of an object which specifies how to obtain the value for sending to the configured MQTT broker from the configured serial device.
It might contain fields defined below.
- type:
(optional) Python data type to which to convert the obtained data before serializing it to string for sending to the configured MQTT broker.
Available values are: float, int, str, date, datetime and time.
- mapping:
(optional) An object containing string-to-string mapping applied to the obtained data before converting it to the final value type.
Example:
mapping: 0: normal 1: high temperature warning 2: low temperature warning
- data:
A sequence of data part specifications. They specify how to convert the obtained data parts to Python values which could be serialized as an MQTT message.
Example:
value: mapping: 0: normal 1: overvoltage 2: undervoltage 3: low voltage disconnect 4: fault data: - short: address: 0x3200 bitmask: 0b1111
- value (when used within handlers as MQTT input value)
A part of configuration which specifies how to extract value from an incoming MQTT message and how to write it to a particular serial device.
It consists of an object which specifies how to obtain the value for writing to the configured serial device from the incoming MQTT messages.
It might contain fields defined below.
- mapping:
(optional) An object containing string-to-string mapping applied to the obtained MQTT message content before converting it to the final value type.
Example:
mapping: false: 0 true: 1
- type:
(optional) Python data type to which to convert the string content of the obtained MQTT message before transforming it into data parts which will then be written to the configured serial device.
Available values are: float, int, str, date, datetime and time.
- data:
A sequence of data part specifications. They specify how to convert the obtained Python value to data parts which could be written to the configured serial device.
Example:
value: type: float data: - short: register_type: holding writable_block: start_address: 0x9003 stop_address: 0x900F address: 0x9008 scale_factor: 100
- data
A part of configuration which specifies the mapping between raw bytes from devices and simple data parts.
It consists of a sequence of data part specifications.
A data part specification is an object which defines how to map an individual data part to raw device bytes. It consists of a single field whose name determines the data type of the data part and whose value determines the device registers which contain the bytes for the data part.
Available data types are:
string: string of byte-sized characters
byte: one-byte unsigned integer
signed_byte: one-byte signed integer
short: two-byte unsigned integer
signed_short: two-byte signed integer
long: four-byte unsigned integer
signed_long: four-byte signed integer
float: four-byte floating point number
double: eight-byte floating point number
All multi-byte numeric data types are by default expected to use big-endian byte order (i.e. the most significant byte has the smallest memory address).
The value of this field, representing the device registers, might contain the following fields:
- register_type:
(optional) Type of the register to read from. Defaults to default.
Available values are:
default: The device-specific default register type. For devices of type ModbusDevice the default register type is the input register type.
coil: A readable and writable register type which holds one bit of data. Available only for devices of type ModbusDevice.
discrete: A read-only register type which holds one bit of data. Available only for devices of type ModbusDevice.
holding: A readable and writable register type which holds two bytes of data. Available only for devices of type ModbusDevice.
input: A read-only register type which holds two bytes of data. Available only for devices of type ModbusDevice.
- writable_block:
(optional) Specification of the block of registers which need to be written to the device at the same time when writing the data to this particular register block.
It might contain fields defined below.
start_address: Start address (inclusive) of the register block to write.
stop_address: Stop address (exclusive) of the register block to write.
- register_count:
(optional) Number of consecutive registers to use. Defaults to 1.
- address:
Start address (inclusive) of the register block to use.
- byte_order:
(optional) Sequence of zero-based byte indices determining how to order the bytes from this register block into the resulting data part.
- byte_offset:
(optional) Number of bytes from this register block to skip before creating the resulting data part.
- byte_count:
(optional) number of bytes from this register block starting at byte_offset to use for creating the resulting data part.
- byte_index:
(optional) Index of a single byte within this register block to use for creating the resulting data part. If defined, it overrides byte_offset and byte_count.
- bitmask:
(optional) Binary integer (i.e. a number prefixed with 0b) determining the bitmask applied to the sequence of bytes extracted from this register block.
Example: 0b10001100
- bitshift:
(optional) Number of bits to shift the sequence of bytes extracted from this register block. If positive, shift to the right (i.e. divide by a power of two). If negative, shift to the left (i.e. multiply by a power of two).
- scale_factor:
(optional) Number by which to divide the bit-shifted data value.
- increase_by:
(optional) Number which is added to the scaled data value.
Example:
data: - long: register_count: 2 address: 0x3102 byte_order: [2, 3, 0, 1] scale_factor: 100
In this example, the data flow when reading from a serial device starts at the device registers 0x3102 and 0x3103. Reading those registers results in four obtained bytes. Those four bytes are then reordered using the defined permutation. Then they are converted to unsigned long integer. And then this value is divided by 100. The outcome is then processed further as a data part.
The data flow when writing to a serial device is reversed. At first the unsigned long integer is multiplied by 100. Then its bytes are reordered using the inverse of the defined permutation. And then the resulting bytes are written to device registers 0x3102 and 0x3103.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for serial_jobs-0.0.4-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 16ea845a57870084d15034b9db9a9b297730c2b84ab6e9debc69febe1eb2e501 |
|
MD5 | 5aabb84ecd38423752666088ac197b7d |
|
BLAKE2b-256 | a43cbb345db0e136b71046e44bf7b151e439d25be7fa4bb729c262d47e0d9203 |