diff --git a/src/CodeCasa.AutomationPipelines.Lights.NetDaemon/Extensions/LightPipelineFactoryExtensions.cs b/src/CodeCasa.AutomationPipelines.Lights.NetDaemon/Extensions/LightPipelineFactoryExtensions.cs index 8c0240f..fe3d1e0 100644 --- a/src/CodeCasa.AutomationPipelines.Lights.NetDaemon/Extensions/LightPipelineFactoryExtensions.cs +++ b/src/CodeCasa.AutomationPipelines.Lights.NetDaemon/Extensions/LightPipelineFactoryExtensions.cs @@ -1,4 +1,5 @@ using CodeCasa.AutomationPipelines.Lights.Pipeline; +using CodeCasa.Lights.NetDaemon; using CodeCasa.Lights.NetDaemon.Extensions; using NetDaemon.HassModel.Entities; @@ -17,7 +18,7 @@ public static class LightPipelineFactoryExtensions /// An action to configure the pipeline behavior. /// An async disposable representing the created pipeline(s) that can be disposed to clean up resources. public static IAsyncDisposable SetupLightPipeline(this LightPipelineFactory lightPipelineFactory, ILightEntityCore lightEntity, - Action pipelineBuilder) + Action> pipelineBuilder) { return lightPipelineFactory.SetupLightPipeline(lightEntity.AsLight(), pipelineBuilder); } diff --git a/src/CodeCasa.AutomationPipelines.Lights.NetDaemon/Extensions/LightTransitionCycleConfiguratorExtensions.cs b/src/CodeCasa.AutomationPipelines.Lights.NetDaemon/Extensions/LightTransitionCycleConfiguratorExtensions.cs index 6449c5f..6a10b3e 100644 --- a/src/CodeCasa.AutomationPipelines.Lights.NetDaemon/Extensions/LightTransitionCycleConfiguratorExtensions.cs +++ b/src/CodeCasa.AutomationPipelines.Lights.NetDaemon/Extensions/LightTransitionCycleConfiguratorExtensions.cs @@ -1,4 +1,5 @@ using CodeCasa.AutomationPipelines.Lights.Cycle; +using CodeCasa.Lights.NetDaemon; using CodeCasa.Lights.NetDaemon.Extensions; using NetDaemon.HassModel.Entities; @@ -17,8 +18,8 @@ public static class LightTransitionCycleConfiguratorExtensions /// An action to configure the cycle for this specific light. /// Specifies the behavior for lights not included in this scoped configuration. Defaults to . /// The configurator instance for method chaining. - public static ILightTransitionCycleConfigurator ForLight(this ILightTransitionCycleConfigurator configurator, - ILightEntityCore lightEntity, Action configure, + public static ILightTransitionCycleConfigurator ForLight(this ILightTransitionCycleConfigurator configurator, + ILightEntityCore lightEntity, Action> configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None) { return configurator.ForLight(lightEntity.AsLight(), configure, excludedLightBehaviour); @@ -32,8 +33,8 @@ public static ILightTransitionCycleConfigurator ForLight(this ILightTransitionCy /// An action to configure the cycle for these lights. /// Specifies the behavior for lights not included in this scoped configuration. Defaults to . /// The configurator instance for method chaining. - public static ILightTransitionCycleConfigurator ForLights(this ILightTransitionCycleConfigurator configurator, - IEnumerable lightEntities, Action configure, + public static ILightTransitionCycleConfigurator ForLights(this ILightTransitionCycleConfigurator configurator, + IEnumerable lightEntities, Action> configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None) { return configurator.ForLights(lightEntities.Select(l => l.AsLight()), configure, excludedLightBehaviour); diff --git a/src/CodeCasa.AutomationPipelines.Lights.NetDaemon/Extensions/LightTransitionPipelineConfiguratorExtensions.cs b/src/CodeCasa.AutomationPipelines.Lights.NetDaemon/Extensions/LightTransitionPipelineConfiguratorExtensions.cs index 9483207..462b5f4 100644 --- a/src/CodeCasa.AutomationPipelines.Lights.NetDaemon/Extensions/LightTransitionPipelineConfiguratorExtensions.cs +++ b/src/CodeCasa.AutomationPipelines.Lights.NetDaemon/Extensions/LightTransitionPipelineConfiguratorExtensions.cs @@ -1,4 +1,5 @@ using CodeCasa.AutomationPipelines.Lights.Pipeline; +using CodeCasa.Lights.NetDaemon; using CodeCasa.Lights.NetDaemon.Extensions; using NetDaemon.HassModel.Entities; @@ -16,8 +17,8 @@ public static class LightTransitionPipelineConfiguratorExtensions /// The NetDaemon light entity to configure. /// An action to configure the pipeline for this specific light. /// The configurator instance for method chaining. - public static ILightTransitionPipelineConfigurator ForLight(this ILightTransitionPipelineConfigurator configurator, - ILightEntityCore lightEntity, Action compositeNodeBuilder) + public static ILightTransitionPipelineConfigurator ForLight(this ILightTransitionPipelineConfigurator configurator, + ILightEntityCore lightEntity, Action> compositeNodeBuilder) { return configurator.ForLight(lightEntity.AsLight(), compositeNodeBuilder); } @@ -29,8 +30,8 @@ public static ILightTransitionPipelineConfigurator ForLight(this ILightTransitio /// The NetDaemon light entities to configure. /// An action to configure the pipeline for these lights. /// The configurator instance for method chaining. - public static ILightTransitionPipelineConfigurator ForLights(this ILightTransitionPipelineConfigurator configurator, - IEnumerable lightEntities, Action compositeNodeBuilder) + public static ILightTransitionPipelineConfigurator ForLights(this ILightTransitionPipelineConfigurator configurator, + IEnumerable lightEntities, Action> compositeNodeBuilder) { return configurator.ForLights(lightEntities.Select(l => l.AsLight()), compositeNodeBuilder); } diff --git a/src/CodeCasa.AutomationPipelines.Lights.NetDaemon/Extensions/LightTransitionReactiveNodeConfiguratorExtensions.cs b/src/CodeCasa.AutomationPipelines.Lights.NetDaemon/Extensions/LightTransitionReactiveNodeConfiguratorExtensions.cs index 01b93d4..e54e1bc 100644 --- a/src/CodeCasa.AutomationPipelines.Lights.NetDaemon/Extensions/LightTransitionReactiveNodeConfiguratorExtensions.cs +++ b/src/CodeCasa.AutomationPipelines.Lights.NetDaemon/Extensions/LightTransitionReactiveNodeConfiguratorExtensions.cs @@ -1,4 +1,5 @@ using CodeCasa.AutomationPipelines.Lights.ReactiveNode; +using CodeCasa.Lights.NetDaemon; using CodeCasa.Lights.NetDaemon.Extensions; using NetDaemon.HassModel.Entities; @@ -16,9 +17,9 @@ public static class LightTransitionReactiveNodeConfiguratorExtensions /// The NetDaemon light entity to configure. /// An action to configure the reactive node for this specific light. /// The configurator instance for method chaining. - public static ILightTransitionReactiveNodeConfigurator ForLight( - this ILightTransitionReactiveNodeConfigurator configurator, - ILightEntityCore lightEntity, Action configure) + public static ILightTransitionReactiveNodeConfigurator ForLight( + this ILightTransitionReactiveNodeConfigurator configurator, + ILightEntityCore lightEntity, Action> configure) { return configurator.ForLight(lightEntity.AsLight(), configure); } @@ -30,9 +31,9 @@ public static ILightTransitionReactiveNodeConfigurator ForLight( /// The NetDaemon light entities to configure. /// An action to configure the reactive node for these lights. /// The configurator instance for method chaining. - public static ILightTransitionReactiveNodeConfigurator ForLights( - this ILightTransitionReactiveNodeConfigurator configurator, - IEnumerable lightEntities, Action configure) + public static ILightTransitionReactiveNodeConfigurator ForLights( + this ILightTransitionReactiveNodeConfigurator configurator, + IEnumerable lightEntities, Action> configure) { return configurator.ForLights(lightEntities.Select(l => l.AsLight()), configure); } diff --git a/src/CodeCasa.AutomationPipelines.Lights.NetDaemon/Extensions/LightTransitionToggleConfiguratorExtensions.cs b/src/CodeCasa.AutomationPipelines.Lights.NetDaemon/Extensions/LightTransitionToggleConfiguratorExtensions.cs index 7bb5934..712a416 100644 --- a/src/CodeCasa.AutomationPipelines.Lights.NetDaemon/Extensions/LightTransitionToggleConfiguratorExtensions.cs +++ b/src/CodeCasa.AutomationPipelines.Lights.NetDaemon/Extensions/LightTransitionToggleConfiguratorExtensions.cs @@ -1,4 +1,5 @@ using CodeCasa.AutomationPipelines.Lights.Toggle; +using CodeCasa.Lights.NetDaemon; using CodeCasa.Lights.NetDaemon.Extensions; using NetDaemon.HassModel.Entities; @@ -17,8 +18,8 @@ public static class LightTransitionToggleConfiguratorExtensions /// An action to configure the toggle for this specific light. /// Specifies the behavior for lights not included in this scoped configuration. Defaults to . /// The configurator instance for method chaining. - public static ILightTransitionToggleConfigurator ForLight(this ILightTransitionToggleConfigurator configurator, - ILightEntityCore lightEntity, Action configure, + public static ILightTransitionToggleConfigurator ForLight(this ILightTransitionToggleConfigurator configurator, + ILightEntityCore lightEntity, Action> configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None) { return configurator.ForLight(lightEntity.AsLight(), configure, excludedLightBehaviour); @@ -32,8 +33,8 @@ public static ILightTransitionToggleConfigurator ForLight(this ILightTransitionT /// An action to configure the toggle for these lights. /// Specifies the behavior for lights not included in this scoped configuration. Defaults to . /// The configurator instance for method chaining. - public static ILightTransitionToggleConfigurator ForLights(this ILightTransitionToggleConfigurator configurator, - IEnumerable lightEntities, Action configure, + public static ILightTransitionToggleConfigurator ForLights(this ILightTransitionToggleConfigurator configurator, + IEnumerable lightEntities, Action> configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None) { return configurator.ForLights(lightEntities.Select(l => l.AsLight()), configure, excludedLightBehaviour); diff --git a/src/CodeCasa.AutomationPipelines.Lights/CodeCasa.AutomationPipelines.Lights.csproj b/src/CodeCasa.AutomationPipelines.Lights/CodeCasa.AutomationPipelines.Lights.csproj index c18f3ad..5d21a86 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/CodeCasa.AutomationPipelines.Lights.csproj +++ b/src/CodeCasa.AutomationPipelines.Lights/CodeCasa.AutomationPipelines.Lights.csproj @@ -38,6 +38,10 @@ + + + + diff --git a/src/CodeCasa.AutomationPipelines.Lights/CompositeHelper.cs b/src/CodeCasa.AutomationPipelines.Lights/CompositeHelper.cs index dcc56af..82805a7 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/CompositeHelper.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/CompositeHelper.cs @@ -46,13 +46,13 @@ public static void ValidateLightSupported(IEnumerable lights, string sup } } - public static string[] ResolveGroupsAndValidateLightsSupported(IEnumerable lights, IEnumerable supportedLightIds) + public static string[] ResolveGroupsAndValidateLightsSupported(IEnumerable lights, IEnumerable supportedLightIds) where TLight : ILight { return ValidateLightsSupported(lights.SelectMany(le => le.Flatten()).Select(l => l.Id).Distinct(), supportedLightIds); } - public static void ResolveGroupsAndValidateLightSupported(IEnumerable lights, string supportedLightId) + public static void ResolveGroupsAndValidateLightSupported(IEnumerable lights, string supportedLightId) where TLight : ILight { ValidateLightSupported(lights.SelectMany(le => le.Flatten()).Select(l => l.Id).Distinct(), supportedLightId); } diff --git a/src/CodeCasa.AutomationPipelines.Lights/Context/ILightPipelineContext.cs b/src/CodeCasa.AutomationPipelines.Lights/Context/ILightPipelineContext.cs deleted file mode 100644 index 14f0e94..0000000 --- a/src/CodeCasa.AutomationPipelines.Lights/Context/ILightPipelineContext.cs +++ /dev/null @@ -1,19 +0,0 @@ -using CodeCasa.Lights; - -namespace CodeCasa.AutomationPipelines.Lights.Context; - -/// -/// Represents the context for a light pipeline, providing access to the service provider and the light being controlled. -/// -public interface ILightPipelineContext -{ - /// - /// Gets the service provider instance used to resolve dependencies in the pipeline. - /// - IServiceProvider ServiceProvider { get; } - - /// - /// Gets the light being controlled by the pipeline. - /// - ILight Light { get; } -} \ No newline at end of file diff --git a/src/CodeCasa.AutomationPipelines.Lights/Context/LightPipelineContext.cs b/src/CodeCasa.AutomationPipelines.Lights/Context/LightPipelineContext.cs deleted file mode 100644 index d725d59..0000000 --- a/src/CodeCasa.AutomationPipelines.Lights/Context/LightPipelineContext.cs +++ /dev/null @@ -1,24 +0,0 @@ -using CodeCasa.Lights; - -namespace CodeCasa.AutomationPipelines.Lights.Context; - -/// -public class LightPipelineContext : ILightPipelineContext -{ - /// - /// Initializes a new instance of the class. - /// - /// The service provider instance used to resolve dependencies in the pipeline. - /// The light being controlled by the pipeline. - internal LightPipelineContext(IServiceProvider serviceProvider, ILight light) - { - ServiceProvider = serviceProvider; - Light = light; - } - - /// - public IServiceProvider ServiceProvider { get; } - - /// - public ILight Light { get; } -} \ No newline at end of file diff --git a/src/CodeCasa.AutomationPipelines.Lights/Context/LightPipelineContextProvider.cs b/src/CodeCasa.AutomationPipelines.Lights/Context/LightPipelineContextProvider.cs deleted file mode 100644 index fd2e978..0000000 --- a/src/CodeCasa.AutomationPipelines.Lights/Context/LightPipelineContextProvider.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace CodeCasa.AutomationPipelines.Lights.Context -{ - internal class LightPipelineContextProvider - { - private ILightPipelineContext? _lightPipelineContext; - - public ILightPipelineContext GetLightPipelineContext() - { - return _lightPipelineContext ?? throw new InvalidOperationException("Current context not set."); - } - - public void SetLightPipelineContext(ILightPipelineContext context) - { - _lightPipelineContext = context; - } - - public void ResetLight() - { - _lightPipelineContext = null; - } - } -} diff --git a/src/CodeCasa.AutomationPipelines.Lights/Cycle/CompositeLightTransitionCycleConfigurator.cs b/src/CodeCasa.AutomationPipelines.Lights/Cycle/CompositeLightTransitionCycleConfigurator.cs index cff8c61..70cc764 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/Cycle/CompositeLightTransitionCycleConfigurator.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/Cycle/CompositeLightTransitionCycleConfigurator.cs @@ -1,5 +1,4 @@ -using System.Reactive.Concurrency; -using CodeCasa.AutomationPipelines.Lights.Context; +using System.Reactive.Concurrency; using CodeCasa.AutomationPipelines.Lights.Extensions; using CodeCasa.AutomationPipelines.Lights.Nodes; using CodeCasa.Lights; @@ -8,12 +7,13 @@ namespace CodeCasa.AutomationPipelines.Lights.Cycle; -internal class CompositeLightTransitionCycleConfigurator( - Dictionary activeConfigurators, - Dictionary inactiveConfigurators) - : ILightTransitionCycleConfigurator +internal class CompositeLightTransitionCycleConfigurator( + Dictionary> activeConfigurators, + Dictionary> inactiveConfigurators) + : ILightTransitionCycleConfigurator + where TLight : ILight { - public ILightTransitionCycleConfigurator AddOff() + public ILightTransitionCycleConfigurator AddOff() { var matchesNodeState = () => activeConfigurators.Values.All(c => c.Light.IsOff()); activeConfigurators.Values.ForEach(c => c.Add(_ => matchesNodeState())); @@ -21,27 +21,27 @@ public ILightTransitionCycleConfigurator AddOff() return this; } - public ILightTransitionCycleConfigurator AddOn() + public ILightTransitionCycleConfigurator AddOn() { return Add(LightTransition.On()); } - public ILightTransitionCycleConfigurator Add(LightParameters lightParameters, IEqualityComparer? comparer = null) + public ILightTransitionCycleConfigurator Add(LightParameters lightParameters, IEqualityComparer? comparer = null) { return Add(lightParameters.AsTransition(), comparer); } - public ILightTransitionCycleConfigurator Add(Func lightParametersFactory, Func matchesNodeState) + public ILightTransitionCycleConfigurator Add(Func lightParametersFactory, Func matchesNodeState) { return Add(c => lightParametersFactory(c)?.AsTransition(), matchesNodeState); } - public ILightTransitionCycleConfigurator Add(Func lightParametersFactory, Func matchesNodeState) + public ILightTransitionCycleConfigurator Add(Func lightParametersFactory, Func matchesNodeState) { return Add((c, t) => lightParametersFactory(c, t)?.AsTransition(), matchesNodeState); } - public ILightTransitionCycleConfigurator Add(LightTransition lightTransition, IEqualityComparer? comparer = null) + public ILightTransitionCycleConfigurator Add(LightTransition lightTransition, IEqualityComparer? comparer = null) { comparer ??= EqualityComparer.Default; return Add( @@ -51,43 +51,43 @@ public ILightTransitionCycleConfigurator Add(LightTransition lightTransition, IE lightTransition.LightParameters))); } - public ILightTransitionCycleConfigurator Add(Func lightTransitionFactory, Func matchesNodeState) + public ILightTransitionCycleConfigurator Add(Func lightTransitionFactory, Func matchesNodeState) { - return Add(c => new StaticLightTransitionNode(lightTransitionFactory(c), c.ServiceProvider.GetRequiredService()), matchesNodeState); + return Add(c => new StaticLightTransitionNode(lightTransitionFactory(c), c.GetRequiredService()), matchesNodeState); } - public ILightTransitionCycleConfigurator Add(Func lightTransitionFactory, Func matchesNodeState) + public ILightTransitionCycleConfigurator Add(Func lightTransitionFactory, Func matchesNodeState) { return Add(c => new FactoryNode(t => lightTransitionFactory(c, t)), matchesNodeState); } - public ILightTransitionCycleConfigurator Add(Func matchesNodeState) where TNode : IPipelineNode + public ILightTransitionCycleConfigurator Add(Func matchesNodeState) where TNode : IPipelineNode { activeConfigurators.Values.ForEach(c => c.Add(matchesNodeState)); inactiveConfigurators.Values.ForEach(c => c.AddPassThrough(matchesNodeState)); return this; } - public ILightTransitionCycleConfigurator Add(Func> nodeFactory, Func matchesNodeState) + public ILightTransitionCycleConfigurator Add(Func> nodeFactory, Func matchesNodeState) { activeConfigurators.Values.ForEach(c => c.Add(nodeFactory, matchesNodeState)); inactiveConfigurators.Values.ForEach(c => c.AddPassThrough(matchesNodeState)); return this; } - public ILightTransitionCycleConfigurator AddPassThrough(Func matchesNodeState) + public ILightTransitionCycleConfigurator AddPassThrough(Func matchesNodeState) { activeConfigurators.Values.ForEach(c => c.AddPassThrough(matchesNodeState)); inactiveConfigurators.Values.ForEach(c => c.AddPassThrough(matchesNodeState)); return this; } - public ILightTransitionCycleConfigurator ForLight(string lightId, Action configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None) => ForLights([lightId], configure, excludedLightBehaviour); + public ILightTransitionCycleConfigurator ForLight(string lightId, Action> configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None) => ForLights([lightId], configure, excludedLightBehaviour); - public ILightTransitionCycleConfigurator ForLight(ILight light, Action configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None) => ForLights([light], configure, excludedLightBehaviour); + public ILightTransitionCycleConfigurator ForLight(TLight light, Action> configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None) => ForLights([light], configure, excludedLightBehaviour); - public ILightTransitionCycleConfigurator ForLights(IEnumerable lightIds, - Action configure, + public ILightTransitionCycleConfigurator ForLights(IEnumerable lightIds, + Action> configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None) { var lightIdArray = @@ -107,13 +107,13 @@ public ILightTransitionCycleConfigurator ForLights(IEnumerable lightIds, return this; } - configure(new CompositeLightTransitionCycleConfigurator( + configure(new CompositeLightTransitionCycleConfigurator( activeConfigurators.Where(kvp => lightIdArray.Contains(kvp.Key)) .ToDictionary(kvp => kvp.Key, kvp => kvp.Value), [])); return this; } - configure(new CompositeLightTransitionCycleConfigurator( + configure(new CompositeLightTransitionCycleConfigurator( activeConfigurators.Where(kvp => lightIdArray.Contains(kvp.Key)) .ToDictionary(kvp => kvp.Key, kvp => kvp.Value), activeConfigurators.Where(kvp => !lightIdArray.Contains(kvp.Key)) @@ -121,7 +121,7 @@ public ILightTransitionCycleConfigurator ForLights(IEnumerable lightIds, return this; } - public ILightTransitionCycleConfigurator ForLights(IEnumerable lights, Action configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None) + public ILightTransitionCycleConfigurator ForLights(IEnumerable lights, Action> configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None) { var lightIds = CompositeHelper.ResolveGroupsAndValidateLightsSupported(lights, activeConfigurators.Keys); return ForLights(lightIds, configure, excludedLightBehaviour); diff --git a/src/CodeCasa.AutomationPipelines.Lights/Cycle/ILightTransitionCycleConfigurator.cs b/src/CodeCasa.AutomationPipelines.Lights/Cycle/ILightTransitionCycleConfigurator.cs index fb53fe4..97b214e 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/Cycle/ILightTransitionCycleConfigurator.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/Cycle/ILightTransitionCycleConfigurator.cs @@ -1,4 +1,3 @@ -using CodeCasa.AutomationPipelines.Lights.Context; using CodeCasa.Lights; namespace CodeCasa.AutomationPipelines.Lights.Cycle @@ -8,19 +7,20 @@ namespace CodeCasa.AutomationPipelines.Lights.Cycle /// if the light matches a state in the cycle, it advances to the next state. /// If the current state is not recognized, the cycle starts from the beginning. /// - public interface ILightTransitionCycleConfigurator + /// The specific type of light being controlled, which must implement . + public interface ILightTransitionCycleConfigurator where TLight : ILight { /// /// Adds an "off" state to the cycle. /// /// The configurator instance for method chaining. - ILightTransitionCycleConfigurator AddOff(); + ILightTransitionCycleConfigurator AddOff(); /// /// Adds an "on" state to the cycle. /// /// The configurator instance for method chaining. - ILightTransitionCycleConfigurator AddOn(); + ILightTransitionCycleConfigurator AddOn(); /// /// Adds light parameters to the cycle. The cycle will advance to these parameters when the current state matches the previous entry in the cycle. @@ -28,7 +28,7 @@ public interface ILightTransitionCycleConfigurator /// The light parameters to add to the cycle. /// An optional equality comparer for determining if light parameters match. If null, the default equality comparison is used. /// The configurator instance for method chaining. - ILightTransitionCycleConfigurator Add(LightParameters lightParameters, IEqualityComparer? comparer = null); + ILightTransitionCycleConfigurator Add(LightParameters lightParameters, IEqualityComparer? comparer = null); /// /// Adds light parameters created by a factory to the cycle, with a custom state matching function. @@ -37,7 +37,7 @@ public interface ILightTransitionCycleConfigurator /// A factory function that creates light parameters based on the pipeline context. /// A function that determines if the current state matches this cycle entry. /// The configurator instance for method chaining. - ILightTransitionCycleConfigurator Add(Func lightParametersFactory, Func matchesNodeState); + ILightTransitionCycleConfigurator Add(Func lightParametersFactory, Func matchesNodeState); /// /// Adds light parameters created by a factory to the cycle, with a custom state matching function. @@ -47,7 +47,7 @@ public interface ILightTransitionCycleConfigurator /// A factory function that creates light parameters based on the pipeline context and current transition. /// A function that determines if the current state matches this cycle entry. /// The configurator instance for method chaining. - ILightTransitionCycleConfigurator Add(Func lightParametersFactory, Func matchesNodeState); + ILightTransitionCycleConfigurator Add(Func lightParametersFactory, Func matchesNodeState); /// /// Adds a light transition to the cycle. The cycle will advance to this transition when the current state matches the previous entry in the cycle. @@ -55,7 +55,7 @@ public interface ILightTransitionCycleConfigurator /// The light transition to add to the cycle. /// An optional equality comparer for determining if light parameters match. If null, the default equality comparison is used. /// The configurator instance for method chaining. - ILightTransitionCycleConfigurator Add(LightTransition lightTransition, IEqualityComparer? comparer = null); + ILightTransitionCycleConfigurator Add(LightTransition lightTransition, IEqualityComparer? comparer = null); /// /// Adds a light transition created by a factory to the cycle, with a custom state matching function. @@ -64,7 +64,7 @@ public interface ILightTransitionCycleConfigurator /// A factory function that creates a light transition based on the pipeline context. /// A function that determines if the current state matches this cycle entry. /// The configurator instance for method chaining. - ILightTransitionCycleConfigurator Add(Func lightTransitionFactory, Func matchesNodeState); + ILightTransitionCycleConfigurator Add(Func lightTransitionFactory, Func matchesNodeState); /// /// Adds a light transition created by a factory to the cycle, with a custom state matching function. @@ -74,7 +74,7 @@ public interface ILightTransitionCycleConfigurator /// A factory function that creates a light transition based on the pipeline context and current transition. /// A function that determines if the current state matches this cycle entry. /// The configurator instance for method chaining. - ILightTransitionCycleConfigurator Add(Func lightTransitionFactory, Func matchesNodeState); + ILightTransitionCycleConfigurator Add(Func lightTransitionFactory, Func matchesNodeState); /// /// Adds a pipeline node of type to the cycle, with a custom state matching function. @@ -84,7 +84,7 @@ public interface ILightTransitionCycleConfigurator /// The type of the pipeline node to add to the cycle. /// A function that determines if the current state matches this cycle entry. /// The configurator instance for method chaining. - ILightTransitionCycleConfigurator Add(Func matchesNodeState) where TNode : IPipelineNode; + ILightTransitionCycleConfigurator Add(Func matchesNodeState) where TNode : IPipelineNode; /// /// Adds a pipeline node created by a factory to the cycle, with a custom state matching function. @@ -93,7 +93,7 @@ public interface ILightTransitionCycleConfigurator /// A factory function that creates a pipeline node based on the pipeline context. /// A function that determines if the current state matches this cycle entry. /// The configurator instance for method chaining. - ILightTransitionCycleConfigurator Add(Func> nodeFactory, Func matchesNodeState); + ILightTransitionCycleConfigurator Add(Func> nodeFactory, Func matchesNodeState); /// /// Adds a pass-through state to the cycle that maintains the current light state. @@ -101,7 +101,7 @@ public interface ILightTransitionCycleConfigurator /// /// A function that determines if the current state matches this cycle entry. /// The configurator instance for method chaining. - ILightTransitionCycleConfigurator AddPassThrough(Func matchesNodeState); + ILightTransitionCycleConfigurator AddPassThrough(Func matchesNodeState); /// /// Creates a scoped cycle configuration for a specific light identified by its entity ID. @@ -110,7 +110,7 @@ public interface ILightTransitionCycleConfigurator /// An action to configure the cycle for this specific light. /// Specifies the behavior for lights not included in this scoped configuration. Defaults to . /// The configurator instance for method chaining. - ILightTransitionCycleConfigurator ForLight(string lightId, Action configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None); + ILightTransitionCycleConfigurator ForLight(string lightId, Action> configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None); /// /// Creates a scoped cycle configuration for a specific light. @@ -119,7 +119,7 @@ public interface ILightTransitionCycleConfigurator /// An action to configure the cycle for this specific light. /// Specifies the behavior for lights not included in this scoped configuration. Defaults to . /// The configurator instance for method chaining. - ILightTransitionCycleConfigurator ForLight(ILight light, Action configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None); + ILightTransitionCycleConfigurator ForLight(TLight light, Action> configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None); /// /// Creates a scoped cycle configuration for multiple light entities identified by their entity IDs. @@ -128,7 +128,7 @@ public interface ILightTransitionCycleConfigurator /// An action to configure the cycle for these lights. /// Specifies the behavior for lights not included in this scoped configuration. Defaults to . /// The configurator instance for method chaining. - ILightTransitionCycleConfigurator ForLights(IEnumerable lightIds, Action configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None); + ILightTransitionCycleConfigurator ForLights(IEnumerable lightIds, Action> configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None); /// /// Creates a scoped cycle configuration for multiple light entities. @@ -137,6 +137,6 @@ public interface ILightTransitionCycleConfigurator /// An action to configure the cycle for these lights. /// Specifies the behavior for lights not included in this scoped configuration. Defaults to . /// The configurator instance for method chaining. - ILightTransitionCycleConfigurator ForLights(IEnumerable lights, Action configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None); + ILightTransitionCycleConfigurator ForLights(IEnumerable lights, Action> configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None); } } diff --git a/src/CodeCasa.AutomationPipelines.Lights/Cycle/LightTransitionCycleConfigurator.cs b/src/CodeCasa.AutomationPipelines.Lights/Cycle/LightTransitionCycleConfigurator.cs index 20d476a..1cd2e06 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/Cycle/LightTransitionCycleConfigurator.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/Cycle/LightTransitionCycleConfigurator.cs @@ -1,47 +1,47 @@ -using System.Reactive.Concurrency; -using CodeCasa.AutomationPipelines.Lights.Context; -using CodeCasa.AutomationPipelines.Lights.Extensions; using CodeCasa.AutomationPipelines.Lights.Nodes; using CodeCasa.Lights; using CodeCasa.Lights.Extensions; +using Microsoft.Extensions.DependencyInjection; +using System.Reactive.Concurrency; namespace CodeCasa.AutomationPipelines.Lights.Cycle; -internal class LightTransitionCycleConfigurator(ILight light, IScheduler scheduler) : ILightTransitionCycleConfigurator +internal class LightTransitionCycleConfigurator(TLight light, IScheduler scheduler) : ILightTransitionCycleConfigurator + where TLight : ILight { - public ILight Light { get; } = light; + public TLight Light { get; } = light; - internal List<(Func> nodeFactory, Func matchesNodeState)> CycleNodeFactories + internal List<(Func> nodeFactory, Func matchesNodeState)> CycleNodeFactories { get; } = []; - public ILightTransitionCycleConfigurator AddOff() + public ILightTransitionCycleConfigurator AddOff() { return Add(_ => Light.IsOff()); } - public ILightTransitionCycleConfigurator AddOn() + public ILightTransitionCycleConfigurator AddOn() { return Add(LightTransition.On()); } - public ILightTransitionCycleConfigurator Add(LightParameters lightParameters, IEqualityComparer? comparer = null) + public ILightTransitionCycleConfigurator Add(LightParameters lightParameters, IEqualityComparer? comparer = null) { return Add(lightParameters.AsTransition(), comparer); } - public ILightTransitionCycleConfigurator Add(Func lightParametersFactory, Func matchesNodeState) + public ILightTransitionCycleConfigurator Add(Func lightParametersFactory, Func matchesNodeState) { return Add(c => lightParametersFactory(c)?.AsTransition(), matchesNodeState); } - public ILightTransitionCycleConfigurator Add(Func lightParametersFactory, Func matchesNodeState) + public ILightTransitionCycleConfigurator Add(Func lightParametersFactory, Func matchesNodeState) { return Add((c, t) => lightParametersFactory(c, t)?.AsTransition(), matchesNodeState); } - public ILightTransitionCycleConfigurator Add(LightTransition lightTransition, IEqualityComparer? comparer = null) + public ILightTransitionCycleConfigurator Add(LightTransition lightTransition, IEqualityComparer? comparer = null) { comparer ??= EqualityComparer.Default; return Add(new StaticLightTransitionNode(lightTransition, scheduler), _ => comparer.Equals( @@ -49,48 +49,48 @@ public ILightTransitionCycleConfigurator Add(LightTransition lightTransition, IE lightTransition.LightParameters)); } - public ILightTransitionCycleConfigurator Add(Func lightTransitionFactory, Func matchesNodeState) + public ILightTransitionCycleConfigurator Add(Func lightTransitionFactory, Func matchesNodeState) { return Add(c => new StaticLightTransitionNode(lightTransitionFactory(c), scheduler), matchesNodeState); } - public ILightTransitionCycleConfigurator Add(Func lightTransitionFactory, Func matchesNodeState) + public ILightTransitionCycleConfigurator Add(Func lightTransitionFactory, Func matchesNodeState) { return Add(c => new FactoryNode(t => lightTransitionFactory(c, t)), matchesNodeState); } - public ILightTransitionCycleConfigurator Add(Func matchesNodeState) where TNode : IPipelineNode + public ILightTransitionCycleConfigurator Add(Func matchesNodeState) where TNode : IPipelineNode { - return Add(c => c.ServiceProvider.CreateInstanceWithinContext(c), matchesNodeState); + return Add(c => ActivatorUtilities.CreateInstance(c), matchesNodeState); } - public ILightTransitionCycleConfigurator Add(IPipelineNode node, Func matchesNodeState) + public ILightTransitionCycleConfigurator Add(IPipelineNode node, Func matchesNodeState) { return Add(_ => node, matchesNodeState); } - public ILightTransitionCycleConfigurator Add(Func> nodeFactory, Func matchesNodeState) + public ILightTransitionCycleConfigurator Add(Func> nodeFactory, Func matchesNodeState) { CycleNodeFactories.Add((nodeFactory, matchesNodeState)); return this; } - public ILightTransitionCycleConfigurator AddPassThrough(Func matchesNodeState) + public ILightTransitionCycleConfigurator AddPassThrough(Func matchesNodeState) { return Add(new PassThroughNode(), matchesNodeState); } - public ILightTransitionCycleConfigurator ForLight(string lightId, Action configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None) => ForLights([lightId], configure, excludedLightBehaviour); + public ILightTransitionCycleConfigurator ForLight(string lightId, Action> configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None) => ForLights([lightId], configure, excludedLightBehaviour); - public ILightTransitionCycleConfigurator ForLight(ILight light, Action configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None) => ForLights([light], configure, excludedLightBehaviour); + public ILightTransitionCycleConfigurator ForLight(TLight light, Action> configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None) => ForLights([light], configure, excludedLightBehaviour); - public ILightTransitionCycleConfigurator ForLights(IEnumerable lightIds, Action configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None) + public ILightTransitionCycleConfigurator ForLights(IEnumerable lightIds, Action> configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None) { CompositeHelper.ValidateLightSupported(lightIds, Light.Id); return this; } - public ILightTransitionCycleConfigurator ForLights(IEnumerable lights, Action configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None) + public ILightTransitionCycleConfigurator ForLights(IEnumerable lights, Action> configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None) { CompositeHelper.ResolveGroupsAndValidateLightSupported(lights, Light.Id); return this; diff --git a/src/CodeCasa.AutomationPipelines.Lights/Extensions/LightPipelineContextExtensions.cs b/src/CodeCasa.AutomationPipelines.Lights/Extensions/LightPipelineContextExtensions.cs deleted file mode 100644 index 0f27f5e..0000000 --- a/src/CodeCasa.AutomationPipelines.Lights/Extensions/LightPipelineContextExtensions.cs +++ /dev/null @@ -1,50 +0,0 @@ -using CodeCasa.Lights; -using System.Reactive.Concurrency; -using CodeCasa.AutomationPipelines.Lights.Context; -using CodeCasa.AutomationPipelines.Lights.Nodes; -using Microsoft.Extensions.DependencyInjection; - -namespace CodeCasa.AutomationPipelines.Lights.Extensions -{ - /// - /// Extension methods for . - /// - public static class LightPipelineContextExtensions - { - /// - /// Creates a pipeline node that applies the specified light parameters and automatically turns off the light after a specified duration. - /// The timeout is not reset by any external events. - /// - /// The light pipeline context. - /// The light parameters to apply as a transition. - /// The duration after which the light should turn off. - /// A pipeline node that applies the light parameters and handles the turn-off behavior. - public static IPipelineNode LightParametersThatTurnsOffAfter(this ILightPipelineContext context, - LightParameters lightParameters, - TimeSpan timeSpan) - { - var scheduler = context.ServiceProvider.GetRequiredService(); - var innerNode = new StaticLightTransitionNode(lightParameters.AsTransition(), scheduler); - return innerNode.TurnOffAfter(timeSpan, scheduler); - } - - /// - /// Creates a pipeline node that applies the specified light parameters and automatically turns off the light after a specified duration. - /// The timeout can be reset when the observable emits a value. - /// - /// The type of elements emitted by the reset timer observable. - /// The light pipeline context. - /// The light parameters to apply as a transition. - /// The duration after which the light should turn off. - /// An observable that resets the turn-off timer when it emits. - /// A pipeline node that applies the light parameters and handles the turn-off behavior. - public static IPipelineNode LightParametersThatTurnsOffAfter(this ILightPipelineContext context, - LightParameters lightParameters, - TimeSpan timeSpan, IObservable resetTimerObservable) - { - var scheduler = context.ServiceProvider.GetRequiredService(); - var innerNode = new StaticLightTransitionNode(lightParameters.AsTransition(), scheduler); - return innerNode.TurnOffAfter(timeSpan, resetTimerObservable, scheduler); - } - } -} diff --git a/src/CodeCasa.AutomationPipelines.Lights/Extensions/PipelineNodeFactoryExtensions.cs b/src/CodeCasa.AutomationPipelines.Lights/Extensions/PipelineNodeFactoryExtensions.cs new file mode 100644 index 0000000..1130794 --- /dev/null +++ b/src/CodeCasa.AutomationPipelines.Lights/Extensions/PipelineNodeFactoryExtensions.cs @@ -0,0 +1,34 @@ +using CodeCasa.AutomationPipelines.Lights.Nodes; +using CodeCasa.Lights; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CodeCasa.AutomationPipelines.Lights.Extensions +{ + internal static class PipelineNodeFactoryExtensions + { + public static ServiceScopedNode CreateScopedNode( + this Func> factory, + IServiceProvider serviceProvider) + { + var scope = serviceProvider.CreateScope(); + return new ServiceScopedNode(scope, factory(scope.ServiceProvider)); + } + + public static ServiceScopedNode? CreateScopedNodeOrNull( + this Func?> factory, + IServiceProvider serviceProvider) + { + var scope = serviceProvider.CreateScope(); + var node = factory(scope.ServiceProvider); + if (node != null) + { + return new ServiceScopedNode(scope, node); + } + scope.Dispose(); + return null; + } + } +} diff --git a/src/CodeCasa.AutomationPipelines.Lights/Extensions/ServiceCollectionExtensions.cs b/src/CodeCasa.AutomationPipelines.Lights/Extensions/ServiceCollectionExtensions.cs index 50b27d8..3ba949f 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/Extensions/ServiceCollectionExtensions.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/Extensions/ServiceCollectionExtensions.cs @@ -1,4 +1,3 @@ -using CodeCasa.AutomationPipelines.Lights.Context; using CodeCasa.AutomationPipelines.Lights.Pipeline; using CodeCasa.AutomationPipelines.Lights.ReactiveNode; using Microsoft.Extensions.DependencyInjection; @@ -19,9 +18,7 @@ public static IServiceCollection AddLightPipelines(this IServiceCollection servi { return serviceCollection .AddTransient() - .AddTransient() - .AddSingleton() - .AddTransient(serviceProvider => - serviceProvider.GetRequiredService().GetLightPipelineContext()); + .AddTransient(); } + } \ No newline at end of file diff --git a/src/CodeCasa.AutomationPipelines.Lights/Extensions/ServiceProviderExtensions.cs b/src/CodeCasa.AutomationPipelines.Lights/Extensions/ServiceProviderExtensions.cs index f069930..3eba3fb 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/Extensions/ServiceProviderExtensions.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/Extensions/ServiceProviderExtensions.cs @@ -1,26 +1,63 @@ -using CodeCasa.AutomationPipelines.Lights.Context; +using CodeCasa.AutomationPipelines.Lights.Nodes; using CodeCasa.Lights; using Microsoft.Extensions.DependencyInjection; +using System.Reactive.Concurrency; namespace CodeCasa.AutomationPipelines.Lights.Extensions; -internal static class ServiceProviderExtensions +/// +/// Extension methods for to support light automation pipelines. +/// +public static class ServiceProviderExtensions { - public static T - CreateInstanceWithinContext(this IServiceProvider serviceProvider, ILight light) => - serviceProvider.CreateInstanceWithinContext(new LightPipelineContext(serviceProvider, light)); + internal static IServiceScope CreateLightContextScope(this IServiceProvider serviceProvider, TLight light) where TLight : ILight + { + return serviceProvider.CreateScope(cb => + { + cb.AddTransient(typeof(ILight), _ => light); + + if ( + typeof(TLight) != typeof(ILight) && // Only add the second registration if TLight isn't already ILight + typeof(TLight).IsClass || typeof(TLight).IsInterface) // Check at runtime if TLight is a reference type + { + cb.AddTransient(typeof(TLight), _ => light); + } + }); + } - public static T - CreateInstanceWithinContext(this IServiceProvider serviceProvider, ILightPipelineContext context) => - (T)serviceProvider.CreateInstanceWithinContext(typeof(T), context); + /// + /// Creates a pipeline node that applies the specified light parameters and automatically turns off the light after a specified duration. + /// The timeout is not reset by any external events. + /// + /// The service provider. + /// The light parameters to apply as a transition. + /// The duration after which the light should turn off. + /// A pipeline node that applies the light parameters and handles the turn-off behavior. + public static IPipelineNode CreateAutoOffLightNode(this IServiceProvider serviceProvider, + LightParameters lightParameters, + TimeSpan timeSpan) + { + var scheduler = serviceProvider.GetRequiredService(); + var innerNode = new StaticLightTransitionNode(lightParameters.AsTransition(), scheduler); + return innerNode.TurnOffAfter(timeSpan, scheduler); + } - public static object CreateInstanceWithinContext(this IServiceProvider serviceProvider, Type instanceType, - ILightPipelineContext context) + /// + /// Creates a pipeline node that applies the specified light parameters and automatically turns off the light after a specified duration. + /// The timeout can be reset when the observable emits a value. + /// + /// The type of elements emitted by the reset timer observable. + /// The service provider. + /// The light parameters to apply as a transition. + /// The duration after which the light should turn off. + /// An observable that resets the turn-off timer when it emits. + /// A pipeline node that applies the light parameters and handles the turn-off behavior. + public static IPipelineNode CreateAutoOffLightNode(this IServiceProvider serviceProvider, + LightParameters lightParameters, + TimeSpan timeSpan, IObservable resetTimerObservable) { - var contextProvider = serviceProvider.GetRequiredService(); - contextProvider.SetLightPipelineContext(context); - var instance = ActivatorUtilities.CreateInstance(serviceProvider, instanceType); - contextProvider.ResetLight(); - return instance; + var scheduler = serviceProvider.GetRequiredService(); + var innerNode = new StaticLightTransitionNode(lightParameters.AsTransition(), scheduler); + return innerNode.TurnOffAfter(timeSpan, resetTimerObservable, scheduler); } } \ No newline at end of file diff --git a/src/CodeCasa.AutomationPipelines.Lights/Nodes/ScopedNode.cs b/src/CodeCasa.AutomationPipelines.Lights/Nodes/ServiceScopedNode.cs similarity index 87% rename from src/CodeCasa.AutomationPipelines.Lights/Nodes/ScopedNode.cs rename to src/CodeCasa.AutomationPipelines.Lights/Nodes/ServiceScopedNode.cs index b85eb2f..1d982ba 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/Nodes/ScopedNode.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/Nodes/ServiceScopedNode.cs @@ -3,7 +3,7 @@ namespace CodeCasa.AutomationPipelines.Lights.Nodes { - internal class ScopedNode(IServiceScope serviceScope, IPipelineNode innerNode) + internal class ServiceScopedNode(IServiceScope serviceScope, IPipelineNode innerNode) : IPipelineNode, IAsyncDisposable { public async ValueTask DisposeAsync() diff --git a/src/CodeCasa.AutomationPipelines.Lights/Pipeline/CompositeLightTransitionPipelineConfigurator.Reactive.Cycle.cs b/src/CodeCasa.AutomationPipelines.Lights/Pipeline/CompositeLightTransitionPipelineConfigurator.Reactive.Cycle.cs index 8646086..1aa3a91 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/Pipeline/CompositeLightTransitionPipelineConfigurator.Reactive.Cycle.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/Pipeline/CompositeLightTransitionPipelineConfigurator.Reactive.Cycle.cs @@ -3,31 +3,31 @@ namespace CodeCasa.AutomationPipelines.Lights.Pipeline; -internal partial class CompositeLightTransitionPipelineConfigurator +internal partial class CompositeLightTransitionPipelineConfigurator { - public ILightTransitionPipelineConfigurator AddCycle(IObservable triggerObservable, IEnumerable lightParameters) + public ILightTransitionPipelineConfigurator AddCycle(IObservable triggerObservable, IEnumerable lightParameters) { return AddReactiveNode(c => c.AddCycle(triggerObservable, lightParameters)); } - public ILightTransitionPipelineConfigurator AddCycle(IObservable triggerObservable, + public ILightTransitionPipelineConfigurator AddCycle(IObservable triggerObservable, params LightParameters[] lightParameters) { return AddReactiveNode(c => c.AddCycle(triggerObservable, lightParameters)); } - public ILightTransitionPipelineConfigurator AddCycle(IObservable triggerObservable, IEnumerable lightTransitions) + public ILightTransitionPipelineConfigurator AddCycle(IObservable triggerObservable, IEnumerable lightTransitions) { return AddReactiveNode(c => c.AddCycle(triggerObservable, lightTransitions)); } - public ILightTransitionPipelineConfigurator AddCycle(IObservable triggerObservable, + public ILightTransitionPipelineConfigurator AddCycle(IObservable triggerObservable, params LightTransition[] lightTransitions) { return AddReactiveNode(c => c.AddCycle(triggerObservable, lightTransitions)); } - public ILightTransitionPipelineConfigurator AddCycle(IObservable triggerObservable, Action configure) + public ILightTransitionPipelineConfigurator AddCycle(IObservable triggerObservable, Action> configure) { return AddReactiveNode(c => c.AddCycle(triggerObservable, configure)); } diff --git a/src/CodeCasa.AutomationPipelines.Lights/Pipeline/CompositeLightTransitionPipelineConfigurator.Reactive.Toggle.cs b/src/CodeCasa.AutomationPipelines.Lights/Pipeline/CompositeLightTransitionPipelineConfigurator.Reactive.Toggle.cs index ecdf339..1b571a9 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/Pipeline/CompositeLightTransitionPipelineConfigurator.Reactive.Toggle.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/Pipeline/CompositeLightTransitionPipelineConfigurator.Reactive.Toggle.cs @@ -1,56 +1,55 @@ -using CodeCasa.AutomationPipelines.Lights.Context; using CodeCasa.AutomationPipelines.Lights.Toggle; using CodeCasa.Lights; namespace CodeCasa.AutomationPipelines.Lights.Pipeline; -internal partial class CompositeLightTransitionPipelineConfigurator +internal partial class CompositeLightTransitionPipelineConfigurator { /// - public ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservable, + public ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservable, IEnumerable lightParameters) { return AddReactiveNode(c => c.AddToggle(triggerObservable, lightParameters)); } /// - public ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservable, + public ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservable, params LightParameters[] lightParameters) { return AddReactiveNode(c => c.AddToggle(triggerObservable, lightParameters)); } /// - public ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservable, + public ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservable, IEnumerable lightTransitions) { return AddReactiveNode(c => c.AddToggle(triggerObservable, lightTransitions)); } /// - public ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservable, + public ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservable, params LightTransition[] lightTransitions) { return AddReactiveNode(c => c.AddToggle(triggerObservable, lightTransitions)); } /// - public ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservable, - IEnumerable>> nodeFactories) + public ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservable, + IEnumerable>> nodeFactories) { return AddReactiveNode(c => c.AddToggle(triggerObservable, nodeFactories)); } /// - public ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservable, - params Func>[] nodeFactories) + public ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservable, + params Func>[] nodeFactories) { return AddReactiveNode(c => c.AddToggle(triggerObservable, nodeFactories)); } /// - public ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservable, - Action configure) + public ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservable, + Action> configure) { return AddReactiveNode(c => c.AddToggle(triggerObservable, configure)); } diff --git a/src/CodeCasa.AutomationPipelines.Lights/Pipeline/CompositeLightTransitionPipelineConfigurator.Switch.cs b/src/CodeCasa.AutomationPipelines.Lights/Pipeline/CompositeLightTransitionPipelineConfigurator.Switch.cs index 5bbdb00..ec189fb 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/Pipeline/CompositeLightTransitionPipelineConfigurator.Switch.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/Pipeline/CompositeLightTransitionPipelineConfigurator.Switch.cs @@ -1,4 +1,3 @@ -using CodeCasa.AutomationPipelines.Lights.Context; using CodeCasa.AutomationPipelines.Lights.Extensions; using CodeCasa.AutomationPipelines.Lights.ReactiveNode; using Microsoft.Extensions.DependencyInjection; @@ -9,10 +8,10 @@ namespace CodeCasa.AutomationPipelines.Lights.Pipeline; -internal partial class CompositeLightTransitionPipelineConfigurator +internal partial class CompositeLightTransitionPipelineConfigurator { /// - public ILightTransitionPipelineConfigurator Switch(LightParameters trueLightParameters, + public ILightTransitionPipelineConfigurator Switch(LightParameters trueLightParameters, LightParameters falseLightParameters) where TObservable : IObservable { NodeContainers.Values.ForEach(b => b.Switch(trueLightParameters, falseLightParameters)); @@ -20,7 +19,7 @@ public ILightTransitionPipelineConfigurator Switch(LightParameters } /// - public ILightTransitionPipelineConfigurator Switch(IObservable observable, LightParameters trueLightParameters, + public ILightTransitionPipelineConfigurator Switch(IObservable observable, LightParameters trueLightParameters, LightParameters falseLightParameters) { NodeContainers.Values.ForEach(b => b.Switch(observable, trueLightParameters, falseLightParameters)); @@ -28,23 +27,23 @@ public ILightTransitionPipelineConfigurator Switch(IObservable observable, } /// - public ILightTransitionPipelineConfigurator Switch(Func trueLightParametersFactory, - Func falseLightParametersFactory) where TObservable : IObservable + public ILightTransitionPipelineConfigurator Switch(Func trueLightParametersFactory, + Func falseLightParametersFactory) where TObservable : IObservable { NodeContainers.Values.ForEach(b => b.Switch(trueLightParametersFactory, falseLightParametersFactory)); return this; } /// - public ILightTransitionPipelineConfigurator Switch(IObservable observable, Func trueLightParametersFactory, - Func falseLightParametersFactory) + public ILightTransitionPipelineConfigurator Switch(IObservable observable, Func trueLightParametersFactory, + Func falseLightParametersFactory) { NodeContainers.Values.ForEach(b => b.Switch(observable, trueLightParametersFactory, falseLightParametersFactory)); return this; } /// - public ILightTransitionPipelineConfigurator Switch(LightTransition trueLightTransition, + public ILightTransitionPipelineConfigurator Switch(LightTransition trueLightTransition, LightTransition falseLightTransition) where TObservable : IObservable { NodeContainers.Values.ForEach(b => b.Switch(trueLightTransition, falseLightTransition)); @@ -52,7 +51,7 @@ public ILightTransitionPipelineConfigurator Switch(LightTransition } /// - public ILightTransitionPipelineConfigurator Switch(IObservable observable, LightTransition trueLightTransition, + public ILightTransitionPipelineConfigurator Switch(IObservable observable, LightTransition trueLightTransition, LightTransition falseLightTransition) { NodeContainers.Values.ForEach(b => b.Switch(observable, trueLightTransition, falseLightTransition)); @@ -60,53 +59,53 @@ public ILightTransitionPipelineConfigurator Switch(IObservable observable, } /// - public ILightTransitionPipelineConfigurator Switch(Func trueLightTransitionFactory, - Func falseLightTransitionFactory) where TObservable : IObservable + public ILightTransitionPipelineConfigurator Switch(Func trueLightTransitionFactory, + Func falseLightTransitionFactory) where TObservable : IObservable { NodeContainers.Values.ForEach(b => b.Switch(trueLightTransitionFactory, falseLightTransitionFactory)); return this; } /// - public ILightTransitionPipelineConfigurator Switch(IObservable observable, Func trueLightTransitionFactory, - Func falseLightTransitionFactory) + public ILightTransitionPipelineConfigurator Switch(IObservable observable, Func trueLightTransitionFactory, + Func falseLightTransitionFactory) { NodeContainers.Values.ForEach(b => b.Switch(observable, trueLightTransitionFactory, falseLightTransitionFactory)); return this; } /// - public ILightTransitionPipelineConfigurator Switch(Func> trueNodeFactory, Func> falseNodeFactory) where TObservable : IObservable + public ILightTransitionPipelineConfigurator Switch(Func> trueNodeFactory, Func> falseNodeFactory) where TObservable : IObservable { NodeContainers.Values.ForEach(b => b.Switch(trueNodeFactory, falseNodeFactory)); return this; } /// - public ILightTransitionPipelineConfigurator Switch(IObservable observable, Func> trueNodeFactory, - Func> falseNodeFactory) + public ILightTransitionPipelineConfigurator Switch(IObservable observable, Func> trueNodeFactory, + Func> falseNodeFactory) { NodeContainers.Values.ForEach(b => b.Switch(observable, trueNodeFactory, falseNodeFactory)); return this; } /// - public ILightTransitionPipelineConfigurator Switch() where TObservable : IObservable where TTrueNode : IPipelineNode where TFalseNode : IPipelineNode + public ILightTransitionPipelineConfigurator Switch() where TObservable : IObservable where TTrueNode : IPipelineNode where TFalseNode : IPipelineNode { NodeContainers.Values.ForEach(b => b.Switch()); return this; } /// - public ILightTransitionPipelineConfigurator Switch(IObservable observable) where TTrueNode : IPipelineNode where TFalseNode : IPipelineNode + public ILightTransitionPipelineConfigurator Switch(IObservable observable) where TTrueNode : IPipelineNode where TFalseNode : IPipelineNode { NodeContainers.Values.ForEach(b => b.Switch(observable)); return this; } /// - public ILightTransitionPipelineConfigurator AddReactiveNodeSwitch(Action trueConfigure, - Action falseConfigure) where TObservable : IObservable + public ILightTransitionPipelineConfigurator AddReactiveNodeSwitch(Action> trueConfigure, + Action> falseConfigure) where TObservable : IObservable { /* * For this implementation we can either instantiate the TObservable for each container and pass configure to them individual, breaking composite dimming behavior. @@ -118,8 +117,8 @@ public ILightTransitionPipelineConfigurator AddReactiveNodeSwitch(A } /// - public ILightTransitionPipelineConfigurator AddReactiveNodeSwitch(IObservable observable, Action trueConfigure, - Action falseConfigure) + public ILightTransitionPipelineConfigurator AddReactiveNodeSwitch(IObservable observable, Action> trueConfigure, + Action> falseConfigure) { // Note: we use CompositeLightTransitionPipelineConfigurator.AddReactiveNode so configure is also applied on the composite context. return AddReactiveNode(c => c @@ -128,15 +127,20 @@ public ILightTransitionPipelineConfigurator AddReactiveNodeSwitch(IObservable - public ILightTransitionPipelineConfigurator AddPipelineSwitch(Action trueConfigure, Action falseConfigure) where TObservable : IObservable + public ILightTransitionPipelineConfigurator AddPipelineSwitch(Action> trueConfigure, Action> falseConfigure) where TObservable : IObservable { + /* + * For this implementation we can either instantiate the TObservable for each container and pass configure to them individual, breaking composite dimming behavior. + * Or we can create a single TObservable without light context. + * I decided to go with the latter to preserve composite dimming behavior. + */ var observable = ActivatorUtilities.CreateInstance(serviceProvider); return AddPipelineSwitch(observable, trueConfigure, falseConfigure); } /// - public ILightTransitionPipelineConfigurator AddPipelineSwitch(IObservable observable, Action trueConfigure, - Action falseConfigure) + public ILightTransitionPipelineConfigurator AddPipelineSwitch(IObservable observable, Action> trueConfigure, + Action> falseConfigure) { // Note: we use CompositeLightTransitionPipelineConfigurator.AddReactiveNode so configure is also applied on the composite context. return AddReactiveNode(c => c @@ -145,14 +149,14 @@ public ILightTransitionPipelineConfigurator AddPipelineSwitch(IObservable } /// - public ILightTransitionPipelineConfigurator TurnOnOff() where TObservable : IObservable + public ILightTransitionPipelineConfigurator TurnOnOff() where TObservable : IObservable { return Switch(LightTransition.On(), LightTransition.Off()); } /// - public ILightTransitionPipelineConfigurator TurnOnOff(IObservable observable) + public ILightTransitionPipelineConfigurator TurnOnOff(IObservable observable) { return Switch(observable, LightTransition.On(), LightTransition.Off()); } -} \ No newline at end of file +} diff --git a/src/CodeCasa.AutomationPipelines.Lights/Pipeline/CompositeLightTransitionPipelineConfigurator.When.cs b/src/CodeCasa.AutomationPipelines.Lights/Pipeline/CompositeLightTransitionPipelineConfigurator.When.cs index 320c1a6..9e44157 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/Pipeline/CompositeLightTransitionPipelineConfigurator.When.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/Pipeline/CompositeLightTransitionPipelineConfigurator.When.cs @@ -1,4 +1,3 @@ -using CodeCasa.AutomationPipelines.Lights.Context; using CodeCasa.AutomationPipelines.Lights.Extensions; using CodeCasa.AutomationPipelines.Lights.ReactiveNode; using Microsoft.Extensions.DependencyInjection; @@ -9,10 +8,10 @@ namespace CodeCasa.AutomationPipelines.Lights.Pipeline; -internal partial class CompositeLightTransitionPipelineConfigurator +internal partial class CompositeLightTransitionPipelineConfigurator { /// - public ILightTransitionPipelineConfigurator When(LightParameters lightParameters) + public ILightTransitionPipelineConfigurator When(LightParameters lightParameters) where TObservable : IObservable { NodeContainers.Values.ForEach(b => b.When(lightParameters)); @@ -20,7 +19,7 @@ public ILightTransitionPipelineConfigurator When(LightParameters li } /// - public ILightTransitionPipelineConfigurator When(IObservable observable, + public ILightTransitionPipelineConfigurator When(IObservable observable, LightParameters lightParameters) { NodeContainers.Values.ForEach(b => b.When(observable, lightParameters)); @@ -28,23 +27,23 @@ public ILightTransitionPipelineConfigurator When(IObservable observable, } /// - public ILightTransitionPipelineConfigurator When( - Func lightParametersFactory) where TObservable : IObservable + public ILightTransitionPipelineConfigurator When( + Func lightParametersFactory) where TObservable : IObservable { NodeContainers.Values.ForEach(b => b.When(lightParametersFactory)); return this; } /// - public ILightTransitionPipelineConfigurator When(IObservable observable, - Func lightParametersFactory) + public ILightTransitionPipelineConfigurator When(IObservable observable, + Func lightParametersFactory) { NodeContainers.Values.ForEach(b => b.When(observable, lightParametersFactory)); return this; } /// - public ILightTransitionPipelineConfigurator When(LightTransition lightTransition) + public ILightTransitionPipelineConfigurator When(LightTransition lightTransition) where TObservable : IObservable { NodeContainers.Values.ForEach(b => b.When(lightTransition)); @@ -52,7 +51,7 @@ public ILightTransitionPipelineConfigurator When(LightTransition li } /// - public ILightTransitionPipelineConfigurator When(IObservable observable, + public ILightTransitionPipelineConfigurator When(IObservable observable, LightTransition lightTransition) { NodeContainers.Values.ForEach(b => b.When(observable, lightTransition)); @@ -60,39 +59,39 @@ public ILightTransitionPipelineConfigurator When(IObservable observable, } /// - public ILightTransitionPipelineConfigurator When( - Func lightTransitionFactory) where TObservable : IObservable + public ILightTransitionPipelineConfigurator When( + Func lightTransitionFactory) where TObservable : IObservable { NodeContainers.Values.ForEach(b => b.When(lightTransitionFactory)); return this; } /// - public ILightTransitionPipelineConfigurator When(IObservable observable, - Func lightTransitionFactory) + public ILightTransitionPipelineConfigurator When(IObservable observable, + Func lightTransitionFactory) { NodeContainers.Values.ForEach(b => b.When(observable, lightTransitionFactory)); return this; } /// - public ILightTransitionPipelineConfigurator When( - Func> nodeFactory) where TObservable : IObservable + public ILightTransitionPipelineConfigurator When( + Func> nodeFactory) where TObservable : IObservable { NodeContainers.Values.ForEach(b => b.When(nodeFactory)); return this; } /// - public ILightTransitionPipelineConfigurator When(IObservable observable, - Func> nodeFactory) + public ILightTransitionPipelineConfigurator When(IObservable observable, + Func> nodeFactory) { NodeContainers.Values.ForEach(b => b.When(observable, nodeFactory)); return this; } /// - public ILightTransitionPipelineConfigurator When() + public ILightTransitionPipelineConfigurator When() where TObservable : IObservable where TNode : IPipelineNode { NodeContainers.Values.ForEach(b => b.When()); @@ -100,7 +99,7 @@ public ILightTransitionPipelineConfigurator When() } /// - public ILightTransitionPipelineConfigurator When(IObservable observable) + public ILightTransitionPipelineConfigurator When(IObservable observable) where TNode : IPipelineNode { NodeContainers.Values.ForEach(b => b.When(observable)); @@ -108,7 +107,7 @@ public ILightTransitionPipelineConfigurator When(IObservable observ } /// - public ILightTransitionPipelineConfigurator AddReactiveNodeWhen(Action configure) where TObservable : IObservable + public ILightTransitionPipelineConfigurator AddReactiveNodeWhen(Action> configure) where TObservable : IObservable { /* * For this implementation we can either instantiate the TObservable for each container and pass configure to them individual, breaking composite dimming behavior. @@ -120,7 +119,7 @@ public ILightTransitionPipelineConfigurator AddReactiveNodeWhen(Act } /// - public ILightTransitionPipelineConfigurator AddReactiveNodeWhen(IObservable observable, Action configure) + public ILightTransitionPipelineConfigurator AddReactiveNodeWhen(IObservable observable, Action> configure) { // Note: we use CompositeLightTransitionPipelineConfigurator.AddReactiveNode so configure is also applied on the composite context. return AddReactiveNode(c => c @@ -129,14 +128,19 @@ public ILightTransitionPipelineConfigurator AddReactiveNodeWhen(IObservable - public ILightTransitionPipelineConfigurator AddPipelineWhen(Action configure) where TObservable : IObservable + public ILightTransitionPipelineConfigurator AddPipelineWhen(Action> configure) where TObservable : IObservable { + /* + * For this implementation we can either instantiate the TObservable for each container and pass configure to them individual, breaking composite dimming behavior. + * Or we can create a single TObservable without light context. + * I decided to go with the latter to preserve composite dimming behavior. + */ var observable = ActivatorUtilities.CreateInstance(serviceProvider); return AddPipelineWhen(observable, configure); } /// - public ILightTransitionPipelineConfigurator AddPipelineWhen(IObservable observable, Action configure) + public ILightTransitionPipelineConfigurator AddPipelineWhen(IObservable observable, Action> configure) { // Note: we use CompositeLightTransitionPipelineConfigurator.AddReactiveNode so configure is also applied on the composite context. return AddReactiveNode(c => c @@ -145,28 +149,28 @@ public ILightTransitionPipelineConfigurator AddPipelineWhen(IObservable ob } /// - public ILightTransitionPipelineConfigurator TurnOffWhen() where TObservable : IObservable + public ILightTransitionPipelineConfigurator TurnOffWhen() where TObservable : IObservable { NodeContainers.Values.ForEach(b => b.TurnOffWhen()); return this; } /// - public ILightTransitionPipelineConfigurator TurnOffWhen(IObservable observable) + public ILightTransitionPipelineConfigurator TurnOffWhen(IObservable observable) { NodeContainers.Values.ForEach(b => b.TurnOffWhen(observable)); return this; } /// - public ILightTransitionPipelineConfigurator TurnOnWhen() where TObservable : IObservable + public ILightTransitionPipelineConfigurator TurnOnWhen() where TObservable : IObservable { return When(LightTransition.On()); } /// - public ILightTransitionPipelineConfigurator TurnOnWhen(IObservable observable) + public ILightTransitionPipelineConfigurator TurnOnWhen(IObservable observable) { return When(observable, LightTransition.On()); } -} \ No newline at end of file +} diff --git a/src/CodeCasa.AutomationPipelines.Lights/Pipeline/CompositeLightTransitionPipelineConfigurator.cs b/src/CodeCasa.AutomationPipelines.Lights/Pipeline/CompositeLightTransitionPipelineConfigurator.cs index b74d20e..124859b 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/Pipeline/CompositeLightTransitionPipelineConfigurator.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/Pipeline/CompositeLightTransitionPipelineConfigurator.cs @@ -1,10 +1,7 @@ -using CodeCasa.Abstractions; -using CodeCasa.AutomationPipelines.Lights.Context; +using CodeCasa.Abstractions; using CodeCasa.AutomationPipelines.Lights.Extensions; using CodeCasa.AutomationPipelines.Lights.ReactiveNode; using CodeCasa.Lights; -using System.Collections.Generic; -using System.Xml.Linq; namespace CodeCasa.AutomationPipelines.Lights.Pipeline { @@ -12,46 +9,46 @@ namespace CodeCasa.AutomationPipelines.Lights.Pipeline /// Configures light transition pipelines for multiple light entities as a composite. /// This configurator applies configurations across all included lights and allows for selective scoping to subsets of lights. /// - internal partial class CompositeLightTransitionPipelineConfigurator( + internal partial class CompositeLightTransitionPipelineConfigurator( IServiceProvider serviceProvider, LightPipelineFactory lightPipelineFactory, ReactiveNodeFactory reactiveNodeFactory, - Dictionary nodeContainers) - : ILightTransitionPipelineConfigurator + Dictionary> nodeContainers) + : ILightTransitionPipelineConfigurator where TLight : ILight { - public Dictionary NodeContainers { get; } = nodeContainers; + public Dictionary> NodeContainers { get; } = nodeContainers; /// - public ILightTransitionPipelineConfigurator EnableLogging(string? pipelineName = null) + public ILightTransitionPipelineConfigurator EnableLogging(string? pipelineName = null) { NodeContainers.Values.ForEach(b => b.EnableLogging(pipelineName)); return this; } /// - public ILightTransitionPipelineConfigurator DisableLogging() + public ILightTransitionPipelineConfigurator DisableLogging() { NodeContainers.Values.ForEach(b => b.DisableLogging()); return this; } /// - public ILightTransitionPipelineConfigurator AddNode() where TNode : IPipelineNode + public ILightTransitionPipelineConfigurator AddNode() where TNode : IPipelineNode { NodeContainers.Values.ForEach(b => b.AddNode()); return this; } /// - public ILightTransitionPipelineConfigurator AddNode(Func> nodeFactory) + public ILightTransitionPipelineConfigurator AddNode(Func> nodeFactory) { NodeContainers.Values.ForEach(b => b.AddNode(nodeFactory)); return this; } /// - public ILightTransitionPipelineConfigurator AddReactiveNode( - Action configure) + public ILightTransitionPipelineConfigurator AddReactiveNode( + Action> configure) { var nodes = reactiveNodeFactory.CreateReactiveNodes(NodeContainers.Select(nc => nc.Value.Light), c => @@ -69,7 +66,7 @@ public ILightTransitionPipelineConfigurator AddReactiveNode( } /// - public ILightTransitionPipelineConfigurator AddPipeline(Action pipelineNodeOptions) + public ILightTransitionPipelineConfigurator AddPipeline(Action> pipelineNodeOptions) { var pipelines = lightPipelineFactory.CreateLightPipelines(NodeContainers.Select(c => c.Value.Light), pipelineNodeOptions); @@ -78,13 +75,13 @@ public ILightTransitionPipelineConfigurator AddPipeline(Action - public ILightTransitionPipelineConfigurator AddDimmer(IDimmer dimmer) + public ILightTransitionPipelineConfigurator AddDimmer(IDimmer dimmer) { return AddDimmer(dimmer, _ => { }); } /// - public ILightTransitionPipelineConfigurator AddDimmer(IDimmer dimmer, Action dimOptions) + public ILightTransitionPipelineConfigurator AddDimmer(IDimmer dimmer, Action dimOptions) { return AddReactiveNode(c => { @@ -93,16 +90,16 @@ public ILightTransitionPipelineConfigurator AddDimmer(IDimmer dimmer, Action - public ILightTransitionPipelineConfigurator ForLight(string lightId, - Action compositeNodeBuilder) => ForLights([lightId], compositeNodeBuilder); + public ILightTransitionPipelineConfigurator ForLight(string lightId, + Action> compositeNodeBuilder) => ForLights([lightId], compositeNodeBuilder); /// - public ILightTransitionPipelineConfigurator ForLight(ILight light, - Action compositeNodeBuilder) => ForLights([light], compositeNodeBuilder); + public ILightTransitionPipelineConfigurator ForLight(TLight light, + Action> compositeNodeBuilder) => ForLights([light], compositeNodeBuilder); /// - public ILightTransitionPipelineConfigurator ForLights(IEnumerable lightIds, - Action compositeNodeBuilder) + public ILightTransitionPipelineConfigurator ForLights(IEnumerable lightIds, + Action> compositeNodeBuilder) { var lightIdsArray = CompositeHelper.ValidateLightsSupported(lightIds, NodeContainers.Keys); @@ -118,14 +115,14 @@ public ILightTransitionPipelineConfigurator ForLights(IEnumerable lightI return this; } - compositeNodeBuilder(new CompositeLightTransitionPipelineConfigurator(serviceProvider, lightPipelineFactory, reactiveNodeFactory, NodeContainers + compositeNodeBuilder(new CompositeLightTransitionPipelineConfigurator(serviceProvider, lightPipelineFactory, reactiveNodeFactory, NodeContainers .Where(kvp => lightIdsArray.Contains(kvp.Key)).ToDictionary(kvp => kvp.Key, kvp => kvp.Value))); return this; } /// - public ILightTransitionPipelineConfigurator ForLights(IEnumerable lights, - Action compositeNodeBuilder) + public ILightTransitionPipelineConfigurator ForLights(IEnumerable lights, + Action> compositeNodeBuilder) { var lightIds = CompositeHelper.ResolveGroupsAndValidateLightsSupported(lights, NodeContainers.Keys); return ForLights(lightIds, compositeNodeBuilder); diff --git a/src/CodeCasa.AutomationPipelines.Lights/Pipeline/ILightTransitionPipelineConfigurator.Reactive.Cycle.cs b/src/CodeCasa.AutomationPipelines.Lights/Pipeline/ILightTransitionPipelineConfigurator.Reactive.Cycle.cs index cb13ff1..078b838 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/Pipeline/ILightTransitionPipelineConfigurator.Reactive.Cycle.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/Pipeline/ILightTransitionPipelineConfigurator.Reactive.Cycle.cs @@ -3,7 +3,7 @@ namespace CodeCasa.AutomationPipelines.Lights.Pipeline; -public partial interface ILightTransitionPipelineConfigurator +public partial interface ILightTransitionPipelineConfigurator where TLight : ILight { /// /// Adds a cycle node that rotates through the specified light parameters when triggered by . @@ -13,7 +13,7 @@ public partial interface ILightTransitionPipelineConfigurator /// The observable that triggers cycling to the next parameters. /// The collection of light parameters to cycle through. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator AddCycle(IObservable triggerObservable, + ILightTransitionPipelineConfigurator AddCycle(IObservable triggerObservable, IEnumerable lightParameters); /// @@ -24,7 +24,7 @@ ILightTransitionPipelineConfigurator AddCycle(IObservable triggerObservabl /// The observable that triggers cycling to the next parameters. /// The array of light parameters to cycle through. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator AddCycle(IObservable triggerObservable, + ILightTransitionPipelineConfigurator AddCycle(IObservable triggerObservable, params LightParameters[] lightParameters); /// @@ -35,7 +35,7 @@ ILightTransitionPipelineConfigurator AddCycle(IObservable triggerObservabl /// The observable that triggers cycling to the next transition. /// The collection of light transitions to cycle through. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator AddCycle(IObservable triggerObservable, + ILightTransitionPipelineConfigurator AddCycle(IObservable triggerObservable, IEnumerable lightTransitions); /// @@ -46,7 +46,7 @@ ILightTransitionPipelineConfigurator AddCycle(IObservable triggerObservabl /// The observable that triggers cycling to the next transition. /// The array of light transitions to cycle through. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator AddCycle(IObservable triggerObservable, + ILightTransitionPipelineConfigurator AddCycle(IObservable triggerObservable, params LightTransition[] lightTransitions); /// @@ -57,6 +57,6 @@ ILightTransitionPipelineConfigurator AddCycle(IObservable triggerObservabl /// The observable that triggers cycling to the next state. /// An action to configure the cycle behavior. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator AddCycle(IObservable triggerObservable, - Action configure); + ILightTransitionPipelineConfigurator AddCycle(IObservable triggerObservable, + Action> configure); } \ No newline at end of file diff --git a/src/CodeCasa.AutomationPipelines.Lights/Pipeline/ILightTransitionPipelineConfigurator.Reactive.Toggle.cs b/src/CodeCasa.AutomationPipelines.Lights/Pipeline/ILightTransitionPipelineConfigurator.Reactive.Toggle.cs index 18b1399..9213d85 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/Pipeline/ILightTransitionPipelineConfigurator.Reactive.Toggle.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/Pipeline/ILightTransitionPipelineConfigurator.Reactive.Toggle.cs @@ -1,10 +1,9 @@ -using CodeCasa.AutomationPipelines.Lights.Context; using CodeCasa.AutomationPipelines.Lights.Toggle; using CodeCasa.Lights; namespace CodeCasa.AutomationPipelines.Lights.Pipeline; -public partial interface ILightTransitionPipelineConfigurator +public partial interface ILightTransitionPipelineConfigurator where TLight : ILight { /// /// Adds a toggle node that switches between the specified light parameters when triggered by . @@ -14,7 +13,7 @@ public partial interface ILightTransitionPipelineConfigurator /// The observable that triggers toggling to the next parameters. /// The collection of light parameters to toggle between. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservable, + ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservable, IEnumerable lightParameters); /// @@ -25,7 +24,7 @@ ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservab /// The observable that triggers toggling to the next parameters. /// The array of light parameters to toggle between. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservable, + ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservable, params LightParameters[] lightParameters); /// @@ -36,7 +35,7 @@ ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservab /// The observable that triggers toggling to the next transition. /// The collection of light transitions to toggle between. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservable, + ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservable, IEnumerable lightTransitions); /// @@ -47,7 +46,7 @@ ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservab /// The observable that triggers toggling to the next transition. /// The array of light transitions to toggle between. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservable, + ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservable, params LightTransition[] lightTransitions); /// @@ -58,8 +57,8 @@ ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservab /// The observable that triggers toggling to the next node. /// The collection of factory functions that create pipeline nodes. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservable, - IEnumerable>> nodeFactories); + ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservable, + IEnumerable>> nodeFactories); /// /// Adds a toggle node that switches between nodes created by the specified factory functions when triggered by . @@ -69,8 +68,8 @@ ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservab /// The observable that triggers toggling to the next node. /// The array of factory functions that create pipeline nodes. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservable, - params Func>[] nodeFactories); + ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservable, + params Func>[] nodeFactories); /// /// Adds a toggle node configured by the specified action when triggered by . @@ -80,6 +79,6 @@ ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservab /// The observable that triggers toggling to the next state. /// An action to configure the toggle behavior. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservable, - Action configure); + ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservable, + Action> configure); } \ No newline at end of file diff --git a/src/CodeCasa.AutomationPipelines.Lights/Pipeline/ILightTransitionPipelineConfigurator.Switch.cs b/src/CodeCasa.AutomationPipelines.Lights/Pipeline/ILightTransitionPipelineConfigurator.Switch.cs index f2f302b..661f841 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/Pipeline/ILightTransitionPipelineConfigurator.Switch.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/Pipeline/ILightTransitionPipelineConfigurator.Switch.cs @@ -1,10 +1,9 @@ -using CodeCasa.AutomationPipelines.Lights.Context; using CodeCasa.AutomationPipelines.Lights.ReactiveNode; using CodeCasa.Lights; namespace CodeCasa.AutomationPipelines.Lights.Pipeline; -public partial interface ILightTransitionPipelineConfigurator +public partial interface ILightTransitionPipelineConfigurator where TLight : ILight { /// /// Registers a node that switches between two sets of light parameters based on a boolean observable. @@ -17,7 +16,7 @@ public partial interface ILightTransitionPipelineConfigurator /// The light parameters to apply when the observable emits true. /// The light parameters to apply when the observable emits false. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator Switch(LightParameters trueLightParameters, + ILightTransitionPipelineConfigurator Switch(LightParameters trueLightParameters, LightParameters falseLightParameters) where TObservable : IObservable; @@ -30,7 +29,7 @@ ILightTransitionPipelineConfigurator Switch(LightParameters trueLig /// The light parameters to apply when the observable emits true. /// The light parameters to apply when the observable emits false. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator Switch(IObservable observable, + ILightTransitionPipelineConfigurator Switch(IObservable observable, LightParameters trueLightParameters, LightParameters falseLightParameters); /// @@ -44,9 +43,9 @@ ILightTransitionPipelineConfigurator Switch(IObservable observable, /// A factory function that creates light parameters for true values. /// A factory function that creates light parameters for false values. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator Switch( - Func trueLightParametersFactory, - Func falseLightParametersFactory) + ILightTransitionPipelineConfigurator Switch( + Func trueLightParametersFactory, + Func falseLightParametersFactory) where TObservable : IObservable; /// @@ -58,9 +57,9 @@ ILightTransitionPipelineConfigurator Switch( /// A factory function that creates light parameters for true values. /// A factory function that creates light parameters for false values. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator Switch(IObservable observable, - Func trueLightParametersFactory, - Func falseLightParametersFactory); + ILightTransitionPipelineConfigurator Switch(IObservable observable, + Func trueLightParametersFactory, + Func falseLightParametersFactory); /// /// Registers a node that switches between two light transitions based on a boolean observable. @@ -73,7 +72,7 @@ ILightTransitionPipelineConfigurator Switch(IObservable observable, /// The light transition to apply when the observable emits true. /// The light transition to apply when the observable emits false. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator Switch(LightTransition trueLightTransition, + ILightTransitionPipelineConfigurator Switch(LightTransition trueLightTransition, LightTransition falseLightTransition) where TObservable : IObservable; @@ -86,7 +85,7 @@ ILightTransitionPipelineConfigurator Switch(LightTransition trueLig /// The light transition to apply when the observable emits true. /// The light transition to apply when the observable emits false. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator Switch(IObservable observable, + ILightTransitionPipelineConfigurator Switch(IObservable observable, LightTransition trueLightTransition, LightTransition falseLightTransition); /// @@ -100,9 +99,9 @@ ILightTransitionPipelineConfigurator Switch(IObservable observable, /// A factory function that creates a light transition for true values. /// A factory function that creates a light transition for false values. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator Switch( - Func trueLightTransitionFactory, - Func falseLightTransitionFactory) + ILightTransitionPipelineConfigurator Switch( + Func trueLightTransitionFactory, + Func falseLightTransitionFactory) where TObservable : IObservable; /// @@ -114,9 +113,9 @@ ILightTransitionPipelineConfigurator Switch( /// A factory function that creates a light transition for true values. /// A factory function that creates a light transition for false values. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator Switch(IObservable observable, - Func trueLightTransitionFactory, - Func falseLightTransitionFactory); + ILightTransitionPipelineConfigurator Switch(IObservable observable, + Func trueLightTransitionFactory, + Func falseLightTransitionFactory); /// /// Registers a node that switches between two pipeline nodes created by factory functions based on a boolean observable. @@ -129,9 +128,9 @@ ILightTransitionPipelineConfigurator Switch(IObservable observable, /// A factory function that creates a pipeline node for true values. /// A factory function that creates a pipeline node for false values. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator Switch( - Func> trueNodeFactory, - Func> falseNodeFactory) + ILightTransitionPipelineConfigurator Switch( + Func> trueNodeFactory, + Func> falseNodeFactory) where TObservable : IObservable; /// @@ -143,9 +142,9 @@ ILightTransitionPipelineConfigurator Switch( /// A factory function that creates a pipeline node for true values. /// A factory function that creates a pipeline node for false values. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator Switch(IObservable observable, - Func> trueNodeFactory, - Func> falseNodeFactory); + ILightTransitionPipelineConfigurator Switch(IObservable observable, + Func> trueNodeFactory, + Func> falseNodeFactory); /// /// Registers a node that switches between two pipeline node types based on a boolean observable. @@ -158,7 +157,7 @@ ILightTransitionPipelineConfigurator Switch(IObservable observable, /// The type of the pipeline node to apply for true values. /// The type of the pipeline node to apply for false values. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator Switch() + ILightTransitionPipelineConfigurator Switch() where TObservable : IObservable where TTrueNode : IPipelineNode where TFalseNode : IPipelineNode; @@ -173,7 +172,7 @@ ILightTransitionPipelineConfigurator Switch( /// The type of the pipeline node to apply for false values. /// The observable that determines which node to apply. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator Switch(IObservable observable) + ILightTransitionPipelineConfigurator Switch(IObservable observable) where TTrueNode : IPipelineNode where TFalseNode : IPipelineNode; @@ -188,9 +187,9 @@ ILightTransitionPipelineConfigurator Switch(IObservableAn action to configure the reactive node for true values. /// An action to configure the reactive node for false values. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator AddReactiveNodeSwitch( - Action trueConfigure, - Action falseConfigure) + ILightTransitionPipelineConfigurator AddReactiveNodeSwitch( + Action> trueConfigure, + Action> falseConfigure) where TObservable : IObservable; /// @@ -202,9 +201,9 @@ ILightTransitionPipelineConfigurator AddReactiveNodeSwitch( /// An action to configure the reactive node for true values. /// An action to configure the reactive node for false values. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator AddReactiveNodeSwitch(IObservable observable, - Action trueConfigure, - Action falseConfigure); + ILightTransitionPipelineConfigurator AddReactiveNodeSwitch(IObservable observable, + Action> trueConfigure, + Action> falseConfigure); /// /// Registers a pipeline that switches between two configurations based on a boolean observable. @@ -217,9 +216,9 @@ ILightTransitionPipelineConfigurator AddReactiveNodeSwitch(IObservable obs /// An action to configure the pipeline for true values. /// An action to configure the pipeline for false values. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator AddPipelineSwitch( - Action trueConfigure, - Action falseConfigure) + ILightTransitionPipelineConfigurator AddPipelineSwitch( + Action> trueConfigure, + Action> falseConfigure) where TObservable : IObservable; /// @@ -231,9 +230,9 @@ ILightTransitionPipelineConfigurator AddPipelineSwitch( /// An action to configure the pipeline for true values. /// An action to configure the pipeline for false values. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator AddPipelineSwitch(IObservable observable, - Action trueConfigure, - Action falseConfigure); + ILightTransitionPipelineConfigurator AddPipelineSwitch(IObservable observable, + Action> trueConfigure, + Action> falseConfigure); /// /// Registers a node that turns the light on when the observable of type @@ -242,7 +241,7 @@ ILightTransitionPipelineConfigurator AddPipelineSwitch(IObservable observa /// /// The type of the observable to resolve from the service provider. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator TurnOnOff() where TObservable : IObservable; + ILightTransitionPipelineConfigurator TurnOnOff() where TObservable : IObservable; /// /// Registers a node that turns the light on when the emits , @@ -250,5 +249,5 @@ ILightTransitionPipelineConfigurator AddPipelineSwitch(IObservable observa /// /// The observable that determines whether to turn the light on or off. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator TurnOnOff(IObservable observable); -} \ No newline at end of file + ILightTransitionPipelineConfigurator TurnOnOff(IObservable observable); +} diff --git a/src/CodeCasa.AutomationPipelines.Lights/Pipeline/ILightTransitionPipelineConfigurator.When.cs b/src/CodeCasa.AutomationPipelines.Lights/Pipeline/ILightTransitionPipelineConfigurator.When.cs index 16ecfe1..817538d 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/Pipeline/ILightTransitionPipelineConfigurator.When.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/Pipeline/ILightTransitionPipelineConfigurator.When.cs @@ -1,10 +1,9 @@ -using CodeCasa.AutomationPipelines.Lights.Context; using CodeCasa.AutomationPipelines.Lights.ReactiveNode; using CodeCasa.Lights; namespace CodeCasa.AutomationPipelines.Lights.Pipeline; -public partial interface ILightTransitionPipelineConfigurator +public partial interface ILightTransitionPipelineConfigurator where TLight : ILight { /// /// Registers a node that applies the given when the observable @@ -15,7 +14,7 @@ public partial interface ILightTransitionPipelineConfigurator /// The type of the observable to resolve from the service provider. /// The light parameters to apply when the observable emits true. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator When(LightParameters lightParameters) + ILightTransitionPipelineConfigurator When(LightParameters lightParameters) where TObservable : IObservable; /// @@ -26,7 +25,7 @@ ILightTransitionPipelineConfigurator When(LightParameters lightPara /// The observable that determines when to apply the light parameters. /// The light parameters to apply when the observable emits true. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator When(IObservable observable, LightParameters lightParameters); + ILightTransitionPipelineConfigurator When(IObservable observable, LightParameters lightParameters); /// /// Registers a node that applies light parameters created by @@ -37,8 +36,8 @@ ILightTransitionPipelineConfigurator When(LightParameters lightPara /// The type of the observable to resolve from the service provider. /// A factory function that creates light parameters based on the pipeline context. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator When( - Func lightParametersFactory) + ILightTransitionPipelineConfigurator When( + Func lightParametersFactory) where TObservable : IObservable; /// @@ -49,8 +48,8 @@ ILightTransitionPipelineConfigurator When( /// The observable that determines when to apply the light parameters. /// A factory function that creates light parameters based on the pipeline context. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator When(IObservable observable, - Func lightParametersFactory); + ILightTransitionPipelineConfigurator When(IObservable observable, + Func lightParametersFactory); /// /// Registers a node that applies the given when the observable @@ -61,7 +60,7 @@ ILightTransitionPipelineConfigurator When(IObservable observable, /// The type of the observable to resolve from the service provider. /// The light transition to apply when the observable emits true. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator When(LightTransition lightTransition) + ILightTransitionPipelineConfigurator When(LightTransition lightTransition) where TObservable : IObservable; /// @@ -72,7 +71,7 @@ ILightTransitionPipelineConfigurator When(LightTransition lightTran /// The observable that determines when to apply the light transition. /// The light transition to apply when the observable emits true. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator When(IObservable observable, LightTransition lightTransition); + ILightTransitionPipelineConfigurator When(IObservable observable, LightTransition lightTransition); /// /// Registers a node that applies the created by @@ -84,8 +83,8 @@ ILightTransitionPipelineConfigurator When(LightTransition lightTran /// The type of the observable to resolve from the service provider. /// A factory function that creates a light transition based on the pipeline context. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator When( - Func lightTransitionFactory) + ILightTransitionPipelineConfigurator When( + Func lightTransitionFactory) where TObservable : IObservable; /// @@ -97,8 +96,8 @@ ILightTransitionPipelineConfigurator When( /// The observable that determines when to apply the light transition. /// A factory function that creates a light transition based on the pipeline context. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator When(IObservable observable, - Func lightTransitionFactory); + ILightTransitionPipelineConfigurator When(IObservable observable, + Func lightTransitionFactory); /// /// Registers a node created by when the observable of type @@ -109,8 +108,8 @@ ILightTransitionPipelineConfigurator When(IObservable observable, /// The type of the observable to resolve from the service provider. /// A factory function that creates a pipeline node based on the pipeline context. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator When( - Func> nodeFactory) + ILightTransitionPipelineConfigurator When( + Func> nodeFactory) where TObservable : IObservable; /// @@ -121,8 +120,8 @@ ILightTransitionPipelineConfigurator When( /// The observable that determines when to apply the node. /// A factory function that creates a pipeline node based on the pipeline context. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator When(IObservable observable, - Func> nodeFactory); + ILightTransitionPipelineConfigurator When(IObservable observable, + Func> nodeFactory); /// /// Registers a node of type when the observable of type @@ -133,7 +132,7 @@ ILightTransitionPipelineConfigurator When(IObservable observable, /// The type of the observable to resolve from the service provider. /// The type of the pipeline node to resolve from the service provider. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator When() + ILightTransitionPipelineConfigurator When() where TObservable : IObservable where TNode : IPipelineNode; @@ -145,7 +144,7 @@ ILightTransitionPipelineConfigurator When() /// The type of the pipeline node to resolve from the service provider. /// The observable that determines when to apply the node. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator When(IObservable observable) + ILightTransitionPipelineConfigurator When(IObservable observable) where TNode : IPipelineNode; /// @@ -157,8 +156,8 @@ ILightTransitionPipelineConfigurator When(IObservable observable) /// The type of the observable to resolve from the service provider. /// An action to configure the reactive node. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator AddReactiveNodeWhen( - Action configure) + ILightTransitionPipelineConfigurator AddReactiveNodeWhen( + Action> configure) where TObservable : IObservable; /// @@ -169,8 +168,8 @@ ILightTransitionPipelineConfigurator AddReactiveNodeWhen( /// The observable that determines when to apply the reactive node. /// An action to configure the reactive node. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator AddReactiveNodeWhen(IObservable observable, - Action configure); + ILightTransitionPipelineConfigurator AddReactiveNodeWhen(IObservable observable, + Action> configure); /// /// Registers a pipeline configured by when the observable of type @@ -181,8 +180,8 @@ ILightTransitionPipelineConfigurator AddReactiveNodeWhen(IObservable obser /// The type of the observable to resolve from the service provider. /// An action to configure the nested pipeline. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator AddPipelineWhen( - Action configure) + ILightTransitionPipelineConfigurator AddPipelineWhen( + Action> configure) where TObservable : IObservable; /// @@ -193,8 +192,8 @@ ILightTransitionPipelineConfigurator AddPipelineWhen( /// The observable that determines when to apply the pipeline. /// An action to configure the nested pipeline. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator AddPipelineWhen(IObservable observable, - Action configure); + ILightTransitionPipelineConfigurator AddPipelineWhen(IObservable observable, + Action> configure); /// /// Registers a node that turns off the light when the observable @@ -204,7 +203,7 @@ ILightTransitionPipelineConfigurator AddPipelineWhen(IObservable observabl /// /// The type of the observable to resolve from the service provider. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator TurnOffWhen() where TObservable : IObservable; + ILightTransitionPipelineConfigurator TurnOffWhen() where TObservable : IObservable; /// /// Registers a node that turns off the light when the emits . @@ -212,7 +211,7 @@ ILightTransitionPipelineConfigurator AddPipelineWhen(IObservable observabl /// /// The observable that determines when to turn off the light. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator TurnOffWhen(IObservable observable); + ILightTransitionPipelineConfigurator TurnOffWhen(IObservable observable); /// /// Registers a node that turns on the light when the observable @@ -222,7 +221,7 @@ ILightTransitionPipelineConfigurator AddPipelineWhen(IObservable observabl /// /// The type of the observable to resolve from the service provider. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator TurnOnWhen() where TObservable : IObservable; + ILightTransitionPipelineConfigurator TurnOnWhen() where TObservable : IObservable; /// /// Registers a node that turns on the light when the emits . @@ -230,5 +229,5 @@ ILightTransitionPipelineConfigurator AddPipelineWhen(IObservable observabl /// /// The observable that determines when to turn on the light. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator TurnOnWhen(IObservable observable); -} \ No newline at end of file + ILightTransitionPipelineConfigurator TurnOnWhen(IObservable observable); +} diff --git a/src/CodeCasa.AutomationPipelines.Lights/Pipeline/ILightTransitionPipelineConfigurator.cs b/src/CodeCasa.AutomationPipelines.Lights/Pipeline/ILightTransitionPipelineConfigurator.cs index 5950d6e..7cced7b 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/Pipeline/ILightTransitionPipelineConfigurator.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/Pipeline/ILightTransitionPipelineConfigurator.cs @@ -1,5 +1,4 @@ -using CodeCasa.Abstractions; -using CodeCasa.AutomationPipelines.Lights.Context; +using CodeCasa.Abstractions; using CodeCasa.AutomationPipelines.Lights.ReactiveNode; using CodeCasa.Lights; @@ -9,20 +8,21 @@ namespace CodeCasa.AutomationPipelines.Lights.Pipeline; /// Configures a light transition pipeline for controlling light behavior through layered logic nodes. /// Supports adding nodes, reactive nodes, dimmers, and conditional logic with When/Switch operators. /// -public partial interface ILightTransitionPipelineConfigurator +/// The specific type of light being controlled, which must implement . +public partial interface ILightTransitionPipelineConfigurator where TLight : ILight { /// /// Enables logging for the pipeline configuration. /// /// The optional name of the pipeline to include in logs. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator EnableLogging(string? pipelineName = null); + ILightTransitionPipelineConfigurator EnableLogging(string? pipelineName = null); /// /// Disables logging for the pipeline configuration. /// /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator DisableLogging(); + ILightTransitionPipelineConfigurator DisableLogging(); /// /// Adds a pipeline node of type to the pipeline. @@ -30,35 +30,35 @@ public partial interface ILightTransitionPipelineConfigurator /// /// The type of the pipeline node to add. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator AddNode() where TNode : IPipelineNode; + ILightTransitionPipelineConfigurator AddNode() where TNode : IPipelineNode; /// /// Adds a pipeline node created by the specified to the pipeline. /// /// A factory function that creates a pipeline node based on the light pipeline context. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator AddNode(Func> nodeFactory); + ILightTransitionPipelineConfigurator AddNode(Func> nodeFactory); /// /// Adds a reactive node to the pipeline configured by the specified action. /// /// An action to configure the reactive node. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator AddReactiveNode(Action configure); + ILightTransitionPipelineConfigurator AddReactiveNode(Action> configure); /// /// Adds a nested pipeline to the current pipeline configured by the specified action. /// /// An action to configure the nested pipeline. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator AddPipeline(Action pipelineNodeOptions); + ILightTransitionPipelineConfigurator AddPipeline(Action> pipelineNodeOptions); /// /// Adds a dimmer control to the pipeline. /// /// The dimmer device to add. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator AddDimmer(IDimmer dimmer); + ILightTransitionPipelineConfigurator AddDimmer(IDimmer dimmer); /// /// Adds a dimmer control to the pipeline with custom configuration options. @@ -66,7 +66,7 @@ public partial interface ILightTransitionPipelineConfigurator /// The dimmer device to add. /// An action to configure the dimmer options. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator AddDimmer(IDimmer dimmer, Action dimOptions); + ILightTransitionPipelineConfigurator AddDimmer(IDimmer dimmer, Action dimOptions); /// /// Creates a scoped pipeline configuration for a specific light identified by its entity ID. @@ -74,7 +74,7 @@ public partial interface ILightTransitionPipelineConfigurator /// The entity ID of the light to configure. /// An action to configure the pipeline for this specific light. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator ForLight(string lightId, Action compositeNodeBuilder); + ILightTransitionPipelineConfigurator ForLight(string lightId, Action> compositeNodeBuilder); /// /// Creates a scoped pipeline configuration for a specific light. @@ -82,7 +82,7 @@ public partial interface ILightTransitionPipelineConfigurator /// The light to configure. /// An action to configure the pipeline for this specific light. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator ForLight(ILight light, Action compositeNodeBuilder); + ILightTransitionPipelineConfigurator ForLight(TLight light, Action> compositeNodeBuilder); /// /// Creates a scoped pipeline configuration for multiple light entities identified by their entity IDs. @@ -90,7 +90,7 @@ public partial interface ILightTransitionPipelineConfigurator /// The entity IDs of the lights to configure. /// An action to configure the pipeline for these lights. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator ForLights(IEnumerable lightIds, Action compositeNodeBuilder); + ILightTransitionPipelineConfigurator ForLights(IEnumerable lightIds, Action> compositeNodeBuilder); /// /// Creates a scoped pipeline configuration for multiple light entities. @@ -98,5 +98,5 @@ public partial interface ILightTransitionPipelineConfigurator /// The light entities to configure. /// An action to configure the pipeline for these lights. /// The configurator instance for method chaining. - ILightTransitionPipelineConfigurator ForLights(IEnumerable lights, Action compositeNodeBuilder); -} \ No newline at end of file + ILightTransitionPipelineConfigurator ForLights(IEnumerable lights, Action> compositeNodeBuilder); +} diff --git a/src/CodeCasa.AutomationPipelines.Lights/Pipeline/LightPipelineFactory.cs b/src/CodeCasa.AutomationPipelines.Lights/Pipeline/LightPipelineFactory.cs index bd1bfd5..ca97ab1 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/Pipeline/LightPipelineFactory.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/Pipeline/LightPipelineFactory.cs @@ -1,7 +1,9 @@ -using Microsoft.Extensions.Logging; +using CodeCasa.AutomationPipelines.Lights.Extensions; using CodeCasa.AutomationPipelines.Lights.ReactiveNode; using CodeCasa.Lights; using CodeCasa.Lights.Extensions; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; namespace CodeCasa.AutomationPipelines.Lights.Pipeline { @@ -9,19 +11,20 @@ namespace CodeCasa.AutomationPipelines.Lights.Pipeline /// Factory for creating and configuring light transition pipelines. /// public class LightPipelineFactory( - ILogger> logger, IServiceProvider serviceProvider, ReactiveNodeFactory reactiveNodeFactory) + ILogger> logger, IServiceProvider serviceProvider) { /// /// Sets up a light pipeline for the specified light and configures it with the provided builder action. /// + /// The specific type of light. /// The light to set up the pipeline for. /// An action to configure the pipeline behavior. /// An async disposable representing the created pipeline(s) that can be disposed to clean up resources. - public IAsyncDisposable SetupLightPipeline(ILight light, - Action pipelineBuilder) + public IAsyncDisposable SetupLightPipeline(TLight light, + Action> pipelineBuilder) where TLight : ILight { var disposables = new CompositeAsyncDisposable(); - var pipelines = CreateLightPipelines(light.Flatten(), pipelineBuilder); + var pipelines = CreateLightPipelines(light.Flatten().Cast(), pipelineBuilder); foreach (var pipeline in pipelines.Values) { disposables.Add(pipeline); @@ -35,7 +38,7 @@ public IAsyncDisposable SetupLightPipeline(ILight light, /// The light to create a pipeline for. /// An action to configure the pipeline behavior. /// A configured pipeline for controlling the specified light. - internal IPipeline CreateLightPipeline(ILight light, Action pipelineBuilder) + internal IPipeline CreateLightPipeline(TLight light, Action> pipelineBuilder) where TLight : ILight { return CreateLightPipelines([light], pipelineBuilder)[light.Id]; } @@ -46,7 +49,7 @@ internal IPipeline CreateLightPipeline(ILight light, ActionThe light entities to create pipelines for. /// An action to configure the pipeline behavior. /// A dictionary mapping light IDs to their corresponding pipelines. - internal Dictionary> CreateLightPipelines(IEnumerable lights, Action pipelineBuilder) + internal Dictionary> CreateLightPipelines(IEnumerable lights, Action> pipelineBuilder) where TLight : ILight { // Note: we simply assume that these are not groups. var lightArray = lights.ToArray(); @@ -55,33 +58,46 @@ internal Dictionary> CreateLightPipelines(IEn return new Dictionary>(); } - var configurators = lightArray.ToDictionary(l => l.Id, l => new LightTransitionPipelineConfigurator(serviceProvider, this, reactiveNodeFactory, l)); - ILightTransitionPipelineConfigurator configurator = lightArray.Length == 1 + var lightContextScopes = lightArray.ToDictionary(l => l.Id, serviceProvider.CreateLightContextScope); + var configurators = + lightArray.ToDictionary(l => l.Id, + l => + { + var sp = lightContextScopes[l.Id].ServiceProvider; + // Note: we cant resolve LightTransitionPipelineConfigurator directly because it is not registered as a service. + return new LightTransitionPipelineConfigurator(sp, l); + }); + ILightTransitionPipelineConfigurator configurator = lightArray.Length == 1 ? configurators[lightArray[0].Id] - : new CompositeLightTransitionPipelineConfigurator( + : new CompositeLightTransitionPipelineConfigurator( serviceProvider, - this, - reactiveNodeFactory, + serviceProvider.GetRequiredService(), + serviceProvider.GetRequiredService(), configurators); pipelineBuilder(configurator); return configurators.ToDictionary(kvp => kvp.Key, kvp => { var conf = kvp.Value; + IPipeline pipeline; if (conf.Log ?? false) { - return new Pipeline( + pipeline= new Pipeline( conf.Name ?? conf.Light.Id, LightTransition.Off(), conf.Nodes, conf.Light.ApplyTransition, logger); } + else + { + pipeline = new Pipeline( + LightTransition.Off(), + conf.Nodes, + conf.Light.ApplyTransition); + } - return (IPipeline)new Pipeline( - LightTransition.Off(), - conf.Nodes, - conf.Light.ApplyTransition); + return (IPipeline)new ServiceScopedPipeline(lightContextScopes[kvp.Key], pipeline); }); } } diff --git a/src/CodeCasa.AutomationPipelines.Lights/Pipeline/LightTransitionPipelineConfigurator.Reactive.Cycle.cs b/src/CodeCasa.AutomationPipelines.Lights/Pipeline/LightTransitionPipelineConfigurator.Reactive.Cycle.cs index 064a965..1bf3883 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/Pipeline/LightTransitionPipelineConfigurator.Reactive.Cycle.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/Pipeline/LightTransitionPipelineConfigurator.Reactive.Cycle.cs @@ -3,31 +3,31 @@ namespace CodeCasa.AutomationPipelines.Lights.Pipeline; -internal partial class LightTransitionPipelineConfigurator +internal partial class LightTransitionPipelineConfigurator { - public ILightTransitionPipelineConfigurator AddCycle(IObservable triggerObservable, IEnumerable lightParameters) + public ILightTransitionPipelineConfigurator AddCycle(IObservable triggerObservable, IEnumerable lightParameters) { return AddReactiveNode(c => c.AddCycle(triggerObservable, lightParameters)); } - public ILightTransitionPipelineConfigurator AddCycle(IObservable triggerObservable, + public ILightTransitionPipelineConfigurator AddCycle(IObservable triggerObservable, params LightParameters[] lightParameters) { return AddReactiveNode(c => c.AddCycle(triggerObservable, lightParameters)); } - public ILightTransitionPipelineConfigurator AddCycle(IObservable triggerObservable, IEnumerable lightTransitions) + public ILightTransitionPipelineConfigurator AddCycle(IObservable triggerObservable, IEnumerable lightTransitions) { return AddReactiveNode(c => c.AddCycle(triggerObservable, lightTransitions)); } - public ILightTransitionPipelineConfigurator AddCycle(IObservable triggerObservable, + public ILightTransitionPipelineConfigurator AddCycle(IObservable triggerObservable, params LightTransition[] lightTransitions) { return AddReactiveNode(c => c.AddCycle(triggerObservable, lightTransitions)); } - public ILightTransitionPipelineConfigurator AddCycle(IObservable triggerObservable, Action configure) + public ILightTransitionPipelineConfigurator AddCycle(IObservable triggerObservable, Action> configure) { return AddReactiveNode(c => c.AddCycle(triggerObservable, configure)); } diff --git a/src/CodeCasa.AutomationPipelines.Lights/Pipeline/LightTransitionPipelineConfigurator.Reactive.Toggle.cs b/src/CodeCasa.AutomationPipelines.Lights/Pipeline/LightTransitionPipelineConfigurator.Reactive.Toggle.cs index c81a7d8..9d473f1 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/Pipeline/LightTransitionPipelineConfigurator.Reactive.Toggle.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/Pipeline/LightTransitionPipelineConfigurator.Reactive.Toggle.cs @@ -1,56 +1,55 @@ -using CodeCasa.AutomationPipelines.Lights.Context; using CodeCasa.AutomationPipelines.Lights.Toggle; using CodeCasa.Lights; namespace CodeCasa.AutomationPipelines.Lights.Pipeline; -internal partial class LightTransitionPipelineConfigurator +internal partial class LightTransitionPipelineConfigurator { /// - public ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservable, + public ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservable, IEnumerable lightParameters) { return AddReactiveNode(c => c.AddToggle(triggerObservable, lightParameters)); } /// - public ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservable, + public ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservable, params LightParameters[] lightParameters) { return AddReactiveNode(c => c.AddToggle(triggerObservable, lightParameters)); } /// - public ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservable, + public ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservable, IEnumerable lightTransitions) { return AddReactiveNode(c => c.AddToggle(triggerObservable, lightTransitions)); } /// - public ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservable, + public ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservable, params LightTransition[] lightTransitions) { return AddReactiveNode(c => c.AddToggle(triggerObservable, lightTransitions)); } /// - public ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservable, - IEnumerable>> nodeFactories) + public ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservable, + IEnumerable>> nodeFactories) { return AddReactiveNode(c => c.AddToggle(triggerObservable, nodeFactories)); } /// - public ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservable, - params Func>[] nodeFactories) + public ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservable, + params Func>[] nodeFactories) { return AddReactiveNode(c => c.AddToggle(triggerObservable, nodeFactories)); } /// - public ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservable, - Action configure) + public ILightTransitionPipelineConfigurator AddToggle(IObservable triggerObservable, + Action> configure) { return AddReactiveNode(c => c.AddToggle(triggerObservable, configure)); } diff --git a/src/CodeCasa.AutomationPipelines.Lights/Pipeline/LightTransitionPipelineConfigurator.Switch.cs b/src/CodeCasa.AutomationPipelines.Lights/Pipeline/LightTransitionPipelineConfigurator.Switch.cs index 9294ed2..170cd09 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/Pipeline/LightTransitionPipelineConfigurator.Switch.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/Pipeline/LightTransitionPipelineConfigurator.Switch.cs @@ -1,87 +1,85 @@ -using CodeCasa.AutomationPipelines.Lights.Context; -using CodeCasa.AutomationPipelines.Lights.Extensions; using CodeCasa.AutomationPipelines.Lights.Nodes; using CodeCasa.AutomationPipelines.Lights.ReactiveNode; -using System.Reactive.Concurrency; -using System.Reactive.Linq; using CodeCasa.Lights; using Microsoft.Extensions.DependencyInjection; +using System.Reactive.Concurrency; +using System.Reactive.Linq; namespace CodeCasa.AutomationPipelines.Lights.Pipeline; -internal partial class LightTransitionPipelineConfigurator +internal partial class LightTransitionPipelineConfigurator { /// - public ILightTransitionPipelineConfigurator Switch(LightParameters trueLightParameters, + public ILightTransitionPipelineConfigurator Switch(LightParameters trueLightParameters, LightParameters falseLightParameters) where TObservable : IObservable { return Switch(trueLightParameters.AsTransition(), falseLightParameters.AsTransition()); } /// - public ILightTransitionPipelineConfigurator Switch(IObservable observable, LightParameters trueLightParameters, + public ILightTransitionPipelineConfigurator Switch(IObservable observable, LightParameters trueLightParameters, LightParameters falseLightParameters) { return Switch(observable, trueLightParameters.AsTransition(), falseLightParameters.AsTransition()); } /// - public ILightTransitionPipelineConfigurator Switch(Func trueLightParametersFactory, - Func falseLightParametersFactory) where TObservable : IObservable + public ILightTransitionPipelineConfigurator Switch(Func trueLightParametersFactory, + Func falseLightParametersFactory) where TObservable : IObservable { return Switch(c => falseLightParametersFactory(c).AsTransition(), c => trueLightParametersFactory(c).AsTransition()); } /// - public ILightTransitionPipelineConfigurator Switch(IObservable observable, Func trueLightParametersFactory, - Func falseLightParametersFactory) + public ILightTransitionPipelineConfigurator Switch(IObservable observable, Func trueLightParametersFactory, + Func falseLightParametersFactory) { return Switch(observable, c => trueLightParametersFactory(c).AsTransition(), c => falseLightParametersFactory(c).AsTransition()); } /// - public ILightTransitionPipelineConfigurator Switch(LightTransition trueLightTransition, + public ILightTransitionPipelineConfigurator Switch(LightTransition trueLightTransition, LightTransition falseLightTransition) where TObservable : IObservable { return Switch(trueLightTransition, falseLightTransition); } /// - public ILightTransitionPipelineConfigurator Switch(IObservable observable, LightTransition trueLightTransition, + public ILightTransitionPipelineConfigurator Switch(IObservable observable, LightTransition trueLightTransition, LightTransition falseLightTransition) { return Switch(observable, trueLightTransition, falseLightTransition); } /// - public ILightTransitionPipelineConfigurator Switch(Func trueLightTransitionFactory, - Func falseLightTransitionFactory) where TObservable : IObservable + public ILightTransitionPipelineConfigurator Switch(Func trueLightTransitionFactory, + Func falseLightTransitionFactory) where TObservable : IObservable { return Switch( - c => new StaticLightTransitionNode(trueLightTransitionFactory(c), c.ServiceProvider.GetRequiredService()), - c => new StaticLightTransitionNode(falseLightTransitionFactory(c), c.ServiceProvider.GetRequiredService())); + c => new StaticLightTransitionNode(trueLightTransitionFactory(c), c.GetRequiredService()), + c => new StaticLightTransitionNode(falseLightTransitionFactory(c), c.GetRequiredService())); } /// - public ILightTransitionPipelineConfigurator Switch(IObservable observable, Func trueLightTransitionFactory, - Func falseLightTransitionFactory) + public ILightTransitionPipelineConfigurator Switch(IObservable observable, Func trueLightTransitionFactory, + Func falseLightTransitionFactory) { return Switch( observable, - c => new StaticLightTransitionNode(trueLightTransitionFactory(c), c.ServiceProvider.GetRequiredService()), - c => new StaticLightTransitionNode(falseLightTransitionFactory(c), c.ServiceProvider.GetRequiredService())); + c => new StaticLightTransitionNode(trueLightTransitionFactory(c), c.GetRequiredService()), + c => new StaticLightTransitionNode(falseLightTransitionFactory(c), c.GetRequiredService())); } /// - public ILightTransitionPipelineConfigurator Switch(Func> trueNodeFactory, Func> falseNodeFactory) where TObservable : IObservable + public ILightTransitionPipelineConfigurator Switch(Func> trueNodeFactory, Func> falseNodeFactory) where TObservable : IObservable { - var observable = serviceProvider.CreateInstanceWithinContext(Light); + var observable = ActivatorUtilities.CreateInstance(_serviceProvider); return Switch(observable, trueNodeFactory, falseNodeFactory); } /// - public ILightTransitionPipelineConfigurator Switch(IObservable observable, Func> trueNodeFactory, - Func> falseNodeFactory) + public ILightTransitionPipelineConfigurator Switch(IObservable observable, Func> trueNodeFactory, + Func> falseNodeFactory) { return AddReactiveNode(c => c .On(observable.Where(x => x), trueNodeFactory) @@ -89,14 +87,14 @@ public ILightTransitionPipelineConfigurator Switch(IObservable observable, } /// - public ILightTransitionPipelineConfigurator Switch() where TObservable : IObservable where TTrueNode : IPipelineNode where TFalseNode : IPipelineNode + public ILightTransitionPipelineConfigurator Switch() where TObservable : IObservable where TTrueNode : IPipelineNode where TFalseNode : IPipelineNode { - var observable = serviceProvider.CreateInstanceWithinContext(Light); + var observable = ActivatorUtilities.CreateInstance(_serviceProvider); return Switch(observable); } /// - public ILightTransitionPipelineConfigurator Switch(IObservable observable) where TTrueNode : IPipelineNode where TFalseNode : IPipelineNode + public ILightTransitionPipelineConfigurator Switch(IObservable observable) where TTrueNode : IPipelineNode where TFalseNode : IPipelineNode { return AddReactiveNode(c => c .On(observable.Where(x => x)) @@ -104,15 +102,15 @@ public ILightTransitionPipelineConfigurator Switch(IObser } /// - public ILightTransitionPipelineConfigurator AddReactiveNodeSwitch(Action trueConfigure, Action falseConfigure) where TObservable : IObservable + public ILightTransitionPipelineConfigurator AddReactiveNodeSwitch(Action> trueConfigure, Action> falseConfigure) where TObservable : IObservable { - var observable = serviceProvider.CreateInstanceWithinContext(Light); + var observable = ActivatorUtilities.CreateInstance(_serviceProvider); return AddReactiveNodeSwitch(observable, trueConfigure, falseConfigure); } /// - public ILightTransitionPipelineConfigurator AddReactiveNodeSwitch(IObservable observable, Action trueConfigure, - Action falseConfigure) + public ILightTransitionPipelineConfigurator AddReactiveNodeSwitch(IObservable observable, Action> trueConfigure, + Action> falseConfigure) { return AddReactiveNode(c => c .On(observable.Where(x => x), trueConfigure) @@ -120,26 +118,30 @@ public ILightTransitionPipelineConfigurator AddReactiveNodeSwitch(IObservable - public ILightTransitionPipelineConfigurator AddPipelineSwitch(Action trueConfigure, Action falseConfigure) where TObservable : IObservable + public ILightTransitionPipelineConfigurator AddPipelineSwitch(Action> trueConfigure, Action> falseConfigure) where TObservable : IObservable { - return Switch(c => lightPipelineFactory.CreateLightPipeline(c.Light, trueConfigure), c => lightPipelineFactory.CreateLightPipeline(c.Light, falseConfigure)); + return Switch( + s => s.GetRequiredService().CreateLightPipeline(Light, trueConfigure), + s => s.GetRequiredService().CreateLightPipeline(Light, falseConfigure)); } /// - public ILightTransitionPipelineConfigurator AddPipelineSwitch(IObservable observable, Action trueConfigure, - Action falseConfigure) + public ILightTransitionPipelineConfigurator AddPipelineSwitch(IObservable observable, Action> trueConfigure, + Action> falseConfigure) { - return Switch(observable, c => lightPipelineFactory.CreateLightPipeline(c.Light, trueConfigure), c => lightPipelineFactory.CreateLightPipeline(c.Light, falseConfigure)); + return Switch(observable, + s => s.GetRequiredService().CreateLightPipeline(Light, trueConfigure), + s => s.GetRequiredService().CreateLightPipeline(Light, falseConfigure)); } /// - public ILightTransitionPipelineConfigurator TurnOnOff() where TObservable : IObservable + public ILightTransitionPipelineConfigurator TurnOnOff() where TObservable : IObservable { return Switch(LightTransition.On(), LightTransition.Off()); } /// - public ILightTransitionPipelineConfigurator TurnOnOff(IObservable observable) + public ILightTransitionPipelineConfigurator TurnOnOff(IObservable observable) { return Switch(observable, LightTransition.On(), LightTransition.Off()); } diff --git a/src/CodeCasa.AutomationPipelines.Lights/Pipeline/LightTransitionPipelineConfigurator.When.cs b/src/CodeCasa.AutomationPipelines.Lights/Pipeline/LightTransitionPipelineConfigurator.When.cs index c2616f2..2434b16 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/Pipeline/LightTransitionPipelineConfigurator.When.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/Pipeline/LightTransitionPipelineConfigurator.When.cs @@ -1,5 +1,3 @@ -using CodeCasa.AutomationPipelines.Lights.Context; -using CodeCasa.AutomationPipelines.Lights.Extensions; using CodeCasa.AutomationPipelines.Lights.Nodes; using CodeCasa.AutomationPipelines.Lights.ReactiveNode; using System.Reactive.Concurrency; @@ -9,75 +7,75 @@ namespace CodeCasa.AutomationPipelines.Lights.Pipeline; -internal partial class LightTransitionPipelineConfigurator +internal partial class LightTransitionPipelineConfigurator { /// - public ILightTransitionPipelineConfigurator When(LightParameters lightParameters) + public ILightTransitionPipelineConfigurator When(LightParameters lightParameters) where TObservable : IObservable { return When(lightParameters.AsTransition()); } /// - public ILightTransitionPipelineConfigurator When(IObservable observable, + public ILightTransitionPipelineConfigurator When(IObservable observable, LightParameters lightParameters) { return When(observable, lightParameters.AsTransition()); } /// - public ILightTransitionPipelineConfigurator When( - Func lightParametersFactory) where TObservable : IObservable + public ILightTransitionPipelineConfigurator When( + Func lightParametersFactory) where TObservable : IObservable { return When(c => lightParametersFactory(c).AsTransition()); } /// - public ILightTransitionPipelineConfigurator When(IObservable observable, - Func lightParametersFactory) + public ILightTransitionPipelineConfigurator When(IObservable observable, + Func lightParametersFactory) { return When(observable, c => lightParametersFactory(c).AsTransition()); } /// - public ILightTransitionPipelineConfigurator When(LightTransition lightTransition) + public ILightTransitionPipelineConfigurator When(LightTransition lightTransition) where TObservable : IObservable { return When(_ => lightTransition); } /// - public ILightTransitionPipelineConfigurator When(IObservable observable, + public ILightTransitionPipelineConfigurator When(IObservable observable, LightTransition lightTransition) { return When(observable, _ => lightTransition); } /// - public ILightTransitionPipelineConfigurator When( - Func lightTransitionFactory) where TObservable : IObservable + public ILightTransitionPipelineConfigurator When( + Func lightTransitionFactory) where TObservable : IObservable { - return When(c => new StaticLightTransitionNode(lightTransitionFactory(c), c.ServiceProvider.GetRequiredService())); + return When(c => new StaticLightTransitionNode(lightTransitionFactory(c), c.GetRequiredService())); } /// - public ILightTransitionPipelineConfigurator When(IObservable observable, - Func lightTransitionFactory) + public ILightTransitionPipelineConfigurator When(IObservable observable, + Func lightTransitionFactory) { - return When(observable, c => new StaticLightTransitionNode(lightTransitionFactory(c), c.ServiceProvider.GetRequiredService())); + return When(observable, c => new StaticLightTransitionNode(lightTransitionFactory(c), c.GetRequiredService())); } /// - public ILightTransitionPipelineConfigurator When( - Func> nodeFactory) where TObservable : IObservable + public ILightTransitionPipelineConfigurator When( + Func> nodeFactory) where TObservable : IObservable { - var observable = serviceProvider.CreateInstanceWithinContext(Light); + var observable = ActivatorUtilities.CreateInstance(_serviceProvider); return When(observable, nodeFactory); } /// - public ILightTransitionPipelineConfigurator When(IObservable observable, - Func> nodeFactory) + public ILightTransitionPipelineConfigurator When(IObservable observable, + Func> nodeFactory) { return AddReactiveNode(c => c .On(observable.Where(x => x), nodeFactory) @@ -85,16 +83,16 @@ public ILightTransitionPipelineConfigurator When(IObservable observable, } /// - public ILightTransitionPipelineConfigurator When() + public ILightTransitionPipelineConfigurator When() where TObservable : IObservable where TNode : IPipelineNode { - var observable = serviceProvider.CreateInstanceWithinContext(Light); + var observable = ActivatorUtilities.CreateInstance(_serviceProvider); return When(observable); } /// - public ILightTransitionPipelineConfigurator When(IObservable observable) + public ILightTransitionPipelineConfigurator When(IObservable observable) where TNode : IPipelineNode { return AddReactiveNode(c => c @@ -103,14 +101,14 @@ public ILightTransitionPipelineConfigurator When(IObservable observ } /// - public ILightTransitionPipelineConfigurator AddReactiveNodeWhen(Action configure) where TObservable : IObservable + public ILightTransitionPipelineConfigurator AddReactiveNodeWhen(Action> configure) where TObservable : IObservable { - var observable = serviceProvider.CreateInstanceWithinContext(Light); + var observable = ActivatorUtilities.CreateInstance(_serviceProvider); return AddReactiveNodeWhen(observable, configure); } /// - public ILightTransitionPipelineConfigurator AddReactiveNodeWhen(IObservable observable, Action configure) + public ILightTransitionPipelineConfigurator AddReactiveNodeWhen(IObservable observable, Action> configure) { return AddReactiveNode(c => c .On(observable.Where(x => x), configure) @@ -118,38 +116,42 @@ public ILightTransitionPipelineConfigurator AddReactiveNodeWhen(IObservable - public ILightTransitionPipelineConfigurator AddPipelineWhen(Action pipelineConfigurator) where TObservable : IObservable + public ILightTransitionPipelineConfigurator AddPipelineWhen(Action> pipelineConfigurator) where TObservable : IObservable { - return When(c => lightPipelineFactory.CreateLightPipeline(c.Light, pipelineConfigurator)); + return When(c => + c.GetRequiredService() + .CreateLightPipeline(c.GetRequiredService(), pipelineConfigurator)); } /// - public ILightTransitionPipelineConfigurator AddPipelineWhen(IObservable observable, Action pipelineConfigurator) + public ILightTransitionPipelineConfigurator AddPipelineWhen(IObservable observable, Action> pipelineConfigurator) { - return When(observable, c => lightPipelineFactory.CreateLightPipeline(c.Light, pipelineConfigurator)); + return When(observable, c => + c.GetRequiredService() + .CreateLightPipeline(c.GetRequiredService(), pipelineConfigurator)); } /// - public ILightTransitionPipelineConfigurator TurnOffWhen() where TObservable : IObservable + public ILightTransitionPipelineConfigurator TurnOffWhen() where TObservable : IObservable { return When(LightTransition.Off()); } /// - public ILightTransitionPipelineConfigurator TurnOffWhen(IObservable observable) + public ILightTransitionPipelineConfigurator TurnOffWhen(IObservable observable) { return When(observable, LightTransition.Off()); } /// - public ILightTransitionPipelineConfigurator TurnOnWhen() where TObservable : IObservable + public ILightTransitionPipelineConfigurator TurnOnWhen() where TObservable : IObservable { return When(LightTransition.On()); } /// - public ILightTransitionPipelineConfigurator TurnOnWhen(IObservable observable) + public ILightTransitionPipelineConfigurator TurnOnWhen(IObservable observable) { return When(observable, LightTransition.On()); } -} \ No newline at end of file +} diff --git a/src/CodeCasa.AutomationPipelines.Lights/Pipeline/LightTransitionPipelineConfigurator.cs b/src/CodeCasa.AutomationPipelines.Lights/Pipeline/LightTransitionPipelineConfigurator.cs index 37f3558..3765a4e 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/Pipeline/LightTransitionPipelineConfigurator.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/Pipeline/LightTransitionPipelineConfigurator.cs @@ -1,8 +1,7 @@ -using CodeCasa.AutomationPipelines.Lights.Context; -using CodeCasa.AutomationPipelines.Lights.Extensions; -using CodeCasa.AutomationPipelines.Lights.ReactiveNode; using CodeCasa.Abstractions; +using CodeCasa.AutomationPipelines.Lights.ReactiveNode; using CodeCasa.Lights; +using Microsoft.Extensions.DependencyInjection; namespace CodeCasa.AutomationPipelines.Lights.Pipeline { @@ -10,29 +9,32 @@ namespace CodeCasa.AutomationPipelines.Lights.Pipeline /// Configures a light transition pipeline for a single light entity. /// This configurator allows adding pipeline nodes, reactive nodes, dimmers, and conditional logic to light automation. /// - internal partial class LightTransitionPipelineConfigurator( - IServiceProvider serviceProvider, - LightPipelineFactory lightPipelineFactory, - ReactiveNodeFactory reactiveNodeFactory, - ILight light) - : ILightTransitionPipelineConfigurator + internal partial class LightTransitionPipelineConfigurator + : ILightTransitionPipelineConfigurator where TLight : ILight { + private readonly IServiceProvider _serviceProvider; private readonly List> _nodes = new(); - internal ILight Light { get; } = light; + internal TLight Light { get; } + + public LightTransitionPipelineConfigurator(IServiceProvider serviceProvider, TLight light) + { + _serviceProvider = serviceProvider; + Light = light; + } internal string? Name { get; private set; } internal bool? Log { get; private set; } public IReadOnlyCollection> Nodes => _nodes.AsReadOnly(); - public ILightTransitionPipelineConfigurator - AddConditional(IObservable observable, Action trueConfigure, Action falseConfigure) + public ILightTransitionPipelineConfigurator + AddConditional(IObservable observable, Action> trueConfigure, Action> falseConfigure) { throw new NotImplementedException(); } /// - public ILightTransitionPipelineConfigurator EnableLogging(string? pipelineName = null) + public ILightTransitionPipelineConfigurator EnableLogging(string? pipelineName = null) { Name = pipelineName; Log = true; @@ -40,16 +42,16 @@ public ILightTransitionPipelineConfigurator EnableLogging(string? pipelineName = } /// - public ILightTransitionPipelineConfigurator DisableLogging() + public ILightTransitionPipelineConfigurator DisableLogging() { Log = false; return this; } /// - public ILightTransitionPipelineConfigurator AddNode() where TNode : IPipelineNode + public ILightTransitionPipelineConfigurator AddNode() where TNode : IPipelineNode { - _nodes.Add(serviceProvider.CreateInstanceWithinContext(Light)); + _nodes.Add(ActivatorUtilities.CreateInstance(_serviceProvider)); return this; } @@ -58,24 +60,25 @@ public ILightTransitionPipelineConfigurator AddNode() where TNode : IPipe /// /// The pipeline node to add. /// The configurator instance for method chaining. - public ILightTransitionPipelineConfigurator AddNode(IPipelineNode node) + public ILightTransitionPipelineConfigurator AddNode(IPipelineNode node) { _nodes.Add(node); return this; } /// - public ILightTransitionPipelineConfigurator AddNode(Func> nodeFactory) + public ILightTransitionPipelineConfigurator AddNode(Func> nodeFactory) { - _nodes.Add(nodeFactory(new LightPipelineContext(serviceProvider, Light))); + _nodes.Add(nodeFactory(_serviceProvider)); return this; } /// - public ILightTransitionPipelineConfigurator AddReactiveNode( - Action configure) + public ILightTransitionPipelineConfigurator AddReactiveNode( + Action> configure) { - return AddNode(reactiveNodeFactory.CreateReactiveNode(Light, c => + var factory = _serviceProvider.GetRequiredService(); + return AddNode(factory.CreateReactiveNode(Light, c => { if (Log ?? false) { @@ -87,13 +90,13 @@ public ILightTransitionPipelineConfigurator AddReactiveNode( } /// - public ILightTransitionPipelineConfigurator AddDimmer(IDimmer dimmer) + public ILightTransitionPipelineConfigurator AddDimmer(IDimmer dimmer) { return AddDimmer(dimmer, _ => { }); } /// - public ILightTransitionPipelineConfigurator AddDimmer(IDimmer dimmer, Action dimOptions) + public ILightTransitionPipelineConfigurator AddDimmer(IDimmer dimmer, Action dimOptions) { return AddReactiveNode(c => { @@ -102,31 +105,34 @@ public ILightTransitionPipelineConfigurator AddDimmer(IDimmer dimmer, Action - public ILightTransitionPipelineConfigurator ForLight(string lightId, - Action compositeNodeBuilder) => + public ILightTransitionPipelineConfigurator ForLight(string lightId, + Action> compositeNodeBuilder) => ForLights([lightId], compositeNodeBuilder); /// - public ILightTransitionPipelineConfigurator ForLight(ILight light, - Action compositeNodeBuilder) => ForLights([light], compositeNodeBuilder); + public ILightTransitionPipelineConfigurator ForLight(TLight light, + Action> compositeNodeBuilder) => ForLights([light], compositeNodeBuilder); /// - public ILightTransitionPipelineConfigurator ForLights(IEnumerable lightIds, - Action compositeNodeBuilder) + public ILightTransitionPipelineConfigurator ForLights(IEnumerable lightIds, + Action> compositeNodeBuilder) { CompositeHelper.ValidateLightSupported(lightIds, Light.Id); return this; } /// - public ILightTransitionPipelineConfigurator ForLights(IEnumerable lights, - Action compositeNodeBuilder) + public ILightTransitionPipelineConfigurator ForLights(IEnumerable lights, + Action> compositeNodeBuilder) { CompositeHelper.ResolveGroupsAndValidateLightSupported(lights, Light.Id); return this; } /// - public ILightTransitionPipelineConfigurator AddPipeline(Action pipelineNodeOptions) => AddNode(lightPipelineFactory.CreateLightPipeline(Light, pipelineNodeOptions)); - } -} + public ILightTransitionPipelineConfigurator AddPipeline(Action> pipelineNodeOptions) => + AddNode( + _serviceProvider.GetRequiredService() + .CreateLightPipeline(Light, pipelineNodeOptions)); + } + } diff --git a/src/CodeCasa.AutomationPipelines.Lights/Pipeline/ServiceScopedPipeline.cs b/src/CodeCasa.AutomationPipelines.Lights/Pipeline/ServiceScopedPipeline.cs new file mode 100644 index 0000000..9b4f227 --- /dev/null +++ b/src/CodeCasa.AutomationPipelines.Lights/Pipeline/ServiceScopedPipeline.cs @@ -0,0 +1,57 @@ +using CodeCasa.AutomationPipelines.Lights.Utils; +using Microsoft.Extensions.DependencyInjection; + +namespace CodeCasa.AutomationPipelines.Lights.Pipeline; + +internal sealed class ServiceScopedPipeline(IServiceScope scope, IPipeline pipeline) + : IPipeline, IDisposable, IAsyncDisposable +{ + private readonly IServiceScope _scope = scope ?? throw new ArgumentNullException(nameof(scope)); + private readonly IPipeline _instance = pipeline ?? throw new ArgumentNullException(nameof(pipeline)); + + public void Dispose() + { + (_instance as IDisposable)?.Dispose(); + _scope.Dispose(); + } + + public async ValueTask DisposeAsync() + { + await _instance.DisposeOrDisposeAsync(); + await _scope.DisposeOrDisposeAsync(); + } + + public TNode? Input + { + get => _instance.Input; + set => _instance.Input = value; + } + + public TNode? Output => _instance.Output; + + public IObservable OnNewOutput => _instance.OnNewOutput; + + public IPipeline SetDefault(TNode state) + { + _instance.SetDefault(state); + return this; + } + + public IPipeline RegisterNode() where TNode1 : IPipelineNode + { + _instance.RegisterNode(); + return this; + } + + public IPipeline RegisterNode(IPipelineNode node) + { + _instance.RegisterNode(node); + return this; + } + + public IPipeline SetOutputHandler(Action action, bool callActionDistinct = true) + { + _instance.SetOutputHandler(action, callActionDistinct); + return this; + } +} \ No newline at end of file diff --git a/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/CompositeLightTransitionReactiveNodeConfigurator.Cycle.cs b/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/CompositeLightTransitionReactiveNodeConfigurator.Cycle.cs index 4eff034..0d33901 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/CompositeLightTransitionReactiveNodeConfigurator.Cycle.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/CompositeLightTransitionReactiveNodeConfigurator.Cycle.cs @@ -1,4 +1,3 @@ -using CodeCasa.AutomationPipelines.Lights.Context; using CodeCasa.AutomationPipelines.Lights.Cycle; using CodeCasa.AutomationPipelines.Lights.Extensions; using CodeCasa.AutomationPipelines.Lights.Nodes; @@ -7,14 +6,14 @@ namespace CodeCasa.AutomationPipelines.Lights.ReactiveNode; -internal partial class CompositeLightTransitionReactiveNodeConfigurator +internal partial class CompositeLightTransitionReactiveNodeConfigurator { /// - public ILightTransitionReactiveNodeConfigurator AddCycle(IObservable triggerObservable, IEnumerable lightParameters) + public ILightTransitionReactiveNodeConfigurator AddCycle(IObservable triggerObservable, IEnumerable lightParameters) => AddCycle(triggerObservable, lightParameters.ToArray()); /// - public ILightTransitionReactiveNodeConfigurator AddCycle(IObservable triggerObservable, + public ILightTransitionReactiveNodeConfigurator AddCycle(IObservable triggerObservable, params LightParameters[] lightParameters) { return AddCycle(triggerObservable, configure => @@ -27,11 +26,11 @@ public ILightTransitionReactiveNodeConfigurator AddCycle(IObservable trigg } /// - public ILightTransitionReactiveNodeConfigurator AddCycle(IObservable triggerObservable, IEnumerable lightTransitions) + public ILightTransitionReactiveNodeConfigurator AddCycle(IObservable triggerObservable, IEnumerable lightTransitions) => AddCycle(triggerObservable, lightTransitions.ToArray()); /// - public ILightTransitionReactiveNodeConfigurator AddCycle(IObservable triggerObservable, + public ILightTransitionReactiveNodeConfigurator AddCycle(IObservable triggerObservable, params LightTransition[] lightTransitions) { return AddCycle(triggerObservable, configure => @@ -44,23 +43,20 @@ public ILightTransitionReactiveNodeConfigurator AddCycle(IObservable trigg } /// - public ILightTransitionReactiveNodeConfigurator AddCycle(IObservable triggerObservable, Action configure) + public ILightTransitionReactiveNodeConfigurator AddCycle(IObservable triggerObservable, Action> configure) { var cycleConfigurators = configurators.ToDictionary(kvp => kvp.Key, - kvp => new LightTransitionCycleConfigurator(kvp.Value.Light, scheduler)); - var compositeCycleConfigurator = new CompositeLightTransitionCycleConfigurator(cycleConfigurators, []); + kvp => new LightTransitionCycleConfigurator(kvp.Value.Light, scheduler)); + var compositeCycleConfigurator = new CompositeLightTransitionCycleConfigurator(cycleConfigurators, []); configure(compositeCycleConfigurator); configurators.ForEach(kvp => kvp.Value.AddNodeSource(triggerObservable.ToCycleObservable(cycleConfigurators[kvp.Key].CycleNodeFactories.Select(tuple => { - var factory = new Func>(() => - { - var serviceScope = serviceProvider.CreateScope(); - var context = new LightPipelineContext(serviceScope.ServiceProvider, kvp.Value.Light); - return new ScopedNode(serviceScope, tuple.nodeFactory(context)); - }); - var valueIsActiveFunc = () => tuple.matchesNodeState(new LightPipelineContext(serviceProvider, kvp.Value.Light)); + var factory = new Func>(() => + tuple.nodeFactory.CreateScopedNode(kvp.Value.ServiceProvider) // Note: This service provider already has the light registered. We scope it further for node lifetime. + ); + var valueIsActiveFunc = () => tuple.matchesNodeState(serviceProvider); return (factory, valueIsActiveFunc); })))); return this; } -} \ No newline at end of file +} diff --git a/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/CompositeLightTransitionReactiveNodeConfigurator.On.cs b/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/CompositeLightTransitionReactiveNodeConfigurator.On.cs index 38299aa..6112066 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/CompositeLightTransitionReactiveNodeConfigurator.On.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/CompositeLightTransitionReactiveNodeConfigurator.On.cs @@ -1,5 +1,4 @@ -using System.Reactive.Concurrency; -using CodeCasa.AutomationPipelines.Lights.Context; +using System.Reactive.Concurrency; using CodeCasa.AutomationPipelines.Lights.Extensions; using CodeCasa.AutomationPipelines.Lights.Nodes; using CodeCasa.AutomationPipelines.Lights.Pipeline; @@ -8,75 +7,75 @@ namespace CodeCasa.AutomationPipelines.Lights.ReactiveNode; -internal partial class CompositeLightTransitionReactiveNodeConfigurator +internal partial class CompositeLightTransitionReactiveNodeConfigurator { /// - public ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, LightParameters lightParameters) + public ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, LightParameters lightParameters) => On(triggerObservable, lightParameters.AsTransition()); /// - public ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, Func lightParametersFactory) + public ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, Func lightParametersFactory) => On(triggerObservable, c => lightParametersFactory(c).AsTransition()); /// - public ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, LightTransition lightTransition) - => On(triggerObservable, c => new StaticLightTransitionNode(lightTransition, c.ServiceProvider.GetRequiredService())); + public ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, LightTransition lightTransition) + => On(triggerObservable, c => new StaticLightTransitionNode(lightTransition, c.GetRequiredService())); /// - public ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, Func lightTransitionFactory) - => On(triggerObservable, c => new StaticLightTransitionNode(lightTransitionFactory(c), c.ServiceProvider.GetRequiredService())); + public ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, Func lightTransitionFactory) + => On(triggerObservable, c => new StaticLightTransitionNode(lightTransitionFactory(c), c.GetRequiredService())); /// - public ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable) where TNode : IPipelineNode + public ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable) where TNode : IPipelineNode { configurators.Values.ForEach(c => c.On(triggerObservable)); return this; } /// - public ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, Func> nodeFactory) + public ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, Func> nodeFactory) { configurators.Values.ForEach(c => c.On(triggerObservable, nodeFactory)); return this; } /// - public ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, Action pipelineConfigurator) + public ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, Action> pipelineConfigurator) { // Note: we create the pipeline in composite context so all configuration is also applied in that context. var pipelines = lightPipelineFactory.CreateLightPipelines(configurators.Values.Select(c => c.Light), pipelineConfigurator); - configurators.Values.ForEach(c => c.On(triggerObservable, ctx => pipelines[ctx.Light.Id])); + configurators.Values.ForEach(c => c.On(triggerObservable, _ => pipelines[c.Light.Id])); return this; } /// - public ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, Action configure) + public ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, Action> configure) { // Note: we create the pipeline in composite context so all configuration is also applied in that context. var nodes = reactiveNodeFactory.CreateReactiveNodes(configurators.Values.Select(c => c.Light), configure); - configurators.Values.ForEach(c => c.On(triggerObservable, ctx => nodes[ctx.Light.Id])); + configurators.Values.ForEach(c => c.On(triggerObservable, _ => nodes[c.Light.Id])); return this; } /// - public ILightTransitionReactiveNodeConfigurator PassThroughOn(IObservable triggerObservable) + public ILightTransitionReactiveNodeConfigurator PassThroughOn(IObservable triggerObservable) { configurators.Values.ForEach(c => c.PassThroughOn(triggerObservable)); return this; } /// - public ILightTransitionReactiveNodeConfigurator TurnOffWhen(IObservable triggerObservable) + public ILightTransitionReactiveNodeConfigurator TurnOffWhen(IObservable triggerObservable) { configurators.Values.ForEach(c => c.TurnOffWhen(triggerObservable)); return this; } /// - public ILightTransitionReactiveNodeConfigurator TurnOnWhen(IObservable triggerObservable) + public ILightTransitionReactiveNodeConfigurator TurnOnWhen(IObservable triggerObservable) { return On(triggerObservable, LightTransition.On()); } -} \ No newline at end of file +} diff --git a/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/CompositeLightTransitionReactiveNodeConfigurator.Toggle.cs b/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/CompositeLightTransitionReactiveNodeConfigurator.Toggle.cs index 1d4762b..6f5b60a 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/CompositeLightTransitionReactiveNodeConfigurator.Toggle.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/CompositeLightTransitionReactiveNodeConfigurator.Toggle.cs @@ -1,4 +1,3 @@ -using CodeCasa.AutomationPipelines.Lights.Context; using CodeCasa.AutomationPipelines.Lights.Extensions; using CodeCasa.AutomationPipelines.Lights.Nodes; using CodeCasa.AutomationPipelines.Lights.Toggle; @@ -8,14 +7,14 @@ namespace CodeCasa.AutomationPipelines.Lights.ReactiveNode; -internal partial class CompositeLightTransitionReactiveNodeConfigurator +internal partial class CompositeLightTransitionReactiveNodeConfigurator { /// - public ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObservable, IEnumerable lightParameters) + public ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObservable, IEnumerable lightParameters) => AddToggle(triggerObservable, lightParameters.ToArray()); /// - public ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObservable, + public ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObservable, params LightParameters[] lightParameters) { return AddToggle(triggerObservable, configure => @@ -28,11 +27,11 @@ public ILightTransitionReactiveNodeConfigurator AddToggle(IObservable trig } /// - public ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObservable, IEnumerable lightTransitions) + public ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObservable, IEnumerable lightTransitions) => AddToggle(triggerObservable, lightTransitions.ToArray()); /// - public ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObservable, + public ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObservable, params LightTransition[] lightTransitions) { return AddToggle(triggerObservable, configure => @@ -45,11 +44,11 @@ public ILightTransitionReactiveNodeConfigurator AddToggle(IObservable trig } /// - public ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObservable, IEnumerable>> nodeFactories) + public ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObservable, IEnumerable>> nodeFactories) => AddToggle(triggerObservable, nodeFactories.ToArray()); /// - public ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObservable, params Func>[] nodeFactories) + public ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObservable, params Func>[] nodeFactories) { return AddToggle(triggerObservable, configure => { @@ -61,23 +60,20 @@ public ILightTransitionReactiveNodeConfigurator AddToggle(IObservable trig } /// - public ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObservable, Action configure) + public ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObservable, Action> configure) { var toggleConfigurators = configurators.ToDictionary(kvp => kvp.Key, - kvp => new LightTransitionToggleConfigurator(kvp.Value.Light, scheduler)); - var compositeCycleConfigurator = new CompositeLightTransitionToggleConfigurator(toggleConfigurators, []); + kvp => new LightTransitionToggleConfigurator(kvp.Value.Light, scheduler)); + var compositeCycleConfigurator = new CompositeLightTransitionToggleConfigurator(toggleConfigurators, []); configure(compositeCycleConfigurator); configurators.ForEach(kvp => kvp.Value.AddNodeSource(triggerObservable.ToToggleObservable( () => configurators.Values.Any(c => c.Light.IsOn()), () => new TurnOffThenPassThroughNode(), toggleConfigurators[kvp.Key].NodeFactories.Select(fact => { - return new Func>(() => - { - var serviceScope = serviceProvider.CreateScope(); - var context = new LightPipelineContext(serviceScope.ServiceProvider, kvp.Value.Light); - return new ScopedNode(serviceScope, fact(context)); - }); + return new Func>(() => + fact.CreateScopedNode(kvp.Value.ServiceProvider) // Note: This service provider already has the light registered. We scope it further for node lifetime. + ); }), toggleConfigurators[kvp.Key].ToggleTimeout ?? TimeSpan.FromMilliseconds(1000), toggleConfigurators[kvp.Key].IncludeOffValue))); diff --git a/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/CompositeLightTransitionReactiveNodeConfigurator.cs b/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/CompositeLightTransitionReactiveNodeConfigurator.cs index 8d51871..fad90d6 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/CompositeLightTransitionReactiveNodeConfigurator.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/CompositeLightTransitionReactiveNodeConfigurator.cs @@ -1,6 +1,5 @@ -using System.Reactive.Concurrency; +using System.Reactive.Concurrency; using CodeCasa.Abstractions; -using CodeCasa.AutomationPipelines.Lights.Context; using CodeCasa.AutomationPipelines.Lights.Extensions; using CodeCasa.AutomationPipelines.Lights.Pipeline; using CodeCasa.Lights; @@ -11,30 +10,31 @@ namespace CodeCasa.AutomationPipelines.Lights.ReactiveNode; /// Configures light transition reactive nodes for multiple light entities as a composite. /// This configurator applies configurations across all included lights and allows for selective scoping to subsets of lights. /// -internal partial class CompositeLightTransitionReactiveNodeConfigurator( +internal partial class CompositeLightTransitionReactiveNodeConfigurator( IServiceProvider serviceProvider, LightPipelineFactory lightPipelineFactory, ReactiveNodeFactory reactiveNodeFactory, - Dictionary configurators, + Dictionary> configurators, IScheduler scheduler) - : ILightTransitionReactiveNodeConfigurator + : ILightTransitionReactiveNodeConfigurator + where TLight : ILight { /// - public ILightTransitionReactiveNodeConfigurator EnableLogging(string? name = null) + public ILightTransitionReactiveNodeConfigurator EnableLogging(string? name = null) { configurators.Values.ForEach(b => b.EnableLogging(name)); return this; } /// - public ILightTransitionReactiveNodeConfigurator DisableLogging() + public ILightTransitionReactiveNodeConfigurator DisableLogging() { configurators.Values.ForEach(b => b.DisableLogging()); return this; } /// - public ILightTransitionReactiveNodeConfigurator AddReactiveDimmer(IDimmer dimmer) + public ILightTransitionReactiveNodeConfigurator AddReactiveDimmer(IDimmer dimmer) { foreach (var configurator in configurators) { @@ -44,7 +44,7 @@ public ILightTransitionReactiveNodeConfigurator AddReactiveDimmer(IDimmer dimmer } /// - public ILightTransitionReactiveNodeConfigurator SetReactiveDimmerOptions(DimmerOptions dimmerOptions) + public ILightTransitionReactiveNodeConfigurator SetReactiveDimmerOptions(DimmerOptions dimmerOptions) { foreach (var configurator in configurators) { @@ -54,13 +54,13 @@ public ILightTransitionReactiveNodeConfigurator SetReactiveDimmerOptions(DimmerO } /// - public ILightTransitionReactiveNodeConfigurator AddUncoupledDimmer(IDimmer dimmer) + public ILightTransitionReactiveNodeConfigurator AddUncoupledDimmer(IDimmer dimmer) { return AddUncoupledDimmer(dimmer, _ => { }); } /// - public ILightTransitionReactiveNodeConfigurator AddUncoupledDimmer(IDimmer dimmer, Action dimOptions) + public ILightTransitionReactiveNodeConfigurator AddUncoupledDimmer(IDimmer dimmer, Action dimOptions) { var options = new DimmerOptions(); dimOptions(options); @@ -71,7 +71,7 @@ public ILightTransitionReactiveNodeConfigurator AddUncoupledDimmer(IDimmer dimme var configuratorsInOrder = options.ValidateAndOrderMultipleLightTypes(configurators).Select(kvp => kvp.Value).ToArray(); foreach (var configurator in configuratorsInOrder) { - var lightsInDimOrder = configuratorsInOrder.Select(c => c.Light); + var lightsInDimOrder = configuratorsInOrder.Select(c => (ILight)c.Light); configurator.AddDimPulses(options, lightsInDimOrder, dimPulses, brightenPulses); } return this; @@ -79,20 +79,20 @@ public ILightTransitionReactiveNodeConfigurator AddUncoupledDimmer(IDimmer dimme } /// - public ILightTransitionReactiveNodeConfigurator AddNodeSource(IObservable?>> nodeFactorySource) + public ILightTransitionReactiveNodeConfigurator AddNodeSource(IObservable?>> nodeFactorySource) { configurators.Values.ForEach(c => c.AddNodeSource(nodeFactorySource)); return this; } /// - public ILightTransitionReactiveNodeConfigurator ForLight(string lightId, Action configure) => ForLights([lightId], configure); + public ILightTransitionReactiveNodeConfigurator ForLight(string lightId, Action> configure) => ForLights([lightId], configure); /// - public ILightTransitionReactiveNodeConfigurator ForLight(ILight light, Action configure) => ForLights([light], configure); + public ILightTransitionReactiveNodeConfigurator ForLight(TLight light, Action> configure) => ForLights([light], configure); /// - public ILightTransitionReactiveNodeConfigurator ForLights(IEnumerable lightIds, Action configure) + public ILightTransitionReactiveNodeConfigurator ForLights(IEnumerable lightIds, Action> configure) { var lightIdsArray = CompositeHelper.ValidateLightsSupported(lightIds, configurators.Keys); @@ -108,7 +108,7 @@ public ILightTransitionReactiveNodeConfigurator ForLights(IEnumerable li return this; } - configure(new CompositeLightTransitionReactiveNodeConfigurator( + configure(new CompositeLightTransitionReactiveNodeConfigurator( serviceProvider, lightPipelineFactory, reactiveNodeFactory, @@ -118,7 +118,7 @@ public ILightTransitionReactiveNodeConfigurator ForLights(IEnumerable li } /// - public ILightTransitionReactiveNodeConfigurator ForLights(IEnumerable lights, Action configure) + public ILightTransitionReactiveNodeConfigurator ForLights(IEnumerable lights, Action> configure) { var lightIds = CompositeHelper.ResolveGroupsAndValidateLightsSupported(lights, configurators.Keys); return ForLights(lightIds, configure); diff --git a/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/ILightTransitionReactiveNodeConfigurator.Cycle.cs b/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/ILightTransitionReactiveNodeConfigurator.Cycle.cs index 7b3a004..b06975c 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/ILightTransitionReactiveNodeConfigurator.Cycle.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/ILightTransitionReactiveNodeConfigurator.Cycle.cs @@ -3,7 +3,7 @@ namespace CodeCasa.AutomationPipelines.Lights.ReactiveNode; -public partial interface ILightTransitionReactiveNodeConfigurator +public partial interface ILightTransitionReactiveNodeConfigurator where TLight : ILight { /// /// Adds a state-based cycle trigger that rotates through the specified light parameters when triggered by . @@ -14,7 +14,7 @@ public partial interface ILightTransitionReactiveNodeConfigurator /// The observable that triggers cycling to the next parameters. /// The collection of light parameters to cycle through. /// The configurator instance for method chaining. - ILightTransitionReactiveNodeConfigurator AddCycle(IObservable triggerObservable, + ILightTransitionReactiveNodeConfigurator AddCycle(IObservable triggerObservable, IEnumerable lightParameters); /// @@ -26,7 +26,7 @@ ILightTransitionReactiveNodeConfigurator AddCycle(IObservable triggerObser /// The observable that triggers cycling to the next parameters. /// The array of light parameters to cycle through. /// The configurator instance for method chaining. - ILightTransitionReactiveNodeConfigurator AddCycle(IObservable triggerObservable, + ILightTransitionReactiveNodeConfigurator AddCycle(IObservable triggerObservable, params LightParameters[] lightParameters); /// @@ -38,7 +38,7 @@ ILightTransitionReactiveNodeConfigurator AddCycle(IObservable triggerObser /// The observable that triggers cycling to the next transition. /// The collection of light transitions to cycle through. /// The configurator instance for method chaining. - ILightTransitionReactiveNodeConfigurator AddCycle(IObservable triggerObservable, + ILightTransitionReactiveNodeConfigurator AddCycle(IObservable triggerObservable, IEnumerable lightTransitions); /// @@ -50,7 +50,7 @@ ILightTransitionReactiveNodeConfigurator AddCycle(IObservable triggerObser /// The observable that triggers cycling to the next transition. /// The array of light transitions to cycle through. /// The configurator instance for method chaining. - ILightTransitionReactiveNodeConfigurator AddCycle(IObservable triggerObservable, + ILightTransitionReactiveNodeConfigurator AddCycle(IObservable triggerObservable, params LightTransition[] lightTransitions); /// @@ -62,6 +62,6 @@ ILightTransitionReactiveNodeConfigurator AddCycle(IObservable triggerObser /// The observable that triggers cycling to the next state. /// An action to configure the cycle behavior. /// The configurator instance for method chaining. - ILightTransitionReactiveNodeConfigurator AddCycle(IObservable triggerObservable, - Action configure); + ILightTransitionReactiveNodeConfigurator AddCycle(IObservable triggerObservable, + Action> configure); } \ No newline at end of file diff --git a/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/ILightTransitionReactiveNodeConfigurator.On.cs b/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/ILightTransitionReactiveNodeConfigurator.On.cs index a3e5fa2..8de78c5 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/ILightTransitionReactiveNodeConfigurator.On.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/ILightTransitionReactiveNodeConfigurator.On.cs @@ -1,10 +1,9 @@ -using CodeCasa.AutomationPipelines.Lights.Context; using CodeCasa.AutomationPipelines.Lights.Pipeline; using CodeCasa.Lights; namespace CodeCasa.AutomationPipelines.Lights.ReactiveNode; -public partial interface ILightTransitionReactiveNodeConfigurator +public partial interface ILightTransitionReactiveNodeConfigurator where TLight : ILight { /// /// Registers a trigger that applies the given when the emits a value. @@ -13,7 +12,7 @@ public partial interface ILightTransitionReactiveNodeConfigurator /// The observable that triggers the light parameter application. /// The light parameters to apply when triggered. /// The configurator instance for method chaining. - ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, + ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, LightParameters lightParameters); /// @@ -23,8 +22,8 @@ ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, /// The observable that triggers the light parameter application. /// A factory function that creates light parameters based on the pipeline context. /// The configurator instance for method chaining. - ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, - Func lightParametersFactory); + ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, + Func lightParametersFactory); /// /// Registers a trigger that applies the given when the emits a value. @@ -33,7 +32,7 @@ ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, /// The observable that triggers the light transition application. /// The light transition to apply when triggered. /// The configurator instance for method chaining. - ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, + ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, LightTransition lightTransition); /// @@ -43,8 +42,8 @@ ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, /// The observable that triggers the light transition application. /// A factory function that creates a light transition based on the pipeline context. /// The configurator instance for method chaining. - ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, - Func lightTransitionFactory); + ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, + Func lightTransitionFactory); /// /// Registers a trigger that activates a pipeline node of type when the emits a value. @@ -54,7 +53,7 @@ ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, /// The type of the pipeline node to resolve and activate. /// The observable that triggers the node activation. /// The configurator instance for method chaining. - ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable) + ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable) where TNode : IPipelineNode; /// @@ -64,8 +63,8 @@ ILightTransitionReactiveNodeConfigurator On(IObservable triggerObse /// The observable that triggers the node activation. /// A factory function that creates a pipeline node based on the pipeline context. /// The configurator instance for method chaining. - ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, - Func> nodeFactory); + ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, + Func> nodeFactory); /// /// Registers a trigger that activates a nested pipeline configured by when the emits a value. @@ -74,8 +73,8 @@ ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, /// The observable that triggers the pipeline activation. /// An action to configure the nested pipeline. /// The configurator instance for method chaining. - ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, - Action pipelineConfigurator); + ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, + Action> pipelineConfigurator); /// /// Registers a trigger that activates a nested reactive node configured by when the emits a value. @@ -84,8 +83,8 @@ ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, /// The observable that triggers the reactive node activation. /// An action to configure the nested reactive node. /// The configurator instance for method chaining. - ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, - Action configure); + ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, + Action> configure); /// /// Registers a pass-through trigger that allows the current input to pass through unchanged when the emits a value. @@ -94,7 +93,7 @@ ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, /// The type of values emitted by the trigger observable. /// The observable that triggers the pass-through behavior. /// The configurator instance for method chaining. - ILightTransitionReactiveNodeConfigurator PassThroughOn(IObservable triggerObservable); + ILightTransitionReactiveNodeConfigurator PassThroughOn(IObservable triggerObservable); /// /// Registers a trigger that turns off the light when the emits a value. @@ -102,7 +101,7 @@ ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, /// The type of values emitted by the trigger observable. /// The observable that triggers turning off the light. /// The configurator instance for method chaining. - ILightTransitionReactiveNodeConfigurator TurnOffWhen(IObservable triggerObservable); + ILightTransitionReactiveNodeConfigurator TurnOffWhen(IObservable triggerObservable); /// /// Registers a trigger that turns on the light when the emits a value. @@ -110,5 +109,5 @@ ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, /// The type of values emitted by the trigger observable. /// The observable that triggers turning on the light. /// The configurator instance for method chaining. - ILightTransitionReactiveNodeConfigurator TurnOnWhen(IObservable triggerObservable); -} \ No newline at end of file + ILightTransitionReactiveNodeConfigurator TurnOnWhen(IObservable triggerObservable); +} diff --git a/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/ILightTransitionReactiveNodeConfigurator.Toggle.cs b/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/ILightTransitionReactiveNodeConfigurator.Toggle.cs index f40687a..65cbfbb 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/ILightTransitionReactiveNodeConfigurator.Toggle.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/ILightTransitionReactiveNodeConfigurator.Toggle.cs @@ -1,10 +1,9 @@ -using CodeCasa.AutomationPipelines.Lights.Context; using CodeCasa.AutomationPipelines.Lights.Toggle; using CodeCasa.Lights; namespace CodeCasa.AutomationPipelines.Lights.ReactiveNode; -public partial interface ILightTransitionReactiveNodeConfigurator +public partial interface ILightTransitionReactiveNodeConfigurator where TLight : ILight { /// /// Adds a time-based toggle trigger that switches between the specified light parameters when triggered by . @@ -15,7 +14,7 @@ public partial interface ILightTransitionReactiveNodeConfigurator /// The observable that triggers toggling to the next parameters. /// The collection of light parameters to toggle between. /// The configurator instance for method chaining. - ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObservable, + ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObservable, IEnumerable lightParameters); /// @@ -27,7 +26,7 @@ ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObse /// The observable that triggers toggling to the next parameters. /// The array of light parameters to toggle between. /// The configurator instance for method chaining. - ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObservable, + ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObservable, params LightParameters[] lightParameters); /// @@ -39,7 +38,7 @@ ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObse /// The observable that triggers toggling to the next transition. /// The collection of light transitions to toggle between. /// The configurator instance for method chaining. - ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObservable, + ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObservable, IEnumerable lightTransitions); /// @@ -51,7 +50,7 @@ ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObse /// The observable that triggers toggling to the next transition. /// The array of light transitions to toggle between. /// The configurator instance for method chaining. - ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObservable, + ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObservable, params LightTransition[] lightTransitions); /// @@ -63,8 +62,8 @@ ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObse /// The observable that triggers toggling to the next node. /// The collection of factory functions that create pipeline nodes. /// The configurator instance for method chaining. - ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObservable, - IEnumerable>> nodeFactories); + ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObservable, + IEnumerable>> nodeFactories); /// /// Adds a time-based toggle trigger that switches between nodes created by the specified factory functions when triggered by . @@ -75,8 +74,8 @@ ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObse /// The observable that triggers toggling to the next node. /// The array of factory functions that create pipeline nodes. /// The configurator instance for method chaining. - ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObservable, - params Func>[] nodeFactories); + ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObservable, + params Func>[] nodeFactories); /// /// Adds a time-based toggle trigger configured by the specified action when triggered by . @@ -87,6 +86,6 @@ ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObse /// The observable that triggers toggling to the next state. /// An action to configure the toggle behavior. /// The configurator instance for method chaining. - ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObservable, - Action configure); + ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObservable, + Action> configure); } \ No newline at end of file diff --git a/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/ILightTransitionReactiveNodeConfigurator.cs b/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/ILightTransitionReactiveNodeConfigurator.cs index 1d76089..b71c38e 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/ILightTransitionReactiveNodeConfigurator.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/ILightTransitionReactiveNodeConfigurator.cs @@ -1,5 +1,4 @@ -using CodeCasa.Abstractions; -using CodeCasa.AutomationPipelines.Lights.Context; +using CodeCasa.Abstractions; using CodeCasa.Lights; namespace CodeCasa.AutomationPipelines.Lights.ReactiveNode; @@ -8,20 +7,21 @@ namespace CodeCasa.AutomationPipelines.Lights.ReactiveNode; /// Configures a light transition reactive node for controlling light behavior through reactive event handling. /// Supports adding reactive dimmers, uncoupled dimmers, dynamic node sources, and scoped configurations. /// -public partial interface ILightTransitionReactiveNodeConfigurator +/// The specific type of light being controlled, which must implement . +public partial interface ILightTransitionReactiveNodeConfigurator where TLight : ILight { /// /// Enables logging for the reactive node configuration. /// /// The optional name of the reactive node to include in logs. /// The configurator instance for method chaining. - ILightTransitionReactiveNodeConfigurator EnableLogging(string? name = null); + ILightTransitionReactiveNodeConfigurator EnableLogging(string? name = null); /// /// Disables logging for the reactive node configuration. /// /// The configurator instance for method chaining. - ILightTransitionReactiveNodeConfigurator DisableLogging(); + ILightTransitionReactiveNodeConfigurator DisableLogging(); /// /// Adds a reactive dimmer control that will be reset when the reactive node activates a new node. @@ -30,14 +30,14 @@ public partial interface ILightTransitionReactiveNodeConfigurator /// /// The dimmer device to add to the reactive node. /// The configurator instance for method chaining. - ILightTransitionReactiveNodeConfigurator AddReactiveDimmer(IDimmer dimmer); + ILightTransitionReactiveNodeConfigurator AddReactiveDimmer(IDimmer dimmer); /// /// Sets the configuration options for reactive dimmer controls in this node. /// /// The dimmer options to configure dimmer behavior. /// The configurator instance for method chaining. - ILightTransitionReactiveNodeConfigurator SetReactiveDimmerOptions(DimmerOptions dimmerOptions); + ILightTransitionReactiveNodeConfigurator SetReactiveDimmerOptions(DimmerOptions dimmerOptions); /// /// Adds an uncoupled dimmer control that operates independently without being affected by the reactive node's behavior. @@ -45,7 +45,7 @@ public partial interface ILightTransitionReactiveNodeConfigurator /// /// The dimmer device to add as an uncoupled control. /// The configurator instance for method chaining. - ILightTransitionReactiveNodeConfigurator AddUncoupledDimmer(IDimmer dimmer); + ILightTransitionReactiveNodeConfigurator AddUncoupledDimmer(IDimmer dimmer); /// /// Adds an uncoupled dimmer control with custom configuration options. @@ -55,7 +55,7 @@ public partial interface ILightTransitionReactiveNodeConfigurator /// The dimmer device to add as an uncoupled control. /// An action to configure the dimmer options. /// The configurator instance for method chaining. - ILightTransitionReactiveNodeConfigurator AddUncoupledDimmer(IDimmer dimmer, Action dimOptions); + ILightTransitionReactiveNodeConfigurator AddUncoupledDimmer(IDimmer dimmer, Action dimOptions); /// /// Adds a dynamic node source that activates a new node in the reactive node each time the observable emits a factory. @@ -63,9 +63,9 @@ public partial interface ILightTransitionReactiveNodeConfigurator /// /// An observable that emits factory functions for creating pipeline nodes. /// The configurator instance for method chaining. - ILightTransitionReactiveNodeConfigurator + ILightTransitionReactiveNodeConfigurator AddNodeSource( - IObservable?>> + IObservable?>> nodeFactorySource); /// @@ -74,8 +74,8 @@ public partial interface ILightTransitionReactiveNodeConfigurator /// The entity ID of the light to configure. /// An action to configure the reactive node for this specific light. /// The configurator instance for method chaining. - ILightTransitionReactiveNodeConfigurator ForLight(string lightId, - Action configure); + ILightTransitionReactiveNodeConfigurator ForLight(string lightId, + Action> configure); /// /// Creates a scoped reactive node configuration for a specific light. @@ -83,8 +83,8 @@ ILightTransitionReactiveNodeConfigurator ForLight(string lightId, /// The light to configure. /// An action to configure the reactive node for this specific light. /// The configurator instance for method chaining. - ILightTransitionReactiveNodeConfigurator ForLight(ILight light, - Action configure); + ILightTransitionReactiveNodeConfigurator ForLight(TLight light, + Action> configure); /// /// Creates a scoped reactive node configuration for multiple light entities identified by their entity IDs. @@ -92,8 +92,8 @@ ILightTransitionReactiveNodeConfigurator ForLight(ILight light, /// The entity IDs of the lights to configure. /// An action to configure the reactive node for these lights. /// The configurator instance for method chaining. - ILightTransitionReactiveNodeConfigurator ForLights(IEnumerable lightIds, - Action configure); + ILightTransitionReactiveNodeConfigurator ForLights(IEnumerable lightIds, + Action> configure); /// /// Creates a scoped reactive node configuration for multiple light entities. @@ -101,6 +101,6 @@ ILightTransitionReactiveNodeConfigurator ForLights(IEnumerable lightIds, /// The light entities to configure. /// An action to configure the reactive node for these lights. /// The configurator instance for method chaining. - ILightTransitionReactiveNodeConfigurator ForLights(IEnumerable lights, - Action configure); + ILightTransitionReactiveNodeConfigurator ForLights(IEnumerable lights, + Action> configure); } \ No newline at end of file diff --git a/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/LightTransitionReactiveNodeConfigurator.Cycle.cs b/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/LightTransitionReactiveNodeConfigurator.Cycle.cs index fd6ce52..6983f40 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/LightTransitionReactiveNodeConfigurator.Cycle.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/LightTransitionReactiveNodeConfigurator.Cycle.cs @@ -1,20 +1,18 @@ -using CodeCasa.AutomationPipelines.Lights.Context; using CodeCasa.AutomationPipelines.Lights.Cycle; using CodeCasa.AutomationPipelines.Lights.Extensions; using CodeCasa.AutomationPipelines.Lights.Nodes; using CodeCasa.Lights; -using Microsoft.Extensions.DependencyInjection; namespace CodeCasa.AutomationPipelines.Lights.ReactiveNode; -internal partial class LightTransitionReactiveNodeConfigurator +internal partial class LightTransitionReactiveNodeConfigurator { /// - public ILightTransitionReactiveNodeConfigurator AddCycle(IObservable triggerObservable, IEnumerable lightParameters) + public ILightTransitionReactiveNodeConfigurator AddCycle(IObservable triggerObservable, IEnumerable lightParameters) => AddCycle(triggerObservable, lightParameters.ToArray()); /// - public ILightTransitionReactiveNodeConfigurator AddCycle(IObservable triggerObservable, + public ILightTransitionReactiveNodeConfigurator AddCycle(IObservable triggerObservable, params LightParameters[] lightParameters) { return AddCycle(triggerObservable, configure => @@ -27,11 +25,11 @@ public ILightTransitionReactiveNodeConfigurator AddCycle(IObservable trigg } /// - public ILightTransitionReactiveNodeConfigurator AddCycle(IObservable triggerObservable, IEnumerable lightTransitions) + public ILightTransitionReactiveNodeConfigurator AddCycle(IObservable triggerObservable, IEnumerable lightTransitions) => AddCycle(triggerObservable, lightTransitions.ToArray()); /// - public ILightTransitionReactiveNodeConfigurator AddCycle(IObservable triggerObservable, + public ILightTransitionReactiveNodeConfigurator AddCycle(IObservable triggerObservable, params LightTransition[] lightTransitions) { return AddCycle(triggerObservable, configure => @@ -44,22 +42,19 @@ public ILightTransitionReactiveNodeConfigurator AddCycle(IObservable trigg } /// - public ILightTransitionReactiveNodeConfigurator AddCycle(IObservable triggerObservable, - Action configure) + public ILightTransitionReactiveNodeConfigurator AddCycle(IObservable triggerObservable, + Action> configure) { - var cycleConfigurator = new LightTransitionCycleConfigurator(Light, scheduler); + var cycleConfigurator = new LightTransitionCycleConfigurator(Light, _scheduler); configure(cycleConfigurator); AddNodeSource(triggerObservable.ToCycleObservable(cycleConfigurator.CycleNodeFactories.Select(tuple => { - var factory = new Func>(() => - { - var serviceScope = serviceProvider.CreateScope(); - var context = new LightPipelineContext(serviceScope.ServiceProvider, Light); - return new ScopedNode(serviceScope, tuple.nodeFactory(context)); - }); - var valueIsActiveFunc = () => tuple.matchesNodeState(new LightPipelineContext(serviceProvider, Light)); + var factory = new Func>(() => + tuple.nodeFactory.CreateScopedNode(ServiceProvider) // Note: This service provider already has the light registered. We scope it further for node lifetime. + ); + var valueIsActiveFunc = () => tuple.matchesNodeState(ServiceProvider); return (factory, valueIsActiveFunc); }))); return this; } -} \ No newline at end of file +} diff --git a/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/LightTransitionReactiveNodeConfigurator.On.cs b/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/LightTransitionReactiveNodeConfigurator.On.cs index 3dc809c..8dcc621 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/LightTransitionReactiveNodeConfigurator.On.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/LightTransitionReactiveNodeConfigurator.On.cs @@ -1,5 +1,3 @@ -using CodeCasa.AutomationPipelines.Lights.Context; -using CodeCasa.AutomationPipelines.Lights.Extensions; using CodeCasa.AutomationPipelines.Lights.Nodes; using CodeCasa.AutomationPipelines.Lights.Pipeline; using System.Reactive.Concurrency; @@ -9,62 +7,62 @@ namespace CodeCasa.AutomationPipelines.Lights.ReactiveNode; -internal partial class LightTransitionReactiveNodeConfigurator +internal partial class LightTransitionReactiveNodeConfigurator { /// - public ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, + public ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, LightParameters lightParameters) => On(triggerObservable, lightParameters.AsTransition()); /// - public ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, - Func lightParametersFactory) + public ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, + Func lightParametersFactory) => On(triggerObservable, c => lightParametersFactory(c).AsTransition()); /// - public ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, + public ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, LightTransition lightTransition) => - On(triggerObservable, c => new StaticLightTransitionNode(lightTransition, c.ServiceProvider.GetRequiredService())); + On(triggerObservable, c => new StaticLightTransitionNode(lightTransition, c.GetRequiredService())); /// - public ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, - Func lightTransitionFactory) - => On(triggerObservable, c => new StaticLightTransitionNode(lightTransitionFactory(c), c.ServiceProvider.GetRequiredService())); + public ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, + Func lightTransitionFactory) + => On(triggerObservable, c => new StaticLightTransitionNode(lightTransitionFactory(c), c.GetRequiredService())); /// - public ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable) + public ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable) where TNode : IPipelineNode => AddNodeSource(triggerObservable.Select(_ => - new Func?>(c => - c.ServiceProvider.CreateInstanceWithinContext(c)))); + new Func?>(c => + ActivatorUtilities.CreateInstance(c)))); /// - public ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, - Func> nodeFactory) => + public ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, + Func> nodeFactory) => AddNodeSource(triggerObservable.Select(_ => nodeFactory)); /// - public ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, Action pipelineConfigurator) => On(triggerObservable, c => lightPipelineFactory.CreateLightPipeline(c.Light, pipelineConfigurator)); + public ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, Action> pipelineConfigurator) => On(triggerObservable, s => s.GetRequiredService().CreateLightPipeline(Light, pipelineConfigurator)); /// - public ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, Action configure) => On(triggerObservable, c => reactiveNodeFactory.CreateReactiveNode(c.Light, configure)); + public ILightTransitionReactiveNodeConfigurator On(IObservable triggerObservable, Action> configure) => On(triggerObservable, s => s.GetRequiredService().CreateReactiveNode(Light, configure)); /// - public ILightTransitionReactiveNodeConfigurator PassThroughOn(IObservable triggerObservable) + public ILightTransitionReactiveNodeConfigurator PassThroughOn(IObservable triggerObservable) { AddNodeSource(triggerObservable.Select(_ => new PassThroughNode())); return this; } /// - public ILightTransitionReactiveNodeConfigurator TurnOffWhen(IObservable triggerObservable) + public ILightTransitionReactiveNodeConfigurator TurnOffWhen(IObservable triggerObservable) { AddNodeSource(triggerObservable.Select(_ => new TurnOffThenPassThroughNode())); return this; } /// - public ILightTransitionReactiveNodeConfigurator TurnOnWhen(IObservable triggerObservable) + public ILightTransitionReactiveNodeConfigurator TurnOnWhen(IObservable triggerObservable) { return On(triggerObservable, LightTransition.On()); } -} \ No newline at end of file +} diff --git a/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/LightTransitionReactiveNodeConfigurator.Toggle.cs b/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/LightTransitionReactiveNodeConfigurator.Toggle.cs index d9b56ed..e26244b 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/LightTransitionReactiveNodeConfigurator.Toggle.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/LightTransitionReactiveNodeConfigurator.Toggle.cs @@ -1,4 +1,3 @@ -using CodeCasa.AutomationPipelines.Lights.Context; using CodeCasa.AutomationPipelines.Lights.Extensions; using CodeCasa.AutomationPipelines.Lights.Nodes; using CodeCasa.AutomationPipelines.Lights.Toggle; @@ -8,14 +7,14 @@ namespace CodeCasa.AutomationPipelines.Lights.ReactiveNode; -internal partial class LightTransitionReactiveNodeConfigurator +internal partial class LightTransitionReactiveNodeConfigurator { /// - public ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObservable, IEnumerable lightParameters) + public ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObservable, IEnumerable lightParameters) => AddToggle(triggerObservable, lightParameters.ToArray()); /// - public ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObservable, + public ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObservable, params LightParameters[] lightParameters) { return AddToggle(triggerObservable, configure => @@ -28,11 +27,11 @@ public ILightTransitionReactiveNodeConfigurator AddToggle(IObservable trig } /// - public ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObservable, IEnumerable lightTransitions) + public ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObservable, IEnumerable lightTransitions) => AddToggle(triggerObservable, lightTransitions.ToArray()); /// - public ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObservable, + public ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObservable, params LightTransition[] lightTransitions) { return AddToggle(triggerObservable, configure => @@ -45,11 +44,11 @@ public ILightTransitionReactiveNodeConfigurator AddToggle(IObservable trig } /// - public ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObservable, IEnumerable>> nodeFactories) + public ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObservable, IEnumerable>> nodeFactories) => AddToggle(triggerObservable, nodeFactories.ToArray()); /// - public ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObservable, params Func>[] nodeFactories) + public ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObservable, params Func>[] nodeFactories) { return AddToggle(triggerObservable, configure => { @@ -61,24 +60,21 @@ public ILightTransitionReactiveNodeConfigurator AddToggle(IObservable trig } /// - public ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObservable, Action configure) + public ILightTransitionReactiveNodeConfigurator AddToggle(IObservable triggerObservable, Action> configure) { - var toggleConfigurator = new LightTransitionToggleConfigurator(Light, scheduler); + var toggleConfigurator = new LightTransitionToggleConfigurator(Light, _scheduler); configure(toggleConfigurator); AddNodeSource(triggerObservable.ToToggleObservable( () => Light.IsOn(), () => new TurnOffThenPassThroughNode(), toggleConfigurator.NodeFactories.Select(fact => { - return new Func>(() => - { - var serviceScope = serviceProvider.CreateScope(); - var context = new LightPipelineContext(serviceScope.ServiceProvider, Light); - return new ScopedNode(serviceScope, fact(context)); - }); + return new Func>(() => + fact.CreateScopedNode(ServiceProvider) // Note: This service provider already has the light registered. We scope it further for node lifetime. + ); }), toggleConfigurator.ToggleTimeout ?? TimeSpan.FromMilliseconds(1000), toggleConfigurator.IncludeOffValue)); return this; } -} \ No newline at end of file +} diff --git a/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/LightTransitionReactiveNodeConfigurator.cs b/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/LightTransitionReactiveNodeConfigurator.cs index 68f19f5..0ce7f0d 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/LightTransitionReactiveNodeConfigurator.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/LightTransitionReactiveNodeConfigurator.cs @@ -1,8 +1,6 @@ -using CodeCasa.Abstractions; -using CodeCasa.AutomationPipelines.Lights.Context; +using CodeCasa.Abstractions; using CodeCasa.AutomationPipelines.Lights.Extensions; using CodeCasa.AutomationPipelines.Lights.Nodes; -using CodeCasa.AutomationPipelines.Lights.Pipeline; using CodeCasa.Lights; using System.Reactive; using System.Reactive.Concurrency; @@ -15,17 +13,26 @@ namespace CodeCasa.AutomationPipelines.Lights.ReactiveNode; /// Configures a light transition reactive node for a single light. /// This configurator allows adding reactive dimmer controls, node sources, and scoped configurations for light automation. /// -internal partial class LightTransitionReactiveNodeConfigurator( - IServiceProvider serviceProvider, - LightPipelineFactory lightPipelineFactory, - ReactiveNodeFactory reactiveNodeFactory, - ILight light, - IScheduler scheduler) : ILightTransitionReactiveNodeConfigurator +internal partial class LightTransitionReactiveNodeConfigurator + : ILightTransitionReactiveNodeConfigurator where TLight : ILight { + private readonly IScheduler _scheduler; + + /// + /// The service provider scoped to this light. + /// + public IServiceProvider ServiceProvider { get; } /// /// Gets the light associated with this configurator. /// - public ILight Light { get; } = light; + public TLight Light { get; } + + public LightTransitionReactiveNodeConfigurator(IServiceProvider serviceProvider, TLight light, IScheduler scheduler) + { + ServiceProvider = serviceProvider; + Light = light; + _scheduler = scheduler; + } internal string? Name { get; private set; } internal bool? Log { get; private set; } @@ -34,7 +41,7 @@ internal partial class LightTransitionReactiveNodeConfigurator( internal DimmerOptions DimmerOptions { get; private set; } = new (); /// - public ILightTransitionReactiveNodeConfigurator EnableLogging(string? name = null) + public ILightTransitionReactiveNodeConfigurator EnableLogging(string? name = null) { Name = name; Log = true; @@ -42,41 +49,41 @@ public ILightTransitionReactiveNodeConfigurator EnableLogging(string? name = nul } /// - public ILightTransitionReactiveNodeConfigurator DisableLogging() + public ILightTransitionReactiveNodeConfigurator DisableLogging() { Log = false; return this; } /// - public ILightTransitionReactiveNodeConfigurator AddReactiveDimmer(IDimmer dimmer) + public ILightTransitionReactiveNodeConfigurator AddReactiveDimmer(IDimmer dimmer) { Dimmers.Add(dimmer); return this; } /// - public ILightTransitionReactiveNodeConfigurator SetReactiveDimmerOptions(DimmerOptions dimmerOptions) + public ILightTransitionReactiveNodeConfigurator SetReactiveDimmerOptions(DimmerOptions dimmerOptions) { DimmerOptions = dimmerOptions; return this; } /// - public ILightTransitionReactiveNodeConfigurator AddUncoupledDimmer(IDimmer dimmer) + public ILightTransitionReactiveNodeConfigurator AddUncoupledDimmer(IDimmer dimmer) { return AddUncoupledDimmer(dimmer, _ => { }); } /// - public ILightTransitionReactiveNodeConfigurator AddUncoupledDimmer(IDimmer dimmer, Action dimOptions) + public ILightTransitionReactiveNodeConfigurator AddUncoupledDimmer(IDimmer dimmer, Action dimOptions) { var options = new DimmerOptions(); dimOptions(options); options.ValidateSingleLight(Light.Id); - var dimPulses = dimmer.Dimming.ToPulsesWhenTrue(options.TimeBetweenSteps, scheduler); - var brightenPulses = dimmer.Brightening.ToPulsesWhenTrue(options.TimeBetweenSteps, scheduler); + var dimPulses = dimmer.Dimming.ToPulsesWhenTrue(options.TimeBetweenSteps, _scheduler); + var brightenPulses = dimmer.Brightening.ToPulsesWhenTrue(options.TimeBetweenSteps, _scheduler); AddDimPulses(options, [Light], dimPulses, brightenPulses); return this; @@ -86,13 +93,13 @@ internal void AddDimPulses(DimmerOptions options, IEnumerable lightsInDi { var dimHelper = new DimHelper(Light, lightsInDimOrder, options.MinBrightness, options.BrightnessStep); AddNodeSource(dimPulses - .Select(_ => dimHelper.DimStep()) - .Where(t => t != null) - .Select(t => (IPipelineNode)(t == LightTransition.Off() ? new TurnOffThenPassThroughNode() : new StaticLightTransitionNode(t, scheduler)))); + .Select(_ => dimHelper.DimStep()) + .Where(t => t != null) + .Select(t => (IPipelineNode)(t == LightTransition.Off() ? new TurnOffThenPassThroughNode() : new StaticLightTransitionNode(t, _scheduler)))); AddNodeSource(brightenPulses - .Select(_ => dimHelper.BrightenStep()) - .Where(t => t != null) - .Select(t => (IPipelineNode)(t == LightTransition.Off() ? new TurnOffThenPassThroughNode() : new StaticLightTransitionNode(t, scheduler)))); + .Select(_ => dimHelper.BrightenStep()) + .Where(t => t != null) + .Select(t => (IPipelineNode)(t == LightTransition.Off() ? new TurnOffThenPassThroughNode() : new StaticLightTransitionNode(t, _scheduler)))); } /// @@ -100,48 +107,39 @@ internal void AddDimPulses(DimmerOptions options, IEnumerable lightsInDi /// /// An observable that emits pipeline nodes. /// The configurator instance for method chaining. - public ILightTransitionReactiveNodeConfigurator AddNodeSource(IObservable?> nodeSource) + public ILightTransitionReactiveNodeConfigurator AddNodeSource(IObservable?> nodeSource) { NodeObservables.Add(nodeSource); return this; } /// - public ILightTransitionReactiveNodeConfigurator AddNodeSource(IObservable?>> nodeFactorySource) + public ILightTransitionReactiveNodeConfigurator AddNodeSource(IObservable?>> nodeFactorySource) { - return AddNodeSource(nodeFactorySource.Select(nodeFactory => - { - var scope = serviceProvider.CreateScope(); - var context = new LightPipelineContext(scope.ServiceProvider, Light); - var node = nodeFactory(context); - if (node != null) - { - return new ScopedNode(scope, node); - } - scope.Dispose(); - return null; - })); + return AddNodeSource(nodeFactorySource.Select(nodeFactory => + nodeFactory.CreateScopedNodeOrNull(ServiceProvider) // Note: This service provider already has the light registered. We scope it further for node lifetime. + )); } /// - public ILightTransitionReactiveNodeConfigurator ForLight(string lightId, - Action configure) => ForLights([lightId], configure); + public ILightTransitionReactiveNodeConfigurator ForLight(string lightId, + Action> configure) => ForLights([lightId], configure); /// - public ILightTransitionReactiveNodeConfigurator ForLight(ILight light, - Action configure) => ForLights([light], configure); + public ILightTransitionReactiveNodeConfigurator ForLight(TLight light, + Action> configure) => ForLights([light], configure); /// - public ILightTransitionReactiveNodeConfigurator ForLights(IEnumerable lightIds, - Action configure) + public ILightTransitionReactiveNodeConfigurator ForLights(IEnumerable lightIds, + Action> configure) { CompositeHelper.ValidateLightSupported(lightIds, Light.Id); return this; } /// - public ILightTransitionReactiveNodeConfigurator ForLights(IEnumerable lights, - Action configure) + public ILightTransitionReactiveNodeConfigurator ForLights(IEnumerable lights, + Action> configure) { CompositeHelper.ResolveGroupsAndValidateLightSupported(lights, Light.Id); return this; diff --git a/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/ReactiveDimmerPipeline.cs b/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/ReactiveDimmerPipeline.cs new file mode 100644 index 0000000..280e4b2 --- /dev/null +++ b/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/ReactiveDimmerPipeline.cs @@ -0,0 +1,38 @@ +using CodeCasa.AutomationPipelines.Lights.Utils; +using CodeCasa.Lights; +using Microsoft.Extensions.DependencyInjection; + +namespace CodeCasa.AutomationPipelines.Lights.ReactiveNode; + +/// +/// A pipeline that combines a reactive node with a reactive dimmer node for managing light transitions. +/// +internal sealed class ReactiveDimmerPipeline : Pipeline +{ + private readonly IServiceScope _scope; + private readonly IRegisterInterface _registerInterface; + + /// + /// Initializes a new instance of the class. + /// + public ReactiveDimmerPipeline( + IServiceScope scope, + ReactiveNode reactiveNode, + ReactiveDimmerNode reactiveDimmerNode, + IRegisterInterface registerInterface) + { + _scope = scope; + _registerInterface = registerInterface; + _registerInterface.Register(this); + RegisterNode(reactiveNode); + RegisterNode(reactiveDimmerNode); + } + + /// + public override async ValueTask DisposeAsync() + { + _registerInterface.Unregister(this); + await base.DisposeAsync(); + await _scope.DisposeOrDisposeAsync(); + } +} \ No newline at end of file diff --git a/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/ReactiveNodeFactory.cs b/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/ReactiveNodeFactory.cs index dfb6141..1798c3e 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/ReactiveNodeFactory.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/ReactiveNodeFactory.cs @@ -1,14 +1,14 @@ -using System.Reactive; -using System.Reactive.Concurrency; -using System.Reactive.Disposables; -using System.Reactive.Linq; -using System.Reactive.Subjects; using CodeCasa.AutomationPipelines.Lights.Extensions; using CodeCasa.AutomationPipelines.Lights.Nodes; using CodeCasa.AutomationPipelines.Lights.Pipeline; using CodeCasa.Lights; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using System.Reactive; +using System.Reactive.Concurrency; +using System.Reactive.Disposables; +using System.Reactive.Linq; +using System.Reactive.Subjects; namespace CodeCasa.AutomationPipelines.Lights.ReactiveNode { @@ -20,10 +20,11 @@ public class ReactiveNodeFactory(IServiceProvider serviceProvider, IScheduler sc /// /// Creates a reactive node for a single light. /// + /// The type of light being controlled. /// The light to create the reactive node for. /// An action to configure the reactive node. /// A configured reactive node for the specified light. - public IPipelineNode CreateReactiveNode(ILight light, Action configure) + public IPipelineNode CreateReactiveNode(TLight light, Action> configure) where TLight : ILight { return CreateReactiveNodes([light], configure)[light.Id]; } @@ -31,10 +32,11 @@ public IPipelineNode CreateReactiveNode(ILight light, Action /// Creates reactive nodes for multiple light entities. /// + /// The type of light being controlled. /// The light entities to create reactive nodes for. /// An action to configure the reactive nodes. /// A dictionary mapping light IDs to their corresponding reactive nodes. - internal Dictionary> CreateReactiveNodes(IEnumerable lights, Action configure) + internal Dictionary> CreateReactiveNodes(IEnumerable lights, Action> configure) where TLight : ILight { // Note: we simply assume that these are not groups. var lightArray = lights.ToArray(); @@ -43,15 +45,21 @@ internal Dictionary> CreateReactiveNodes( return new Dictionary>(); } - var lightPipelineFactory = serviceProvider.GetRequiredService(); - var reactiveConfigurators = lightArray.ToDictionary(l => l.Id, l => new LightTransitionReactiveNodeConfigurator(serviceProvider, lightPipelineFactory, - this, l, scheduler)); - ILightTransitionReactiveNodeConfigurator configurator = lightArray.Length == 1 + var lightContextScopes = lightArray.ToDictionary(l => l.Id, serviceProvider.CreateLightContextScope); + var reactiveConfigurators = + lightArray.ToDictionary(l => l.Id, + l => + { + var sp = lightContextScopes[l.Id].ServiceProvider; + // Note: we cant resolve LightTransitionReactiveNodeConfigurator directly because it is not registered as a service. + return new LightTransitionReactiveNodeConfigurator(sp, l, sp.GetRequiredService()); + }); + ILightTransitionReactiveNodeConfigurator configurator = lightArray.Length == 1 ? reactiveConfigurators[lightArray[0].Id] - : new CompositeLightTransitionReactiveNodeConfigurator( - serviceProvider, - lightPipelineFactory, - this, + : new CompositeLightTransitionReactiveNodeConfigurator( + serviceProvider, + serviceProvider.GetRequiredService(), + serviceProvider.GetRequiredService(), reactiveConfigurators, scheduler); configure(configurator); @@ -71,7 +79,7 @@ internal Dictionary> CreateReactiveNodes( { return lightArray.ToDictionary(l => l.Id, l => { - var reactiveNode = CreateReactiveNode(reactiveConfigurators[l.Id]); + var reactiveNode = CreateReactiveNodeInternal(reactiveConfigurators[l.Id]); return (IPipelineNode)reactiveNode; }); } @@ -83,7 +91,7 @@ internal Dictionary> CreateReactiveNodes( foreach (var light in lightArray) { var reactiveNodeConfigurator = reactiveConfigurators[light.Id]; - var reactiveNode = CreateReactiveNode(reactiveNodeConfigurator); + var reactiveNode = CreateReactiveNodeInternal(reactiveNodeConfigurator); var lightDimmerOptions = reactiveNodeConfigurator.DimmerOptions; var dimmerNode = new ReactiveDimmerNode( @@ -94,7 +102,9 @@ internal Dictionary> CreateReactiveNodes( scheduler); dimmerNodes.Add(light.Id, dimmerNode); - var innerPipeline = new ReactiveDimmerPipeline(reactiveNode, dimmerNode, registrationManager); + var innerPipeline = new ReactiveDimmerPipeline( + lightContextScopes[light.Id], + reactiveNode, dimmerNode, registrationManager); result.Add(light.Id, innerPipeline); } @@ -128,7 +138,7 @@ internal Dictionary> CreateReactiveNodes( return result; } - private ReactiveNode CreateReactiveNode(LightTransitionReactiveNodeConfigurator reactiveNodeConfigurator) + private ReactiveNode CreateReactiveNodeInternal(LightTransitionReactiveNodeConfigurator reactiveNodeConfigurator) where TLight : ILight { if (reactiveNodeConfigurator.Log ?? false) { @@ -161,35 +171,6 @@ private DimmingContext CreateDimmingContext(OrderedDictionary - /// A pipeline that combines a reactive node with a reactive dimmer node for managing light transitions. - /// - internal class ReactiveDimmerPipeline : Pipeline - { - private readonly IRegisterInterface _registerInterface; - - /// - /// Initializes a new instance of the class. - /// - public ReactiveDimmerPipeline( - ReactiveNode reactiveNode, - ReactiveDimmerNode reactiveDimmerNode, - IRegisterInterface registerInterface) - { - _registerInterface = registerInterface; - _registerInterface.Register(this); - RegisterNode(reactiveNode); - RegisterNode(reactiveDimmerNode); - } - - /// - public override ValueTask DisposeAsync() - { - _registerInterface.Unregister(this); - return base.DisposeAsync(); - } - } - /// /// Interface for managing registrations and tracking when the last item is unregistered. /// diff --git a/src/CodeCasa.AutomationPipelines.Lights/Toggle/CompositeLightTransitionToggleConfigurator.cs b/src/CodeCasa.AutomationPipelines.Lights/Toggle/CompositeLightTransitionToggleConfigurator.cs index b99367f..7dd538a 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/Toggle/CompositeLightTransitionToggleConfigurator.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/Toggle/CompositeLightTransitionToggleConfigurator.cs @@ -1,5 +1,4 @@ -using System.Reactive.Concurrency; -using CodeCasa.AutomationPipelines.Lights.Context; +using System.Reactive.Concurrency; using CodeCasa.AutomationPipelines.Lights.Extensions; using CodeCasa.AutomationPipelines.Lights.Nodes; using CodeCasa.Lights; @@ -7,98 +6,99 @@ namespace CodeCasa.AutomationPipelines.Lights.Toggle { - internal class CompositeLightTransitionToggleConfigurator( - Dictionary activeConfigurators, - Dictionary inactiveConfigurators) : ILightTransitionToggleConfigurator + internal class CompositeLightTransitionToggleConfigurator( + Dictionary> activeConfigurators, + Dictionary> inactiveConfigurators) : ILightTransitionToggleConfigurator + where TLight : ILight { - public ILightTransitionToggleConfigurator SetToggleTimeout(TimeSpan timeout) + public ILightTransitionToggleConfigurator SetToggleTimeout(TimeSpan timeout) { activeConfigurators.Values.ForEach(c => c.SetToggleTimeout(timeout)); inactiveConfigurators.Values.ForEach(c => c.SetToggleTimeout(timeout)); return this; } - public ILightTransitionToggleConfigurator IncludeOffInToggleCycle() + public ILightTransitionToggleConfigurator IncludeOffInToggleCycle() { activeConfigurators.Values.ForEach(c => c.IncludeOffInToggleCycle()); inactiveConfigurators.Values.ForEach(c => c.IncludeOffInToggleCycle()); return this; } - public ILightTransitionToggleConfigurator ExcludeOffFromToggleCycle() + public ILightTransitionToggleConfigurator ExcludeOffFromToggleCycle() { activeConfigurators.Values.ForEach(c => c.ExcludeOffFromToggleCycle()); inactiveConfigurators.Values.ForEach(c => c.ExcludeOffFromToggleCycle()); return this; } - public ILightTransitionToggleConfigurator AddOff() + public ILightTransitionToggleConfigurator AddOff() { return Add(); } - public ILightTransitionToggleConfigurator AddOn() + public ILightTransitionToggleConfigurator AddOn() { return Add(LightTransition.On()); } - public ILightTransitionToggleConfigurator Add(LightParameters lightParameters) + public ILightTransitionToggleConfigurator Add(LightParameters lightParameters) { return Add(lightParameters.AsTransition()); } - public ILightTransitionToggleConfigurator Add(Func lightParametersFactory) + public ILightTransitionToggleConfigurator Add(Func lightParametersFactory) { return Add(c => lightParametersFactory(c)?.AsTransition()); } - public ILightTransitionToggleConfigurator Add(Func lightParametersFactory) + public ILightTransitionToggleConfigurator Add(Func lightParametersFactory) { return Add((c, t) => lightParametersFactory(c, t)?.AsTransition()); } - public ILightTransitionToggleConfigurator Add(LightTransition lightTransition) + public ILightTransitionToggleConfigurator Add(LightTransition lightTransition) { return Add(_ => lightTransition); } - public ILightTransitionToggleConfigurator Add(Func lightTransitionFactory) + public ILightTransitionToggleConfigurator Add(Func lightTransitionFactory) { - return Add(c => new StaticLightTransitionNode(lightTransitionFactory(c), c.ServiceProvider.GetRequiredService())); + return Add(c => new StaticLightTransitionNode(lightTransitionFactory(c), c.GetRequiredService())); } - public ILightTransitionToggleConfigurator Add(Func lightTransitionFactory) + public ILightTransitionToggleConfigurator Add(Func lightTransitionFactory) { return Add(c => new FactoryNode(t => lightTransitionFactory(c, t))); } - public ILightTransitionToggleConfigurator Add() where TNode : IPipelineNode + public ILightTransitionToggleConfigurator Add() where TNode : IPipelineNode { activeConfigurators.Values.ForEach(c => c.Add()); inactiveConfigurators.Values.ForEach(c => c.AddPassThrough()); return this; } - public ILightTransitionToggleConfigurator Add(Func> nodeFactory) + public ILightTransitionToggleConfigurator Add(Func> nodeFactory) { activeConfigurators.Values.ForEach(c => c.Add(nodeFactory)); inactiveConfigurators.Values.ForEach(c => c.AddPassThrough()); return this; } - public ILightTransitionToggleConfigurator AddPassThrough() + public ILightTransitionToggleConfigurator AddPassThrough() { activeConfigurators.Values.ForEach(c => c.AddPassThrough()); inactiveConfigurators.Values.ForEach(c => c.AddPassThrough()); return this; } - public ILightTransitionToggleConfigurator ForLight(string lightId, Action configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None) => ForLights([lightId], configure, excludedLightBehaviour); + public ILightTransitionToggleConfigurator ForLight(string lightId, Action> configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None) => ForLights([lightId], configure, excludedLightBehaviour); - public ILightTransitionToggleConfigurator ForLight(ILight light, Action configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None) => ForLights([light], configure, excludedLightBehaviour); + public ILightTransitionToggleConfigurator ForLight(TLight light, Action> configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None) => ForLights([light], configure, excludedLightBehaviour); - public ILightTransitionToggleConfigurator ForLights(IEnumerable lightIds, - Action configure, + public ILightTransitionToggleConfigurator ForLights(IEnumerable lightIds, + Action> configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None) { var lightIdsArray = @@ -118,13 +118,13 @@ public ILightTransitionToggleConfigurator ForLights(IEnumerable lightIds return this; } - configure(new CompositeLightTransitionToggleConfigurator( + configure(new CompositeLightTransitionToggleConfigurator( activeConfigurators.Where(kvp => lightIdsArray.Contains(kvp.Key)) .ToDictionary(kvp => kvp.Key, kvp => kvp.Value), [])); return this; } - configure(new CompositeLightTransitionToggleConfigurator( + configure(new CompositeLightTransitionToggleConfigurator( activeConfigurators.Where(kvp => lightIdsArray.Contains(kvp.Key)) .ToDictionary(kvp => kvp.Key, kvp => kvp.Value), activeConfigurators.Where(kvp => !lightIdsArray.Contains(kvp.Key)) @@ -132,7 +132,7 @@ public ILightTransitionToggleConfigurator ForLights(IEnumerable lightIds return this; } - public ILightTransitionToggleConfigurator ForLights(IEnumerable lights, Action configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None) + public ILightTransitionToggleConfigurator ForLights(IEnumerable lights, Action> configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None) { var lightIds = CompositeHelper.ResolveGroupsAndValidateLightsSupported(lights, activeConfigurators.Keys); return ForLights(lightIds, configure, excludedLightBehaviour); diff --git a/src/CodeCasa.AutomationPipelines.Lights/Toggle/ILightTransitionToggleConfigurator.cs b/src/CodeCasa.AutomationPipelines.Lights/Toggle/ILightTransitionToggleConfigurator.cs index 6af0141..f50dd70 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/Toggle/ILightTransitionToggleConfigurator.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/Toggle/ILightTransitionToggleConfigurator.cs @@ -1,4 +1,3 @@ -using CodeCasa.AutomationPipelines.Lights.Context; using CodeCasa.Lights; namespace CodeCasa.AutomationPipelines.Lights.Toggle @@ -8,52 +7,53 @@ namespace CodeCasa.AutomationPipelines.Lights.Toggle /// 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 + /// The specific type of light being controlled, which must implement . + public interface ILightTransitionToggleConfigurator where TLight : ILight { /// /// Sets the timeout duration after which the toggle cycle restarts from the beginning. /// /// The timeout duration between triggers that determines when to restart the cycle. /// The configurator instance for method chaining. - ILightTransitionToggleConfigurator SetToggleTimeout(TimeSpan timeout); + ILightTransitionToggleConfigurator SetToggleTimeout(TimeSpan timeout); /// /// Includes the "off" state in the toggle cycle. When the light is off and toggled, it will advance to the first state in the cycle. /// /// The configurator instance for method chaining. - ILightTransitionToggleConfigurator IncludeOffInToggleCycle(); + ILightTransitionToggleConfigurator IncludeOffInToggleCycle(); /// /// Excludes the "off" state from the toggle cycle. When the light is off and toggled, it will turn on to the first state but "off" won't be part of the sequential cycle. /// /// The configurator instance for method chaining. - ILightTransitionToggleConfigurator ExcludeOffFromToggleCycle(); + ILightTransitionToggleConfigurator ExcludeOffFromToggleCycle(); /// /// Adds an "off" state to the toggle sequence. /// /// The configurator instance for method chaining. - ILightTransitionToggleConfigurator AddOff(); + ILightTransitionToggleConfigurator AddOff(); /// /// Adds an "on" state to the toggle sequence. /// /// The configurator instance for method chaining. - ILightTransitionToggleConfigurator AddOn(); + ILightTransitionToggleConfigurator AddOn(); /// /// Adds light parameters to the toggle sequence. Quick consecutive triggers will advance through all added parameter sets. /// /// The light parameters to add to the toggle sequence. /// The configurator instance for method chaining. - ILightTransitionToggleConfigurator Add(LightParameters lightParameters); + ILightTransitionToggleConfigurator Add(LightParameters lightParameters); /// /// Adds light parameters created by a factory to the toggle sequence. /// /// A factory function that creates light parameters based on the pipeline context. /// The configurator instance for method chaining. - ILightTransitionToggleConfigurator Add(Func lightParametersFactory); + ILightTransitionToggleConfigurator Add(Func lightParametersFactory); /// /// Adds light parameters created by a factory to the toggle sequence. @@ -61,21 +61,21 @@ public interface ILightTransitionToggleConfigurator /// /// A factory function that creates light parameters based on the pipeline context and current transition. /// The configurator instance for method chaining. - ILightTransitionToggleConfigurator Add(Func lightParametersFactory); + ILightTransitionToggleConfigurator Add(Func lightParametersFactory); /// /// Adds a light transition to the toggle sequence. Quick consecutive triggers will advance through all added transitions. /// /// The light transition to add to the toggle sequence. /// The configurator instance for method chaining. - ILightTransitionToggleConfigurator Add(LightTransition lightTransition); + ILightTransitionToggleConfigurator Add(LightTransition lightTransition); /// /// Adds a light transition created by a factory to the toggle sequence. /// /// A factory function that creates a light transition based on the pipeline context. /// The configurator instance for method chaining. - ILightTransitionToggleConfigurator Add(Func lightTransitionFactory); + ILightTransitionToggleConfigurator Add(Func lightTransitionFactory); /// /// Adds a light transition created by a factory to the toggle sequence. @@ -83,7 +83,7 @@ public interface ILightTransitionToggleConfigurator /// /// A factory function that creates a light transition based on the pipeline context and current transition. /// The configurator instance for method chaining. - ILightTransitionToggleConfigurator Add(Func lightTransitionFactory); + ILightTransitionToggleConfigurator Add(Func lightTransitionFactory); /// /// Adds a pipeline node of type to the toggle sequence. @@ -91,7 +91,7 @@ public interface ILightTransitionToggleConfigurator /// /// The type of the pipeline node to add to the toggle sequence. /// The configurator instance for method chaining. - ILightTransitionToggleConfigurator Add() where TNode : IPipelineNode; + ILightTransitionToggleConfigurator Add() where TNode : IPipelineNode; /// /// Adds a pipeline node created by a factory to the toggle sequence. @@ -99,13 +99,13 @@ public interface ILightTransitionToggleConfigurator /// /// A factory function that creates a pipeline node based on the pipeline context. /// The configurator instance for method chaining. - ILightTransitionToggleConfigurator Add(Func> nodeFactory); + ILightTransitionToggleConfigurator Add(Func> nodeFactory); /// /// Adds a pass-through state to the toggle sequence that maintains the current light state. /// /// The configurator instance for method chaining. - ILightTransitionToggleConfigurator AddPassThrough(); + ILightTransitionToggleConfigurator AddPassThrough(); /// /// Creates a scoped toggle configuration for a specific light identified by its entity ID. @@ -114,7 +114,7 @@ public interface ILightTransitionToggleConfigurator /// An action to configure the toggle for this specific light. /// Specifies the behavior for lights not included in this scoped configuration. Defaults to . /// The configurator instance for method chaining. - ILightTransitionToggleConfigurator ForLight(string lightId, Action configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None); + ILightTransitionToggleConfigurator ForLight(string lightId, Action> configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None); /// /// Creates a scoped toggle configuration for a specific light. @@ -123,7 +123,7 @@ public interface ILightTransitionToggleConfigurator /// An action to configure the toggle for this specific light. /// Specifies the behavior for lights not included in this scoped configuration. Defaults to . /// The configurator instance for method chaining. - ILightTransitionToggleConfigurator ForLight(ILight light, Action configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None); + ILightTransitionToggleConfigurator ForLight(TLight light, Action> configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None); /// /// Creates a scoped toggle configuration for multiple light entities identified by their entity IDs. @@ -132,7 +132,7 @@ public interface ILightTransitionToggleConfigurator /// An action to configure the toggle for these lights. /// Specifies the behavior for lights not included in this scoped configuration. Defaults to . /// The configurator instance for method chaining. - ILightTransitionToggleConfigurator ForLights(IEnumerable lightIds, Action configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None); + ILightTransitionToggleConfigurator ForLights(IEnumerable lightIds, Action> configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None); /// /// Creates a scoped toggle configuration for multiple light entities. @@ -141,6 +141,6 @@ public interface ILightTransitionToggleConfigurator /// An action to configure the toggle for these lights. /// Specifies the behavior for lights not included in this scoped configuration. Defaults to . /// The configurator instance for method chaining. - ILightTransitionToggleConfigurator ForLights(IEnumerable lights, Action configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None); + ILightTransitionToggleConfigurator ForLights(IEnumerable lights, Action> configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None); } } diff --git a/src/CodeCasa.AutomationPipelines.Lights/Toggle/LightTransitionToggleConfigurator.cs b/src/CodeCasa.AutomationPipelines.Lights/Toggle/LightTransitionToggleConfigurator.cs index 45e996b..26ba703 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/Toggle/LightTransitionToggleConfigurator.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/Toggle/LightTransitionToggleConfigurator.cs @@ -1,113 +1,112 @@ -using System.Reactive.Concurrency; -using CodeCasa.AutomationPipelines.Lights.Context; -using CodeCasa.AutomationPipelines.Lights.Extensions; +using System.Reactive.Concurrency; using CodeCasa.AutomationPipelines.Lights.Nodes; using CodeCasa.Lights; using Microsoft.Extensions.DependencyInjection; namespace CodeCasa.AutomationPipelines.Lights.Toggle { - internal class LightTransitionToggleConfigurator(ILight light, IScheduler scheduler) : ILightTransitionToggleConfigurator + internal class LightTransitionToggleConfigurator(TLight light, IScheduler scheduler) : ILightTransitionToggleConfigurator + where TLight : ILight { - public ILight Light { get; } = light; + public TLight Light { get; } = light; internal TimeSpan? ToggleTimeout { get; private set; } internal bool? IncludeOffValue { get; private set; } - internal List>> NodeFactories + internal List>> NodeFactories { get; } = []; - public ILightTransitionToggleConfigurator SetToggleTimeout(TimeSpan timeout) + public ILightTransitionToggleConfigurator SetToggleTimeout(TimeSpan timeout) { ToggleTimeout = timeout; return this; } - public ILightTransitionToggleConfigurator IncludeOffInToggleCycle() + public ILightTransitionToggleConfigurator IncludeOffInToggleCycle() { IncludeOffValue = true; return this; } - public ILightTransitionToggleConfigurator ExcludeOffFromToggleCycle() + public ILightTransitionToggleConfigurator ExcludeOffFromToggleCycle() { IncludeOffValue = false; return this; } - public ILightTransitionToggleConfigurator AddOff() + public ILightTransitionToggleConfigurator AddOff() { return Add(); } - public ILightTransitionToggleConfigurator AddOn() + public ILightTransitionToggleConfigurator AddOn() { return Add(LightTransition.On()); } - public ILightTransitionToggleConfigurator Add(LightParameters lightParameters) + public ILightTransitionToggleConfigurator Add(LightParameters lightParameters) { return Add(lightParameters.AsTransition()); } - public ILightTransitionToggleConfigurator Add(Func lightParametersFactory) + public ILightTransitionToggleConfigurator Add(Func lightParametersFactory) { return Add(c => lightParametersFactory(c)?.AsTransition()); } - public ILightTransitionToggleConfigurator Add(Func lightParametersFactory) + public ILightTransitionToggleConfigurator Add(Func lightParametersFactory) { return Add((c, t) => lightParametersFactory(c, t)?.AsTransition()); } - public ILightTransitionToggleConfigurator Add(LightTransition lightTransition) + public ILightTransitionToggleConfigurator Add(LightTransition lightTransition) { return Add(new StaticLightTransitionNode(lightTransition, scheduler)); } - public ILightTransitionToggleConfigurator Add(Func lightTransitionFactory) + public ILightTransitionToggleConfigurator Add(Func lightTransitionFactory) { - return Add(c => new StaticLightTransitionNode(lightTransitionFactory(c), c.ServiceProvider.GetRequiredService())); + return Add(c => new StaticLightTransitionNode(lightTransitionFactory(c), c.GetRequiredService())); } - public ILightTransitionToggleConfigurator Add(Func lightTransitionFactory) + public ILightTransitionToggleConfigurator Add(Func lightTransitionFactory) { return Add(c => new FactoryNode(t => lightTransitionFactory(c, t))); } - public ILightTransitionToggleConfigurator Add() where TNode : IPipelineNode + public ILightTransitionToggleConfigurator Add() where TNode : IPipelineNode { - return Add(c => c.ServiceProvider.CreateInstanceWithinContext(c)); + return Add(c => ActivatorUtilities.CreateInstance(c)); } - public ILightTransitionToggleConfigurator Add(IPipelineNode node) + public ILightTransitionToggleConfigurator Add(IPipelineNode node) { return Add(_ => node); } - public ILightTransitionToggleConfigurator Add(Func> nodeFactory) + public ILightTransitionToggleConfigurator Add(Func> nodeFactory) { NodeFactories.Add(nodeFactory); return this; } - public ILightTransitionToggleConfigurator AddPassThrough() + public ILightTransitionToggleConfigurator AddPassThrough() { return Add(new PassThroughNode()); } - public ILightTransitionToggleConfigurator ForLight(string lightId, Action configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None) => ForLights([lightId], configure, excludedLightBehaviour); + public ILightTransitionToggleConfigurator ForLight(string lightId, Action> configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None) => ForLights([lightId], configure, excludedLightBehaviour); - public ILightTransitionToggleConfigurator ForLight(ILight light, Action configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None) => ForLights([light], configure, excludedLightBehaviour); + public ILightTransitionToggleConfigurator ForLight(TLight light, Action> configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None) => ForLights([light], configure, excludedLightBehaviour); - public ILightTransitionToggleConfigurator ForLights(IEnumerable lightIds, Action configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None) + public ILightTransitionToggleConfigurator ForLights(IEnumerable lightIds, Action> configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None) { CompositeHelper.ValidateLightSupported(lightIds, Light.Id); return this; } - public ILightTransitionToggleConfigurator ForLights(IEnumerable lights, Action configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None) + public ILightTransitionToggleConfigurator ForLights(IEnumerable lights, Action> configure, ExcludedLightBehaviours excludedLightBehaviour = ExcludedLightBehaviours.None) { CompositeHelper.ResolveGroupsAndValidateLightSupported(lights, Light.Id); return this; diff --git a/src/CodeCasa.AutomationPipelines/CodeCasa.AutomationPipelines.csproj b/src/CodeCasa.AutomationPipelines/CodeCasa.AutomationPipelines.csproj index 085a7f0..3f418f7 100644 --- a/src/CodeCasa.AutomationPipelines/CodeCasa.AutomationPipelines.csproj +++ b/src/CodeCasa.AutomationPipelines/CodeCasa.AutomationPipelines.csproj @@ -34,8 +34,8 @@ - - + + diff --git a/src/CodeCasa.Lights.NetDaemon.Scenes/CodeCasa.Lights.NetDaemon.Scenes.csproj b/src/CodeCasa.Lights.NetDaemon.Scenes/CodeCasa.Lights.NetDaemon.Scenes.csproj index 972c22c..00722e4 100644 --- a/src/CodeCasa.Lights.NetDaemon.Scenes/CodeCasa.Lights.NetDaemon.Scenes.csproj +++ b/src/CodeCasa.Lights.NetDaemon.Scenes/CodeCasa.Lights.NetDaemon.Scenes.csproj @@ -34,7 +34,7 @@ - + diff --git a/src/CodeCasa.Lights.NetDaemon/CodeCasa.Lights.NetDaemon.csproj b/src/CodeCasa.Lights.NetDaemon/CodeCasa.Lights.NetDaemon.csproj index 268d841..018fa17 100644 --- a/src/CodeCasa.Lights.NetDaemon/CodeCasa.Lights.NetDaemon.csproj +++ b/src/CodeCasa.Lights.NetDaemon/CodeCasa.Lights.NetDaemon.csproj @@ -39,7 +39,7 @@ - + diff --git a/src/CodeCasa.NetDaemon.Extensions.Observables/CodeCasa.NetDaemon.Extensions.Observables.csproj b/src/CodeCasa.NetDaemon.Extensions.Observables/CodeCasa.NetDaemon.Extensions.Observables.csproj index f82839e..b0c1ae7 100644 --- a/src/CodeCasa.NetDaemon.Extensions.Observables/CodeCasa.NetDaemon.Extensions.Observables.csproj +++ b/src/CodeCasa.NetDaemon.Extensions.Observables/CodeCasa.NetDaemon.Extensions.Observables.csproj @@ -34,7 +34,7 @@ - + diff --git a/src/CodeCasa.NetDaemon.RuntimeState/CodeCasa.NetDaemon.RuntimeState.csproj b/src/CodeCasa.NetDaemon.RuntimeState/CodeCasa.NetDaemon.RuntimeState.csproj index da9e528..e607c29 100644 --- a/src/CodeCasa.NetDaemon.RuntimeState/CodeCasa.NetDaemon.RuntimeState.csproj +++ b/src/CodeCasa.NetDaemon.RuntimeState/CodeCasa.NetDaemon.RuntimeState.csproj @@ -34,9 +34,9 @@ - - - + + + diff --git a/src/CodeCasa.NetDaemon.TypedEntities/CodeCasa.NetDaemon.TypedEntities.csproj b/src/CodeCasa.NetDaemon.TypedEntities/CodeCasa.NetDaemon.TypedEntities.csproj index 57c364b..c1018cb 100644 --- a/src/CodeCasa.NetDaemon.TypedEntities/CodeCasa.NetDaemon.TypedEntities.csproj +++ b/src/CodeCasa.NetDaemon.TypedEntities/CodeCasa.NetDaemon.TypedEntities.csproj @@ -34,7 +34,7 @@ - + diff --git a/src/CodeCasa.Notifications.InputSelect.NetDaemon/CodeCasa.Notifications.InputSelect.NetDaemon.csproj b/src/CodeCasa.Notifications.InputSelect.NetDaemon/CodeCasa.Notifications.InputSelect.NetDaemon.csproj index 1da2b58..1910c12 100644 --- a/src/CodeCasa.Notifications.InputSelect.NetDaemon/CodeCasa.Notifications.InputSelect.NetDaemon.csproj +++ b/src/CodeCasa.Notifications.InputSelect.NetDaemon/CodeCasa.Notifications.InputSelect.NetDaemon.csproj @@ -34,7 +34,7 @@ - + diff --git a/src/CodeCasa.Notifications.InputSelect.NetDaemon/Service/DashboardNotificationBackgroundService.cs b/src/CodeCasa.Notifications.InputSelect.NetDaemon/Service/DashboardNotificationBackgroundService.cs index 1392105..207324e 100644 --- a/src/CodeCasa.Notifications.InputSelect.NetDaemon/Service/DashboardNotificationBackgroundService.cs +++ b/src/CodeCasa.Notifications.InputSelect.NetDaemon/Service/DashboardNotificationBackgroundService.cs @@ -1,4 +1,4 @@ -using System.Reactive.Concurrency; +using System.Reactive.Concurrency; using CodeCasa.NetDaemon.RuntimeState; using CodeCasa.Notifications.InputSelect.NetDaemon.Config; using CodeCasa.Notifications.InputSelect.NetDaemon.Interact; diff --git a/src/CodeCasa.Notifications.Lights/Config/ILightNotificationConfig.cs b/src/CodeCasa.Notifications.Lights/Config/ILightNotificationConfig.cs new file mode 100644 index 0000000..e1e0c31 --- /dev/null +++ b/src/CodeCasa.Notifications.Lights/Config/ILightNotificationConfig.cs @@ -0,0 +1,25 @@ +using CodeCasa.AutomationPipelines; +using CodeCasa.Lights; + +namespace CodeCasa.Notifications.Lights.Config; + +/// +/// Defines the configuration for a light notification, including its priority and the ability to create pipeline nodes. +/// +public interface ILightNotificationConfig +{ + /// + /// Gets or sets the priority of the notification. + /// Higher values indicate higher priority. + /// + public int Priority { get; set; } + + /// + /// Creates a factory function that produces a pipeline node for the specified light type. + /// + /// + /// A factory function that takes a and returns + /// a , or null if the light type is not supported. + /// + public Func?> CreateFactory(); +} \ No newline at end of file diff --git a/src/CodeCasa.Notifications.Lights/Config/LightNotificationConfig.cs b/src/CodeCasa.Notifications.Lights/Config/LightNotificationConfig.cs deleted file mode 100644 index 211d786..0000000 --- a/src/CodeCasa.Notifications.Lights/Config/LightNotificationConfig.cs +++ /dev/null @@ -1,39 +0,0 @@ -using CodeCasa.AutomationPipelines; -using CodeCasa.AutomationPipelines.Lights.Context; -using CodeCasa.Lights; - -namespace CodeCasa.Notifications.Lights.Config; - -/// -/// Configuration for a light notification. -/// -public class LightNotificationConfig -{ - /// - /// Gets or sets the priority of the notification. - /// Higher values indicate higher priority. - /// - public int Priority { get; set; } - - /// - /// Gets the type of the pipeline node associated with the notification. - /// - public Type? NodeType { get; } - - /// - /// Gets the factory function to create the pipeline node associated with the notification. - /// - public Func>? NodeFactory { get; } - - internal LightNotificationConfig(Type nodeType, int priority) - { - NodeType = nodeType; - Priority = priority; - } - - internal LightNotificationConfig(Func> nodeFactory, int priority) - { - NodeFactory = nodeFactory; - Priority = priority; - } -} \ No newline at end of file diff --git a/src/CodeCasa.Notifications.Lights/Config/NodeFactoryLightNotificationConfig.cs b/src/CodeCasa.Notifications.Lights/Config/NodeFactoryLightNotificationConfig.cs new file mode 100644 index 0000000..2c1ebfb --- /dev/null +++ b/src/CodeCasa.Notifications.Lights/Config/NodeFactoryLightNotificationConfig.cs @@ -0,0 +1,48 @@ +using CodeCasa.AutomationPipelines; +using CodeCasa.Lights; +using Microsoft.Extensions.DependencyInjection; + +namespace CodeCasa.Notifications.Lights.Config; + +/// +/// A light notification configuration that creates pipeline nodes using a provided factory function. +/// +/// The type of light that this configuration is designed for, which must implement . +public class NodeFactoryLightNotificationConfig : ILightNotificationConfig where TLight : ILight +{ + /// + public int Priority { get; set; } + + /// + /// Gets the factory function used to create pipeline nodes for the specified light type. + /// + public Func> NodeFactory { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The factory function that creates pipeline nodes from a service provider. + /// The priority of the notification. Higher values indicate higher priority. + internal NodeFactoryLightNotificationConfig(Func> nodeFactory, int priority) + { + NodeFactory = nodeFactory; + Priority = priority; + } + + /// + public Func?> CreateFactory() + { + return s => + { + var light = s.GetService(); + if (light == null) + { + // This can occur if the notification is applied to a pipeline with a different light type. + // If that occurs we return regardless of the node actually requiring the light. + return null; + } + + return NodeFactory(s); + }; + } +} \ No newline at end of file diff --git a/src/CodeCasa.Notifications.Lights/Config/NodeTypeLightNotificationConfig.cs b/src/CodeCasa.Notifications.Lights/Config/NodeTypeLightNotificationConfig.cs new file mode 100644 index 0000000..9a96127 --- /dev/null +++ b/src/CodeCasa.Notifications.Lights/Config/NodeTypeLightNotificationConfig.cs @@ -0,0 +1,30 @@ +using CodeCasa.AutomationPipelines; +using CodeCasa.Lights; +using Microsoft.Extensions.DependencyInjection; + +namespace CodeCasa.Notifications.Lights.Config; + +/// +/// A light notification configuration that creates pipeline nodes by resolving a specific node type from the service provider. +/// +/// The type of pipeline node to create, which must implement . +public class NodeTypeLightNotificationConfig : ILightNotificationConfig where TNode : IPipelineNode +{ + /// + public int Priority { get; set; } + + /// + /// Initializes a new instance of the class. + /// + /// The priority of the notification. Higher values indicate higher priority. + internal NodeTypeLightNotificationConfig(int priority) + { + Priority = priority; + } + + /// + public Func?> CreateFactory() + { + return context => ActivatorUtilities.CreateInstance(context); + } +} \ No newline at end of file diff --git a/src/CodeCasa.Notifications.Lights/Extensions/LightTransitionPipelineConfiguratorExtensions.cs b/src/CodeCasa.Notifications.Lights/Extensions/LightTransitionPipelineConfiguratorExtensions.cs index b0ecd69..69870ae 100644 --- a/src/CodeCasa.Notifications.Lights/Extensions/LightTransitionPipelineConfiguratorExtensions.cs +++ b/src/CodeCasa.Notifications.Lights/Extensions/LightTransitionPipelineConfiguratorExtensions.cs @@ -1,7 +1,4 @@ -using CodeCasa.AutomationPipelines; -using CodeCasa.AutomationPipelines.Lights.Context; -using CodeCasa.AutomationPipelines.Lights.Extensions; -using CodeCasa.AutomationPipelines.Lights.Pipeline; +using CodeCasa.AutomationPipelines.Lights.Pipeline; using CodeCasa.Lights; using System.Reactive.Linq; @@ -15,33 +12,17 @@ public static class LightTransitionPipelineConfiguratorExtensions /// /// Adds light notifications to the pipeline. /// + /// The type of light being controlled. /// The pipeline configurator. /// The context providing light notifications. /// The updated pipeline configurator. - public static ILightTransitionPipelineConfigurator AddNotifications( - this ILightTransitionPipelineConfigurator configurator, LightNotificationManagerContext lightNotificationManagerContext) + public static ILightTransitionPipelineConfigurator AddNotifications( + this ILightTransitionPipelineConfigurator configurator, LightNotificationManagerContext lightNotificationManagerContext) + where TLight : ILight { return configurator.AddReactiveNode(c => { - c.AddNodeSource(lightNotificationManagerContext.LightNotifications.Select(lnc => - { - if (lnc == null) - { - return new Func?>(_ => null); - } - - if (lnc.NodeFactory != null) - { - return lnc.NodeFactory; - } - - if (lnc.NodeType == null) - { - throw new InvalidOperationException("Both NodeFactory and NodeType are null."); - } - - return ctx => (IPipelineNode)ctx.ServiceProvider.CreateInstanceWithinContext(lnc.NodeType, ctx); - })); + c.AddNodeSource(lightNotificationManagerContext.LightNotifications.Select(lnc => lnc == null ? _ => null : lnc.CreateFactory())); }); } } diff --git a/src/CodeCasa.Notifications.Lights/LightNotificationContext.cs b/src/CodeCasa.Notifications.Lights/LightNotificationContext.cs index 1a55d60..17dbca1 100644 --- a/src/CodeCasa.Notifications.Lights/LightNotificationContext.cs +++ b/src/CodeCasa.Notifications.Lights/LightNotificationContext.cs @@ -1,5 +1,4 @@ -using CodeCasa.AutomationPipelines; -using CodeCasa.AutomationPipelines.Lights.Context; +using CodeCasa.AutomationPipelines; using CodeCasa.Lights; using CodeCasa.Notifications.Lights.Config; @@ -48,49 +47,91 @@ public LightNotification Notify(LightNotification lightNotificationToRepl /// The created or updated light notification. public LightNotification Notify(string notificationId, int priority) where TNode : IPipelineNode { - return lightNotificationManager.Notify(new LightNotificationConfig(typeof(TNode), priority), notificationId); + return lightNotificationManager.Notify(new NodeTypeLightNotificationConfig(priority), notificationId); } /// /// Notifies with a new light notification using a factory, replacing an existing one. /// + /// The type of light to filter which lights show the notification. /// The existing light notification to replace. /// The factory function to create the pipeline node. /// The created or updated light notification. - public LightNotification Notify(LightNotification lightNotificationToReplace, Func> nodeFactory) => Notify(lightNotificationToReplace, nodeFactory, 0); + public LightNotification Notify(LightNotification lightNotificationToReplace, Func> nodeFactory) where TLight : ILight => Notify(lightNotificationToReplace, nodeFactory, 0); /// /// Notifies with a new light notification using a factory and specific priority, replacing an existing one. /// + /// The type of light to filter which lights show the notification. /// The existing light notification to replace. /// The factory function to create the pipeline node. /// The priority of the notification. /// The created or updated light notification. - public LightNotification Notify(LightNotification lightNotificationToReplace, Func> nodeFactory, int priority) + public LightNotification Notify(LightNotification lightNotificationToReplace, Func> nodeFactory, int priority) where TLight : ILight { - return Notify(lightNotificationToReplace.Id, nodeFactory, priority); + return Notify(lightNotificationToReplace.Id, nodeFactory, priority); } /// /// Notifies with a new light notification using a factory and specific ID. /// + /// The type of light to filter which lights show the notification. /// The unique identifier for the notification. /// The factory function to create the pipeline node. /// The created or updated light notification. - public LightNotification Notify(string notificationId, Func> nodeFactory) => Notify(notificationId, nodeFactory, 0); + public LightNotification Notify(string notificationId, Func> nodeFactory) where TLight : ILight => Notify(notificationId, nodeFactory, 0); /// /// Notifies with a new light notification using a factory, specific priority, and ID. /// + /// The type of light to filter which lights show the notification. /// The unique identifier for the notification. /// The factory function to create the pipeline node. /// The priority of the notification. /// The created or updated light notification. - public LightNotification Notify(string notificationId, Func> nodeFactory, int priority) + public LightNotification Notify(string notificationId, Func> nodeFactory, int priority) where TLight : ILight { - return lightNotificationManager.Notify(new LightNotificationConfig(nodeFactory, priority), notificationId); + return lightNotificationManager.Notify(new NodeFactoryLightNotificationConfig(nodeFactory, priority), notificationId); } + /// + /// Notifies with a new light notification using a factory, replacing an existing one. + /// The notification will be shown on all lights. + /// + /// The existing light notification to replace. + /// The factory function to create the pipeline node. + /// The created or updated light notification. + public LightNotification Notify(LightNotification lightNotificationToReplace, Func> nodeFactory) => Notify(lightNotificationToReplace, nodeFactory, 0); + + /// + /// Notifies with a new light notification using a factory and specific priority, replacing an existing one. + /// The notification will be shown on all lights. + /// + /// The existing light notification to replace. + /// The factory function to create the pipeline node. + /// The priority of the notification. + /// The created or updated light notification. + public LightNotification Notify(LightNotification lightNotificationToReplace, Func> nodeFactory, int priority) => Notify(lightNotificationToReplace, nodeFactory, priority); + + /// + /// Notifies with a new light notification using a factory and specific ID. + /// The notification will be shown on all lights. + /// + /// The unique identifier for the notification. + /// The factory function to create the pipeline node. + /// The created or updated light notification. + public LightNotification Notify(string notificationId, Func> nodeFactory) => Notify(notificationId, nodeFactory, 0); + + /// + /// Notifies with a new light notification using a factory, specific priority, and ID. + /// The notification will be shown on all lights. + /// + /// The unique identifier for the notification. + /// The factory function to create the pipeline node. + /// The priority of the notification. + /// The created or updated light notification. + public LightNotification Notify(string notificationId, Func> nodeFactory, int priority) => Notify(notificationId, nodeFactory, priority); + /// /// Removes a specific light notification. /// diff --git a/src/CodeCasa.Notifications.Lights/LightNotificationManager.cs b/src/CodeCasa.Notifications.Lights/LightNotificationManager.cs index 7dd3da2..5e9c13e 100644 --- a/src/CodeCasa.Notifications.Lights/LightNotificationManager.cs +++ b/src/CodeCasa.Notifications.Lights/LightNotificationManager.cs @@ -10,15 +10,15 @@ namespace CodeCasa.Notifications.Lights public class LightNotificationManager { private readonly Lock _lock = new(); - private readonly BehaviorSubject _subject = new(null); + private readonly BehaviorSubject _subject = new(null); - private readonly Dictionary _activeNotifications = new(); + private readonly Dictionary _activeNotifications = new(); /// /// Gets an observable sequence of the current active light notification configuration. /// /// An observable that emits the current light notification configuration or null if none are active. - public IObservable LightNotifications() + public IObservable LightNotifications() { lock (_lock) { @@ -29,31 +29,31 @@ public class LightNotificationManager /// /// Notifies with a new light notification configuration, replacing an existing notification. /// - /// The configuration for the new notification. + /// The configuration for the new notification. /// The existing notification to replace. /// The created light notification. - public LightNotification Notify(LightNotificationConfig lightNotificationOptions, LightNotification lightNotificationToReplace) + public LightNotification Notify(ILightNotificationConfig lightNotificationConfig, LightNotification lightNotificationToReplace) { - return Notify(lightNotificationOptions, lightNotificationToReplace.Id); + return Notify(lightNotificationConfig, lightNotificationToReplace.Id); } /// /// Notifies with a new light notification configuration using a specific ID. /// - /// The configuration for the new notification. + /// The configuration for the new notification. /// The unique identifier for the notification. /// The created light notification. - public LightNotification Notify(LightNotificationConfig lightNotificationOptions, string id) + public LightNotification Notify(ILightNotificationConfig lightNotificationConfig, string id) { lock (_lock) { var highestPrio = _activeNotifications.Any() ? (int?)_activeNotifications.Values.Max(n => n.Priority) : null; - if (highestPrio == null || lightNotificationOptions.Priority >= highestPrio) + if (highestPrio == null || lightNotificationConfig.Priority >= highestPrio) { - _subject.OnNext(lightNotificationOptions); + _subject.OnNext(lightNotificationConfig); } - _activeNotifications[id] = lightNotificationOptions; + _activeNotifications[id] = lightNotificationConfig; return new LightNotification(id, Disposable.Create(() => Remove(id))); } } diff --git a/src/CodeCasa.Notifications.Lights/LightNotificationManagerContext.cs b/src/CodeCasa.Notifications.Lights/LightNotificationManagerContext.cs index 6255f31..78a387c 100644 --- a/src/CodeCasa.Notifications.Lights/LightNotificationManagerContext.cs +++ b/src/CodeCasa.Notifications.Lights/LightNotificationManagerContext.cs @@ -9,7 +9,7 @@ namespace CodeCasa.Notifications.Lights /// public class LightNotificationManagerContext : IDisposable { - private readonly BehaviorSubject _subject = new(null); + private readonly BehaviorSubject _subject = new(null); private readonly IDisposable _subscriptionDisposable; /// @@ -24,7 +24,7 @@ public LightNotificationManagerContext(LightNotificationManager lightNotificatio /// /// Gets an observable sequence of the current light notification configuration. /// - public IObservable LightNotifications => _subject.AsObservable(); + public IObservable LightNotifications => _subject.AsObservable(); /// public void Dispose() diff --git a/src/CodeCasa.Notifications.Phone.NetDaemon/CodeCasa.Notifications.Phone.NetDaemon.csproj b/src/CodeCasa.Notifications.Phone.NetDaemon/CodeCasa.Notifications.Phone.NetDaemon.csproj index 4ca2629..76719eb 100644 --- a/src/CodeCasa.Notifications.Phone.NetDaemon/CodeCasa.Notifications.Phone.NetDaemon.csproj +++ b/src/CodeCasa.Notifications.Phone.NetDaemon/CodeCasa.Notifications.Phone.NetDaemon.csproj @@ -34,7 +34,7 @@ - + diff --git a/tests/CodeCasa.AutomationPipelines.Lights.Tests/ReactiveNodeTests.cs b/tests/CodeCasa.AutomationPipelines.Lights.Tests/ReactiveNodeTests.cs index 1e82fb3..e1ef6ac 100644 --- a/tests/CodeCasa.AutomationPipelines.Lights.Tests/ReactiveNodeTests.cs +++ b/tests/CodeCasa.AutomationPipelines.Lights.Tests/ReactiveNodeTests.cs @@ -7,7 +7,6 @@ using Microsoft.Extensions.Logging; using Moq; using System.Reactive.Subjects; -using CodeCasa.AutomationPipelines.Lights.Context; using ReactiveNodeClass = CodeCasa.AutomationPipelines.Lights.ReactiveNode.ReactiveNode; namespace CodeCasa.AutomationPipelines.Lights.Tests @@ -19,7 +18,6 @@ public sealed class ReactiveNodeTests private IScheduler _scheduler = null!; private ReactiveNodeFactory _reactiveNodeFactory = null!; private Mock _lightMock = null!; - private LightPipelineContextProvider _contextProvider = null!; private Mock _scopeFactoryMock = null!; private Mock _scopeMock = null!; private Mock _scopedServiceProviderMock = null!; @@ -32,7 +30,7 @@ public void Initialize() _reactiveNodeFactory = new ReactiveNodeFactory(_serviceProviderMock.Object, _scheduler); var pipelineLoggerMock = new Mock>>(); - var lightPipelineFactory = new LightPipelineFactory(pipelineLoggerMock.Object, _serviceProviderMock.Object, _reactiveNodeFactory); + var lightPipelineFactory = new LightPipelineFactory(pipelineLoggerMock.Object, _serviceProviderMock.Object); _serviceProviderMock.Setup(x => x.GetService(typeof(LightPipelineFactory))) .Returns(lightPipelineFactory); @@ -49,29 +47,34 @@ public void Initialize() _lightMock.Setup(l => l.GetParameters()).Returns(new LightParameters()); _lightMock.Setup(l => l.GetChildren()).Returns(Array.Empty()); - _contextProvider = new LightPipelineContextProvider(); - _serviceProviderMock.Setup(x => x.GetService(typeof(LightPipelineContextProvider))) - .Returns(_contextProvider); - _scopeFactoryMock = new Mock(); _scopeMock = new Mock(); _scopeMock.As(); _scopedServiceProviderMock = new Mock(); + var serviceProviderIsServiceMock = new Mock(); + serviceProviderIsServiceMock.Setup(x => x.IsService(It.IsAny())).Returns(false); + _serviceProviderMock.Setup(x => x.GetService(typeof(IServiceScopeFactory))) .Returns(_scopeFactoryMock.Object); + _serviceProviderMock.Setup(x => x.GetService(typeof(IServiceProviderIsService))) + .Returns(serviceProviderIsServiceMock.Object); + _scopeFactoryMock.Setup(x => x.CreateScope()) .Returns(_scopeMock.Object); _scopeMock.Setup(x => x.ServiceProvider) .Returns(_scopedServiceProviderMock.Object); - _scopedServiceProviderMock.Setup(x => x.GetService(typeof(LightPipelineContextProvider))) - .Returns(_contextProvider); - _scopedServiceProviderMock.Setup(x => x.GetService(typeof(IScheduler))) .Returns(_scheduler); + + _scopedServiceProviderMock.Setup(x => x.GetService(typeof(IServiceScopeFactory))) + .Returns(_scopeFactoryMock.Object); + + _scopedServiceProviderMock.Setup(x => x.GetService(typeof(IServiceProviderIsService))) + .Returns(serviceProviderIsServiceMock.Object); } [TestMethod] @@ -160,7 +163,7 @@ public void On_Triggered_ReplacesAndDisposesOldNode() // Assert 1 Assert.HasCount(1, createdIds, "Should have created one node"); - Assert.HasCount(0, disposedIds, "Should not have disposed any node yet"); + Assert.IsEmpty(disposedIds, "Should not have disposed any node yet"); // Trigger 2 triggerSubject.OnNext(2); @@ -177,10 +180,10 @@ public void On_Triggered_ActivatesNode_WithContext() // Arrange var triggerSubject = new Subject(); - _serviceProviderMock.Setup(x => x.GetService(typeof(ILightPipelineContext))) - .Returns(() => _contextProvider.GetLightPipelineContext()); - _scopedServiceProviderMock.Setup(x => x.GetService(typeof(ILightPipelineContext))) - .Returns(() => _contextProvider.GetLightPipelineContext()); + _serviceProviderMock.Setup(x => x.GetService(typeof(ILight))) + .Returns(_lightMock.Object); + _scopedServiceProviderMock.Setup(x => x.GetService(typeof(ILight))) + .Returns(_lightMock.Object); // Act var node = _reactiveNodeFactory.CreateReactiveNode(_lightMock.Object, config => @@ -232,9 +235,9 @@ public void Dispose() public class ContextAwarePipelineNode : PipelineNode { - public ContextAwarePipelineNode(ILightPipelineContext context) + public ContextAwarePipelineNode(ILight light) { - if (context.Light.Id == "test_light") + if (light.Id == "test_light") { Output = new LightParameters { Brightness = 100 }.AsTransition(); } @@ -257,18 +260,20 @@ public void On_Triggered_CreatesScopedServiceProvider_AndDisposesIt() config.On(triggerSubject); }); + _scopeFactoryMock.Verify(x => x.CreateScope(), Times.Once, "Scope should be created on creating the reactive node."); + // Trigger 1 triggerSubject.OnNext(1); // Assert 1 - _scopeFactoryMock.Verify(x => x.CreateScope(), Times.Once, "Scope should be created on trigger"); + _scopeFactoryMock.Verify(x => x.CreateScope(), Times.Exactly(2), "New scope should be created on trigger"); _scopeMock.As().Verify(x => x.DisposeAsync(), Times.Never, "Scope should not be disposed yet"); // Trigger 2 triggerSubject.OnNext(2); // Assert 2 - _scopeFactoryMock.Verify(x => x.CreateScope(), Times.Exactly(2), "New scope should be created on second trigger"); + _scopeFactoryMock.Verify(x => x.CreateScope(), Times.Exactly(3), "New scope should be created on second trigger"); _scopeMock.As().Verify(x => x.DisposeAsync(), Times.Once, "Old scope should be disposed"); }