From 977021a7504c43a414c0cc2481937e28dae26bee Mon Sep 17 00:00:00 2001 From: -help Date: Sun, 26 Mar 2017 03:39:37 -0400 Subject: [PATCH 1/7] Converted application to work with latest version of the LIFX HTTP API (V1). Updated setting color and power methods to use new SetState method. Implemented breathe, pulse, and cycle effects. Implemented listing scenes and activating scenes. Implemented setting multiple states. TODO: Could add validate power and update examples in console app. --- LifxHttp/ApiResult.cs | 23 +- LifxHttp/ApiResults.cs | 16 ++ LifxHttp/Enums/Direction.cs | 20 ++ LifxHttp/{ => Enums}/PowerState.cs | 2 +- LifxHttp/Extensions.cs | 16 +- LifxHttp/Helpers/LifxColorConverter.cs | 51 ++++ LifxHttp/ILifxApi.cs | 49 ++-- LifxHttp/ILightTarget.cs | 13 +- LifxHttp/LifxClient.cs | 364 +++++++++++++++++-------- LifxHttp/LifxColor.cs | 28 +- LifxHttp/LifxHttp.csproj | 155 ++++++----- LifxHttp/Light.cs | 172 +++++++++--- LifxHttp/LightCollection.cs | 211 +++++++++----- LifxHttp/LightState.cs | 93 +++++++ LifxHttp/RefitStubs.cs | 44 +-- LifxHttp/Scene.cs | 47 ++++ LifxHttp/Selector.cs | 21 +- LifxHttpSample/Program.cs | 140 ++++++++-- 18 files changed, 1079 insertions(+), 386 deletions(-) create mode 100644 LifxHttp/ApiResults.cs create mode 100644 LifxHttp/Enums/Direction.cs rename LifxHttp/{ => Enums}/PowerState.cs (94%) create mode 100644 LifxHttp/Helpers/LifxColorConverter.cs create mode 100644 LifxHttp/LightState.cs create mode 100644 LifxHttp/Scene.cs diff --git a/LifxHttp/ApiResult.cs b/LifxHttp/ApiResult.cs index ac7cd62..d162f1b 100644 --- a/LifxHttp/ApiResult.cs +++ b/LifxHttp/ApiResult.cs @@ -6,7 +6,17 @@ using System.Threading.Tasks; namespace LifxHttp -{ +{ + /// + /// Operation returned from setting multiple states. Contains selector and state properties. + /// + [JsonObject(MemberSerialization.OptIn)] + public sealed class ApiOperation : LightState + { + [JsonProperty("selector")] + string Selector; + } + /// /// Returned from API actions /// @@ -16,7 +26,16 @@ public sealed class ApiResult internal static bool Successful(ApiResult result) { return result.IsSuccessful; - } + } + + [JsonProperty("operation")] + public ApiOperation Operation { get; private set; } + + /// + /// When setting multiple states, each result contains operation(s) and the results of each operation. + /// + [JsonProperty("results")] + public List Results { get; set; } [JsonProperty("id")] public string Id { get; private set; } diff --git a/LifxHttp/ApiResults.cs b/LifxHttp/ApiResults.cs new file mode 100644 index 0000000..1d85fc6 --- /dev/null +++ b/LifxHttp/ApiResults.cs @@ -0,0 +1,16 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace LifxHttp +{ + [JsonObject(MemberSerialization.OptIn)] + public sealed class ApiResults + { + [JsonProperty("results")] + public List Results { get; set; } + } +} diff --git a/LifxHttp/Enums/Direction.cs b/LifxHttp/Enums/Direction.cs new file mode 100644 index 0000000..0b59d64 --- /dev/null +++ b/LifxHttp/Enums/Direction.cs @@ -0,0 +1,20 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using System.Threading.Tasks; + +namespace LifxHttp.Enums +{ + [JsonConverter(typeof(StringEnumConverter))] + public enum Direction + { + [EnumMember(Value = "forward")] + Forward, + [EnumMember(Value = "backward")] + Backward + } +} diff --git a/LifxHttp/PowerState.cs b/LifxHttp/Enums/PowerState.cs similarity index 94% rename from LifxHttp/PowerState.cs rename to LifxHttp/Enums/PowerState.cs index 2811ecf..07f6db5 100644 --- a/LifxHttp/PowerState.cs +++ b/LifxHttp/Enums/PowerState.cs @@ -7,7 +7,7 @@ using System.Text; using System.Threading.Tasks; -namespace LifxHttp +namespace LifxHttp.Enums { [JsonConverter(typeof(StringEnumConverter))] public enum PowerState diff --git a/LifxHttp/Extensions.cs b/LifxHttp/Extensions.cs index 34c5459..b2196e3 100644 --- a/LifxHttp/Extensions.cs +++ b/LifxHttp/Extensions.cs @@ -16,15 +16,15 @@ public static List AsGroups(this IEnumerable lights) new Dictionary>(); foreach (Light light in lights) { - if (!groups.ContainsKey(light.group)) + if (!groups.ContainsKey(light.Group)) { - groups[light.group] = new List(); + groups[light.Group] = new List(); } - groups[light.group].Add(light); + groups[light.Group].Add(light); } // Grab client from a light LifxClient client = (groups.Count > 0) ? groups.First().Value.First().Client : null; - return groups.Select(entry => new Group(client, entry.Key.id, entry.Key.name, entry.Value)).ToList(); + return groups.Select(entry => new Group(client, entry.Key.Id, entry.Key.Name, entry.Value)).ToList(); } public static List AsLocations(this IEnumerable lights) { @@ -32,15 +32,15 @@ public static List AsLocations(this IEnumerable lights) new Dictionary>(); foreach (Light light in lights) { - if (!groups.ContainsKey(light.location)) + if (!groups.ContainsKey(light.Location)) { - groups[light.location] = new List(); + groups[light.Location] = new List(); } - groups[light.location].Add(light); + groups[light.Location].Add(light); } // Grab client from a light LifxClient client = (groups.Count > 0) ? groups.First().Value.First().Client : null; - return groups.Select(entry => new Location(client, entry.Key.id, entry.Key.name, entry.Value)).ToList(); + return groups.Select(entry => new Location(client, entry.Key.Id, entry.Key.Name, entry.Value)).ToList(); } public static bool IsSuccessful(this IEnumerable results, MatchMode matchMode = MatchMode.Any) diff --git a/LifxHttp/Helpers/LifxColorConverter.cs b/LifxHttp/Helpers/LifxColorConverter.cs new file mode 100644 index 0000000..24241c6 --- /dev/null +++ b/LifxHttp/Helpers/LifxColorConverter.cs @@ -0,0 +1,51 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace LifxHttp.Helpers +{ + public class LifxColorConverter : JsonConverter + { + public override bool CanConvert(Type objectType) + { + //Assume we can convert to anything for now + return true; + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + if (reader.Value is null) + { + var token = JToken.Load(reader); + double? hue = null; + double? saturation = null; + double? brightness = null; + int? kelvin = null; + + if (token["hue"] != null) + hue = (double)token["hue"]; + if (token["saturation"] != null) + saturation = (double)token["saturation"]; + if (token["brightness"] != null) + brightness = (double)token["brightness"]; + if (token["kelvin"] != null) + kelvin = (int)token["kelvin"]; + return new LifxColor.HSBK(hue, saturation, brightness, kelvin); + } + else + { + return serializer.Deserialize(reader); + } + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + //Write to string to get correct color formatting. + writer.WriteValue(value.ToString()); + } + } +} diff --git a/LifxHttp/ILifxApi.cs b/LifxHttp/ILifxApi.cs index 7cb1083..9b7a095 100644 --- a/LifxHttp/ILifxApi.cs +++ b/LifxHttp/ILifxApi.cs @@ -10,30 +10,37 @@ namespace LifxHttp internal interface ILifxApi { [Get("/lights/{selector}")] - Task GetLight([Header("Authorization")] string auth, string selector); + Task> ListLights([Header("Authorization")] string auth, string selector); + + [Get("/scenes")] + Task> ListScenes([Header("Authorization")] string auth); - [Get("/lights/{selector}")] - Task> ListLights([Header("Authorization")] string auth, string selector); - - [Post("/lights/{selector}/toggle")] - Task> TogglePower([Header("Authorization")] string auth, string selector); - - [Post("/lights/{selector}/toggle")] - Task TogglePowerSingle([Header("Authorization")] string auth, string selector); - - [Put("/lights/{selector}/power")] + [Put("/lights/{selector}/state")] [Headers("Content-Type: application/x-www-form-urlencoded")] - Task> SetPower([Header("Authorization")] string auth, string selector, [Body] string args); - - [Put("/lights/{selector}/power")] - [Headers("Content-Type: application/x-www-form-urlencoded")] - Task SetPowerSingle([Header("Authorization")] string auth, string selector, [Body] string args); - - [Put("/lights/{selector}/color")] + Task SetState([Header("Authorization")] string auth, string selector, [Body] string args); + + [Put("/lights/states")] + [Headers("Content-Type: application/json")] + Task SetStates([Header("Authorization")] string auth, [Body] LifxClient.SetStatesSpec args); + + [Put("/scenes/scene_id::{sceneUUID}/activate")] [Headers("Content-Type: application/x-www-form-urlencoded")] - Task> SetColor([Header("Authorization")] string auth, string selector, [Body] string args); - [Put("/lights/{selector}/color")] + Task ActivateScene([Header("Authorization")] string auth, string sceneUUID, [Body] string args); + + [Post("/lights/{selector}/toggle")] [Headers("Content-Type: application/x-www-form-urlencoded")] - Task SetColorSingle([Header("Authorization")] string auth, string selector, [Body] string args); + Task TogglePower([Header("Authorization")] string auth, string selector, [Body] string args); + + [Post("/lights/{selector}/effects/pulse")] + [Headers("Content-Type: application/x-www-form-urlencoded")] + Task PulseEffect([Header("Authorization")] string auth, string selector, [Body] string args); + + [Post("/lights/{selector}/effects/breathe")] + [Headers("Content-Type: application/x-www-form-urlencoded")] + Task BreatheEffect([Header("Authorization")] string auth, string selector, [Body] string args); + + [Post("/lights/{selector}/cycle")] + [Headers("Content-Type: application/json")] + Task Cycle([Header("Authorization")] string auth, string selector, [Body] LifxClient.SetStateSpec args); } } diff --git a/LifxHttp/ILightTarget.cs b/LifxHttp/ILightTarget.cs index 4f6ac68..796e204 100644 --- a/LifxHttp/ILightTarget.cs +++ b/LifxHttp/ILightTarget.cs @@ -1,4 +1,5 @@ -using System; +using LifxHttp.Enums; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -13,8 +14,12 @@ public interface ILightTarget bool IsOn { get; } - Task TogglePower(); - Task SetPower(bool powerState, float duration = LifxClient.DEFAULT_DURATION); - Task SetColor(LifxColor color, float duration = LifxClient.DEFAULT_DURATION, bool powerOn = LifxClient.DEFAULT_POWER_ON); + Task TogglePower(double duration = LifxClient.DEFAULT_DURATION); + Task SetPower(PowerState powerState, double duration = LifxClient.DEFAULT_DURATION); + Task SetColor(LifxColor color, double duration = LifxClient.DEFAULT_DURATION, bool powerOn = LifxClient.DEFAULT_POWER_ON); + Task SetState(PowerState powerState, LifxColor color, double brightness, double duration = LifxClient.DEFAULT_DURATION, double infrared = LifxClient.DEFAULT_INFRARED); + Task PulseEffect(LifxColor color, double period, double cycles, LifxColor fromColor = LifxClient.DEFAULT_FROM_COLOR, bool persist = LifxClient.DEFAULT_PERSIST, bool powerOn = LifxClient.DEFAULT_POWER_ON); + Task BreatheEffect(LifxColor color, double period, double cycles, LifxColor fromColor = LifxClient.DEFAULT_FROM_COLOR, bool persist = LifxClient.DEFAULT_PERSIST, bool powerOn = LifxClient.DEFAULT_POWER_ON, double peak = LifxClient.DEFAULT_PEAK); + Task Cycle(List states, LightState defaults, Direction direction = LifxClient.DEFAULT_DIRECTION); } } diff --git a/LifxHttp/LifxClient.cs b/LifxHttp/LifxClient.cs index d5f508e..184f152 100644 --- a/LifxHttp/LifxClient.cs +++ b/LifxHttp/LifxClient.cs @@ -3,6 +3,8 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using LifxHttp.Enums; +using Newtonsoft.Json; namespace LifxHttp { @@ -11,8 +13,20 @@ namespace LifxHttp /// public class LifxClient { - internal const float DEFAULT_DURATION = 1f; + internal const double DEFAULT_DURATION = 1.0d; internal const bool DEFAULT_POWER_ON = true; + internal const double DEFAULT_INFRARED = 0.0d; + internal const LifxColor DEFAULT_FROM_COLOR = null; + internal const double DEFAULT_PEAK = 0.5d; + internal const bool DEFAULT_PERSIST = false; + internal const Enums.Direction DEFAULT_DIRECTION = Direction.Forward; + internal const double MIN_BRIGHTNESS = 0.0d; + internal const double MAX_BRIGHTNESS = 1.0d; + internal const double MIN_DURATION = 0.0d; + internal const double MAX_DURATION = 3155760000.0d; + internal const double MIN_PEAK = 0.0d; + internal const double MAX_PEAK = 1.0d; + private ILifxApi lifxApi; protected readonly string auth; @@ -23,8 +37,38 @@ public class LifxClient public LifxClient(string token) { auth = "Bearer " + token; - lifxApi = Refit.RestService.For("https://api.lifx.com/v1beta1"); - + lifxApi = Refit.RestService.For("https://api.lifx.com/v1"); + } + + [JsonObject(MemberSerialization.OptIn)] + public class SetStateSpec + { + [JsonProperty("states")] + public List States { get; set; } + [JsonProperty("defaults")] + public LightState Defaults { get; set; } + [JsonProperty("direction")] + public Direction Direction { get; set; } + public SetStateSpec(List states, LightState defaults, Direction direction) + { + States = states; + Defaults = defaults; + Direction = direction; + } + } + + [JsonObject(MemberSerialization.OptIn)] + public class SetStatesSpec + { + [JsonProperty("states")] + public List States { get; set; } + [JsonProperty("defaults")] + public LightState Defaults { get; set; } + public SetStatesSpec(List states, LightState defaults) + { + States = states; + Defaults = defaults; + } } /// @@ -36,26 +80,15 @@ public async Task> ListLights(Selector selector = null) { if (selector == null) { selector = Selector.All; } List lights; - if (selector.IsSingle) - { - Light light = await lifxApi.GetLight(auth, selector.ToString()); - lights = new List(); - if (light != null) - { - lights.Add(light); - } - } - else - { - lights = await lifxApi.ListLights(auth, selector.ToString()); - } - foreach (var l in lights) + lights = await lifxApi.ListLights(auth, selector.ToString()); + foreach (var light in lights) { // Attach this client to lights - l.Client = this; + light.Client = this; } return lights; } + /// /// Gets light groups belonging to the authenticated account /// @@ -65,6 +98,7 @@ public async Task> ListGroups(Selector selector = null) { return (await ListLights(selector)).AsGroups(); } + /// /// Gets locations belonging to the authenticated account /// @@ -73,118 +107,212 @@ public async Task> ListGroups(Selector selector = null) public async Task> ListLocations(Selector selector = null) { return (await ListLights(selector)).AsLocations(); - } + } + /// - /// Turn off lights if they are on, or turn them on if they are off. - /// Physically powered off lights are ignored. + /// Lists all the scenes available in the user's account. Scenes listed here can be activated with the ActivateScene. As documented here: https://api.developer.lifx.com/docs/list-scenes. /// - /// Filter for which lights are targetted - /// Result indicating success of operation - public async Task> TogglePower(Selector selector) + /// All scenes available. + public async Task> ListScenes() { - if (selector == null) { selector = Selector.All; } - if (selector.IsSingle) - { - ApiResult result; - try - { - result = await lifxApi.TogglePowerSingle(auth, selector.ToString()); - } - catch (Refit.ApiException e) - { - result = e.GetContentAs(); - } - return new List() { result }; - } - else - { - try - { - return await lifxApi.TogglePower(auth, selector.ToString()); - } - catch (Refit.ApiException e) - { - return e.GetContentAs>(); - } - } + return await lifxApi.ListScenes(auth); } - /// - /// Turn lights on, or turn lights off. - /// - /// True for on, false for off - /// Optionally set a duration which will fade on (or off) over the given duration in seconds. + + /// + /// Toggles light power state. As documented here: https://api.developer.lifx.com/docs/toggle-power. + /// /// Filter for which lights are targetted - /// Result indicating success of operation - public async Task> SetPower(Selector selector, bool powerState, float duration = DEFAULT_DURATION) + /// How long in seconds you want the power action to take. Range: 0.0 – 3155760000.0 (100 years). + /// + public async Task TogglePower(Selector selector, double duration = DEFAULT_DURATION) { - return await SetPower(selector, powerState ? PowerState.On : PowerState.Off, duration); + if (selector == null) { selector = Selector.All; } + duration = duration < MIN_DURATION ? MIN_DURATION : duration > MAX_DURATION ? MAX_DURATION : duration; + string args = string.Format("duration={0}", duration); + try + { + return await lifxApi.TogglePower(auth, selector.ToString(), args); + } + catch (Refit.ApiException e) + { + return e.GetContentAs(); + } } - /// - /// Turn lights on, or turn lights off. - /// - /// Desired power state for lights - /// Optionally set a duration which will fade on (or off) over the given duration in seconds. - /// Filter for which lights are targetted - /// Result indicating success of operation - public async Task> SetPower(Selector selector, PowerState powerState, float duration = DEFAULT_DURATION) + + /// + /// Set power to an explicit state (on/off). + /// + /// Filter for which lights are targetted + /// On or off. + /// How long in seconds you want the power action to take. Range: 0.0 – 3155760000.0 (100 years). + /// + public async Task SetPower(Selector selector, PowerState powerState, double duration = DEFAULT_DURATION) + { + if (selector == null) { selector = Selector.All; } + string args = string.Format("power={0}&duration={1}", powerState.ToString().ToLowerInvariant(), duration); + try + { + return await lifxApi.SetState(auth, selector.ToString(), args); + } + catch (Refit.ApiException e) + { + return e.GetContentAs(); + } + } + + /// + /// Set the light to a specific color. + /// + /// Filter for which lights are targetted + /// The color to set the light to. + /// How long in seconds you want the power action to take. Range: 0.0 – 3155760000.0 (100 years). + /// If true, turn the bulb on if it is not already on. + /// + public async Task SetColor(Selector selector, LifxColor color, double duration = DEFAULT_DURATION, bool powerOn = DEFAULT_POWER_ON) { if (selector == null) { selector = Selector.All; } - string args = string.Format("state={0}&duration={1}", powerState.ToString().ToLowerInvariant(), duration); - if (selector.IsSingle) - { - ApiResult result; - try - { - result = await lifxApi.SetPowerSingle(auth, selector.ToString(), args); - } - catch (Refit.ApiException e) - { - result = e.GetContentAs(); - } - return new List() { result }; - } - else - { - try - { - return await lifxApi.SetPower(auth, selector.ToString(), args); - } - catch (Refit.ApiException e) - { - return e.GetContentAs>(); - } + string args = string.Format("color={0}&duration={1}", color, duration); + if (powerOn) { args = args + "&power=" + PowerState.On.ToString().ToLowerInvariant(); } + try + { + return await lifxApi.SetState(auth, selector.ToString(), args); } - } - - public async Task> SetColor(Selector selector, LifxColor color, float duration = DEFAULT_DURATION, bool powerOn = DEFAULT_POWER_ON) + catch (Refit.ApiException e) + { + return e.GetContentAs(); + } + } + + /// + /// Set an explicit state. As documented here: https://api.developer.lifx.com/docs/set-state. + /// + /// Filter for which lights are targetted + /// On or off. + /// The color to set the light to. + /// The brightness level from 0.0 to 1.0. Overrides any brightness set in color (if any). + /// How long in seconds you want the power action to take. Range: 0.0 – 3155760000.0 (100 years). + /// The maximum brightness of the infrared channel. + /// + public async Task SetState(Selector selector, PowerState powerState, LifxColor color, double brightness, double duration = LifxClient.DEFAULT_DURATION, double infrared = LifxClient.DEFAULT_INFRARED) { if (selector == null) { selector = Selector.All; } - string args = string.Format("color={0}&duration={1}&power_on={2}", color, duration, powerOn); - if (selector.IsSingle) - { - ApiResult result; - try - { - result = await lifxApi.SetColorSingle(auth, selector.ToString(), args); - } - catch (Refit.ApiException e) - { - result = e.GetContentAs(); - } - return new List() { result }; - } - else - { - try - { - return await lifxApi.SetColor(auth, selector.ToString(), args); - } - catch (Refit.ApiException e) - { - return e.GetContentAs>(); - } + string args = string.Format("power={0}&color={1}&brightness={2}&duration={3}&infrared={4}", powerState.ToString().ToLowerInvariant(), color, brightness, duration, infrared); + try + { + return await lifxApi.SetState(auth, selector.ToString(), args); } - } - + catch (Refit.ApiException e) + { + return e.GetContentAs(); + } + } + + /// + /// Set multiple states across multiple selectors. As documented here: https://api.developer.lifx.com/docs/set-states. + /// + /// A list of states (each which may have a unique selector to chose the light(s) which will be set to said state). + /// A state which contains default properties that are inherited by states that don't explicitely set them. + /// + public async Task SetStates(List states, LightState defaults) + { + SetStatesSpec args = new SetStatesSpec(states, defaults); + try + { + return await lifxApi.SetStates(auth, args); + } + catch (Refit.ApiException e) + { + return e.GetContentAs(); + } + } + + /// + /// Activates a scene from the users account. As documented here: https://api.developer.lifx.com/docs/activate-scene. + /// + /// The UUID for the scene you wish to activate. + /// The time in seconds to spend performing the scene transition. + /// + public async Task ActivateScene(string sceneUUID, double duration = DEFAULT_DURATION) + { + string args = string.Format("duration={0}", duration); + try + { + return await lifxApi.ActivateScene(auth, sceneUUID, args); + } + catch (Refit.ApiException e) + { + return e.GetContentAs(); + } + } + + /// + /// Triggers a pulsing ("strobe") effect on the light. During each cycle the light switches between two colors ("fromColor" and "color"). As documented here: https://api.developer.lifx.com/docs/pulse-effect. + /// + /// Filter for which lights are targetted + /// Color which the light changes to. Light alternates between this and fromColor. + /// The time (in seconds) to complete one cycle of the effect. + /// The number of times to repeat the effect. + /// Color which the light begins on and changes back to at the start of the next cycle. If omitted, this will be the current color of the light. Set to LifxColor.OffState to toggle between an off state and your chosen color (which must have a brightness level set above 0... default named colors will result in the light staying off). + /// If false, this sets the light back to its previous state when the effect ends. If true the light remains on the last color of the effect. + /// If true, turn the bulb on if it is not already on. + /// + public async Task PulseEffect(Selector selector, LifxColor color, double period, double cycles, LifxColor fromColor = null, bool persist = false, bool powerOn = DEFAULT_POWER_ON) + { + if (selector == null) { selector = Selector.All; } + string args = string.Format("color={0}&from_color={1}&period={2}&cycles={3}&persist={4}&power_on={5}", color, fromColor, period, cycles, persist, powerOn); + try + { + return await lifxApi.PulseEffect(auth, selector.ToString(), args); + } + catch (Refit.ApiException e) + { + return e.GetContentAs(); + } + } + + /// + /// Triggers a breathing ("fade") effect on the light. During each cycle the light fades between two colors ("fromColor" and "color"). As documented here: https://api.developer.lifx.com/docs/breathe-effect. + /// + /// Filter for which lights are targetted + /// Color which the light fades to. Light alternates between this and fromColor. + /// The time (in seconds) to complete one cycle of the effect. + /// The number of times to repeat the effect. + /// Color which the light begins on and changes back to at the start of the next cycle. If omitted, this will be the current color of the light. Set to LifxColor.OffState to toggle between an off state and your chosen color (which must have a brightness level set above 0... default named colors will result in the light staying "off"). + /// If false, this sets the light back to its previous state when the effect ends. If true the light remains on the last color of the effect. + /// If true, turn the bulb on if it is not already on. + /// Defines where in a period the target color is at its maximum. Minimum 0.0, maximum 1.0. + /// + public async Task BreatheEffect(Selector selector, LifxColor color, double period, double cycles, LifxColor fromColor = null, bool persist = false, bool powerOn = DEFAULT_POWER_ON, double peak = DEFAULT_PEAK) + { + if (selector == null) { selector = Selector.All; } + string args = string.Format("color={0}&from_color={1}&period={2}&cycles={3}&persist={4}&power_on={5}&peak={6}", color, fromColor, period, cycles, persist, powerOn, peak); + try + { + return await lifxApi.BreatheEffect(auth, selector.ToString(), args); + } + catch (Refit.ApiException e) + { + return e.GetContentAs(); + } + } + + /// Triggers a state cycle effect on the light. During each cycle the light transitions between 2 - 5 predefined states that will inherit properties from the default state. As documented here: https://api.developer.lifx.com/docs/cycle. + /// A list of states. Within each you can set power, color, brightness, power action duration, and infrared channel value. Must have 2 to 5 entries. + /// A state which contains default properties that are inherited by states that don't explicitely set them. + /// Direction in which to cycle through the list of states. + /// + public async Task Cycle(Selector selector, List states, LightState defaults, Direction direction = DEFAULT_DIRECTION) + { + if (selector == null) { selector = Selector.All; } + SetStateSpec args = new SetStateSpec(states, defaults, direction); + try + { + return await lifxApi.Cycle(auth, selector.ToString(), args); + } + catch (Refit.ApiException e) + { + return e.GetContentAs(); + } + } } } diff --git a/LifxHttp/LifxColor.cs b/LifxHttp/LifxColor.cs index 025d0a7..ab8be20 100644 --- a/LifxHttp/LifxColor.cs +++ b/LifxHttp/LifxColor.cs @@ -1,4 +1,5 @@ -using Newtonsoft.Json; +using LifxHttp.Helpers; +using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Linq; @@ -12,6 +13,7 @@ namespace LifxHttp /// kelvin, and brightness components. However, other means of expressing /// colors are available /// + [JsonConverter(typeof(LifxColorConverter))] public abstract class LifxColor { /// @@ -62,6 +64,10 @@ public abstract class LifxColor /// Sets hue to 325 /// public static readonly LifxColor Pink = new Named("pink"); + /// + /// A pseudo "off" state color with no brightness - used for effects + /// + public static readonly LifxColor OffState = new White(0); public static readonly IEnumerable NamedColors = new List() { DefaultWhite, Red, Orange, Yellow, Cyan, Green, Blue, Purple, Pink @@ -99,20 +105,20 @@ public override string ToString() public class HSBK : LifxColor { [JsonProperty] - private float? hue; + private double? hue; [JsonProperty] - private float? saturation; + private double? saturation; [JsonProperty] - private float? brightness; + private double? brightness; [JsonProperty] private int? kelvin; - public float Hue { get { return hue ?? float.NaN; } } - public float Saturation { get { return saturation ?? float.NaN; } } - public float Brightness { get { return brightness ?? float.NaN; } } + public double Hue { get { return hue ?? double.NaN; } } + public double Saturation { get { return saturation ?? double.NaN; } } + public double Brightness { get { return brightness ?? double.NaN; } } public int Kelvin { get { return kelvin ?? TemperatureDefault; } } internal HSBK() { } - public HSBK(float? hue = null, float? saturation = null, float? brightness = null, int? kelvin = null) + public HSBK(double? hue = null, double? saturation = null, double? brightness = null, int? kelvin = null) { if (hue == null && saturation == null && brightness == null && kelvin == null) { @@ -147,7 +153,7 @@ public override string ToString() return sb.ToString(); } - internal HSBK WithBrightness(float brightness) + internal HSBK WithBrightness(double brightness) { return new HSBK(this.hue, this.saturation, brightness, this.kelvin); } @@ -158,7 +164,7 @@ internal HSBK WithBrightness(float brightness) /// public sealed class HSB : HSBK { - public HSB(float hue, float saturation = 1f, float brightness = 1f) : base(hue, saturation, brightness) { } + public HSB(double hue, double saturation = 1f, double brightness = 1f) : base(hue, saturation, brightness) { } } /// @@ -166,7 +172,7 @@ public HSB(float hue, float saturation = 1f, float brightness = 1f) : base(hue, /// public sealed class White : HSBK { - public White(float brightness = 1f, int kelvin = TemperatureDefault) : base(null, null, brightness, kelvin) { } + public White(double brightness = 1f, int kelvin = TemperatureDefault) : base(null, null, brightness, kelvin) { } } /// diff --git a/LifxHttp/LifxHttp.csproj b/LifxHttp/LifxHttp.csproj index a447b14..763fdfd 100644 --- a/LifxHttp/LifxHttp.csproj +++ b/LifxHttp/LifxHttp.csproj @@ -1,82 +1,87 @@ - - - - - 10.0 - Debug - AnyCPU - {1E9E789F-2750-4E8C-8F0D-D5A41322C953} - Library - Properties - LifxHttp - LifxHttp - en-US - 512 - {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - Profile111 - v4.5 - - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - - - $(SolutionDir)\packages\Newtonsoft.Json.6.0.5\lib\portable-net45+wp80+win8+wpa81\Newtonsoft.Json.dll - True - - - $(SolutionDir)\packages\refit.2.3.0\lib\Portable-Net45+WinRT45+WP8+WPA81\Refit.dll - False - - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - + + + + + 10.0 + Debug + AnyCPU + {1E9E789F-2750-4E8C-8F0D-D5A41322C953} + Library + Properties + LifxHttp + LifxHttp + en-US + 512 + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + Profile111 + v4.5 + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + $(SolutionDir)\packages\Newtonsoft.Json.6.0.5\lib\portable-net45+wp80+win8+wpa81\Newtonsoft.Json.dll + True + + + $(SolutionDir)\packages\refit.2.3.0\lib\Portable-Net45+WinRT45+WP8+WPA81\Refit.dll + False + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + --> \ No newline at end of file diff --git a/LifxHttp/Light.cs b/LifxHttp/Light.cs index a2351f0..4fc1233 100644 --- a/LifxHttp/Light.cs +++ b/LifxHttp/Light.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using LifxHttp.Enums; namespace LifxHttp { @@ -15,26 +16,44 @@ namespace LifxHttp public sealed class Light : ILightTarget { public const string ColorCapability = "has_color"; - public const string ColorTemperatureCapability = "has_variable_color_temp"; - - [JsonObject(MemberSerialization.Fields)] + public const string ColorTemperatureCapability = "has_variable_color_temp"; + public const string IRCapability = "has_ir"; + public const string MultizoneCapability = "has_multizone"; + + [JsonObject(MemberSerialization.OptIn)] internal class CollectionSpec { - public string id; - public string name; + [JsonProperty("id")] + public string Id { get; set; } + [JsonProperty("name")] + public string Name { get; set; } public override bool Equals(object obj) { CollectionSpec spec = obj as CollectionSpec; - return spec != null && spec.id == id && spec.name == name; + return spec != null && spec.Id == Id && spec.Name == Name; } public override int GetHashCode() { - return (id.GetHashCode() * 77) + name.GetHashCode(); + return (Id.GetHashCode() * 77) + Name.GetHashCode(); } - } - + } + + [JsonObject(MemberSerialization.OptIn)] + internal class ProductSpec + { + [JsonProperty("name")] + public string ProductName { get; set; } + [JsonProperty("company")] + public string Company { get; set; } + [JsonProperty("identifier")] + public string ProductId { get; set; } + [JsonProperty("capabilities")] + public Dictionary Capabilities { get; set; } + } + internal LifxClient Client { get; set; } + /// /// Serial number of the light /// @@ -51,6 +70,7 @@ public override int GetHashCode() public bool IsConnected { get; private set; } public bool IsOn { get { return PowerState == PowerState.On; } } + [JsonProperty("power")] public PowerState PowerState { get; private set; } @@ -59,34 +79,37 @@ public LifxColor.HSBK Color { get { return color == null ? null : color.WithBrightness(Brightness); } set { color = value; } - } + } + + [JsonProperty("infrared")] + public double? Infrared { get; private set; } [JsonProperty("brightness")] - public float Brightness { get; private set; } + public double Brightness { get; private set; } [JsonProperty("group")] - internal CollectionSpec group = new CollectionSpec(); - public string GroupId { get { return group.id; } } - public string GroupName { get { return group.name; } } + internal CollectionSpec Group = new CollectionSpec(); + public string GroupId { get { return Group.Id; } } + public string GroupName { get { return Group.Name; } } [JsonProperty("location")] - internal CollectionSpec location = new CollectionSpec(); - public string LocationId { get { return location.id; } } - public string LocationName { get { return location.name; } } + internal CollectionSpec Location = new CollectionSpec(); + public string LocationId { get { return Location.Id; } } + public string LocationName { get { return Location.Name; } } [JsonProperty("last_seen")] [JsonConverter(typeof(IsoDateTimeConverter))] public DateTime LastSeen { get; private set; } [JsonProperty("seconds_since_seen")] - public float SecondsSinceSeen { get; private set; } - - [JsonProperty("product_name")] - public string ProductName { get; private set; } - - [JsonProperty("capabilities")] - private Dictionary capabilities; - private LifxColor.HSBK color; + public double SecondsSinceSeen { get; private set; } + + [JsonProperty("product")] + internal ProductSpec Product = new ProductSpec(); + public string ProductName { get { return Product.ProductName; } private set { ProductName = value; } } + public string Company { get { return Product.Company; } private set { Company = value; } } + public string ProductId { get { return Product.ProductId; } private set { ProductId = value; } } + public Dictionary capabilities { get { return Product.Capabilities; } } public IEnumerable Capabilities { @@ -108,21 +131,96 @@ public IEnumerable Capabilities public bool HasCapability(string capabilitity) { return capabilities != null && capabilities.ContainsKey(capabilitity) && capabilities[capabilitity]; + } + + private LifxColor.HSBK color; + + /// + /// Toggles light power state. As documented here: https://api.developer.lifx.com/docs/toggle-power. + /// + /// How long in seconds you want the power action to take. Range: 0.0 – 3155760000.0 (100 years). + /// + public async Task TogglePower(double duration = LifxClient.DEFAULT_DURATION) + { + return (await Client.TogglePower(this)).Results.First(); } - public async Task TogglePower() + /// + /// Set power to an explicit state (on/off). + /// + /// On or off. + /// How long in seconds you want the power action to take. Range: 0.0 – 3155760000.0 (100 years). + /// + public async Task SetPower(PowerState powerState, double duration = LifxClient.DEFAULT_DURATION) { - return (await Client.TogglePower(this)).First(); + return (await Client.SetPower(this, powerState, duration)).Results.First(); } - public async Task SetPower(bool powerState, float duration = LifxClient.DEFAULT_DURATION) + /// + /// Set the light to a specific color. + /// + /// The color to set the light to. + /// How long in seconds you want the power action to take. Range: 0.0 – 3155760000.0 (100 years). + /// If true, turn the bulb on if it is not already on. + /// + public async Task SetColor(LifxColor color, double duration = LifxClient.DEFAULT_DURATION, bool powerOn = LifxClient.DEFAULT_POWER_ON) + { + return (await Client.SetColor(this, color, duration, powerOn)).Results.First(); + } + + /// + /// Set an explicit state. As documented here: https://api.developer.lifx.com/docs/set-state. + /// + /// On or off. + /// The color to set the light to. + /// The brightness level from 0.0 to 1.0. Overrides any brightness set in color (if any). + /// How long in seconds you want the power action to take. Range: 0.0 – 3155760000.0 (100 years). + /// The maximum brightness of the infrared channel. + /// + public async Task SetState(PowerState powerState, LifxColor color, double brightness, double duration = LifxClient.DEFAULT_DURATION, double infrared = LifxClient.DEFAULT_INFRARED) + { + return (await Client.SetState(this, powerState, color, brightness, duration, infrared)).Results.First(); + } + + /// + /// Triggers a pulsing ("strobe") effect on the light. During each cycle the light switches between two colors ("fromColor" and "color"). As documented here: https://api.developer.lifx.com/docs/pulse-effect. + /// + /// Color which the light changes to. Light alternates between this and fromColor. + /// The time (in seconds) to complete one cycle of the effect. + /// The number of times to repeat the effect. + /// Color which the light begins on and changes back to at the start of the next cycle. If omitted, this will be the current color of the light. Set to LifxColor.OffState to toggle between an off state and your chosen color (which must have a brightness level set above 0... default named colors will result in the light staying off). + /// If false, this sets the light back to its previous state when the effect ends. If true the light remains on the last color of the effect. + /// If true, turn the bulb on if it is not already on. + /// + public async Task PulseEffect(LifxColor color, double period, double cycles, LifxColor fromColor = LifxClient.DEFAULT_FROM_COLOR, bool persist = LifxClient.DEFAULT_PERSIST, bool powerOn = LifxClient.DEFAULT_POWER_ON) { - return (await Client.SetPower(this, powerState, duration)).First(); + return (await Client.PulseEffect(this, color, period, cycles, fromColor, persist, powerOn)).Results.First(); + } + + /// + /// Triggers a breathing ("fade") effect on the light. During each cycle the light fades between two colors ("fromColor" and "color"). As documented here: https://api.developer.lifx.com/docs/breathe-effect. + /// + /// Color which the light fades to. Light alternates between this and fromColor. + /// The time (in seconds) to complete one cycle of the effect. + /// The number of times to repeat the effect. + /// Color which the light begins on and changes back to at the start of the next cycle. If omitted, this will be the current color of the light. Set to LifxColor.OffState to toggle between an off state and your chosen color (which must have a brightness level set above 0... default named colors will result in the light staying "off"). + /// If false, this sets the light back to its previous state when the effect ends. If true the light remains on the last color of the effect. + /// If true, turn the bulb on if it is not already on. + /// Defines where in a period the target color is at its maximum. Minimum 0.0, maximum 1.0. + /// + public async Task BreatheEffect(LifxColor color, double period, double cycles, LifxColor fromColor = LifxClient.DEFAULT_FROM_COLOR, bool persist = LifxClient.DEFAULT_PERSIST, bool powerOn = LifxClient.DEFAULT_POWER_ON, double peak = LifxClient.DEFAULT_PEAK) + { + return (await Client.BreatheEffect(this, color, period, cycles, fromColor, persist, powerOn, peak)).Results.First(); } - public async Task SetColor(LifxColor color, float duration = LifxClient.DEFAULT_DURATION, bool powerOn = LifxClient.DEFAULT_POWER_ON) - { - return (await Client.SetColor(this, color, duration, powerOn)).First(); + /// Triggers a state cycle effect on the light. During each cycle the light transitions between 2 - 5 predefined states that will inherit properties from the default state. As documented here: https://api.developer.lifx.com/docs/cycle. + /// A list of states. Within each you can set power, color, brightness, power action duration, and infrared channel value. Must have 2 to 5 entries. + /// A state which contains default properties that are inherited by states that don't explicitely set them. + /// Direction in which to cycle through the list of states. + /// + public async Task Cycle(List states, LightState defaults, Direction direction = LifxClient.DEFAULT_DIRECTION) + { + return (await Client.Cycle(this, states, defaults, direction)).Results.First(); } /// @@ -147,18 +245,18 @@ public async Task Refresh() PowerState = light.PowerState; Color = light.Color; Brightness = light.Brightness; - group = light.group; - location = light.location; + Group = light.Group; + Location = light.Location; LastSeen = light.LastSeen; SecondsSinceSeen = light.SecondsSinceSeen; - ProductName = light.ProductName; + Product = light.Product; } public override string ToString() { return Label; - } - + } + public static implicit operator Selector(Light light) { return new Selector.LightId(light.Id); diff --git a/LifxHttp/LightCollection.cs b/LifxHttp/LightCollection.cs index b81aafc..e6d717a 100644 --- a/LifxHttp/LightCollection.cs +++ b/LifxHttp/LightCollection.cs @@ -1,75 +1,150 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LifxHttp -{ - public abstract class LightCollection : IEnumerable, ILightTarget> - { - public string Id { get; private set; } - public string Label { get; private set; } - - public bool IsOn { get { return lights.Any(l => l.IsOn); } } - - private List lights; - private LifxClient client; - - internal LightCollection(LifxClient client, string id, string label, List lights) - { - this.client = client; - Id = id; - Label = label; - this.lights = lights; - } - - public IEnumerator GetEnumerator() +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using LifxHttp.Enums; + +namespace LifxHttp +{ + public abstract class LightCollection : IEnumerable, ILightTarget + { + public string Id { get; private set; } + public string Label { get; private set; } + + public bool IsOn { get { return lights.Any(l => l.IsOn); } } + + private List lights; + private LifxClient client; + + internal LightCollection(LifxClient client, string id, string label, List lights) + { + this.client = client; + Id = id; + Label = label; + this.lights = lights; + } + + public IEnumerator GetEnumerator() + { + return lights.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return lights.GetEnumerator(); + } + + /// + /// Toggles light power state. As documented here: https://api.developer.lifx.com/docs/toggle-power. + /// + /// How long in seconds you want the power action to take. Range: 0.0 – 3155760000.0 (100 years). + /// + public async Task TogglePower(double duration = LifxClient.DEFAULT_DURATION) + { + if (client == null) { return new ApiResults(); } + return await client.TogglePower(this, duration); + } + + /// + /// Set power to an explicit state (on/off). + /// + /// On or off. + /// How long in seconds you want the power action to take. Range: 0.0 – 3155760000.0 (100 years). + /// + public async Task SetPower(PowerState powerState, double duration = LifxClient.DEFAULT_DURATION) { - return lights.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return lights.GetEnumerator(); - } - public async Task> TogglePower() - { - if (client == null) { return new List(); } - return await client.TogglePower(this); - } - - public async Task> SetPower(bool powerState, float duration = LifxClient.DEFAULT_DURATION) - { - if (client == null) { return new List(); } return await client.SetPower(this, powerState, duration); } - public async Task> SetColor(LifxColor color, float duration = LifxClient.DEFAULT_DURATION, bool powerOn = LifxClient.DEFAULT_POWER_ON) + /// + /// Set the light to a specific color. + /// + /// The color to set the light to. + /// How long in seconds you want the power action to take. Range: 0.0 – 3155760000.0 (100 years). + /// If true, turn the bulb on if it is not already on. + /// + public async Task SetColor(LifxColor color, double duration = LifxClient.DEFAULT_DURATION, bool powerOn = LifxClient.DEFAULT_POWER_ON) { - if (client == null) { return new List(); } return await client.SetColor(this, color, duration, powerOn); - } - - public async Task Refresh() - { - if (client != null) - { - lights = await client.ListLights(this); - } - } - - public abstract Selector ToSelector(); - - public override string ToString() - { - return Label; - } - - public static implicit operator Selector(LightCollection lightCollection) + } + + /// + /// Set an explicit state. As documented here: https://api.developer.lifx.com/docs/set-state. + /// + /// On or off. + /// The color to set the light to. + /// The brightness level from 0.0 to 1.0. Overrides any brightness set in color (if any). + /// How long in seconds you want the power action to take. Range: 0.0 – 3155760000.0 (100 years). + /// The maximum brightness of the infrared channel. + /// + public async Task SetState(PowerState powerState, LifxColor color, double brightness, double duration = LifxClient.DEFAULT_DURATION, double infrared = LifxClient.DEFAULT_INFRARED) { - return lightCollection.ToSelector(); - } - } -} + return await client.SetState(this, powerState, color, brightness, duration, infrared); + } + + /// + /// Triggers a pulsing ("strobe") effect on the light. During each cycle the light switches between two colors ("fromColor" and "color"). As documented here: https://api.developer.lifx.com/docs/pulse-effect. + /// + /// Color which the light changes to. Light alternates between this and fromColor. + /// The time (in seconds) to complete one cycle of the effect. + /// The number of times to repeat the effect. + /// Color which the light begins on and changes back to at the start of the next cycle. If omitted, this will be the current color of the light. Set to LifxColor.OffState to toggle between an off state and your chosen color (which must have a brightness level set above 0... default named colors will result in the light staying off). + /// If false, this sets the light back to its previous state when the effect ends. If true the light remains on the last color of the effect. + /// If true, turn the bulb on if it is not already on. + /// + public async Task PulseEffect(LifxColor color, double period, double cycles, LifxColor fromColor = LifxClient.DEFAULT_FROM_COLOR, bool persist = LifxClient.DEFAULT_PERSIST, bool powerOn = LifxClient.DEFAULT_POWER_ON) + { + if (client == null) { return new ApiResults(); } + return await client.PulseEffect(this, color, period, cycles, fromColor, persist, powerOn); + } + + /// + /// Triggers a breathing ("fade") effect on the light. During each cycle the light fades between two colors ("fromColor" and "color"). As documented here: https://api.developer.lifx.com/docs/breathe-effect. + /// + /// Color which the light fades to. Light alternates between this and fromColor. + /// The time (in seconds) to complete one cycle of the effect. + /// The number of times to repeat the effect. + /// Color which the light begins on and changes back to at the start of the next cycle. If omitted, this will be the current color of the light. Set to LifxColor.OffState to toggle between an off state and your chosen color (which must have a brightness level set above 0... default named colors will result in the light staying "off"). + /// If false, this sets the light back to its previous state when the effect ends. If true the light remains on the last color of the effect. + /// If true, turn the bulb on if it is not already on. + /// Defines where in a period the target color is at its maximum. Minimum 0.0, maximum 1.0. + /// + public async Task BreatheEffect(LifxColor color, double period, double cycles, LifxColor fromColor = LifxClient.DEFAULT_FROM_COLOR, bool persist = LifxClient.DEFAULT_PERSIST, bool powerOn = LifxClient.DEFAULT_POWER_ON, double peak = LifxClient.DEFAULT_PEAK) + { + if (client == null) { return new ApiResults(); } + return await client.BreatheEffect(this, color, period, cycles, fromColor, persist, powerOn, peak); + } + + /// Triggers a state cycle effect on the light. During each cycle the light transitions between 2 - 5 predefined states that will inherit properties from the default state. As documented here: https://api.developer.lifx.com/docs/cycle. + /// A list of states. Within each you can set power, color, brightness, power action duration, and infrared channel value. Must have 2 to 5 entries. + /// A state which contains default properties that are inherited by states that don't explicitely set them. + /// Direction in which to cycle through the list of states. + /// + public async Task Cycle(List states, LightState defaults, Direction direction = LifxClient.DEFAULT_DIRECTION) + { + return await client.Cycle(this, states, defaults, direction); + } + + public async Task Refresh() + { + if (client != null) + { + lights = await client.ListLights(this); + } + } + + public abstract Selector ToSelector(); + + public override string ToString() + { + return Label; + } + + public static implicit operator Selector(LightCollection lightCollection) + { + return lightCollection.ToSelector(); + } + } +} diff --git a/LifxHttp/LightState.cs b/LifxHttp/LightState.cs new file mode 100644 index 0000000..0941701 --- /dev/null +++ b/LifxHttp/LightState.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; +using LifxHttp.Enums; +using LifxHttp.Helpers; + +namespace LifxHttp +{ + [JsonObject(MemberSerialization.OptIn)] + public class LightState + { + /// + /// Selector used for Set States. + /// + [JsonProperty("selector", NullValueHandling = NullValueHandling.Ignore)] + public Selector Selector { get; set; } + [JsonProperty("power", NullValueHandling = NullValueHandling.Ignore)] + public PowerState PowerState { get; set; } + [JsonProperty("color", NullValueHandling = NullValueHandling.Ignore)] + public LifxColor Color { get; set; } + private double? _brightness; + [JsonProperty("brightness", NullValueHandling = NullValueHandling.Ignore)] + public double? Brightness { get { return _brightness; } + set { + if (value.HasValue) + _brightness = value < LifxClient.MIN_BRIGHTNESS ? LifxClient.MIN_BRIGHTNESS : value > LifxClient.MAX_BRIGHTNESS ? LifxClient.MAX_BRIGHTNESS : value; + else _brightness = value; + } + } + private double? _duration; + [JsonProperty("duration", NullValueHandling = NullValueHandling.Ignore)] + public double? Duration { get { return _duration; } + set { + if (value.HasValue) + _duration = value < LifxClient.MIN_DURATION ? LifxClient.MIN_DURATION : value > LifxClient.MAX_DURATION ? LifxClient.MAX_DURATION : value; + else _duration = value; + } + } + [JsonProperty("infrared", NullValueHandling = NullValueHandling.Ignore)] + public double? Infrared { get; set; } + + public LightState(PowerState powerState, LifxColor color, double? brightness = null, double? duration = null, double? infrared = null) + { + PowerState = powerState; + Color = color; + Brightness = brightness; + Duration = duration; + Infrared = infrared; + } + + public LightState(Selector selector, PowerState powerState, LifxColor color, double? brightness = null, double? duration = null, double? infrared = null) + { + Selector = selector; + PowerState = powerState; + Color = color; + Brightness = brightness; + Duration = duration; + Infrared = infrared; + } + + public LightState() { } + + public override string ToString() + { + StringBuilder result = new StringBuilder(); + if(Selector != null) + { + result.Append(string.Format("Selector: {0} ", Selector)); + } + result.Append(string.Format("Power State: {0} ", PowerState)); + if (Color != null) + { + result.Append(string.Format("Color: {0} ", Color)); + } + if (Brightness != null) + { + result.Append(string.Format("Brightness: {0} ", Brightness)); + } + if (Duration != null) + { + result.Append(string.Format("Duration: {0} ", Duration)); + } + if (Infrared != null) + { + result.Append(string.Format("Infrared: {0} ", Infrared)); + } + return result.ToString(); + } + } +} diff --git a/LifxHttp/RefitStubs.cs b/LifxHttp/RefitStubs.cs index 5c64afc..826ee28 100644 --- a/LifxHttp/RefitStubs.cs +++ b/LifxHttp/RefitStubs.cs @@ -44,52 +44,58 @@ public AutoGeneratedILifxApi(HttpClient client, IRequestBuilder requestBuilder) Client = client; } - public virtual Task GetLight(string auth,string selector) + public virtual Task> ListLights(string auth,string selector) { var arguments = new object[] { auth,selector }; - return (Task) methodImpls["GetLight"](Client, arguments); + return (Task>) methodImpls["ListLights"](Client, arguments); } - public virtual Task> ListLights(string auth,string selector) + public virtual Task> ListScenes(string auth) { - var arguments = new object[] { auth,selector }; - return (Task>) methodImpls["ListLights"](Client, arguments); + var arguments = new object[] { auth }; + return (Task>) methodImpls["ListScenes"](Client, arguments); } - public virtual Task> TogglePower(string auth,string selector) + public virtual Task SetState(string auth,string selector,string args) { - var arguments = new object[] { auth,selector }; - return (Task>) methodImpls["TogglePower"](Client, arguments); + var arguments = new object[] { auth,selector,args }; + return (Task) methodImpls["SetState"](Client, arguments); } - public virtual Task TogglePowerSingle(string auth,string selector) + public virtual Task SetStates(string auth,LifxClient.SetStatesSpec args) { - var arguments = new object[] { auth,selector }; - return (Task) methodImpls["TogglePowerSingle"](Client, arguments); + var arguments = new object[] { auth,args }; + return (Task) methodImpls["SetStates"](Client, arguments); + } + + public virtual Task ActivateScene(string auth,string sceneUUID,string args) + { + var arguments = new object[] { auth,sceneUUID,args }; + return (Task) methodImpls["ActivateScene"](Client, arguments); } - public virtual Task> SetPower(string auth,string selector,string args) + public virtual Task TogglePower(string auth,string selector,string args) { var arguments = new object[] { auth,selector,args }; - return (Task>) methodImpls["SetPower"](Client, arguments); + return (Task) methodImpls["TogglePower"](Client, arguments); } - public virtual Task SetPowerSingle(string auth,string selector,string args) + public virtual Task PulseEffect(string auth,string selector,string args) { var arguments = new object[] { auth,selector,args }; - return (Task) methodImpls["SetPowerSingle"](Client, arguments); + return (Task) methodImpls["PulseEffect"](Client, arguments); } - public virtual Task> SetColor(string auth,string selector,string args) + public virtual Task BreatheEffect(string auth,string selector,string args) { var arguments = new object[] { auth,selector,args }; - return (Task>) methodImpls["SetColor"](Client, arguments); + return (Task) methodImpls["BreatheEffect"](Client, arguments); } - public virtual Task SetColorSingle(string auth,string selector,string args) + public virtual Task Cycle(string auth,string selector,LifxClient.SetStateSpec args) { var arguments = new object[] { auth,selector,args }; - return (Task) methodImpls["SetColorSingle"](Client, arguments); + return (Task) methodImpls["Cycle"](Client, arguments); } } diff --git a/LifxHttp/Scene.cs b/LifxHttp/Scene.cs new file mode 100644 index 0000000..6fc88a0 --- /dev/null +++ b/LifxHttp/Scene.cs @@ -0,0 +1,47 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace LifxHttp +{ + [JsonObject(MemberSerialization.OptIn)] + public struct AccountSpec + { + [JsonProperty("uuid")] + public string UUID { get; private set; } + } + + [JsonObject(MemberSerialization.OptIn)] + public class Scene + { + [JsonProperty("uuid")] + public string UUID { get; private set; } + [JsonProperty("name")] + public string Name { get; private set; } + [JsonProperty("account")] + public AccountSpec Account { get; private set; } + [JsonProperty("states")] + public List States { get; set; } + [JsonProperty("created_at")] + public string CreatedAt { get; set; } + [JsonProperty("updated_at")] + public string UpdatedAt { get; set; } + + public override string ToString() + { + StringBuilder result = new StringBuilder(string.Format("UUID: {0} Name:{1} Account:{2} Created At:{3} Updated At:{4}", UUID, Name, Account.UUID, CreatedAt, UpdatedAt)); + result.AppendLine(); + result.AppendLine("States: "); + foreach (var state in States) + { + result.AppendLine(state.ToString()); + } + return result.ToString(); + } + } + + +} diff --git a/LifxHttp/Selector.cs b/LifxHttp/Selector.cs index c2e6358..1085d34 100644 --- a/LifxHttp/Selector.cs +++ b/LifxHttp/Selector.cs @@ -12,12 +12,14 @@ namespace LifxHttp public class Selector { private const string TYPE_ALL = "all"; - private const string TYPE_RANDOM = "random"; private const string TYPE_LIGHT_ID = "id"; + private const string TYPE_LIGHT_LABEL = "label"; private const string TYPE_GROUP_ID = "group_id"; private const string TYPE_GROUP_LABEL = "group"; private const string TYPE_LOCATION_ID = "location_id"; - private const string TYPE_LOCATION_LABEL = "location"; + private const string TYPE_LOCATION_LABEL = "location"; + private const string TYPE_SCENE_ID = "scene_id"; + private const string TYPE_RANDOM = "random"; // Unsure if this is still supported. /// /// All lights belonging to the authenticated account. @@ -53,7 +55,7 @@ public class LightId : Selector /// public class LightLabel : Selector { - public LightLabel(string label) : base(label) { IsSingle = true; } + public LightLabel(string label) : base(TYPE_LIGHT_LABEL, label) { IsSingle = true; } } /// @@ -72,9 +74,8 @@ public class GroupLabel : Selector public GroupLabel(string label) : base(TYPE_GROUP_LABEL, label) { } } - /// - /// OOnly the lights belonging to the location matching the given ID. + /// Only the lights belonging to the location matching the given ID. /// public class LocationId : Selector { @@ -87,6 +88,14 @@ public LocationId(string id) : base(TYPE_LOCATION_ID, id) { } public class LocationLabel : Selector { public LocationLabel(string label) : base(TYPE_LOCATION_LABEL, label) { } + } + + /// + /// The lights that are referenced in the scene ID. + /// + public class SceneId : Selector + { + public SceneId(string id) : base(TYPE_SCENE_ID, id) { } } public static explicit operator Selector(string selector) @@ -103,10 +112,12 @@ public static explicit operator Selector(string selector) switch (selector.Substring(0, criteria)) { case TYPE_LIGHT_ID: return new LightId(remainder); + case TYPE_LIGHT_LABEL: return new LightLabel(remainder); case TYPE_GROUP_ID: return new GroupId(remainder); case TYPE_GROUP_LABEL: return new GroupLabel(remainder); case TYPE_LOCATION_ID: return new LocationId(remainder); case TYPE_LOCATION_LABEL: return new LocationLabel(remainder); + case TYPE_SCENE_ID: return new SceneId(remainder); default: return new LightLabel(selector); } } diff --git a/LifxHttpSample/Program.cs b/LifxHttpSample/Program.cs index 4fd9d24..4214307 100644 --- a/LifxHttpSample/Program.cs +++ b/LifxHttpSample/Program.cs @@ -4,13 +4,15 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using LifxHttp.Enums; namespace LifxHttpSample { class Program { - private const string TOKEN = "REDACTED - Generate from https://cloud.lifx.com/settings"; - private const int DELAY = 2000; + private const string TOKEN = "caca8643f950e1fa8d592ff4d5f55c9e2d23c3b670045827fae0f497eafb8ad1"; + private const int DELAY = 2000; + private const int EFFECT_DELAY = 10000; static void Main(string[] args) { @@ -27,13 +29,16 @@ static void Main(string[] args) private static async Task RunDemos(LifxClient client) { - await DemoListing(client); + await DemoListing(client); + await DemoScenes(client); await DemoModifyLight(client); await DemoModifyCollections(client); + await DemoEffects(client); } private static async Task DemoListing(LifxClient client) - { + { + Console.WriteLine(); Console.WriteLine("Lights:"); foreach (var light in await client.ListLights()) { @@ -53,12 +58,29 @@ private static async Task DemoListing(LifxClient client) { Console.WriteLine("{0} - {1} lights", group, group.Count()); } - Console.WriteLine(); + } + + private static async Task DemoScenes(LifxClient client) + { + Console.WriteLine(); + Console.WriteLine("Scenes:"); + List scenes = await client.ListScenes(); + if (scenes.Count() > 0) { + foreach (var scene in scenes) + { + Console.WriteLine(scene.ToString()); + } + Console.WriteLine(string.Format("Activating Scene: {0}", scenes.First().Name)); + await client.ActivateScene(scenes.First().UUID); + await Task.Delay(EFFECT_DELAY); + } + else Console.WriteLine("No scenes on account."); } private static async Task DemoModifyLight(LifxClient client) - { - Light light = (await client.ListLights(new Selector.GroupLabel("Living Room"))).FirstOrDefault(); + { + Console.WriteLine(); + Light light = (await client.ListLights(new Selector.GroupLabel("Room 1"))).FirstOrDefault(); if (light == null) { // Find a connected light @@ -78,7 +100,7 @@ private static async Task DemoModifyLight(LifxClient client) } Console.WriteLine("Using light: {0}", light); Console.WriteLine("Turning light off"); - await light.SetPower(false); + //await light.SetPower(false); await Task.Delay(DELAY); Console.WriteLine("Toggling light on"); await light.TogglePower(); @@ -108,7 +130,9 @@ private static async Task DemoModifyLight(LifxClient client) } private static async Task DemoModifyCollections(LifxClient client) - { + { + Console.WriteLine(); + List lights = (await client.ListLights(new Selector.LightLabel("LIFX 027d98"))); Group group = (await client.ListGroups()).FirstOrDefault(); if (group == null) { @@ -117,24 +141,106 @@ private static async Task DemoModifyCollections(LifxClient client) else { Console.WriteLine("Using group: {0}", group); - Console.WriteLine("Toggling group"); - await group.TogglePower(); + Console.WriteLine("Toggling group, 3 second duration."); + await group.TogglePower(3); + await Task.Delay(DELAY + 3000); + Console.WriteLine("Turning group green"); - await group.SetColor(LifxColor.Green); + await group.SetColor(LifxColor.Green, 0); + await Task.Delay(DELAY); } Location location = (await client.ListLocations()).FirstOrDefault(); + await Task.Delay(DELAY); if (location == null) { Console.WriteLine("No locations"); } else + { + Console.WriteLine("Using location: {0}", location); + + Console.WriteLine("Turning off location"); + await location.SetPower(PowerState.Off); + await Task.Delay(DELAY); + + Console.WriteLine("Setting color to white with 5 second duration."); + await location.SetColor(new LifxColor.White(1), 5); + await Task.Delay(DELAY + 5000); + + Console.WriteLine("Set light to a blue color, using 90% brightness override. Uses SetState explicitely."); + await location.SetState(PowerState.On, new LifxColor.HSBK(180, 1, 1, 2000), 0.9, 2); + await Task.Delay(DELAY); + } + } + + private static async Task DemoEffects(LifxClient client) { + Console.WriteLine(); + Light light = (await client.ListLights(new Selector.GroupLabel("Room 1"))).FirstOrDefault(); + if (light == null) { - Console.WriteLine("Using location: {0}", location); - Console.WriteLine("Turning off location"); - await location.SetPower(false); - Console.WriteLine("Turning location pink"); - await location.SetColor(LifxColor.Pink); + // Find a connected light + foreach (var l in await client.ListLights()) + { + if (l.IsConnected) + { + light = l; + break; + } + } + if (!light.IsConnected) + { + Console.WriteLine("No connected lights"); + return; + } } + Console.WriteLine("Using light: {0}", light); + + Console.WriteLine("Pulsing slowly between previously set color and green"); + await light.PulseEffect(LifxColor.Green, 1, 10); + await Task.Delay(EFFECT_DELAY); + + /* If strobing with a single color (therefore the light alternates between the "color" parameter and "fromColor" + parameter which should be set to LifxColor.OffState (zero brightness) you must set the "color" parameter to a custom color + with a brightness value explicitly set. The default named parameters appear to leave the light in a fixed zero brightness state + until the end of the effect. */ + Console.WriteLine("Pulsing quickly in white"); + await light.PulseEffect(new LifxColor.White(1, 3500), 0.1, 50, fromColor: LifxColor.OffState); + await Task.Delay(EFFECT_DELAY); + + Console.WriteLine("Breathing red alert light."); + await light.BreatheEffect(new LifxColor.RGB(120, 0, 0), 2.5, 4, LifxColor.OffState, peak: 0.4d); + await Task.Delay(EFFECT_DELAY); + + Console.WriteLine("\"Party breathe\", will pass through intermediate colors."); + await light.BreatheEffect(LifxColor.Cyan, 5, 4, LifxColor.Orange, peak: 0.5d); + await Task.Delay(EFFECT_DELAY); + + Console.WriteLine("Cycling through some various states."); + List stateList = new List(); + stateList.Add(new LightState(PowerState.On, LifxColor.Pink, 0.5d)); + stateList.Add(new LightState(PowerState.On, LifxColor.Pink, 1.0d)); + stateList.Add(new LightState(PowerState.On, LifxColor.DefaultWhite, 1.0d)); + stateList.Add(new LightState(PowerState.On, LifxColor.Green)); + stateList.Add(new LightState(PowerState.On, LifxColor.Blue, 1.0d)); + stateList.Add(new LightState(PowerState.On, LifxColor.Blue, 0.5d)); + LightState defaults = new LightState(); + defaults.Duration = 3.0d; + defaults.Brightness = 1.0d; + + // Cycle forward + for (int i = 0; i < stateList.Count(); i++) + { + await light.Cycle(stateList, defaults); + await Task.Delay(4000); + } + // Cycle backward + for (int i = 0; i < stateList.Count(); i++) + { + await light.Cycle(stateList, defaults, Direction.Backward); + await Task.Delay(4000); + } + + //await location.PulseEffect(new LifxColor.White(1, 3500), 0.025, 10000, fromColor: new LifxColor.White(0.5f, 2000)); } } } From afd8410eb513d946ba4e4c594501761182c1d004 Mon Sep 17 00:00:00 2001 From: kowalej Date: Sun, 26 Mar 2017 04:08:10 -0400 Subject: [PATCH 2/7] Fixed format of activate scene API. --- LifxHttp/ILifxApi.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LifxHttp/ILifxApi.cs b/LifxHttp/ILifxApi.cs index 9b7a095..eee74ef 100644 --- a/LifxHttp/ILifxApi.cs +++ b/LifxHttp/ILifxApi.cs @@ -23,7 +23,7 @@ internal interface ILifxApi [Headers("Content-Type: application/json")] Task SetStates([Header("Authorization")] string auth, [Body] LifxClient.SetStatesSpec args); - [Put("/scenes/scene_id::{sceneUUID}/activate")] + [Put("/scenes/scene_id:{sceneUUID}/activate")] [Headers("Content-Type: application/x-www-form-urlencoded")] Task ActivateScene([Header("Authorization")] string auth, string sceneUUID, [Body] string args); From 2b6d9431ebd72a49e635dc33845024d493aab158 Mon Sep 17 00:00:00 2001 From: kowalej Date: Sun, 26 Mar 2017 04:23:02 -0400 Subject: [PATCH 3/7] Removed program --- LifxHttpSample/LifxHttpSample.csproj | 151 ++++++++-------- LifxHttpSample/Program.cs | 246 --------------------------- 2 files changed, 75 insertions(+), 322 deletions(-) delete mode 100644 LifxHttpSample/Program.cs diff --git a/LifxHttpSample/LifxHttpSample.csproj b/LifxHttpSample/LifxHttpSample.csproj index 0127525..56f6807 100644 --- a/LifxHttpSample/LifxHttpSample.csproj +++ b/LifxHttpSample/LifxHttpSample.csproj @@ -1,83 +1,82 @@ - - - - - Debug - AnyCPU - {0F13DC78-BB39-4D5F-98E5-71FB4DCC110A} - Exe - Properties - LifxHttpSample - LifxHttpSample - v4.5.2 - 512 - true - - - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - ..\packages\Newtonsoft.Json.6.0.5\lib\net45\Newtonsoft.Json.dll - True - - - ..\packages\refit.2.3.0\lib\Net45\Refit.dll - True - - - - - - - - - - - - - - - - - - - - {1e9e789f-2750-4e8c-8f0d-d5a41322c953} - LifxHttp - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - + + + + + Debug + AnyCPU + {0F13DC78-BB39-4D5F-98E5-71FB4DCC110A} + Exe + Properties + LifxHttpSample + LifxHttpSample + v4.5.2 + 512 + true + + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Newtonsoft.Json.6.0.5\lib\net45\Newtonsoft.Json.dll + True + + + ..\packages\refit.2.3.0\lib\Net45\Refit.dll + True + + + + + + + + + + + + + + + + + + + {1e9e789f-2750-4e8c-8f0d-d5a41322c953} + LifxHttp + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + --> \ No newline at end of file diff --git a/LifxHttpSample/Program.cs b/LifxHttpSample/Program.cs deleted file mode 100644 index 4214307..0000000 --- a/LifxHttpSample/Program.cs +++ /dev/null @@ -1,246 +0,0 @@ -using LifxHttp; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using LifxHttp.Enums; - -namespace LifxHttpSample -{ - class Program - { - private const string TOKEN = "caca8643f950e1fa8d592ff4d5f55c9e2d23c3b670045827fae0f497eafb8ad1"; - private const int DELAY = 2000; - private const int EFFECT_DELAY = 10000; - - static void Main(string[] args) - { - try - { - RunDemos(new LifxClient(args.Length > 0 ? args[0] : TOKEN)).Wait(); - } - catch (AggregateException e) - { - throw e.InnerException; - } - Console.ReadKey(); - } - - private static async Task RunDemos(LifxClient client) - { - await DemoListing(client); - await DemoScenes(client); - await DemoModifyLight(client); - await DemoModifyCollections(client); - await DemoEffects(client); - } - - private static async Task DemoListing(LifxClient client) - { - Console.WriteLine(); - Console.WriteLine("Lights:"); - foreach (var light in await client.ListLights()) - { - Console.WriteLine("{0} - {1}", light, light.Color); - } - Console.WriteLine(); - - Console.WriteLine("Groups:"); - foreach (var group in await client.ListGroups()) - { - Console.WriteLine("{0} - {1} lights", group, group.Count()); - } - Console.WriteLine(); - - Console.WriteLine("Locations:"); - foreach (var group in await client.ListLocations()) - { - Console.WriteLine("{0} - {1} lights", group, group.Count()); - } - } - - private static async Task DemoScenes(LifxClient client) - { - Console.WriteLine(); - Console.WriteLine("Scenes:"); - List scenes = await client.ListScenes(); - if (scenes.Count() > 0) { - foreach (var scene in scenes) - { - Console.WriteLine(scene.ToString()); - } - Console.WriteLine(string.Format("Activating Scene: {0}", scenes.First().Name)); - await client.ActivateScene(scenes.First().UUID); - await Task.Delay(EFFECT_DELAY); - } - else Console.WriteLine("No scenes on account."); - } - - private static async Task DemoModifyLight(LifxClient client) - { - Console.WriteLine(); - Light light = (await client.ListLights(new Selector.GroupLabel("Room 1"))).FirstOrDefault(); - if (light == null) - { - // Find a connected light - foreach (var l in await client.ListLights()) - { - if (l.IsConnected) - { - light = l; - break; - } - } - if (!light.IsConnected) - { - Console.WriteLine("No connected lights"); - return; - } - } - Console.WriteLine("Using light: {0}", light); - Console.WriteLine("Turning light off"); - //await light.SetPower(false); - await Task.Delay(DELAY); - Console.WriteLine("Toggling light on"); - await light.TogglePower(); - await Task.Delay(DELAY); - Console.WriteLine("Turning light soft red"); - await light.SetColor(new LifxColor.HSB(0, 0.2f, 0.5f)); - await Task.Delay(DELAY); - Console.WriteLine("Turning light blue-green"); - await light.SetColor(new LifxColor.RGB(0x00c89c)); - await Task.Delay(DELAY); - Console.WriteLine("Turning light white"); - await light.SetColor(LifxColor.DefaultWhite); - await Task.Delay(DELAY); - Console.WriteLine("Turning light hot white"); - await light.SetColor(new LifxColor.White(0.8f, LifxColor.TemperatureMax)); - await Task.Delay(DELAY); - - Console.WriteLine("Named colors:"); - foreach (var c in LifxColor.NamedColors) - { - Console.Write("{0}: ", c); - await light.SetColor(c); - await light.Refresh(); - Console.WriteLine("{0}", light.Color); - await Task.Delay(DELAY); - } - } - - private static async Task DemoModifyCollections(LifxClient client) - { - Console.WriteLine(); - List lights = (await client.ListLights(new Selector.LightLabel("LIFX 027d98"))); - Group group = (await client.ListGroups()).FirstOrDefault(); - if (group == null) - { - Console.WriteLine("No groups"); - } - else - { - Console.WriteLine("Using group: {0}", group); - Console.WriteLine("Toggling group, 3 second duration."); - await group.TogglePower(3); - await Task.Delay(DELAY + 3000); - - Console.WriteLine("Turning group green"); - await group.SetColor(LifxColor.Green, 0); - await Task.Delay(DELAY); - } - Location location = (await client.ListLocations()).FirstOrDefault(); - await Task.Delay(DELAY); - if (location == null) - { - Console.WriteLine("No locations"); - } - else - { - Console.WriteLine("Using location: {0}", location); - - Console.WriteLine("Turning off location"); - await location.SetPower(PowerState.Off); - await Task.Delay(DELAY); - - Console.WriteLine("Setting color to white with 5 second duration."); - await location.SetColor(new LifxColor.White(1), 5); - await Task.Delay(DELAY + 5000); - - Console.WriteLine("Set light to a blue color, using 90% brightness override. Uses SetState explicitely."); - await location.SetState(PowerState.On, new LifxColor.HSBK(180, 1, 1, 2000), 0.9, 2); - await Task.Delay(DELAY); - } - } - - private static async Task DemoEffects(LifxClient client) { - Console.WriteLine(); - Light light = (await client.ListLights(new Selector.GroupLabel("Room 1"))).FirstOrDefault(); - if (light == null) - { - // Find a connected light - foreach (var l in await client.ListLights()) - { - if (l.IsConnected) - { - light = l; - break; - } - } - if (!light.IsConnected) - { - Console.WriteLine("No connected lights"); - return; - } - } - Console.WriteLine("Using light: {0}", light); - - Console.WriteLine("Pulsing slowly between previously set color and green"); - await light.PulseEffect(LifxColor.Green, 1, 10); - await Task.Delay(EFFECT_DELAY); - - /* If strobing with a single color (therefore the light alternates between the "color" parameter and "fromColor" - parameter which should be set to LifxColor.OffState (zero brightness) you must set the "color" parameter to a custom color - with a brightness value explicitly set. The default named parameters appear to leave the light in a fixed zero brightness state - until the end of the effect. */ - Console.WriteLine("Pulsing quickly in white"); - await light.PulseEffect(new LifxColor.White(1, 3500), 0.1, 50, fromColor: LifxColor.OffState); - await Task.Delay(EFFECT_DELAY); - - Console.WriteLine("Breathing red alert light."); - await light.BreatheEffect(new LifxColor.RGB(120, 0, 0), 2.5, 4, LifxColor.OffState, peak: 0.4d); - await Task.Delay(EFFECT_DELAY); - - Console.WriteLine("\"Party breathe\", will pass through intermediate colors."); - await light.BreatheEffect(LifxColor.Cyan, 5, 4, LifxColor.Orange, peak: 0.5d); - await Task.Delay(EFFECT_DELAY); - - Console.WriteLine("Cycling through some various states."); - List stateList = new List(); - stateList.Add(new LightState(PowerState.On, LifxColor.Pink, 0.5d)); - stateList.Add(new LightState(PowerState.On, LifxColor.Pink, 1.0d)); - stateList.Add(new LightState(PowerState.On, LifxColor.DefaultWhite, 1.0d)); - stateList.Add(new LightState(PowerState.On, LifxColor.Green)); - stateList.Add(new LightState(PowerState.On, LifxColor.Blue, 1.0d)); - stateList.Add(new LightState(PowerState.On, LifxColor.Blue, 0.5d)); - LightState defaults = new LightState(); - defaults.Duration = 3.0d; - defaults.Brightness = 1.0d; - - // Cycle forward - for (int i = 0; i < stateList.Count(); i++) - { - await light.Cycle(stateList, defaults); - await Task.Delay(4000); - } - // Cycle backward - for (int i = 0; i < stateList.Count(); i++) - { - await light.Cycle(stateList, defaults, Direction.Backward); - await Task.Delay(4000); - } - - //await location.PulseEffect(new LifxColor.White(1, 3500), 0.025, 10000, fromColor: new LifxColor.White(0.5f, 2000)); - } - } -} From 15e31b556bd2ae5ea0dacc1a777663a388c3f55e Mon Sep 17 00:00:00 2001 From: kowalej Date: Sun, 26 Mar 2017 04:24:58 -0400 Subject: [PATCH 4/7] Fixed redacted token. --- LifxHttpSample/LifxHttpSample.csproj | 1 + LifxHttpSample/Program.cs | 248 +++++++++++++++++++++++++++ 2 files changed, 249 insertions(+) create mode 100644 LifxHttpSample/Program.cs diff --git a/LifxHttpSample/LifxHttpSample.csproj b/LifxHttpSample/LifxHttpSample.csproj index 56f6807..502eae4 100644 --- a/LifxHttpSample/LifxHttpSample.csproj +++ b/LifxHttpSample/LifxHttpSample.csproj @@ -51,6 +51,7 @@ + diff --git a/LifxHttpSample/Program.cs b/LifxHttpSample/Program.cs new file mode 100644 index 0000000..e617dc7 --- /dev/null +++ b/LifxHttpSample/Program.cs @@ -0,0 +1,248 @@ +using LifxHttp; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using LifxHttp.Enums; + +namespace LifxHttpSample +{ + class Program + { + private const string TOKEN = "REDACTED - Generate from https://cloud.lifx.com/settingss"; + private const int DELAY = 2000; + private const int EFFECT_DELAY = 10000; + + static void Main(string[] args) + { + try + { + RunDemos(new LifxClient(args.Length > 0 ? args[0] : TOKEN)).Wait(); + } + catch (AggregateException e) + { + throw e.InnerException; + } + Console.ReadKey(); + } + + private static async Task RunDemos(LifxClient client) + { + await DemoListing(client); + await DemoScenes(client); + await DemoModifyLight(client); + await DemoModifyCollections(client); + await DemoEffects(client); + } + + private static async Task DemoListing(LifxClient client) + { + Console.WriteLine(); + Console.WriteLine("Lights:"); + foreach (var light in await client.ListLights()) + { + Console.WriteLine("{0} - {1}", light, light.Color); + } + Console.WriteLine(); + + Console.WriteLine("Groups:"); + foreach (var group in await client.ListGroups()) + { + Console.WriteLine("{0} - {1} lights", group, group.Count()); + } + Console.WriteLine(); + + Console.WriteLine("Locations:"); + foreach (var group in await client.ListLocations()) + { + Console.WriteLine("{0} - {1} lights", group, group.Count()); + } + } + + private static async Task DemoScenes(LifxClient client) + { + Console.WriteLine(); + Console.WriteLine("Scenes:"); + List scenes = await client.ListScenes(); + if (scenes.Count() > 0) + { + foreach (var scene in scenes) + { + Console.WriteLine(scene.ToString()); + } + Console.WriteLine(string.Format("Activating Scene: {0}", scenes.First().Name)); + await client.ActivateScene(scenes.First().UUID); + await Task.Delay(EFFECT_DELAY); + } + else Console.WriteLine("No scenes on account."); + } + + private static async Task DemoModifyLight(LifxClient client) + { + Console.WriteLine(); + Light light = (await client.ListLights(new Selector.GroupLabel("Room 1"))).FirstOrDefault(); + if (light == null) + { + // Find a connected light + foreach (var l in await client.ListLights()) + { + if (l.IsConnected) + { + light = l; + break; + } + } + if (!light.IsConnected) + { + Console.WriteLine("No connected lights"); + return; + } + } + Console.WriteLine("Using light: {0}", light); + Console.WriteLine("Turning light off"); + //await light.SetPower(false); + await Task.Delay(DELAY); + Console.WriteLine("Toggling light on"); + await light.TogglePower(); + await Task.Delay(DELAY); + Console.WriteLine("Turning light soft red"); + await light.SetColor(new LifxColor.HSB(0, 0.2f, 0.5f)); + await Task.Delay(DELAY); + Console.WriteLine("Turning light blue-green"); + await light.SetColor(new LifxColor.RGB(0x00c89c)); + await Task.Delay(DELAY); + Console.WriteLine("Turning light white"); + await light.SetColor(LifxColor.DefaultWhite); + await Task.Delay(DELAY); + Console.WriteLine("Turning light hot white"); + await light.SetColor(new LifxColor.White(0.8f, LifxColor.TemperatureMax)); + await Task.Delay(DELAY); + + Console.WriteLine("Named colors:"); + foreach (var c in LifxColor.NamedColors) + { + Console.Write("{0}: ", c); + await light.SetColor(c); + await light.Refresh(); + Console.WriteLine("{0}", light.Color); + await Task.Delay(DELAY); + } + } + + private static async Task DemoModifyCollections(LifxClient client) + { + Console.WriteLine(); + List lights = (await client.ListLights(new Selector.LightLabel("LIFX 027d98"))); + Group group = (await client.ListGroups()).FirstOrDefault(); + if (group == null) + { + Console.WriteLine("No groups"); + } + else + { + Console.WriteLine("Using group: {0}", group); + Console.WriteLine("Toggling group, 3 second duration."); + await group.TogglePower(3); + await Task.Delay(DELAY + 3000); + + Console.WriteLine("Turning group green"); + await group.SetColor(LifxColor.Green, 0); + await Task.Delay(DELAY); + } + Location location = (await client.ListLocations()).FirstOrDefault(); + await Task.Delay(DELAY); + if (location == null) + { + Console.WriteLine("No locations"); + } + else + { + Console.WriteLine("Using location: {0}", location); + + Console.WriteLine("Turning off location"); + await location.SetPower(PowerState.Off); + await Task.Delay(DELAY); + + Console.WriteLine("Setting color to white with 5 second duration."); + await location.SetColor(new LifxColor.White(1), 5); + await Task.Delay(DELAY + 5000); + + Console.WriteLine("Set light to a blue color, using 90% brightness override. Uses SetState explicitely."); + await location.SetState(PowerState.On, new LifxColor.HSBK(180, 1, 1, 2000), 0.9, 2); + await Task.Delay(DELAY); + } + } + + private static async Task DemoEffects(LifxClient client) + { + Console.WriteLine(); + Light light = (await client.ListLights(new Selector.GroupLabel("Room 1"))).FirstOrDefault(); + if (light == null) + { + // Find a connected light + foreach (var l in await client.ListLights()) + { + if (l.IsConnected) + { + light = l; + break; + } + } + if (!light.IsConnected) + { + Console.WriteLine("No connected lights"); + return; + } + } + Console.WriteLine("Using light: {0}", light); + + Console.WriteLine("Pulsing slowly between previously set color and green"); + await light.PulseEffect(LifxColor.Green, 1, 10); + await Task.Delay(EFFECT_DELAY); + + /* If strobing with a single color (therefore the light alternates between the "color" parameter and "fromColor" + parameter which should be set to LifxColor.OffState (zero brightness) you must set the "color" parameter to a custom color + with a brightness value explicitly set. The default named parameters appear to leave the light in a fixed zero brightness state + until the end of the effect. */ + Console.WriteLine("Pulsing quickly in white"); + await light.PulseEffect(new LifxColor.White(1, 3500), 0.1, 50, fromColor: LifxColor.OffState); + await Task.Delay(EFFECT_DELAY); + + Console.WriteLine("Breathing red alert light."); + await light.BreatheEffect(new LifxColor.RGB(120, 0, 0), 2.5, 4, LifxColor.OffState, peak: 0.4d); + await Task.Delay(EFFECT_DELAY); + + Console.WriteLine("\"Party breathe\", will pass through intermediate colors."); + await light.BreatheEffect(LifxColor.Cyan, 5, 4, LifxColor.Orange, peak: 0.5d); + await Task.Delay(EFFECT_DELAY); + + Console.WriteLine("Cycling through some various states."); + List stateList = new List(); + stateList.Add(new LightState(PowerState.On, LifxColor.Pink, 0.5d)); + stateList.Add(new LightState(PowerState.On, LifxColor.Pink, 1.0d)); + stateList.Add(new LightState(PowerState.On, LifxColor.DefaultWhite, 1.0d)); + stateList.Add(new LightState(PowerState.On, LifxColor.Green)); + stateList.Add(new LightState(PowerState.On, LifxColor.Blue, 1.0d)); + stateList.Add(new LightState(PowerState.On, LifxColor.Blue, 0.5d)); + LightState defaults = new LightState(); + defaults.Duration = 3.0d; + defaults.Brightness = 1.0d; + + // Cycle forward + for (int i = 0; i < stateList.Count(); i++) + { + await light.Cycle(stateList, defaults); + await Task.Delay(4000); + } + // Cycle backward + for (int i = 0; i < stateList.Count(); i++) + { + await light.Cycle(stateList, defaults, Direction.Backward); + await Task.Delay(4000); + } + + //await location.PulseEffect(new LifxColor.White(1, 3500), 0.025, 10000, fromColor: new LifxColor.White(0.5f, 2000)); + } + } +} From 46fe13b797d8e540acb965cfe7401d91780211aa Mon Sep 17 00:00:00 2001 From: kowalej Date: Sun, 26 Mar 2017 15:14:53 -0400 Subject: [PATCH 5/7] Added color validation. --- .gitignore | 2 ++ LifxHttp/Helpers/LifxColorConverter.cs | 22 +++++++++++++++++----- LifxHttp/ILifxApi.cs | 5 ++++- LifxHttp/LifxClient.cs | 13 +++++++++++++ LifxHttp/Package.nuspec | 10 +++++----- LifxHttp/RefitStubs.cs | 6 ++++++ LifxHttpSample/LifxHttpSample.csproj | 1 + LifxHttpSample/Program.cs | 13 +++++++++++-- 8 files changed, 59 insertions(+), 13 deletions(-) diff --git a/.gitignore b/.gitignore index b4cd389..6e0b222 100644 --- a/.gitignore +++ b/.gitignore @@ -189,3 +189,5 @@ GeneratedArtifacts/ _Pvt_Extensions/ ModelManifest.xml *.nupkg + +LifxHttpSample/TestProgram.cs diff --git a/LifxHttp/Helpers/LifxColorConverter.cs b/LifxHttp/Helpers/LifxColorConverter.cs index 24241c6..f43090d 100644 --- a/LifxHttp/Helpers/LifxColorConverter.cs +++ b/LifxHttp/Helpers/LifxColorConverter.cs @@ -8,11 +8,23 @@ namespace LifxHttp.Helpers { + public static class JsonExtensions + { + public static bool IsNullOrEmpty(this JToken token) + { + return (token == null) || + (token.Type == JTokenType.Array && !token.HasValues) || + (token.Type == JTokenType.Object && !token.HasValues) || + (token.Type == JTokenType.String && token.ToString() == String.Empty) || + (token.Type == JTokenType.Null); + } + } + public class LifxColorConverter : JsonConverter { public override bool CanConvert(Type objectType) { - //Assume we can convert to anything for now + //Assume we can convert to anything for now. return true; } @@ -26,13 +38,13 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist double? brightness = null; int? kelvin = null; - if (token["hue"] != null) + if (!token["hue"].IsNullOrEmpty()) hue = (double)token["hue"]; - if (token["saturation"] != null) + if (!token["saturation"].IsNullOrEmpty()) saturation = (double)token["saturation"]; - if (token["brightness"] != null) + if (!token["brightness"].IsNullOrEmpty()) brightness = (double)token["brightness"]; - if (token["kelvin"] != null) + if (!token["kelvin"].IsNullOrEmpty()) kelvin = (int)token["kelvin"]; return new LifxColor.HSBK(hue, saturation, brightness, kelvin); } diff --git a/LifxHttp/ILifxApi.cs b/LifxHttp/ILifxApi.cs index eee74ef..00490da 100644 --- a/LifxHttp/ILifxApi.cs +++ b/LifxHttp/ILifxApi.cs @@ -13,7 +13,10 @@ internal interface ILifxApi Task> ListLights([Header("Authorization")] string auth, string selector); [Get("/scenes")] - Task> ListScenes([Header("Authorization")] string auth); + Task> ListScenes([Header("Authorization")] string auth); + + [Get("/color?string={colorName}")] + Task ValidateColor([Header("Authorization")] string auth, string colorName); [Put("/lights/{selector}/state")] [Headers("Content-Type: application/x-www-form-urlencoded")] diff --git a/LifxHttp/LifxClient.cs b/LifxHttp/LifxClient.cs index 184f152..78da604 100644 --- a/LifxHttp/LifxClient.cs +++ b/LifxHttp/LifxClient.cs @@ -314,5 +314,18 @@ public async Task Cycle(Selector selector, List states, return e.GetContentAs(); } } + + public async Task> ValidateColor(string colorName) + { + try + { + var color = await lifxApi.ValidateColor(auth, colorName); + return new Tuple(true, color); + } + catch (Refit.ApiException e) + { + return new Tuple(false, new LifxColor.HSBK()); + } + } } } diff --git a/LifxHttp/Package.nuspec b/LifxHttp/Package.nuspec index c5a209a..749c1a5 100644 --- a/LifxHttp/Package.nuspec +++ b/LifxHttp/Package.nuspec @@ -1,17 +1,17 @@ - + $id$ $version$ - Michael Ensly + Michael Ensly, Jason Kowaleski $author$ https://raw.githubusercontent.com/mensly/LifxHttpNet/master/COPYING https://github.com/mensly/LifxHttpNet false $description$ - Initial release with most simple remote tasks available - Copyright 2015 - lifx http iot cloud lighting remote + Updated release which is up to date with V1.0 of LIFX API. + Copyright 2017 + lifx http iot cloud lighting remote smart lighting diff --git a/LifxHttp/RefitStubs.cs b/LifxHttp/RefitStubs.cs index 826ee28..1ea36f7 100644 --- a/LifxHttp/RefitStubs.cs +++ b/LifxHttp/RefitStubs.cs @@ -56,6 +56,12 @@ public virtual Task> ListScenes(string auth) return (Task>) methodImpls["ListScenes"](Client, arguments); } + public virtual Task ValidateColor(string auth,string colorName) + { + var arguments = new object[] { auth,colorName }; + return (Task) methodImpls["ValidateColor"](Client, arguments); + } + public virtual Task SetState(string auth,string selector,string args) { var arguments = new object[] { auth,selector,args }; diff --git a/LifxHttpSample/LifxHttpSample.csproj b/LifxHttpSample/LifxHttpSample.csproj index 502eae4..2d89916 100644 --- a/LifxHttpSample/LifxHttpSample.csproj +++ b/LifxHttpSample/LifxHttpSample.csproj @@ -54,6 +54,7 @@ + diff --git a/LifxHttpSample/Program.cs b/LifxHttpSample/Program.cs index e617dc7..2faf987 100644 --- a/LifxHttpSample/Program.cs +++ b/LifxHttpSample/Program.cs @@ -10,7 +10,7 @@ namespace LifxHttpSample { class Program { - private const string TOKEN = "REDACTED - Generate from https://cloud.lifx.com/settingss"; + private const string TOKEN = "REDACTED - Generate from https://cloud.lifx.com/settings"; private const int DELAY = 2000; private const int EFFECT_DELAY = 10000; @@ -34,6 +34,7 @@ private static async Task RunDemos(LifxClient client) await DemoModifyLight(client); await DemoModifyCollections(client); await DemoEffects(client); + await DemoValidateColor(client); } private static async Task DemoListing(LifxClient client) @@ -241,8 +242,16 @@ until the end of the effect. */ await light.Cycle(stateList, defaults, Direction.Backward); await Task.Delay(4000); } + } - //await location.PulseEffect(new LifxColor.White(1, 3500), 0.025, 10000, fromColor: new LifxColor.White(0.5f, 2000)); + private static async Task DemoValidateColor(LifxClient client) + { + string colorName = "Pink"; + Console.WriteLine(); + Console.WriteLine(string.Format("Validating color: {0}", colorName)); + var color = await client.ValidateColor(colorName); + if (color.Item1) { Console.WriteLine(string.Format("{0} is a valid color name. Values are {1}.", colorName, color.Item2)); } + else Console.WriteLine(string.Format("{0} is not a valid color name.", colorName)); } } } From af6e424a3a2aa0aba61584ba6bee6a919bfa4556 Mon Sep 17 00:00:00 2001 From: kowalej Date: Mon, 27 Mar 2017 00:47:27 -0400 Subject: [PATCH 6/7] Updated README to list functions --- README.md | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b7578ef..941629f 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,29 @@ # LifxHttpNet -.Net wrapper around the Lifx cloud HTTP API +### .Net wrapper around the LIFX cloud HTTP API: -* This library acts as a thin wrapper around the public Lifx HTTP API as described +* This library acts as a thin wrapper around the public LIFX HTTP API as described at http://developer.lifx.com * All operations make use of the async Task APIs via [Refit](http://paulcbetts.github.io/refit/) for REST calls * This library does not perform any caching or management of model objects other than to simplify accessing the API -* I hope that this library helps enthusiasts tinker with their Lifx lights, though much +* I hope that this library helps enthusiasts tinker with their LIFX lights, though much more backend code would be required for a full-featured Lifx client * Use of this library requires a Personal Access token acquired from: https://cloud.lifx.com/settings * Use of this library should only be in accordance to the terms at: - http://developer.lifx.com/terms.html \ No newline at end of file + http://developer.lifx.com/terms.html + +### Supports the following operations: +* List Lights +* Toggle Power +* Set Power State +* Set Color +* Set State +* Set States +* Breathe Effect +* Pulse Effect +* Cycle +* List Scenes +* Activate Scene +* Validate Color \ No newline at end of file From 81f73038308e742e251322dfdb723286cbd23f53 Mon Sep 17 00:00:00 2001 From: kowalej Date: Mon, 3 Apr 2017 23:47:41 -0400 Subject: [PATCH 7/7] Fixed demo program --- LifxHttpSample/Program.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/LifxHttpSample/Program.cs b/LifxHttpSample/Program.cs index 2faf987..8d16dd3 100644 --- a/LifxHttpSample/Program.cs +++ b/LifxHttpSample/Program.cs @@ -102,7 +102,7 @@ private static async Task DemoModifyLight(LifxClient client) } Console.WriteLine("Using light: {0}", light); Console.WriteLine("Turning light off"); - //await light.SetPower(false); + await light.SetPower(false); await Task.Delay(DELAY); Console.WriteLine("Toggling light on"); await light.TogglePower(); @@ -218,7 +218,6 @@ until the end of the effect. */ await light.BreatheEffect(LifxColor.Cyan, 5, 4, LifxColor.Orange, peak: 0.5d); await Task.Delay(EFFECT_DELAY); - Console.WriteLine("Cycling through some various states."); List stateList = new List(); stateList.Add(new LightState(PowerState.On, LifxColor.Pink, 0.5d)); stateList.Add(new LightState(PowerState.On, LifxColor.Pink, 1.0d)); @@ -231,12 +230,17 @@ until the end of the effect. */ defaults.Brightness = 1.0d; // Cycle forward + Console.WriteLine("Cycling forward through set of 6 light states."); for (int i = 0; i < stateList.Count(); i++) { await light.Cycle(stateList, defaults); await Task.Delay(4000); } + + await Task.Delay(1000); + // Cycle backward + Console.WriteLine("Cycling backward through set of 6 light states."); for (int i = 0; i < stateList.Count(); i++) { await light.Cycle(stateList, defaults, Direction.Backward);