Implementation of an HTTP Web Thing.
Using
Nuget
Add the following dependency to your project:
dotnet add package Mozilla.IoT.WebThing
Example
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.
Dimmable Light
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.
Sensor
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(); });