# Module Development Guide

# Introduction

Business lines extend the functionality of the Edge by deploying modules that connect to devices to be able to report telemetry, upload files from devices, receive model updates/commands/methods from the cloud, etc. These modules run as Docker​ containers and so have to be delivered as Docker images.

# Requirements

  1. A module must have a type definition registered in the type definition registry.
  2. A module must be published as a Docker image in a registry accessible from the Edge (built and based on ABB Ability™-provided base images whenever possible)​.
  3. All module communication to/from the cloud must be performed using the Edge message bus (MQTT-based). Only intranet access is allowed from the module. Modules can also communicate with each other using predefined MQTT topics.

# Type Definition

  1. Each edge gateway must have an associated type that is registered in the type registry before the edge is configured.

  2. An edge gateway type must include references to all the modules that are allowed to run on it under the modules reference definition.

  3. A module will be injected with its service name as {module_id} variable.

  4. Each edge module must have at least two models:

    • abb.ability.device model, which is used to declare properties, variables, alarms, events and methods supported/reported by this module and to maintain a relationship to the edge gateway, and
    • abb.ability.configuration model, which allows configuration of the module.
  5. Both device and configuration model must extend base module types: abb.ability.device.edge.module and abb.ability.configuration.edge.module correspondingly.

    Type inheritance not supported yet

    Note that until type inheritance is supported in Ability™, these base types must be embedded in your module types, and until seeding is implemented, the latest version of these types can be found here.

  6. Each module configuration must have a docker property group which encapsulates everything that the edge proxy can configure for your module's container. Currently supported configuration properties are:

    • image - Docker image of this module
    • env - map of environment variables to pass to the module
      {
          ...
          "docker": {
              "image": "abbability.azurecr.io/edge/sample/module-develop:1.0.0",
              "env": {
                  "foo": "bar"
              }
          }
          ...
      }
      

    TIP

    Always use version as a tag when publishing your module (instead of using latest for example), since if this image property does not change, Docker container will not be updated.

    TIP

    Private Docker registries can be configured on the edge via IMAGEREGISTRIES configuration property in /var/ability/config/edge.env, which must contain a map of registries.

    rely on the Edge Message Bus subscription to the models topic.

# Examples of type definitions

# Connecting to MQTT Broker

Each module is injected with an Edge Message Bus server url in the environment variable mqtt_url, and password file location is injected as mqtt_password_file. Use the value of module_id variable as a username and mqtt_client_id variable value as an MQTT client ID if you wish to maintain a persistent session.

TIP

If your module is configured with more than one replica, you must use a different client ID for each of the replicas, or the broker will disconnect the previous client upon the connection of the new one with the same client ID.

Note that default QoS level for messages published by a proxy is at least once (all incoming messages except for {topics_local_in}, since it is up to the publishing module in that case), which means that modules must be able to handle duplicate messages (idempotent method implementations are highly recommended). It's up to the module to define QoS for outgoing messages.

Each module is entitled only to a set of topics that is injected with environment variables. Any topic name ending with _in represents a read only access, and topic names ending with _out represents a write-only access.

Warning on denial of access

Note that the denial of access does not constitute an error. This means that if a client subscribes to a topic and is denied, it doesn’t know it has been denied as it receives a positive acknowledgement. Same applies to publishing - if a client publishes to a topic and is denied, again the client doesn’t know it has been denied as it receives a positive acknowledgement.

# Receiving Model Updates

To receive updates to your object models, subscribe to {topics_model_in}/#.

  1. Each model is delivered under a separate topic, i.e. configuration model updates are delivered via {topics_model_in}/abb.ability.configuration.
  2. The configuration model is sent to a module on each external update; other models must be explicitly requested.
  3. The version of the model is included in the payload and can be used to identify redundant update messages.

Object models are retained

This means that the latest object model of a particular model definition will be delivered to the module every time it subscribes to this topic.

  1. Model updates for devices behind the module are delivered with the objectId in the last topic element, i.e.

    {topics_model_in}/abb.ability.device/CHILDS_OBJECT_ID
    

# Updating Object Models

Publish to {topics_model_out}. Payload must contain a valid JSON object representing the full object model. Use to propagate model changes. If you wish to update a different model, add that model definition id as a last part of this topic, i.e.

{topics_model_out}/abb.ability.custom

WARNING

Note that devices cannot update abb.ability.configuration models.

# Direct Methods

Subscribe to {topics_methods_in}/# to receive method requests. The last two parts of a topic are {method name}/{{request id}, and payload is either a valid JSON or an empty body. To respond, send a message with a valid JSON to the topic {topics_methods_out}/{status}/{request id}, where the request id has to match the one in the request message, and the status has to be an integer. The payload of this message must be a valid JSON object.

If your module serves as a gateway/proxy for other devices, you will receive their method invocations on the topic {topics_methods_in}/$proxy/{request id}, and the payload will be in the following format:

{
   "objectId": "target device object id",
   "method": "target method name",
   "input": "{payload as defined in the method definition of the device type, optional}"
}

Responses must be delivered via the same topic as for the modules' methods.

Last updated: 1/10/2022, 11:05:26 AM
Feedback