diff --git a/src/HueApi.Tests/CameraMotionTests.cs b/src/HueApi.Tests/CameraMotionTests.cs new file mode 100644 index 0000000..42082ad --- /dev/null +++ b/src/HueApi.Tests/CameraMotionTests.cs @@ -0,0 +1,65 @@ +using HueApi.Models.Requests; +using Microsoft.Extensions.Configuration; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Linq; +using System.Threading.Tasks; + +namespace HueApi.Tests +{ + [TestClass] + public class CameraMotionTests + { + private readonly LocalHueApi localHueClient; + + public CameraMotionTests() + { + var builder = new ConfigurationBuilder().AddUserSecrets(); + var config = builder.Build(); + + localHueClient = new LocalHueApi(config["ip"], key: config["key"]); + } + + [TestMethod] + public async Task Get() + { + var result = await localHueClient.GetCameraMotionsAsync(); + + Assert.IsNotNull(result); + Assert.IsFalse(result.HasErrors); + } + + [TestMethod] + public async Task GetById() + { + var all = await localHueClient.GetCameraMotionsAsync(); + var id = all.Data.First().Id; + + var result = await localHueClient.GetCameraMotionAsync(id); + + Assert.IsNotNull(result); + Assert.IsFalse(result.HasErrors); + + Assert.IsTrue(result.Data.Count == 1); + + } + + + [TestMethod] + public async Task PutById() + { + var all = await localHueClient.GetCameraMotionsAsync(); + var id = all.Data.Last().Id; + + var req = new UpdateSensitivitySensorRequest(); + var result = await localHueClient.UpdateCameraMotionAsync(id, req); + + Assert.IsNotNull(result); + Assert.IsFalse(result.HasErrors); + + Assert.IsTrue(result.Data.Count == 1); + Assert.AreEqual(id, result.Data.First().Rid); + + } + } +} diff --git a/src/HueApi.Tests/ContactSensorTests.cs b/src/HueApi.Tests/ContactSensorTests.cs new file mode 100644 index 0000000..2ced525 --- /dev/null +++ b/src/HueApi.Tests/ContactSensorTests.cs @@ -0,0 +1,65 @@ +using HueApi.Models.Requests; +using Microsoft.Extensions.Configuration; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Linq; +using System.Threading.Tasks; + +namespace HueApi.Tests +{ + [TestClass] + public class ContactSensorTests + { + private readonly LocalHueApi localHueClient; + + public ContactSensorTests() + { + var builder = new ConfigurationBuilder().AddUserSecrets(); + var config = builder.Build(); + + localHueClient = new LocalHueApi(config["ip"], key: config["key"]); + } + + [TestMethod] + public async Task Get() + { + var result = await localHueClient.GetContactSensorsAsync(); + + Assert.IsNotNull(result); + Assert.IsFalse(result.HasErrors); + } + + [TestMethod] + public async Task GetById() + { + var all = await localHueClient.GetContactSensorsAsync(); + var id = all.Data.First().Id; + + var result = await localHueClient.GetContactSensorAsync(id); + + Assert.IsNotNull(result); + Assert.IsFalse(result.HasErrors); + + Assert.IsTrue(result.Data.Count == 1); + + } + + + [TestMethod] + public async Task PutById() + { + var all = await localHueClient.GetContactSensorsAsync(); + var id = all.Data.Last().Id; + + var req = new UpdateSensitivitySensorRequest(); + var result = await localHueClient.UpdateContactSensorsAsync(id, req); + + Assert.IsNotNull(result); + Assert.IsFalse(result.HasErrors); + + Assert.IsTrue(result.Data.Count == 1); + Assert.AreEqual(id, result.Data.First().Rid); + + } + } +} diff --git a/src/HueApi.Tests/MotionTests.cs b/src/HueApi.Tests/MotionTests.cs index e615989..36e6da6 100644 --- a/src/HueApi.Tests/MotionTests.cs +++ b/src/HueApi.Tests/MotionTests.cs @@ -51,7 +51,7 @@ public async Task PutById() var all = await localHueClient.GetMotionsAsync(); var id = all.Data.Last().Id; - UpdateSensorRequest req = new UpdateSensorRequest(); + var req = new UpdateSensitivitySensorRequest(); var result = await localHueClient.UpdateMotionAsync(id, req); Assert.IsNotNull(result); diff --git a/src/HueApi.Tests/TamperSensorTests.cs b/src/HueApi.Tests/TamperSensorTests.cs new file mode 100644 index 0000000..0f7ae1f --- /dev/null +++ b/src/HueApi.Tests/TamperSensorTests.cs @@ -0,0 +1,65 @@ +using HueApi.Models.Requests; +using Microsoft.Extensions.Configuration; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Linq; +using System.Threading.Tasks; + +namespace HueApi.Tests +{ + [TestClass] + public class TamperSensorTests + { + private readonly LocalHueApi localHueClient; + + public TamperSensorTests() + { + var builder = new ConfigurationBuilder().AddUserSecrets(); + var config = builder.Build(); + + localHueClient = new LocalHueApi(config["ip"], key: config["key"]); + } + + [TestMethod] + public async Task Get() + { + var result = await localHueClient.GetTamperSensorsAsync(); + + Assert.IsNotNull(result); + Assert.IsFalse(result.HasErrors); + } + + [TestMethod] + public async Task GetById() + { + var all = await localHueClient.GetTamperSensorsAsync(); + var id = all.Data.First().Id; + + var result = await localHueClient.GetTamperSensorAsync(id); + + Assert.IsNotNull(result); + Assert.IsFalse(result.HasErrors); + + Assert.IsTrue(result.Data.Count == 1); + + } + + + [TestMethod] + public async Task PutById() + { + var all = await localHueClient.GetTamperSensorsAsync(); + var id = all.Data.Last().Id; + + var req = new BaseResourceRequest(); + var result = await localHueClient.UpdateTamperSensorAsync(id, req); + + Assert.IsNotNull(result); + Assert.IsFalse(result.HasErrors); + + Assert.IsTrue(result.Data.Count == 1); + Assert.AreEqual(id, result.Data.First().Rid); + + } + } +} diff --git a/src/HueApi/BaseHueApi.cs b/src/HueApi/BaseHueApi.cs index a99d7a7..03dbc1d 100644 --- a/src/HueApi/BaseHueApi.cs +++ b/src/HueApi/BaseHueApi.cs @@ -18,7 +18,6 @@ public abstract class BaseHueApi protected const string ResourceUrl = "clip/v2/resource"; protected const string LightUrl = $"{ResourceUrl}/light"; protected const string SceneUrl = $"{ResourceUrl}/scene"; - protected const string SmartSceneUrl = $"{ResourceUrl}/smart_scene"; protected const string RoomUrl = $"{ResourceUrl}/room"; protected const string ZoneUrl = $"{ResourceUrl}/zone"; protected const string BridgeHomeUrl = $"{ResourceUrl}/bridge_home"; @@ -30,6 +29,7 @@ public abstract class BaseHueApi protected const string ZgpConnectivityUrl = $"{ResourceUrl}/zgp_connectivity"; protected const string ZigbeeDeviceDiscoveryUrl = $"{ResourceUrl}/zigbee_device_discovery"; protected const string MotionUrl = $"{ResourceUrl}/motion"; + protected const string CameraMotionUrl = $"{ResourceUrl}/camera_motion"; protected const string TemperatureUrl = $"{ResourceUrl}/temperature"; protected const string LightLevelUrl = $"{ResourceUrl}/light_level"; protected const string ButtonUrl = $"{ResourceUrl}/button"; @@ -43,6 +43,11 @@ public abstract class BaseHueApi protected const string HomekitUrl = $"{ResourceUrl}/homekit"; protected const string MatterUrl = $"{ResourceUrl}/matter"; protected const string MatterFabricUrl = $"{ResourceUrl}/matter_fabric"; + //recource + protected const string SmartSceneUrl = $"{ResourceUrl}/smart_scene"; + protected const string ContactUrl = $"{ResourceUrl}/contact"; + protected const string TamperUrl = $"{ResourceUrl}/tamper"; + protected string ResourceIdUrl(string resourceUrl, Guid id) => $"{resourceUrl}/{id}"; @@ -136,7 +141,13 @@ public abstract class BaseHueApi #region Motion public Task> GetMotionsAsync() => HueGetRequestAsync(MotionUrl); public Task> GetMotionAsync(Guid id) => HueGetRequestAsync(ResourceIdUrl(MotionUrl, id)); - public Task UpdateMotionAsync(Guid id, UpdateSensorRequest data) => HuePutRequestAsync(ResourceIdUrl(MotionUrl, id), data); + public Task UpdateMotionAsync(Guid id, UpdateSensitivitySensorRequest data) => HuePutRequestAsync(ResourceIdUrl(MotionUrl, id), data); + #endregion + + #region CameraMotion + public Task> GetCameraMotionsAsync() => HueGetRequestAsync(CameraMotionUrl); + public Task> GetCameraMotionAsync(Guid id) => HueGetRequestAsync(ResourceIdUrl(CameraMotionUrl, id)); + public Task UpdateCameraMotionAsync(Guid id, UpdateSensitivitySensorRequest data) => HuePutRequestAsync(ResourceIdUrl(CameraMotionUrl, id), data); #endregion #region Temperature @@ -227,7 +238,17 @@ public abstract class BaseHueApi public Task DeleteMatterFabricAsync(Guid id) => HueDeleteRequestAsync(ResourceIdUrl(MatterFabricUrl, id)); #endregion + #region Contact + public Task> GetContactSensorsAsync() => HueGetRequestAsync(ContactUrl); + public Task> GetContactSensorAsync(Guid id) => HueGetRequestAsync(ResourceIdUrl(ContactUrl, id)); + public Task UpdateContactSensorsAsync(Guid id, UpdateSensorRequest data) => HuePutRequestAsync(ResourceIdUrl(ContactUrl, id), data); + #endregion + #region Tamper + public Task> GetTamperSensorsAsync() => HueGetRequestAsync(TamperUrl); + public Task> GetTamperSensorAsync(Guid id) => HueGetRequestAsync(ResourceIdUrl(TamperUrl, id)); + public Task UpdateTamperSensorAsync(Guid id, BaseResourceRequest data) => HuePutRequestAsync(ResourceIdUrl(TamperUrl, id), data); + #endregion protected async Task> HueGetRequestAsync(string url) { diff --git a/src/HueApi/HueApi.csproj b/src/HueApi/HueApi.csproj index 1fc1050..c395f8b 100644 --- a/src/HueApi/HueApi.csproj +++ b/src/HueApi/HueApi.csproj @@ -6,7 +6,7 @@ enable enable nullable - 1.5.3 + 1.6.0 Michiel Post For Clip v2 API. Open source library for interaction with the Philips Hue Bridge. Allows you to control your lights from C#. https://github.com/michielpost/Q42.HueApi diff --git a/src/HueApi/Models/ButtonResource.cs b/src/HueApi/Models/ButtonResource.cs index 0a9d686..67afb6d 100644 --- a/src/HueApi/Models/ButtonResource.cs +++ b/src/HueApi/Models/ButtonResource.cs @@ -15,12 +15,27 @@ public class ButtonResource : HueResource public class Button { - [JsonPropertyName("last_event")] - public ButtonLastEvent? LastEvent { get; set; } + [JsonPropertyName("button_report")] + public ButtonReport? ButtonReport { get; set; } + + [JsonPropertyName("repeat_interval")] + public int? RepeatInterval { get; set; } + + [JsonPropertyName("event_values")] + public List? EventValues { get; set; } + } + + public class ButtonReport + { + [JsonPropertyName("updated")] + public DateTimeOffset? Updated { get; set; } + + [JsonPropertyName("event")] + public ButtonEvent? Event { get; set; } } [JsonConverter(typeof(JsonStringEnumConverter))] - public enum ButtonLastEvent + public enum ButtonEvent { initial_press, repeat, short_release, long_release, double_short_release, long_press } diff --git a/src/HueApi/Models/Requests/UpdateSensitivitySensorRequest.cs b/src/HueApi/Models/Requests/UpdateSensitivitySensorRequest.cs new file mode 100644 index 0000000..72935a9 --- /dev/null +++ b/src/HueApi/Models/Requests/UpdateSensitivitySensorRequest.cs @@ -0,0 +1,17 @@ +using HueApi.Models.Sensors; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace HueApi.Models.Requests +{ + public class UpdateSensitivitySensorRequest : UpdateSensorRequest + { + [JsonPropertyName("sensitivity")] + public Sensitivity? Sensitivity { get; set; } + + } +} diff --git a/src/HueApi/Models/Requests/UpdateSensorRequest.cs b/src/HueApi/Models/Requests/UpdateSensorRequest.cs index 0ed1533..16fc684 100644 --- a/src/HueApi/Models/Requests/UpdateSensorRequest.cs +++ b/src/HueApi/Models/Requests/UpdateSensorRequest.cs @@ -1,3 +1,4 @@ +using HueApi.Models.Sensors; using System; using System.Collections.Generic; using System.Linq; @@ -12,6 +13,5 @@ public class UpdateSensorRequest : BaseResourceRequest [JsonPropertyName("enabled")] public bool Enabled { get; set; } - } } diff --git a/src/HueApi/Models/Sensors/CameraMotionResource.cs b/src/HueApi/Models/Sensors/CameraMotionResource.cs new file mode 100644 index 0000000..0625932 --- /dev/null +++ b/src/HueApi/Models/Sensors/CameraMotionResource.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace HueApi.Models.Sensors +{ + public class CameraMotionResource : MotionResource + { + + } +} diff --git a/src/HueApi/Models/Sensors/ContactSensor.cs b/src/HueApi/Models/Sensors/ContactSensor.cs new file mode 100644 index 0000000..f58ffc6 --- /dev/null +++ b/src/HueApi/Models/Sensors/ContactSensor.cs @@ -0,0 +1,29 @@ +using System.Text.Json.Serialization; + +namespace HueApi.Models.Sensors +{ + public class ContactSensor : HueResource + { + [JsonPropertyName("enabled")] + public bool Enabled { get; set; } = default!; + + [JsonPropertyName("contact_report")] + public ContactReport? ContactReport { get; set; } + } + + public class ContactReport + { + [JsonPropertyName("changed")] + public DateTimeOffset Changed { get; set; } + + [JsonPropertyName("state")] + public ContactState State { get; set; } + + } + + [JsonConverter(typeof(JsonStringEnumConverter))] + public enum ContactState + { + contact, no_contact + } +} diff --git a/src/HueApi/Models/Sensors/LightLevel.cs b/src/HueApi/Models/Sensors/LightLevel.cs index cf04734..cdc1c44 100644 --- a/src/HueApi/Models/Sensors/LightLevel.cs +++ b/src/HueApi/Models/Sensors/LightLevel.cs @@ -14,12 +14,23 @@ public class LightLevel : HueResource public class Light { + [JsonPropertyName("light_level_report")] + public LightLevelReport LightLevelReport { get; set; } = default!; + + } + + public class LightLevelReport + { + + [JsonPropertyName("changed")] + public DateTimeOffset Changed { get; set; } + + /// + /// Light level in 10000*log10(lux) +1 measured by sensor. Logarithmic scale used because the human eye adjusts to light levels and small changes at low lux levels are more noticeable than at high lux levels. This allows use of linear scale configuration sliders. + /// [JsonPropertyName("light_level")] public int LightLevel { get; set; } = default!; - [JsonPropertyName("light_level_valid")] - public bool LightLevelValid { get; set; } - public double LuxLevel { get diff --git a/src/HueApi/Models/Sensors/MotionResource.cs b/src/HueApi/Models/Sensors/MotionResource.cs index e2b017b..6635c69 100644 --- a/src/HueApi/Models/Sensors/MotionResource.cs +++ b/src/HueApi/Models/Sensors/MotionResource.cs @@ -14,15 +14,45 @@ public class MotionResource : HueResource [JsonPropertyName("motion")] public Motion Motion { get; set; } = default!; + + [JsonPropertyName("sensitivity")] + public Sensitivity? Sensitivity { get; set; } + } + + public class Sensitivity + { + [JsonPropertyName("status")] + public SensitivityStatus Status { get; set; } = default!; + + [JsonPropertyName("sensitivity")] + public int SensitivityValue { get; set; } + + [JsonPropertyName("sensitivity_max")] + public int? SensitivityMax { get; set; } + } + + [JsonConverter(typeof(JsonStringEnumConverter))] + public enum SensitivityStatus + { + set, changing } public class Motion { + [JsonPropertyName("motion_report")] + public MotionReport? MotionReport { get; set; } + } + + public class MotionReport + { + [JsonPropertyName("changed")] + public DateTimeOffset Changed { get; set; } + + /// + /// true if motion is detected + /// [JsonPropertyName("motion")] - public bool MotionState { get; set; } + public bool Motion { get; set; } - [JsonPropertyName("motion_valid")] - public bool MotionValid { get; set; } - } } diff --git a/src/HueApi/Models/Sensors/TamperSensor.cs b/src/HueApi/Models/Sensors/TamperSensor.cs new file mode 100644 index 0000000..cbaff48 --- /dev/null +++ b/src/HueApi/Models/Sensors/TamperSensor.cs @@ -0,0 +1,29 @@ +using System.Text.Json.Serialization; + +namespace HueApi.Models.Sensors +{ + public class TamperSensor : HueResource + { + [JsonPropertyName("tamper_reports")] + public List TamperReports { get; set; } = new(); + } + + public class TamperReports + { + [JsonPropertyName("changed")] + public DateTimeOffset Changed { get; set; } + + [JsonPropertyName("source")] + public string? Source { get; set; } + + [JsonPropertyName("state")] + public TamperState State { get; set; } + + } + + [JsonConverter(typeof(JsonStringEnumConverter))] + public enum TamperState + { + tampered, not_tampered + } +}