diff --git a/WifiPlug.API.sln b/WifiPlug.API.sln
index 3319052..f941467 100644
--- a/WifiPlug.API.sln
+++ b/WifiPlug.API.sln
@@ -16,6 +16,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
README.md = README.md
EndProjectSection
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example.EventCli", "samples\Example.EventCli\Example.EventCli.csproj", "{B2323C21-8EB7-4683-96EC-E3D699C391E9}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -34,6 +36,10 @@ Global
{46209885-F892-42A7-A857-82FD7EBF178A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{46209885-F892-42A7-A857-82FD7EBF178A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{46209885-F892-42A7-A857-82FD7EBF178A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B2323C21-8EB7-4683-96EC-E3D699C391E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B2323C21-8EB7-4683-96EC-E3D699C391E9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B2323C21-8EB7-4683-96EC-E3D699C391E9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B2323C21-8EB7-4683-96EC-E3D699C391E9}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -41,6 +47,7 @@ Global
GlobalSection(NestedProjects) = preSolution
{3A580E5E-A795-42D9-A3B1-DCAA9C7807C7} = {6873EB4E-6281-4CB1-8C41-05F2A1AF1EDA}
{46209885-F892-42A7-A857-82FD7EBF178A} = {6873EB4E-6281-4CB1-8C41-05F2A1AF1EDA}
+ {B2323C21-8EB7-4683-96EC-E3D699C391E9} = {6873EB4E-6281-4CB1-8C41-05F2A1AF1EDA}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {8234EEE7-61B4-403F-8C60-65A019D95FEA}
diff --git a/samples/Example.EventCli/Example.EventCli.csproj b/samples/Example.EventCli/Example.EventCli.csproj
new file mode 100644
index 0000000..196fcf9
--- /dev/null
+++ b/samples/Example.EventCli/Example.EventCli.csproj
@@ -0,0 +1,12 @@
+
+
+
+ Exe
+ netcoreapp2.1
+
+
+
+
+
+
+
diff --git a/samples/Example.EventCli/Program.cs b/samples/Example.EventCli/Program.cs
new file mode 100644
index 0000000..9646cec
--- /dev/null
+++ b/samples/Example.EventCli/Program.cs
@@ -0,0 +1,13 @@
+using System;
+using WifiPlug.Api;
+
+namespace Example.EventCli
+{
+ class Program
+ {
+ static void Main(string[] args)
+ {
+ //EventClient eventClient = new EventClient("ws://localhost", "devkey", "devsecret");
+ }
+ }
+}
diff --git a/src/WifiPlug.Api/ApiClient.cs b/src/WifiPlug.Api/ApiClient.cs
index 8ae8299..fe29e6f 100644
--- a/src/WifiPlug.Api/ApiClient.cs
+++ b/src/WifiPlug.Api/ApiClient.cs
@@ -20,7 +20,7 @@ namespace WifiPlug.Api
public class ApiClient : IApiClient
{
#region Constants
- internal const string API_URL = "https://api.wifiplug.co.uk/v1.0/";
+ internal const string ApiUrl = "https://api.wifiplug.co.uk/v1.0/";
#endregion
#region Fields
@@ -428,7 +428,7 @@ public ApiClient() : this(null) { }
///
/// The API key.
/// The API secret.
- public ApiClient(string apiKey, string apiSecret) : this(API_URL) {
+ public ApiClient(string apiKey, string apiSecret) : this(ApiUrl) {
if (apiKey == null)
throw new ArgumentNullException(nameof(apiKey));
else if (apiSecret == null)
@@ -450,7 +450,7 @@ public ApiClient(string apiKey, string apiSecret) : this(API_URL) {
public ApiClient(string apiUrl) {
// setup client
_client = new HttpClient();
- _client.BaseAddress = new Uri(apiUrl == null ? API_URL : apiUrl);
+ _client.BaseAddress = new Uri(apiUrl == null ? ApiUrl : apiUrl);
_client.DefaultRequestHeaders.Add("X-API-Client", "api-client-net/1.0");
// initialize operations
diff --git a/src/WifiPlug.Api/Authentication/OAuth2Authentication.cs b/src/WifiPlug.Api/Authentication/OAuth2Authentication.cs
new file mode 100644
index 0000000..1b63e66
--- /dev/null
+++ b/src/WifiPlug.Api/Authentication/OAuth2Authentication.cs
@@ -0,0 +1,119 @@
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WifiPlug.Api.Authentication
+{
+ ///
+ /// Provides full OAuth2 authentication.
+ ///
+ sealed class OAuth2Authentication : BearerAuthentication
+ {
+ #region Constants
+ internal const string TokenUrl = "https://account.wifiplug.co.uk/oauth2/token";
+ #endregion
+
+ #region Fields
+ private Uri _tokenUri;
+ private string _refreshToken;
+ #endregion
+
+ #region Methods
+ ///
+ /// Applies bearer authentication to an outgoing request.
+ ///
+ /// The request.
+ public override void Apply(HttpRequestMessage request) {
+ request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _bearerToken);
+ }
+
+ ///
+ /// Deserializes the bearer authentication from a stream.
+ ///
+ /// The stream.
+ public override void Deserialize(Stream stream) {
+ // deserialize bearer token
+ base.Deserialize(stream);
+
+ // read refresh token
+ BinaryReader reader = new BinaryReader(stream, Encoding.UTF8, true);
+
+
+ }
+
+ ///
+ /// Serializes the bearer authentication to a stream.
+ ///
+ /// The stream.
+ public override void Serialize(Stream stream) {
+ // serialize bearer token
+ base.Serialize(stream);
+
+ // write refresh token
+ BinaryWriter writer = new BinaryWriter(stream, Encoding.UTF8, true);
+ }
+
+ ///
+ /// Refreshes the token.
+ ///
+ /// The client.
+ ///
+ public override Task ReauthorizeAsync(ApiClient client) {
+ // create refresh client
+ HttpClient refreshClient = new HttpClient();
+ refreshClient.BaseAddress = _tokenUri;
+
+ // make request
+ //refreshClient.PostAsync("/")
+
+ return Task.FromResult(false);
+ }
+ #endregion
+
+ #region Entities
+ class RefreshAccessTokenEntity
+ {
+ [JsonProperty("grant_type")]
+ public string GrantType { get; } = "refresh_token";
+
+ [JsonProperty("refresh_token")]
+ public string RefreshToken { get; set; }
+ }
+ #endregion
+
+ #region Constructors
+ ///
+ /// Creates an empty oAuth2 authentication object.
+ ///
+ public OAuth2Authentication()
+ : base() {
+ }
+
+ ///
+ /// Creates a new oAuth2 authentication object from an access and refresh token.
+ ///
+ /// The access token.
+ /// The refresh token.
+ public OAuth2Authentication(string accessToken, string refreshToken)
+ : this(null, accessToken, refreshToken) {
+ _refreshToken = refreshToken;
+ }
+
+ ///
+ /// Creates a new oAuth2 authentication object from an access and refresh token.
+ ///
+ /// The optional token URI.
+ /// The access token.
+ /// The refresh token.
+ public OAuth2Authentication(string tokenUri, string accessToken, string refreshToken) {
+ _tokenUri = tokenUri == null ? new Uri(TokenUrl) : new Uri(tokenUri);
+ _refreshToken = refreshToken;
+ }
+ #endregion
+ }
+}
diff --git a/src/WifiPlug.Api/Entities/DeviceAddEntity.cs b/src/WifiPlug.Api/Entities/DeviceAddEntity.cs
new file mode 100644
index 0000000..cf92518
--- /dev/null
+++ b/src/WifiPlug.Api/Entities/DeviceAddEntity.cs
@@ -0,0 +1,43 @@
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace WifiPlug.Api.Entities
+{
+ ///
+ /// Represents a request to add a device, this is a legacy request and not supported by most API keys.
+ ///
+ public class DeviceAddEntity
+ {
+ ///
+ /// Gets or sets the name.
+ ///
+ [JsonProperty(PropertyName = "name")]
+ public string Name { get; set; }
+
+ ///
+ /// Gets or sets the MAC address.
+ ///
+ [JsonProperty(PropertyName = "mac_address")]
+ public string MacAddress { get; set; }
+
+ ///
+ /// Gets or sets the type code.
+ ///
+ [JsonProperty(PropertyName = "type_code")]
+ public int TypeCode { get; set; }
+
+ ///
+ /// Gets or sets the variant.
+ ///
+ [JsonProperty(PropertyName = "variant")]
+ public string Variant { get; set; }
+
+ ///
+ /// Gets or sets the firmware version.
+ ///
+ [JsonProperty(PropertyName = "firmware_version")]
+ public string FirmwareVersion { get; set; }
+ }
+}
diff --git a/src/WifiPlug.Api/Entities/GroupTimerAddEntity.cs b/src/WifiPlug.Api/Entities/GroupTimerAddEntity.cs
index 9f2d752..c64255c 100644
--- a/src/WifiPlug.Api/Entities/GroupTimerAddEntity.cs
+++ b/src/WifiPlug.Api/Entities/GroupTimerAddEntity.cs
@@ -22,6 +22,7 @@ public class GroupTimerAddEntity
/// Gets or sets the days the timer will repeat on, if any.
///
[JsonProperty(PropertyName = "repeats", DefaultValueHandling = DefaultValueHandling.Ignore)]
+ [JsonConverter(typeof(TimerRepetitionConverter))]
public TimerRepetition Repeats { get; set; }
///
diff --git a/src/WifiPlug.Api/Event.cs b/src/WifiPlug.Api/Event.cs
new file mode 100644
index 0000000..dd28f5d
--- /dev/null
+++ b/src/WifiPlug.Api/Event.cs
@@ -0,0 +1,24 @@
+using Newtonsoft.Json.Linq;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace WifiPlug.Api
+{
+ ///
+ /// Represents a received event.
+ ///
+ class Event
+ {
+ #region Properties
+ public string Name { get; private set; }
+ public string ResourceType { get; private set; }
+ public string Resource { get; private set; }
+ public JObject Payload { get; private set; }
+ #endregion
+
+ #region Constructors
+
+ #endregion
+ }
+}
diff --git a/src/WifiPlug.Api/EventClient.cs b/src/WifiPlug.Api/EventClient.cs
new file mode 100644
index 0000000..f4599cc
--- /dev/null
+++ b/src/WifiPlug.Api/EventClient.cs
@@ -0,0 +1,100 @@
+using System;
+using System.Collections.Generic;
+using System.Net.WebSockets;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace WifiPlug.Api
+{
+ ///
+ /// Provides access to the WIFIPLUG event subscription API.
+ ///
+ class EventClient : IObservable
+ {
+ #region Constants
+ internal const string API_URL = "wss://event.wifiplug.co.uk/v1.0";
+ #endregion
+
+ #region Fields
+ private string _apiKey = null;
+ private string _apiSecret = null;
+ private ClientWebSocket _client = null;
+ private Uri _uri = null;
+ #endregion
+
+ #region Methods
+ ///
+ /// Subscribes to the provided selector.
+ ///
+ /// The subscription selector.
+ /// The cancellation token.
+ /// await SubscribeAsync("device/*/*);
+ ///
+ public async Task SubscribeAsync(string selector, CancellationToken cancellationToken = default(CancellationToken)) {
+ // connect if not open
+ if (_client.State != WebSocketState.Open)
+ await _client.ConnectAsync(_uri, cancellationToken).ConfigureAwait(false);
+
+ // throw if cancelled
+ cancellationToken.ThrowIfCancellationRequested();
+
+ // subscribe
+ //_client.SendAsync(new ArraySegment());
+ }
+
+ ///
+ /// Unsubscribes the provided selector, you can only unsubscribe the exact selector you subscribed previously.
+ ///
+ /// The selector.
+ /// The cancellation token.
+ ///
+ public async Task UnsubscribeAsync(string selector, CancellationToken cancellationToken = default(CancellationToken)) {
+ // connect if not open
+ if (_client.State != WebSocketState.Open)
+ await _client.ConnectAsync(_uri, cancellationToken).ConfigureAwait(false);
+
+ // throw if cancelled
+ cancellationToken.ThrowIfCancellationRequested();
+
+ // unsubscribe
+ }
+
+ ///
+ /// Closes the underlying event streaming client gracefully.
+ ///
+ /// The cancellation token.
+ ///
+ public Task CloseAsync(CancellationToken cancellationToken = default(CancellationToken)) {
+ return _client.CloseAsync(WebSocketCloseStatus.NormalClosure, "Client requested closure", cancellationToken);
+ }
+ #endregion
+
+ public IDisposable Subscribe(IObserver observer) {
+ throw new NotImplementedException();
+ }
+
+ #region Constructors
+ ///
+ /// Creates a new event client and configures the provided event URL and api credentials.
+ ///
+ /// Your API credentials must allow event streaming.
+ /// The API key.
+ /// The API secret.
+ public EventClient(string apiKey, string apiSecret)
+ : this(null, apiKey, apiSecret){
+ }
+
+ ///
+ /// Creates a new event client and configures the provided event URL and api credentials.
+ ///
+ /// Your API credentials must allow event streaming.
+ /// The event URL.
+ /// The API key.
+ /// The API secret.
+ public EventClient(string eventUrl, string apiKey, string apiSecret) {
+ _client = new ClientWebSocket();
+ }
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/src/WifiPlug.Api/Operations/DeviceOperations.cs b/src/WifiPlug.Api/Operations/DeviceOperations.cs
index b3bb2bb..74f17ae 100644
--- a/src/WifiPlug.Api/Operations/DeviceOperations.cs
+++ b/src/WifiPlug.Api/Operations/DeviceOperations.cs
@@ -23,6 +23,17 @@ public class DeviceOperations : IDeviceOperations
///
protected ApiClient _client;
+ ///
+ /// Adds a device.
+ ///
+ /// The group entity.
+ /// The cancellation token.
+ /// This operation is internal and won't work with normal API keys. Nor is it stable.
+ /// The added device.
+ public Task AddDeviceAsync(DeviceAddEntity entity, CancellationToken cancellationToken = default(CancellationToken)) {
+ return _client.RequestJsonSerializedAsync(HttpMethod.Post, "device/add", entity, cancellationToken);
+ }
+
///
/// Gets a live energy reading from the device service, if applicable.
///
diff --git a/src/WifiPlug.Api/Operations/IDeviceOperations.cs b/src/WifiPlug.Api/Operations/IDeviceOperations.cs
index e6ccf75..bfb16e6 100644
--- a/src/WifiPlug.Api/Operations/IDeviceOperations.cs
+++ b/src/WifiPlug.Api/Operations/IDeviceOperations.cs
@@ -13,6 +13,15 @@ namespace WifiPlug.Api.Operations
///
public interface IDeviceOperations
{
+ ///
+ /// Adds a device.
+ ///
+ /// The group entity.
+ /// The cancellation token.
+ /// This operation is internal and won't work with normal API keys. Nor is it stable.
+ /// The added device.
+ Task AddDeviceAsync(DeviceAddEntity entity, CancellationToken cancellationToken = default(CancellationToken));
+
///
/// Gets a live energy reading from the device service, if applicable.
///
diff --git a/src/WifiPlug.Api/WifiPlug.Api.csproj b/src/WifiPlug.Api/WifiPlug.Api.csproj
index 723c2ad..e077cf8 100644
--- a/src/WifiPlug.Api/WifiPlug.Api.csproj
+++ b/src/WifiPlug.Api/WifiPlug.Api.csproj
@@ -11,12 +11,12 @@
https://wifiplug.co.uk
https://github.com/wifiplug/api-client-net
git
- 1.0.9.0
- 1.0.9.0
+ 1.0.10.0
+ 1.0.10.0
true
https://s3.eu-west-2.amazonaws.com/wifiplug-pub/nuget-icons/wifiplug.png
https://github.com/wifiplug/api-client-net/blob/master/LICENSE
- 1.0.9
+ 1.0.10
@@ -29,6 +29,7 @@
+