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