Skip to content

Latest commit

 

History

History
363 lines (309 loc) · 9.6 KB

README.md

File metadata and controls

363 lines (309 loc) · 9.6 KB

Gitpod ready-to-code

webthing-arduino

A library with simple websocket client for the ESP8266 and the ESP32 boards that implements Mozilla's proposed Web of Things API.

Installation (Platformio)

  • From local lib
    lib_deps = file:///path/to/lib/webthing-arduino
    
  • From Git
    lib_deps = https://github.com/qube-ai/webthing-arduino.git
    

SSL

  • For ssl use beginSSL() method of QubeAdapter.

Message Schema

Schema

Message sent by the Tunnel to the Thing

These are the only messageType(s) allowed by the library, and any other types will not pe processed.

  • Set Property The setProperty message type is sent from a Tunnel server to a Thing in order to set the value of one or more of its properties. This is equivalent to a PUT request on a Property resource URL using the REST API, but with the WebSocket API a property value can be changed multiple times in quick succession over an open socket and multiple properties can be set at the same time.
{
  "messageType": "setProperty",
  "thingId": "thingId",
  "data": {
    "propertyId": "propertyId",
    "newValue": "newValue",
  }
}

response : 
 
{
  "messageType": "updatedProperty",
  "propertyId": "propertyId",
  "value": "newValue",
  "thingId": "thingId"
}
  • Get thing description The getThingDescription message type is sent from a tunnel server to a Thing to request the description of a Thing. This is equivalent to a GET request on a Thing resource URL using the REST API. This will give you the thingDescription of the provided thingId.
{
  "thingId": "thingId",
  "messageType": "getThingDescription",
}

response :
{
  ... <Full Thing Description>
}
  • Get All Things The getThingDescription message type is sent from tunnel sercer to a Thing to request the description of a Thing. This is equivalent to a GET request on a Thing resource URL using the REST API. This will give you the thingDescription of the all things.
{
  "messageType": "getAllThings",
}

response :
{
  "messageType": "descriptionOfThings",
  "things": "thing_id"
}
  • Get Property This will give the value of the propertyId of the provided thingId.
{
  "messageType": "getProperty",
  "thingId": "thingId",
}

response :
{
  "messageType": "getProperty",
  "thingId": "thingId",
  "data": {
    "propertyId": "value",
    .
    .
    "propertyIdN": "valueN",
  }
}

Message sent by the Thing to the Tunnel Server

These are the only messageType(s) that the library will/can send to tunnel server.

  • Property Status The propertyStatus message type is sent from a Thing to a tunnel server whenever a property of a Thing changes. The payload data of this message is in the same format as a response to a GET request on Property resource using the REST API, but the message is pushed to the client whenever a property changes and can include multiple properties at the same time.
{
  "messageType": "propertyStatus",
  "thingId" : "some_thing_id",
  "data": {
    "led": true
  }
}

Error Messages

These are all the error messages sent by the library.

  • Invalid Json
{
  "messsageType": "error",
  "thingId": "some_thing_id",
  "errorMessage" :"deserializeJson() failed "
}
  • Unkown Message type
{
  "messsageType": "error",
  "thingId": "some_thing_id",
  "errorMessage": "unknown messageType"
}
  • Start WS connection
{
  "messsageType": "StartWs",
  "thingId": "some_thing_id"
}
  • Invalid Thing ID
{
  "messageType":"error",
  "thingId": "some_thing_id",
  "errorCode":"404",
  "errorMessage":"Thing not found"
}
  • Invalid Event ID
{
  "messageType":"error",
  "thingId": "some_thing_id",
  "eventId": "some_event_id",
  "errorCode":"404",
  "errorMessage":"Event not found"
}
  • Invalid Property ID
{
  "messageType": "error",
  "thingId": "some_thing_id",
  "propertyId": "some_property_id",
  "errorCode": "404",
  "errorMessage": "Property not found"
}
  • Invalid Action ID
{
  "messageType": "error",
  "thingId": "some_thing_id",
  "actionId": "some_action_id",
  "errorCode": "404",
  "errorMessage": "Action not found"
}
  • If action object was null ptr

This should never happen, but just in case.

{
  "messageType": "error",
  "thingId": "some_thing_id",
  "actionId": "some_action_id",
  "errorCode": "404",
  "errorMessage": "Request action object was null ptr."
}

Example Sketch

#include <Arduino.h>
#include "Thing.h"
#include "QubeAdapter.h"

// TODO: Hardcode your wifi credentials here (and keep it private)
const char *ssid = "WillowCove";
const char *password = "Deepwaves007";

const int ledPin = LED_BUILTIN;

QubeAdapter *adapter;

void onOffChanged(ThingPropertyValue newValue); 
ThingActionObject *action_generator(DynamicJsonDocument *);

const char *ledTypes[] = {"OnOffSwitch", "Light", nullptr};
ThingDevice led("led", "Built-in LED", ledTypes);
ThingProperty ledOn("on", "", BOOLEAN, "OnOffProperty", onOffChanged);
StaticJsonDocument<256> fadeInput;
JsonObject fadeInputObj = fadeInput.to<JsonObject>();
ThingAction fade("fade", "Fade", "Fade the lamp to a given level",
                 "FadeAction", &fadeInputObj, action_generator);
ThingEvent overheated("overheated",
                      "The lamp has exceeded its safe operating temperature",
                      NUMBER, "OverheatedEvent");
                      
bool lastOn = false;

void onOffChanged(ThingPropertyValue newValue) {
  Serial.print("On/Off changed to : ");
  Serial.println(newValue.boolean);
  digitalWrite(ledPin, newValue.boolean ? LOW : HIGH);
}

void setup(void)
{
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, HIGH);
  Serial.begin(115200);
  Serial.println("");
  Serial.print("Connecting to \"");
  Serial.print(ssid);
  Serial.println("\"");
#if defined(ESP8266) || defined(ESP32)
  WiFi.mode(WIFI_STA);
#endif
  WiFi.begin(ssid, password);
  Serial.println("");

  // Wait for connection
  bool blink = true;
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
    digitalWrite(ledPin, blink ? LOW : HIGH); // active low led
    blink = !blink;
  }
  digitalWrite(ledPin, HIGH); // active low led
  adapter = new QubeAdapter("led", WiFi.localIP());

  led.addProperty(&ledOn);
  fadeInputObj["type"] = "object";
  JsonObject fadeInputProperties =
      fadeInputObj.createNestedObject("properties");
  JsonObject brightnessInput =
      fadeInputProperties.createNestedObject("brightness");
  brightnessInput["type"] = "integer";
  brightnessInput["minimum"] = 0;
  brightnessInput["maximum"] = 100;
  brightnessInput["unit"] = "percent";
  JsonObject durationInput =
      fadeInputProperties.createNestedObject("duration");
  durationInput["type"] = "integer";
  durationInput["minimum"] = 1;
  durationInput["unit"] = "milliseconds";
  led.addAction(&fade);

  overheated.unit = "degree celsius";
  led.addEvent(&overheated);

  adapter->addDevice(&led);
  adapter->begin("192.168.29.154", 8765);
  Serial.println("HTTP server started");
  Serial.print("http://");
  Serial.print(WiFi.localIP());
  Serial.print("/things/");
  Serial.println(led.id);
}

void loop()
{
  adapter->update();
}

void do_fade(const JsonVariant &input) {
  Serial.println("Fade called");
  Serial.println(input.as<String>());
  fadeInputObj = input.as<JsonObject>();
  Serial.println(fadeInputObj["level"].as<String>());
  Serial.println(fadeInputObj["duration"].as<String>());
  Serial.println(fadeInputObj["unit"].as<String>());
}

ThingActionObject *action_generator(DynamicJsonDocument *input) {
  return new ThingActionObject("fade", input, do_fade, nullptr);
}

Architecture for tunnel

updatedProperty message also contains thing_id. thingId is missing from the message in SequenceDiagram.

Sequence diagram