Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/CodeCasa.Abstractions/StateChange.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace CodeCasa.Abstractions;

/// <summary>
/// Represents a change in state for an entity.
/// </summary>
/// <typeparam name="TEntity">The type of the entity.</typeparam>
/// <typeparam name="TState">The type of the state.</typeparam>
/// <param name="Entity">The entity whose state changed.</param>
/// <param name="Old">The old state.</param>
/// <param name="New">The new state.</param>
public record StateChange<TEntity, TState>(TEntity Entity, TState? Old, TState? New);
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public partial interface ILightTransitionReactiveNodeConfigurator
/// <summary>
/// Adds a time-based toggle trigger that switches between the specified light parameters when triggered by <paramref name="triggerObservable"/>.
/// 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.
/// </summary>
/// <typeparam name="T">The type of values emitted by the trigger observable.</typeparam>
/// <param name="triggerObservable">The observable that triggers toggling to the next parameters.</param>
Expand All @@ -20,6 +21,7 @@ ILightTransitionReactiveNodeConfigurator AddToggle<T>(IObservable<T> triggerObse
/// <summary>
/// Adds a time-based toggle trigger that switches between the specified light parameters when triggered by <paramref name="triggerObservable"/>.
/// 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.
/// </summary>
/// <typeparam name="T">The type of values emitted by the trigger observable.</typeparam>
/// <param name="triggerObservable">The observable that triggers toggling to the next parameters.</param>
Expand All @@ -31,6 +33,7 @@ ILightTransitionReactiveNodeConfigurator AddToggle<T>(IObservable<T> triggerObse
/// <summary>
/// Adds a time-based toggle trigger that switches between the specified light transitions when triggered by <paramref name="triggerObservable"/>.
/// 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.
/// </summary>
/// <typeparam name="T">The type of values emitted by the trigger observable.</typeparam>
/// <param name="triggerObservable">The observable that triggers toggling to the next transition.</param>
Expand All @@ -42,6 +45,7 @@ ILightTransitionReactiveNodeConfigurator AddToggle<T>(IObservable<T> triggerObse
/// <summary>
/// Adds a time-based toggle trigger that switches between the specified light transitions when triggered by <paramref name="triggerObservable"/>.
/// 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.
/// </summary>
/// <typeparam name="T">The type of values emitted by the trigger observable.</typeparam>
/// <param name="triggerObservable">The observable that triggers toggling to the next transition.</param>
Expand All @@ -53,6 +57,7 @@ ILightTransitionReactiveNodeConfigurator AddToggle<T>(IObservable<T> triggerObse
/// <summary>
/// Adds a time-based toggle trigger that switches between nodes created by the specified factory functions when triggered by <paramref name="triggerObservable"/>.
/// 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.
/// </summary>
/// <typeparam name="T">The type of values emitted by the trigger observable.</typeparam>
/// <param name="triggerObservable">The observable that triggers toggling to the next node.</param>
Expand All @@ -64,6 +69,7 @@ ILightTransitionReactiveNodeConfigurator AddToggle<T>(IObservable<T> triggerObse
/// <summary>
/// Adds a time-based toggle trigger that switches between nodes created by the specified factory functions when triggered by <paramref name="triggerObservable"/>.
/// 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.
/// </summary>
/// <typeparam name="T">The type of values emitted by the trigger observable.</typeparam>
/// <param name="triggerObservable">The observable that triggers toggling to the next node.</param>
Expand All @@ -75,6 +81,7 @@ ILightTransitionReactiveNodeConfigurator AddToggle<T>(IObservable<T> triggerObse
/// <summary>
/// Adds a time-based toggle trigger configured by the specified <paramref name="configure"/> action when triggered by <paramref name="triggerObservable"/>.
/// 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.
/// </summary>
/// <typeparam name="T">The type of values emitted by the trigger observable.</typeparam>
/// <param name="triggerObservable">The observable that triggers toggling to the next state.</param>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace CodeCasa.AutomationPipelines.Lights.Toggle
/// <summary>
/// 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.
/// </summary>
public interface ILightTransitionToggleConfigurator
{
Expand Down
35 changes: 28 additions & 7 deletions src/CodeCasa.Lights.NetDaemon/NetDaemonLight.cs
Original file line number Diff line number Diff line change
@@ -1,32 +1,43 @@
using CodeCasa.Lights.NetDaemon.Extensions;
using CodeCasa.Lights.NetDaemon.Generated;
using NetDaemon.HassModel.Entities;
using System.Reactive.Linq;

namespace CodeCasa.Lights.NetDaemon
{
/// <summary>
/// Adapts a NetDaemon light entity core to the <see cref="ILight"/> interface.
/// </summary>
public class NetDaemonLight(ILightEntityCore lightEntity) : ILight
public class NetDaemonLight : ILight
{
private readonly LightEntity _lightEntity;

/// <summary>
/// Adapts a NetDaemon light entity core to the <see cref="ILight"/> interface.
/// </summary>
public NetDaemonLight(ILightEntityCore lightEntity)
{
_lightEntity = new LightEntity(lightEntity);
}

/// <summary>
/// Gets the unique identifier for this light entity.
/// </summary>
public string Id => lightEntity.EntityId;
public string Id => _lightEntity.EntityId;

/// <summary>
/// Gets the current parameters of the light entity.
/// </summary>
/// <returns>A <see cref="LightParameters"/> object representing the current state of the light.</returns>
public LightParameters GetParameters() => lightEntity.GetLightParameters();
public LightParameters GetParameters() => _lightEntity.GetLightParameters();

/// <summary>
/// Applies a light transition to this light entity.
/// </summary>
/// <param name="transition">The transition to apply to the light.</param>
public void ApplyTransition(LightTransition transition)
{
lightEntity.ApplyTransition(transition);
_lightEntity.ApplyTransition(transition);
}

/// <summary>
Expand All @@ -35,20 +46,30 @@ public void ApplyTransition(LightTransition transition)
/// <returns>An array of child light entities wrapped as <see cref="ILight"/> instances, or an empty array if this light has no children.</returns>
public ILight[] GetChildren()
{
var childEntityIds = new LightEntity(lightEntity).Attributes?.EntityId;
var childEntityIds = new LightEntity(_lightEntity).Attributes?.EntityId;
if (childEntityIds == null || !childEntityIds.Any())
{
return [];
}
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<ILight>();
}

/// <inheritdoc/>
public IObservable<Abstractions.StateChange<ILight, LightParameters>> StateChanges() =>
_lightEntity.StateChanges().Select(sc => new Abstractions.StateChange<ILight, LightParameters>(this,
sc.Old?.Attributes?.ToLightParameters(), sc.New?.Attributes?.ToLightParameters()));

/// <inheritdoc/>
public IObservable<Abstractions.StateChange<ILight, LightParameters>> StateChangesWithCurrent() =>
_lightEntity.StateChangesWithCurrent().Select(sc => new Abstractions.StateChange<ILight, LightParameters>(this,
sc.Old?.Attributes?.ToLightParameters(), sc.New?.Attributes?.ToLightParameters()));
}
}
4 changes: 4 additions & 0 deletions src/CodeCasa.Lights/CodeCasa.Lights.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,9 @@
<PackagePath>\</PackagePath>
</None>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\CodeCasa.Abstractions\CodeCasa.Abstractions.csproj" />
</ItemGroup>

</Project>
16 changes: 15 additions & 1 deletion src/CodeCasa.Lights/ILight.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace CodeCasa.Lights;
using CodeCasa.Abstractions;

namespace CodeCasa.Lights;

/// <summary>
/// Represents a single light or group of lights.
Expand Down Expand Up @@ -26,4 +28,16 @@ public interface ILight
/// </summary>
/// <returns>An array of child lights, or an empty array if this light has no children.</returns>
ILight[] GetChildren();

/// <summary>
/// Returns an observable that emits notifications when the state of the light changes.
/// </summary>
/// <returns>An <see cref="IObservable{T}"/> that emits <see cref="StateChange{ILight, LightParameters}"/> events.</returns>
IObservable<StateChange<ILight, LightParameters>> StateChanges();

/// <summary>
/// Returns an observable that emits the current state and then notifications when the state of the light changes.
/// </summary>
/// <returns>An <see cref="IObservable{T}"/> that emits <see cref="StateChange{ILight, LightParameters}"/> events.</returns>
IObservable<StateChange<ILight, LightParameters>> StateChangesWithCurrent();
}