Core
IoT Platform 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-platform-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-platform-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.