A library with simple websocket client for the ESP8266 and the ESP32 boards that implements Mozilla's proposed Web of Things API.
- From local lib
lib_deps = file:///path/to/lib/webthing-arduino
- From Git
lib_deps = https://github.com/qube-ai/webthing-arduino.git
- For ssl use
beginSSL()
method of QubeAdapter.
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",
}
}
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
}
}
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."
}
#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);
}
updatedProperty
message also contains thing_id. thingId is missing from the message in SequenceDiagram.