diff --git a/src/CodeCasa.Abstractions/StateChange.cs b/src/CodeCasa.Abstractions/StateChange.cs new file mode 100644 index 0000000..6c9ff28 --- /dev/null +++ b/src/CodeCasa.Abstractions/StateChange.cs @@ -0,0 +1,11 @@ +namespace CodeCasa.Abstractions; + +/// +/// Represents a change in state for an entity. +/// +/// The type of the entity. +/// The type of the state. +/// The entity whose state changed. +/// The old state. +/// The new state. +public record StateChange(TEntity Entity, TState? Old, TState? New); \ No newline at end of file diff --git a/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/ILightTransitionReactiveNodeConfigurator.Toggle.cs b/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/ILightTransitionReactiveNodeConfigurator.Toggle.cs index 7d8b7e9..f40687a 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/ILightTransitionReactiveNodeConfigurator.Toggle.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/ILightTransitionReactiveNodeConfigurator.Toggle.cs @@ -9,6 +9,7 @@ public partial interface ILightTransitionReactiveNodeConfigurator /// /// Adds a time-based toggle trigger that switches between the specified light parameters when triggered by . /// Quick consecutive triggers advance through all parameter sets sequentially. After a timeout period, the next trigger restarts from the beginning. + /// If the light is currently on, the first trigger will turn it off. /// /// The type of values emitted by the trigger observable. /// The observable that triggers toggling to the next parameters. @@ -20,6 +21,7 @@ ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObse /// /// Adds a time-based toggle trigger that switches between the specified light parameters when triggered by . /// Quick consecutive triggers advance through all parameter sets sequentially. After a timeout period, the next trigger restarts from the beginning. + /// If the light is currently on, the first trigger will turn it off. /// /// The type of values emitted by the trigger observable. /// The observable that triggers toggling to the next parameters. @@ -31,6 +33,7 @@ ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObse /// /// Adds a time-based toggle trigger that switches between the specified light transitions when triggered by . /// Quick consecutive triggers advance through all transitions sequentially. After a timeout period, the next trigger restarts from the beginning. + /// If the light is currently on, the first trigger will turn it off. /// /// The type of values emitted by the trigger observable. /// The observable that triggers toggling to the next transition. @@ -42,6 +45,7 @@ ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObse /// /// Adds a time-based toggle trigger that switches between the specified light transitions when triggered by . /// Quick consecutive triggers advance through all transitions sequentially. After a timeout period, the next trigger restarts from the beginning. + /// If the light is currently on, the first trigger will turn it off. /// /// The type of values emitted by the trigger observable. /// The observable that triggers toggling to the next transition. @@ -53,6 +57,7 @@ ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObse /// /// Adds a time-based toggle trigger that switches between nodes created by the specified factory functions when triggered by . /// Quick consecutive triggers advance through all node factories sequentially. After a timeout period, the next trigger restarts from the beginning. + /// If the light is currently on, the first trigger will turn it off. /// /// The type of values emitted by the trigger observable. /// The observable that triggers toggling to the next node. @@ -64,6 +69,7 @@ ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObse /// /// Adds a time-based toggle trigger that switches between nodes created by the specified factory functions when triggered by . /// Quick consecutive triggers advance through all node factories sequentially. After a timeout period, the next trigger restarts from the beginning. + /// If the light is currently on, the first trigger will turn it off. /// /// The type of values emitted by the trigger observable. /// The observable that triggers toggling to the next node. @@ -75,6 +81,7 @@ ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObse /// /// Adds a time-based toggle trigger configured by the specified action when triggered by . /// Quick consecutive triggers advance through all configured states sequentially. After a timeout period, the next trigger restarts from the beginning. + /// If the light is currently on, the first trigger will turn it off. /// /// The type of values emitted by the trigger observable. /// The observable that triggers toggling to the next state. diff --git a/src/CodeCasa.AutomationPipelines.Lights/Toggle/ILightTransitionToggleConfigurator.cs b/src/CodeCasa.AutomationPipelines.Lights/Toggle/ILightTransitionToggleConfigurator.cs index 0a54a39..6af0141 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/Toggle/ILightTransitionToggleConfigurator.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/Toggle/ILightTransitionToggleConfigurator.cs @@ -6,6 +6,7 @@ namespace CodeCasa.AutomationPipelines.Lights.Toggle /// /// Configurator for time-based toggle behavior. Quick consecutive triggers advance through all states sequentially. /// After a timeout period, the next trigger restarts from the beginning. + /// If the light is currently on, the first trigger will turn it off. /// public interface ILightTransitionToggleConfigurator { diff --git a/src/CodeCasa.Lights.NetDaemon/NetDaemonLight.cs b/src/CodeCasa.Lights.NetDaemon/NetDaemonLight.cs index 9e73bb1..9c6d0e0 100644 --- a/src/CodeCasa.Lights.NetDaemon/NetDaemonLight.cs +++ b/src/CodeCasa.Lights.NetDaemon/NetDaemonLight.cs @@ -1,24 +1,35 @@ using CodeCasa.Lights.NetDaemon.Extensions; using CodeCasa.Lights.NetDaemon.Generated; using NetDaemon.HassModel.Entities; +using System.Reactive.Linq; namespace CodeCasa.Lights.NetDaemon { /// /// Adapts a NetDaemon light entity core to the interface. /// - public class NetDaemonLight(ILightEntityCore lightEntity) : ILight + public class NetDaemonLight : ILight { + private readonly LightEntity _lightEntity; + + /// + /// Adapts a NetDaemon light entity core to the interface. + /// + public NetDaemonLight(ILightEntityCore lightEntity) + { + _lightEntity = new LightEntity(lightEntity); + } + /// /// Gets the unique identifier for this light entity. /// - public string Id => lightEntity.EntityId; + public string Id => _lightEntity.EntityId; /// /// Gets the current parameters of the light entity. /// /// A object representing the current state of the light. - public LightParameters GetParameters() => lightEntity.GetLightParameters(); + public LightParameters GetParameters() => _lightEntity.GetLightParameters(); /// /// Applies a light transition to this light entity. @@ -26,7 +37,7 @@ public class NetDaemonLight(ILightEntityCore lightEntity) : ILight /// The transition to apply to the light. public void ApplyTransition(LightTransition transition) { - lightEntity.ApplyTransition(transition); + _lightEntity.ApplyTransition(transition); } /// @@ -35,7 +46,7 @@ public void ApplyTransition(LightTransition transition) /// An array of child light entities wrapped as instances, or an empty array if this light has no children. public ILight[] GetChildren() { - var childEntityIds = new LightEntity(lightEntity).Attributes?.EntityId; + var childEntityIds = new LightEntity(_lightEntity).Attributes?.EntityId; if (childEntityIds == null || !childEntityIds.Any()) { return []; @@ -43,12 +54,22 @@ public ILight[] GetChildren() return childEntityIds.Select(id => { // A group light may include itself in its list of children; avoid infinite recursion by returning this instance. - if (id == lightEntity.EntityId) + if (id == _lightEntity.EntityId) { return this; } - return new NetDaemonLight(new LightEntity(lightEntity.HaContext, id)); + return new NetDaemonLight(new LightEntity(_lightEntity.HaContext, id)); }).ToArray(); } + + /// + public IObservable> StateChanges() => + _lightEntity.StateChanges().Select(sc => new Abstractions.StateChange(this, + sc.Old?.Attributes?.ToLightParameters(), sc.New?.Attributes?.ToLightParameters())); + + /// + public IObservable> StateChangesWithCurrent() => + _lightEntity.StateChangesWithCurrent().Select(sc => new Abstractions.StateChange(this, + sc.Old?.Attributes?.ToLightParameters(), sc.New?.Attributes?.ToLightParameters())); } } diff --git a/src/CodeCasa.Lights/CodeCasa.Lights.csproj b/src/CodeCasa.Lights/CodeCasa.Lights.csproj index 6f3865f..09abe56 100644 --- a/src/CodeCasa.Lights/CodeCasa.Lights.csproj +++ b/src/CodeCasa.Lights/CodeCasa.Lights.csproj @@ -32,5 +32,9 @@ \ + + + + diff --git a/src/CodeCasa.Lights/ILight.cs b/src/CodeCasa.Lights/ILight.cs index 65b7c9a..74cbfc7 100644 --- a/src/CodeCasa.Lights/ILight.cs +++ b/src/CodeCasa.Lights/ILight.cs @@ -1,4 +1,6 @@ -namespace CodeCasa.Lights; +using CodeCasa.Abstractions; + +namespace CodeCasa.Lights; /// /// Represents a single light or group of lights. @@ -26,4 +28,16 @@ public interface ILight /// /// An array of child lights, or an empty array if this light has no children. ILight[] GetChildren(); + + /// + /// Returns an observable that emits notifications when the state of the light changes. + /// + /// An that emits events. + IObservable> StateChanges(); + + /// + /// Returns an observable that emits the current state and then notifications when the state of the light changes. + /// + /// An that emits events. + IObservable> StateChangesWithCurrent(); } \ No newline at end of file