From 05f98e4b4bcc5b08685f3e53b4be91dd538c93ed Mon Sep 17 00:00:00 2001 From: Jasper Date: Thu, 29 Jan 2026 12:30:34 +0100 Subject: [PATCH 1/2] feat(ReactiveNode): add AddNodeSource overloads for DI composition Add two new AddNodeSource overloads to ILightTransitionReactiveNodeConfigurator: - AddNodeSource(): Resolves observable node sources from DI - AddNodeSource(Func>): Factory pattern for composing observables from existing DI services The factory overload enables cleaner extension methods that can leverage existing service registrations without creating dedicated node source classes. --- ...LightTransitionReactiveNodeConfigurator.cs | 17 +++++++++++++++++ ...LightTransitionReactiveNodeConfigurator.cs | 19 +++++++++++++++++++ ...LightTransitionReactiveNodeConfigurator.cs | 14 ++++++++++++++ 3 files changed, 50 insertions(+) diff --git a/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/CompositeLightTransitionReactiveNodeConfigurator.cs b/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/CompositeLightTransitionReactiveNodeConfigurator.cs index fad90d6..1c24a4e 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/CompositeLightTransitionReactiveNodeConfigurator.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/CompositeLightTransitionReactiveNodeConfigurator.cs @@ -76,6 +76,23 @@ public ILightTransitionReactiveNodeConfigurator AddUncoupledDimmer(IDimm } return this; + + } + + /// + public ILightTransitionReactiveNodeConfigurator AddNodeSource() + where TNodeSource : IObservable?>> + { + configurators.Values.ForEach(c => c.AddNodeSource()); + return this; + } + + /// + public ILightTransitionReactiveNodeConfigurator AddNodeSource( + Func?>>> nodeFactorySourceFactory) + { + configurators.Values.ForEach(c => c.AddNodeSource(nodeFactorySourceFactory)); + return this; } /// diff --git a/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/ILightTransitionReactiveNodeConfigurator.cs b/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/ILightTransitionReactiveNodeConfigurator.cs index b71c38e..6e09150 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/ILightTransitionReactiveNodeConfigurator.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/ILightTransitionReactiveNodeConfigurator.cs @@ -57,6 +57,25 @@ public partial interface ILightTransitionReactiveNodeConfigurator where /// The configurator instance for method chaining. ILightTransitionReactiveNodeConfigurator AddUncoupledDimmer(IDimmer dimmer, Action dimOptions); + /// + /// Adds a dynamic node source resolved from the service provider. + /// The node source type must implement where T is a factory function for creating pipeline nodes. + /// Useful for reusable, class-based node sources registered in dependency injection. + /// + /// The type of the node source, which must be an observable that emits node factory functions. + /// The configurator instance for method chaining. + ILightTransitionReactiveNodeConfigurator AddNodeSource() + where TNodeSource : IObservable?>>; + + /// + /// Adds a dynamic node source created by a factory function that receives the service provider. + /// Useful for extension methods that need to compose existing DI services to build the observable. + /// + /// A factory function that receives the service provider and returns an observable that emits node factory functions. + /// The configurator instance for method chaining. + ILightTransitionReactiveNodeConfigurator AddNodeSource( + Func?>>> nodeFactorySourceFactory); + /// /// Adds a dynamic node source that activates a new node in the reactive node each time the observable emits a factory. /// The emitted factory is invoked to create and activate the new pipeline node. diff --git a/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/LightTransitionReactiveNodeConfigurator.cs b/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/LightTransitionReactiveNodeConfigurator.cs index 0ce7f0d..05e97c3 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/LightTransitionReactiveNodeConfigurator.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/ReactiveNode/LightTransitionReactiveNodeConfigurator.cs @@ -113,6 +113,20 @@ public ILightTransitionReactiveNodeConfigurator AddNodeSource(IObservabl return this; } + /// + public ILightTransitionReactiveNodeConfigurator AddNodeSource() + where TNodeSource : IObservable?>> + { + return AddNodeSource(ActivatorUtilities.CreateInstance(ServiceProvider)); + } + + /// + public ILightTransitionReactiveNodeConfigurator AddNodeSource( + Func?>>> nodeFactorySourceFactory) + { + return AddNodeSource(nodeFactorySourceFactory(ServiceProvider)); + } + /// public ILightTransitionReactiveNodeConfigurator AddNodeSource(IObservable?>> nodeFactorySource) { From d1e5f1c424934a9a90b305a3d1dc8e77e502ed0c Mon Sep 17 00:00:00 2001 From: Jasper Date: Fri, 30 Jan 2026 08:59:06 +0100 Subject: [PATCH 2/2] Making TurnOffThenPassThroughNode public. --- .../Nodes/TurnOffThenPassThroughNode.cs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/CodeCasa.AutomationPipelines.Lights/Nodes/TurnOffThenPassThroughNode.cs b/src/CodeCasa.AutomationPipelines.Lights/Nodes/TurnOffThenPassThroughNode.cs index 3ad407e..00d0a98 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/Nodes/TurnOffThenPassThroughNode.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/Nodes/TurnOffThenPassThroughNode.cs @@ -2,8 +2,25 @@ namespace CodeCasa.AutomationPipelines.Lights.Nodes; -internal class TurnOffThenPassThroughNode : PipelineNode +/// +/// A pipeline node that initially outputs , +/// then switches to pass-through mode after receiving its first input. +/// +/// +/// This node is useful for scenarios where a light should be turned off, +/// but then forward subsequent inputs from upstream nodes without modification. +/// The pass-through behavior is activated upon receiving the first input. +/// +public sealed class TurnOffThenPassThroughNode : PipelineNode { + /// + /// Initializes a new instance of the class. + /// + /// + /// The initial output is set to . + /// Pass-through mode is not enabled in the constructor because the input + /// is immediately set when this node is added to the timeline. + /// public TurnOffThenPassThroughNode() { // Note: we cannot simply call ChangeOutputAndTurnOnPassThroughOnNextInput here, as the input will immediately be set when this node is added to the timeline.