Skip to main content

Package for building and standardizing MQTT event messages.

Project description

ebb-events:

EbbCarbon package to standardize event message structures. For environmental and industrial automation montioring, we are using many physical sensors to read/gather data about our systems and the environment. In order to communicate this information across systems, we are utilizing the MQTT messaging protocol to publish and subscribe to various messages and topics (however, this structure can be used to publish events across various other protocols as well). In an attempt to standardize the event message structure, this package works to define a standard topic hierarchy, standard event types to use, and a standard payload structure that can be replicated and re-used throughout the industry. This structure will be consistent with the CloudEvent structure defined [here] (https://cloudevents.io/) while implementing some additional standards and best practices so that these events can be consumed and used by CloudEvent users as well.

Use:

Install the ebb-events package from pip installer via: pip install ebb-events. Use ebb-events to format your event messages and topics.

from ebb_events.builders.event_builder import EventEnvelope
from ebb_events.enums import EventType

event_envelope = EventEnvelope(
    organization="test-org",
    system_id="test-system",
    subsystem_id="test-subsystem",
    device_id="test-device-01",
)

# Builds a topic string for a data event message publish
event_topic = event_envelope.build_event_topic(event_type=EventType.DATA)
# Builds a JSON payload of the expected ebb-events structure
event_payload = event_envelope.build_event_payload_json(
    message={...},
    event_type=EventType.DATA,
    serial_number="ABC123",
    metadata={...},
    datetime_obj=my_datetime
)

Use ebb-events package to consume events published with this expected ebb-events structure

from ebb_events.consumers.event_consumer import EventConsumer

my_event_payload = {...}
event_consumer = EventConsumer(payload=my_event_payload)

event_consumer.get_event_message()  # Retrieves the dict message found in the payload's `data` field
event_consumer.get_event_time()
event_consumer.get_event_type()
event_conumser.get_event_system_id()
event_consumer.get_device_serial_number()

Topic Structure:

One thing that this package enforces is a topic structure for publishing MQTT messages to a broker. Well defined topics help Our topic structure is as follows:

  • <organization>/<system-id>/<event-type>/<subsystem-id>/<device-id>

Topic Naming Rules:

  • Lower case only (subscriptions are case sensitive)
  • Dashes (not underscores)
  • Alphanumeric topics only (Illegal characters: #, +, *, <spaces>)
  • No leading “/”
  • 50 character maximum per topic section (256 maximum total)

Topic Hierarchy Explained:

  1. <organization>: The highest level in the hierarchy representing the <organization> that is publishing and owning this event.
    • Example: ebbcarbon
  2. <system-id>: The unique <system-id> from which this message is originating. The <system-id> should be unique and un-changing (e.g. don't rely on things like geographic location if the producer might move locations).
    • Example: system-name
  3. <event-type>: The specific type of event being published. As of now, we support four event-types.
    • data: Sensor readings and measurements
    • state: Sensor current state (e.g. online status, battery life, memory, power, etc.)
    • config: Sensor setup (e.g. calibration coefficients, calibration date, physical location, configured outputs/fields, etc.)
    • cmd: Instructions for subscribing clients
    • heartbeat: Heartbeat publish with minimal information to show publisher is up and running
  4. <subsystem-id>: Systems are typically made up of several subsystems, each of which contains numberous sensors monitoring their own data and readings. This section of the topic hierarchy should define which subsystem the sensor data is coming from.
    • Example: system-name is made up of 4 subsystems, 3 of which are different "my-subsystem-name" subsystems -> the various "my-subsystem-name" subsystems could be labeled my-subsystem-name-01, my-subsystem-name-02, and my-subsystem-name-03
  5. <device-id>: The unique identity of a device as it relates to the subsystem on which it lives. This is not the device's serial number or physical unique ID since the physical device might be replaced.
    • Example: my-subsystem-name-01 has sensors ("my-sensor") in slots 01, 02, and 03. Therefore <device-id> could be my-sensor-02
      • If the physical "my-sensor" in slot 02 fails and is replaced with a new "my-sensor", even though the serial number is now different, the <device-id> in your topic would remain the same because this part of the topic refers to the <device-id> as it relates to the overall subsystem, not as it relates to the physical device itself.
      • The new "my-sensor" is still my-sensor-02.

Topic Example:

For a data message being published by one of Ebb's edge-nodes with measurements from "my-sensor" in sensor slot 02 of subsystem 1, which makes up system-name:

  • ebbcarbon/system-name/data/subsystem-1/my-sensor-02

Event Payload Structure:

This package defines the event payload schema to follow for all event types. The schema includes relavant metadata in the EventEnvelope and the actual message information in the "data" field of the payload. Specific message structures of the "data" field differ based on event type and the context of the event. The envelope + data make up the overall event payload. All MQTT event messages that are published and consumed should subscribe to this general structure making it easier to share data across organizations and systems. Names of payload fields follow attribute names from CloudEvents as well.

Event Payload:

Structure shared by all event messages regardless of event type

{
    "id": str(uuid),
    "time": str,  # [RFC3339](https://datatracker.ietf.org/doc/html/rfc3339) format
    "source": str,  # same as topic string
    "type": str,
    "data": {
        "metadata": {
            "serial_number": str,
            ...
        },
        ...  # unique nested JSON dependent on event-type
    }
}

Suggested data Event Message Structure:

The ebb-events package allows users to publish payloads of any structure (as long as they are JSON serializable) so that it can be helpful for publishing all types of events. However, we have a recommened message structure that we encourage all to adopt when publishing sensor data events that will allow consumers to process the readings smoothly and accurately. Each variable that is measured by a sensor should named following the convention outlined here whenever possible, and should have a corresponding value and units attached to it and should be formatted in the message argument of build_event_payload_json/dict(message={}) like so:

message = {
    "variable_name_1": {
        "value": ____,
        "units": str
    },
    "variable_name_2": {
        "value": ____,
        "units": str
    },
    ...
}

To confirm that your message follow this structure, you can utilize the following utility helper which returns True for valid date event messages, and returns a dictionary of field validation errors for invalid messages: ebb_events.event_payload_utils.validate_data_event_payload_message(payload_message=my_message)

EventEnvelope Class:

The EventEnvelope class is used to consolidate all of the pieces of an event payload and build the expected structure for a user so that users don't have to worry about constructing the properly formatted topic or payload. The EventEnvelope class expects to be provided with certain fields that are then used to build the topic and payload via class methods. These methods handle all the validation and formatting needed to ensure that your events follow the ebb-events standards.

Required fields: organization, system_id, subsystem_id, device_id Useful methods: EventEnvelope().build_event_topic(event_type=___), EventEnvelope().build_event_payload(message=___, event_type=___)

Event Consumer:

This package defines an event consumer that can be used to process incoming events that follow this ebb-events structure. To use this consumer, initialize the class with a given JSON payload: EventConsumer(payload=my_payload). Then, you can use the various getters present on the class in order to retrieve different pieces of the payload depending on your needs (e.g. system_id, message, metadata, etc.). If you need all the parts of the payload that go into building it (e.g. an entire EventEnvelope wrapper for the payload) you may also use the get_event_envelope() method to do so.

Exception Handling

If you attempt to consume an event that DOES NOT match the ebb-events structure, you can still initialize an EventConsumer(payload=my_payload) object and the payload will live as EventConsumer().raw_payload. However, all of the getter methods will raise PayloadFormatExceptions because the payload does not match the expected format. Therefore, for a payload that does not match the ebb-events structure, you must process the payload as a raw JSON/dict object instead.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ebb_events-1.0.0.tar.gz (13.8 kB view details)

Uploaded Source

Built Distribution

ebb_events-1.0.0-py3-none-any.whl (13.1 kB view details)

Uploaded Python 3

File details

Details for the file ebb_events-1.0.0.tar.gz.

File metadata

  • Download URL: ebb_events-1.0.0.tar.gz
  • Upload date:
  • Size: 13.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.8.2 CPython/3.10.12 Darwin/23.4.0

File hashes

Hashes for ebb_events-1.0.0.tar.gz
Algorithm Hash digest
SHA256 0b70e67e61d5c8f174690f20ac8ed76fddc4ffa9c276f10589ef413c8b6dfd65
MD5 d15c5a8665b31c1875cef99ac2990aa9
BLAKE2b-256 bda45926ab184f59704fcefd78d875bc50076eb24b645c47405faa4a26d03174

See more details on using hashes here.

File details

Details for the file ebb_events-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: ebb_events-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 13.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.8.2 CPython/3.10.12 Darwin/23.4.0

File hashes

Hashes for ebb_events-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 38b3fafa5968e2eeb441a1abeaba7801a50a9dad4580cdb23368675b042f00b1
MD5 fe71e852e85b566e06ead50548cbf400
BLAKE2b-256 15bdf7da43fc578021f8bdae6b07c3acfc80890514dacddec6a8b52c61904cd5

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page