Core
IoT Backend v2.x
2

Define a Device model #

We will now define a new Device model with the associated Decoder in order to receive raw payloads.

The device is a temperature and pressure sensor, we will use the standard temperature measurement but we need to define a custom co2 measurement.

Define a custom measurement #

To define a measure, it is necessary to declare an object indicating to the storage engine the names and types of the different values of the measure.

It is also advisable to create a type for this measure in order to take advantage of the strong typing in the rest of the project.

In this example, we will declare a CO2 measurement that will contain its value in a co2 property of type float:

Copied to clipboard!
import { MeasureDefinition } from "@kuzzleio/iot-backend";

export type CO2Measurement = {
  co2: number,
};

export const co2MeasureDefinition: MeasureDefinition = {
  valueMappings: {
    co2: { type: "float" },
  },
};

Then the measure should be registered on the framework:

Copied to clipboard!
import { DeviceManagePlugin } from "kuzzle-device-manager";
import { co2MeasureDefinition } from "./CO2Measurement";

const deviceManager = app.plugin.get < DeviceManagerPlugin > "device-manager";

deviceManager.models.registerMeasure("co2", co2MeasureDefinition);

Decoder #

First, you need to create a Decoder that will transform the raw payload into standardized measures.

For this, you need to extends the Decoder class and

  • define measure decoded by the Decoder
  • implements the decode method

The decode method take 2 parameters:

  • decodedPayload utility class to extract the measures
  • payload the raw payload as a JSONObject

Each measure should be extracted with the following information:

  • device reference which acts as device unique identifier (e.g. ABC123)
  • measure type (e.g. temperature)
  • measure name (e.g. temperature)
  • measure timestamp of the measurement (e.g. 1675959435515)
  • measure values (e.g. 21.2)

An example of the complete implementation can be found in the apps/api/lib/modules/devices/ directory

In this example, we will implements a Decoder to decode the following raw payload:

Copied to clipboard!
{
  "deviceEUI": "ABC123",
  "temp": 21.2
}

Our Decoder will define two measures, temperature and co2.

Copied to clipboard!
import {
  DecodedPayload,
  Decoder,
  HumidityMeasurement,
  TemperatureMeasurement,
} from "@kuzzleio/iot-backend";
import { JSONObject } from "kuzzle";
import { has } from "lodash";
import { CO2Measurement } from "./CO2Measurement";

export class ExampleDecoder extends Decoder {
  /**
   * Declare the measure extracted by this Decoder
   */
  public measures = [
    { name: "temperature", type: "temperature" },
    { name: "co2", type: "co2" },
  ] as const;

  constructor() {
    super();

    /**
     * Register a custom mappings for the "payloads" collection
     */
    this.payloadsMappings = {
      deviceId: { type: "keyword" },
    };
  }

  /**
   * Ensure the payload contains the correct values
   */
  async validate(payload: JSONObject): Promise<boolean> {
    // This throw an exception if the property "deviceId" is not present
    this.ensureProperties(payload, ["deviceId"]);

    const properties = ["temperature", "co2"];

    return properties.every((property) => has(payload, property));
  }

  async decode(
    decodedPayload: DecodedPayload<ExampleDecoder>,
    payload: JSONObject
  ): Promise<DecodedPayload<Decoder>> {
    const deviceId = payload.deviceId;

    const measuredAt = payload.timestamp || Date.now();

    decodedPayload.addMeasurement<TemperatureMeasurement>(
      deviceId, // device reference
      "temperature", // measure name
      {
        measuredAt, // timestamp of the measure
        type: "temperature", // measure type
        values: {
          temperature: payload.temperature,
        },
      }
    );

    decodedPayload.addMeasurement<CO2Measurement>(deviceId, "co2", {
      measuredAt,
      type: "co2",
      values: {
        co2: payload.co2,
      },
    });

    return decodedPayload;
  }
}

Then we can define a new Device model associated with the Decoder we just wrote.

For this, we will use the Device Manager plugin:

Copied to clipboard!
import { DeviceManagePlugin } from "kuzzle-device-manager";

const deviceManager = app.plugin.get < DeviceManagerPlugin > "device-manager";

deviceManager.models.registerDevice("Example", {
  decoder: new ExampleDecoder(),
});

Once registered, the new device model automatically exposes a HTTP route to send raw payloads:

Copied to clipboard!
curl -X POST \
  -H "Content-Type: application/json" \
  "http://localhost:7512/_/devicemanager/payload/example"\
  --data '{"deviceId": "ABC123", "co2": 123, "temperature": 21.2 }'

You can now open the Admin > Orphans view of the IoT Console and see you newly created device.

By default, the IoT Platform automatically create new devices when a payload is received for the first time.