From 2867a5cfb288ab22edf5c42071380ece6987608b Mon Sep 17 00:00:00 2001 From: Rafael Lillo Date: Tue, 11 Feb 2020 22:49:45 +0000 Subject: [PATCH 1/3] Remove DS_Store --- .DS_Store | Bin 6148 -> 0 bytes .gitignore | 1 + .vscode/launch.json | 27 ----------------- .vscode/tasks.json | 36 ----------------------- sample/.DS_Store | Bin 6148 -> 0 bytes src/.DS_Store | Bin 6148 -> 0 bytes src/Mozilla.IoT.WebThing/.DS_Store | Bin 6148 -> 0 bytes test/.DS_Store | Bin 6148 -> 0 bytes test/Mozilla.IoT.WebThing.Test/.DS_Store | Bin 6148 -> 0 bytes 9 files changed, 1 insertion(+), 63 deletions(-) delete mode 100644 .DS_Store delete mode 100644 .vscode/launch.json delete mode 100644 .vscode/tasks.json delete mode 100644 sample/.DS_Store delete mode 100644 src/.DS_Store delete mode 100644 src/Mozilla.IoT.WebThing/.DS_Store delete mode 100644 test/.DS_Store delete mode 100644 test/Mozilla.IoT.WebThing.Test/.DS_Store diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 29b1fd470ca5bb8cee843c8ca51c1a3752460b1c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHK%WA_w3>-C13G|XP0_}LU znqBW4EDM0FuJseJ1hAkh;!B6I>AL#J?mRLlitSO?=Y6wl+lJHIOu2LHaK#%M4EXmr z;|1^d;KHH5@nGR|dpb7l@s<1Np)-;w^?0 zI^)UnvSU}U&>`M@h@ULpp@^N%{gb6ba={owfly$mz^qJ{TK_lnU;6(cDQ2NSC@?7n zq*`y+D_+TK>*#V?YZLvM{$tFwbPiUEiB^g^(Ng@_%S-yq_3YRcG&JkH1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0{ zIuwhy^ZXX+ur5)f6p#XA1x|Ch@cMsE|D*pOleCfoQsAf*u-WQvwd5yNZJm6a*V;yZ vpnJ|Y-Hr30aENkDjB?C{m*a~_%Dm=t?stVlV$c~6I#E9Zu8T|x{I>!>42c+Q diff --git a/src/Mozilla.IoT.WebThing/.DS_Store b/src/Mozilla.IoT.WebThing/.DS_Store deleted file mode 100644 index 2ba683d80b9e74141fe72822cf858a8778b27b30..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKO-sW-5Pe&FsCem7@RC1ZgW?Yer5*(J(yCW&Ev=&xeC$r%hHMrSK%`l>6fCc6#7(bbp{p;nTEI7jiU99LS8UKnD_w-M( zWPeC&iVok3lA7X<<013KS}?}e%rz$G?3OcQv_y>PFS+`XRkT<^PV0&|pl_3Vfpd=E zv{s3>T2HmsF;^{Eamvg>^M3lQ>7SUnjm-R#+g*HBye4PB8Q3ERcxH<L1`xtCZo|A3bP zzu`Hzc*GVBp1JpmCq^1Z_ITUvAKGo#wMtJ*1yX@jAQeajQh^f`h~hcDIDwO?sX!|5 zKNQgKL!m3yz~0e59UQC$AWj%IXFo#z)zhg8QLQ-M@qsKB`|XIlSn=s)!TLsG6%fmGm6DIk;OX1U;%qPC7M xr?s}w@93Y#TubL*rI=`?my`#|?H##wY1XP!_RNyxhI0CmT8Grx) diff --git a/test/Mozilla.IoT.WebThing.Test/.DS_Store b/test/Mozilla.IoT.WebThing.Test/.DS_Store deleted file mode 100644 index 5008ddfcf53c02e82d7eee2e57c38e5672ef89f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0 Date: Wed, 12 Feb 2020 21:56:52 +0000 Subject: [PATCH 2/3] Improve docs and add sample --- .DS_Store | Bin 6148 -> 0 bytes .gitignore | 3 +- Mozzila.IoT.WebThing.sln | 25 +++- README.md | 123 +++++++++--------- sample/MultiThing/MultiThing.csproj | 12 ++ sample/MultiThing/Program.cs | 26 ++++ sample/MultiThing/Startup.cs | 51 ++++++++ .../MultiThing/Things/ExampleDimmableLight.cs | 68 ++++++++++ .../Things/FakeGpioHumiditySensor.cs | 47 +++++++ .../MultiThing/appsettings.Development.json | 9 ++ sample/MultiThing/appsettings.json | 10 ++ sample/SampleThing/Things/LampThing.cs | 41 +++--- 12 files changed, 319 insertions(+), 96 deletions(-) delete mode 100644 .DS_Store create mode 100644 sample/MultiThing/MultiThing.csproj create mode 100644 sample/MultiThing/Program.cs create mode 100644 sample/MultiThing/Startup.cs create mode 100644 sample/MultiThing/Things/ExampleDimmableLight.cs create mode 100644 sample/MultiThing/Things/FakeGpioHumiditySensor.cs create mode 100644 sample/MultiThing/appsettings.Development.json create mode 100644 sample/MultiThing/appsettings.json diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 29b1fd470ca5bb8cee843c8ca51c1a3752460b1c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHK%WA_w3>-C13G|XP0_}LU znqBW4EDM0FuJseJ1hAkh;!B6I>AL#J?mRLlitSO?=Y6wl+lJHIOu2LHaK#%M4EXmr z;|1^d;KHH5@nGR|dpb7l@s<1Np)-;w^?0 zI^)UnvSU}U&>`M@h@ULpp@^N%{gb6ba={owfly$mz^qJ{TK_lnU;6(cDQ2NSC@?7n zq*`y+D_+TK>*#V?YZLvM{$tFwbPiUEiB^g^(Ng@_%S-yq_3YRcG&Jk "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 have a `Property` object which holds the actual state and also a method to turn the light on/off. For our purposes, we just want to log the new state if the light is switched on/off. +The **`on`** property reports and sets the on/off state of the light. For this, we need to create a new property in Thing. For our purposes, we just want to log the new state if the light is switched on/off. ```csharp -var onDescription = new Dictionary +public class LampThing : Thing { - ["@type"] = "OnOffProperty", - ["title"] = "On/Off", - ["type"] = "boolean", - ["description"] = "Whether the lamp is turned on" -}; - -var property = new Property(light, "on", true, onDescription); -property.ValuedChanged += (sender, value) => -{ - Console.WriteLine($"On-State is now {value}"); -}; - -light.AddProperty(property); + ... + [ThingProperty(Type = new []{ "OnOffProperty" }, Title = "On/Off", + Description = "Whether the lamp is turned on")] + public bool On { get; set; } +} ``` 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. ```csharp -var brightnessDescription = new Dictionary +public class LampThing : Thing { - ["@type"] = "BrightnessProperty", - ["title"] = "Brightness", - ["type"] = "integer", - ["description"] = "The level of light from 0-100", - ["minimum"] = 0, - ["maximum"] = 100, - ["unit"] = "percent" -}; - -var level = new Property(light, "level", true, onDescription); -level.ValuedChanged += (sender, value) => -{ - Console.WriteLine($"Brightness is now {value}"); -}; - -light.AddProperty(level); + ... + + 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}"); + } + } +} ``` Now we can add our newly created thing and add Thing middleware to Asp Net Core: @@ -86,13 +85,15 @@ 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.AddThing(option => option.IsSingleThing = true); + services.AddThings() + .AddThing(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { - app.UseEndpoints(config => { - config.MapThing(light); + app.UseEndpoints(endpoints => + { + endpoints.MapThings(); }); } ``` @@ -108,33 +109,29 @@ A [`MultiLevelSensor`](https://iot.mozilla.org/schemas/#MultiLevelSensor) (a sen First we create a new Thing: ```csharp -var sensor = new Thing +public class Humidity : Thing { - Name = "My Humidity Sensor", - Type = new [] { "MultiLevelSensor" }, - Description = "A web connected humidity sensor" -}; + 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. - ```csharp - var levelDescription = new Dictionary - { - ["@type"] = "LevelProperty", - ["title"] = "Humidity", - ["type"] = "number", - ["description"] = "The current humidity in %", - ["minimum"] = 0, - ["maximum"] = 100, - ["unit"] = "percent", - ["readOnly"] = true - }; - - sensor.AddProperty(new Property(sensor, "level", 0, levelDescription)); - ``` +```csharp +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. @@ -143,8 +140,6 @@ Now we have a sensor that constantly reports 0%. To make it usable, we need a th Task.Factory.StartNew(async () => { await Task.Delay(3_000); - await level.NotifyOfExternalUpdate(ReadFromGPIO()); + await Level = ReadFromGPIO(); }); -``` - -This will update our `Value` object with the sensor readings via the `this.level.NotifyOfExternalUpdate(ReadFromGPIO());` call. The `Value` object now notifies the property and the thing that the value has changed, which in turn notifies all websocket listeners. +``` \ No newline at end of file diff --git a/sample/MultiThing/MultiThing.csproj b/sample/MultiThing/MultiThing.csproj new file mode 100644 index 0000000..ba34a27 --- /dev/null +++ b/sample/MultiThing/MultiThing.csproj @@ -0,0 +1,12 @@ + + + + netcoreapp3.1 + + + + + + + + diff --git a/sample/MultiThing/Program.cs b/sample/MultiThing/Program.cs new file mode 100644 index 0000000..2c918f7 --- /dev/null +++ b/sample/MultiThing/Program.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace MultiThing +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/sample/MultiThing/Startup.cs b/sample/MultiThing/Startup.cs new file mode 100644 index 0000000..bb44ca6 --- /dev/null +++ b/sample/MultiThing/Startup.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.HttpsPolicy; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using MultiThing.Things; + +namespace MultiThing +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddThings() + .AddThing() + .AddThing(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapThings(); + }); + } + } +} diff --git a/sample/MultiThing/Things/ExampleDimmableLight.cs b/sample/MultiThing/Things/ExampleDimmableLight.cs new file mode 100644 index 0000000..f9e4549 --- /dev/null +++ b/sample/MultiThing/Things/ExampleDimmableLight.cs @@ -0,0 +1,68 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Mozilla.IoT.WebThing; +using Mozilla.IoT.WebThing.Attributes; + +namespace MultiThing.Things +{ + public class ExampleDimmableLight : Thing + { + public override string Name => "my-lamp-1234"; + + public override string? Title => "My Lamp"; + + public override string[]? Type { get; } = new[] {"OnOffSwitch", "Light"}; + + public override string? Description => "A web connected lamp"; + + private bool _on = true; + + [ThingProperty(Title = "On/Off", Type = new []{ "OnOffProperty" }, Description = "Whether the lamp is turned on")] + public bool On + { + get => _on; + set + { + _on = value; + + Console.WriteLine($"On-State is now {_on}"); + } + } + + private int _brightness = 50; + + [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 {_brightness}"); + } + } + + + [ThingAction(Title = "Fade", Description = "Fade the lamp to a given level")] + public async Task Fade( + [ThingParameter(Minimum = 0, Maximum = 100, Unit = "percent")]int brightness, + [ThingParameter(Minimum = 1, Unit = "milliseconds")]int duration, + [FromServices]ILogger logger) + { + await Task.Delay(duration); + + logger.LogInformation("Going to set Brightness to {brightness}", brightness); + Brightness = brightness; + + logger.LogInformation("Going to send event OverheatedEvent"); + OverheatedEvent?.Invoke(this, 102); + } + + [ThingEvent(Description = "The lamp has exceeded its safe operating temperature", Unit = "degree celsius")] + public event EventHandler OverheatedEvent; + } +} diff --git a/sample/MultiThing/Things/FakeGpioHumiditySensor.cs b/sample/MultiThing/Things/FakeGpioHumiditySensor.cs new file mode 100644 index 0000000..646020a --- /dev/null +++ b/sample/MultiThing/Things/FakeGpioHumiditySensor.cs @@ -0,0 +1,47 @@ +using System; +using System.Threading.Tasks; +using Mozilla.IoT.WebThing; +using Mozilla.IoT.WebThing.Attributes; + +namespace MultiThing.Things +{ + public class FakeGpioHumiditySensor : Thing + { + private readonly Random _random; + public FakeGpioHumiditySensor() + { + _random = new Random(); + Task.Factory.StartNew(() => + { + while (true) + { + Task.Delay(3_000).GetAwaiter().GetResult(); + var newLevel = ReadFromGPIO(); + Console.WriteLine("setting new humidity level: {0}", newLevel); + Level = newLevel; + } + }, TaskCreationOptions.LongRunning); + } + public override string Name => "my-humidity-sensor-1234"; + + public override string? Title => "My Humidity Sensor"; + + public override string[]? Type { get; } = new[] {"MultiLevelSensor"}; + + public override string? Description => "A web connected humidity sensor"; + + + [ThingProperty(Type = new []{"LevelProperty"}, Title = "Humidity", Description = "The current humidity in %", + Minimum = 0, Maximum = 100, Unit = "percent")] + public double Level { get; private set; } + + /// + /// Mimic an actual sensor updating its reading every couple seconds. + /// + /// + private double ReadFromGPIO() { + return Math.Abs(70.0d * _random.Next() * (-0.5 + _random.Next())); + } + + } +} diff --git a/sample/MultiThing/appsettings.Development.json b/sample/MultiThing/appsettings.Development.json new file mode 100644 index 0000000..dba68eb --- /dev/null +++ b/sample/MultiThing/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/sample/MultiThing/appsettings.json b/sample/MultiThing/appsettings.json new file mode 100644 index 0000000..81ff877 --- /dev/null +++ b/sample/MultiThing/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/sample/SampleThing/Things/LampThing.cs b/sample/SampleThing/Things/LampThing.cs index 6f9fe36..3ea6cb8 100644 --- a/sample/SampleThing/Things/LampThing.cs +++ b/sample/SampleThing/Things/LampThing.cs @@ -1,4 +1,7 @@ using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using Mozilla.IoT.WebThing; using Mozilla.IoT.WebThing.Attributes; @@ -6,26 +9,7 @@ namespace SampleThing.Things { public class LampThing : Thing { - public LampThing() - { - // Task.Factory.StartNew(() => - // { - // while (true) - // { - // Task.Delay(3_000).GetAwaiter().GetResult(); - // var @event = Overheated; - // try - // { - // @event?.Invoke(this, 10); - // } - // catch (Exception e) - // { - // Console.WriteLine(e); - // } - // } - // }); - } - public override string Name => "Lamp"; + 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" }; @@ -37,7 +21,7 @@ public LampThing() Description = "The level of light from 0-100", Minimum = 0, Maximum = 100)] public int Brightness { get; set; } - [ThingEvent(Title = "Overheated", + [ThingEvent(Title = "Overheated", Unit = "degree celsius", Type = new [] {"OverheatedEvent"}, Description = "The lamp has exceeded its safe operating temperature")] public event EventHandler Overheated; @@ -45,11 +29,18 @@ public LampThing() [ThingAction(Name = "fade", Title = "Fade", Type = new []{"FadeAction"}, Description = "Fade the lamp to a given level")] - public void Fade( - [ThingParameter(Minimum = 0, Maximum = 100)]int level, - [ThingParameter(Minimum = 0, Unit = "milliseconds")]int duration) + public async Task Fade( + [ThingParameter(Minimum = 0, Maximum = 100, Unit = "percent")]int brightness, + [ThingParameter(Minimum = 1, Unit = "milliseconds")]int duration, + [FromServices]ILogger logger) { - Console.WriteLine("Fade executed...."); + await Task.Delay(duration); + + logger.LogInformation("Going to set Brightness to {brightness}", brightness); + Brightness = brightness; + + logger.LogInformation("Going to send event Overheated"); + Overheated?.Invoke(this, 102); } } } From 9b8bc9c7a3169c57fb45b7e61a0675e3fbb485e4 Mon Sep 17 00:00:00 2001 From: Rafael Lillo Date: Wed, 12 Feb 2020 22:00:04 +0000 Subject: [PATCH 3/3] Add limitation docs --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 421dcae..d245bfd 100644 --- a/README.md +++ b/README.md @@ -142,4 +142,8 @@ Task.Factory.StartNew(async () => { await Task.Delay(3_000); await Level = ReadFromGPIO(); }); -``` \ No newline at end of file +``` + +## Limitation + +Current version, 2.0.0-previewX, Websocket is not working \ No newline at end of file