Implementation of an HTTP Web Thing.
Add the following dependency to your project:
dotnet add package Mozilla.IoT.WebThing
In this example we will set up a dimmable light and a humidity sensor (both using fake data, of course). Both working examples can be found in here.
Imagine you have a dimmable light that you want to expose via the web of things API. The light can be turned on/off and the brightness can be set from 0% to 100%. Besides the name, description, and type, a Light
is required to expose two properties:
on
: the state of the light, whether it is turned on or off- Setting this property via a
PUT {"on": true/false}
call to the REST API toggles the light.
- Setting this property via a
brightness
: the brightness level of the light from 0-100%- Setting this property via a PUT call to the REST API sets the brightness level of this light.
First we create a new Thing:
public class LampThing : Thing
{
public override string Name => "my-lamp-123";
public override string? Title => "My Lamp";
public override string? Description => "A web connected lamp";
public override string[]? Type { get; } = new[] { "Light", "OnOffSwitch" };
}
Now we can add the required properties.
The on
property reports and sets the on/off state of the light. For this, we need to create a new property in Thing and add ThingPropertyAttribute
. For our purposes, we just want to log the new state if the light is switched on/off.
public class LampThing : Thing
{
...
private bool _on;
[ThingProperty(Type = new []{ "OnOffProperty" }, Title = "On/Off", Description = "Whether the lamp is turned on")]
public bool On
{
get => _on;
set
{
_on = value;
Console.WriteLine($"On is now {value}");
OnPropertyChanged();
}
}
}
The brightness
property reports the brightness level of the light and sets the level. Like before, instead of actually setting the level of a light, we just log the level.
public class LampThing : Thing
{
...
private int _brightness;
[ThingProperty(Type = new []{ "BrightnessProperty" },Title = "Brightness",
Description = "The level of light from 0-100", Minimum = 0, Maximum = 100,
Unit = "percent")]
public int Brightness
{
get => _brightness;
set
{
_brightness = value;
Console.WriteLine($"Brightness is now {value}");
OnPropertyChanged();
}
}
}
Now we can add our newly created thing and add Thing middleware to Asp Net Core:
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddThings()
.AddThing<LampThing>();
// If you want use Web Sockets.
services.AddWebSockets(opt => { });
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// If you want use Web Sockets.
app.UseWebSockets();
app.UseEndpoints(endpoints =>
{
endpoints.MapThings();
});
}
This will start the server, making the light available via the WoT REST API and announcing it as a discoverable resource on your local network via mDNS.
Let's now also connect a humidity sensor to the server we set up for our light.
A MultiLevelSensor
(a sensor that returns a level instead of just on/off) has one required property (besides the name, type, and optional description): level
. We want to monitor this property and get notified if the value changes.
First we create a new Thing:
public class Humidity : Thing
{
public override string? Title => "My Humidity Sensor";
public override string[]? Type { get; } = new[] {"MultiLevelSensor"};
public override string? Description => "A web connected humidity sensor";
}
Then we create and add the appropriate property:
level
: tells us what the sensor is actually reading- Contrary to the light, the value cannot be set via an API call, as it wouldn't make much sense, to SET what a sensor is reading. Therefore, we are creating a readOnly property.
public class Humidity : Thing
{
...
[ThingProperty(Type = new []{"LevelProperty"}, Title = "Humidity", Description = "The current humidity in %",
Minimum = 0, Maximum = 100, Unit = "percent")]
public double Level { get; private set; }
}
Now we have a sensor that constantly reports 0%. To make it usable, we need a thread or some kind of inAdd when the sensor has a new reading available. For this purpose we start a task that queries the physical sensor every few seconds. For our purposes, it just calls a fake method.
// Start a task that polls the sensor reading every 3 seconds
Task.Factory.StartNew(async () => {
await Task.Delay(3_000);
await Level = ReadFromGPIO();
});