diff --git a/CodeCasa.sln b/CodeCasa.sln
index 23836a0..17aa403 100644
--- a/CodeCasa.sln
+++ b/CodeCasa.sln
@@ -37,6 +37,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeCasa.AutomationPipeline
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeCasa.Lights.NetDaemon.Scenes", "src\CodeCasa.Lights.NetDaemon.Scenes\CodeCasa.Lights.NetDaemon.Scenes.csproj", "{AA5FEE82-9A79-4AA7-BC3E-4769638FF8A6}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeCasa.AutomationPipelines.Lights.Tests", "tests\CodeCasa.AutomationPipelines.Lights.Tests\CodeCasa.AutomationPipelines.Lights.Tests.csproj", "{96DB93C1-036A-436A-AF7A-AEC07243A929}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -107,6 +109,10 @@ Global
{AA5FEE82-9A79-4AA7-BC3E-4769638FF8A6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AA5FEE82-9A79-4AA7-BC3E-4769638FF8A6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AA5FEE82-9A79-4AA7-BC3E-4769638FF8A6}.Release|Any CPU.Build.0 = Release|Any CPU
+ {96DB93C1-036A-436A-AF7A-AEC07243A929}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {96DB93C1-036A-436A-AF7A-AEC07243A929}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {96DB93C1-036A-436A-AF7A-AEC07243A929}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {96DB93C1-036A-436A-AF7A-AEC07243A929}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -116,6 +122,7 @@ Global
{E2FA49AB-BFC2-4DDD-B743-F5AAB96F3FEC} = {5BCD08C3-3034-4D08-AC01-2AB6DFD67C33}
{FA26C18B-24D0-4F3D-958C-A9BA61861C65} = {5BCD08C3-3034-4D08-AC01-2AB6DFD67C33}
{5C7FB111-095B-F881-7268-4284370C8AAA} = {5BCD08C3-3034-4D08-AC01-2AB6DFD67C33}
+ {96DB93C1-036A-436A-AF7A-AEC07243A929} = {5BCD08C3-3034-4D08-AC01-2AB6DFD67C33}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {5AAE8D3A-9457-4676-8F57-B2D78594CCC7}
diff --git a/src/CodeCasa.AutomationPipelines.Lights/CodeCasa.AutomationPipelines.Lights.csproj b/src/CodeCasa.AutomationPipelines.Lights/CodeCasa.AutomationPipelines.Lights.csproj
index eb95380..1e78424 100644
--- a/src/CodeCasa.AutomationPipelines.Lights/CodeCasa.AutomationPipelines.Lights.csproj
+++ b/src/CodeCasa.AutomationPipelines.Lights/CodeCasa.AutomationPipelines.Lights.csproj
@@ -17,6 +17,10 @@
cc_icon.png
https://github.com/DevJasperNL/CodeCasa/releases
+
+
+
+
diff --git a/src/CodeCasa.AutomationPipelines.Lights/Nodes/FactoryNode.cs b/src/CodeCasa.AutomationPipelines.Lights/Nodes/FactoryNode.cs
index 267d579..ddefcb2 100644
--- a/src/CodeCasa.AutomationPipelines.Lights/Nodes/FactoryNode.cs
+++ b/src/CodeCasa.AutomationPipelines.Lights/Nodes/FactoryNode.cs
@@ -1,11 +1,11 @@
namespace CodeCasa.AutomationPipelines.Lights.Nodes;
-internal class FactoryNode(Func lightTransitionFactory)
+internal class FactoryNode(Func stateFactory)
: PipelineNode
{
///
protected override void InputReceived(TState? input)
{
- Output = lightTransitionFactory(input);
+ Output = stateFactory(input);
}
}
\ No newline at end of file
diff --git a/src/CodeCasa.AutomationPipelines.Lights/Nodes/LightTransitionNode.cs b/src/CodeCasa.AutomationPipelines.Lights/Nodes/LightTransitionNode.cs
index f4179fc..71492fe 100644
--- a/src/CodeCasa.AutomationPipelines.Lights/Nodes/LightTransitionNode.cs
+++ b/src/CodeCasa.AutomationPipelines.Lights/Nodes/LightTransitionNode.cs
@@ -13,13 +13,11 @@ namespace CodeCasa.AutomationPipelines.Lights.Nodes
public abstract class LightTransitionNode(IScheduler scheduler) : IPipelineNode
{
private readonly Subject _newOutputSubject = new();
- private LightTransition? _input;
private LightParameters? _inputLightDestinationParameters;
private DateTime? _inputStartOfTransition;
private DateTime? _inputEndOfTransition;
private LightTransition? _output;
private bool _passThroughNextInput;
- private bool _passThrough;
private IDisposable? _scheduledAction;
///
@@ -33,29 +31,31 @@ public abstract class LightTransitionNode(IScheduler scheduler) : IPipelineNode<
///
public LightTransition? Input
{
- get => _input;
+ get;
set
{
_scheduledAction?.Dispose(); // Always cancel scheduled actions when the input changes.
// We save additional information on the light transition that we can later use to continue the transition if it would be interrupted.
InputLightSourceParameters = _inputLightDestinationParameters;
- _input = value;
+ field = value;
_inputLightDestinationParameters = value?.LightParameters;
var transitionTime = value?.TransitionTime;
_inputStartOfTransition = DateTime.UtcNow;
- _inputEndOfTransition = transitionTime == null ? null : _inputStartOfTransition + transitionTime;
+ _inputEndOfTransition = _inputStartOfTransition + transitionTime;
if (_passThroughNextInput)
{
PassThrough = true;
return;
}
+
if (PassThrough)
{
- SetOutputInternal(_input);
+ SetOutputInternal(field);
return;
}
- InputReceived(_input);
+
+ InputReceived(field);
}
}
@@ -110,24 +110,25 @@ protected void ScheduleInterpolatedLightTransitionUsingInputTransitionTime(Light
///
public bool PassThrough
{
- get => _passThrough;
+ get;
set
{
// Always reset _passThroughNextInput when PassThrough is explicitly called.
_passThroughNextInput = false;
- if (_passThrough == value)
+ if (field == value)
{
return;
}
_scheduledAction?.Dispose(); // Always cancel scheduled actions when the pass through value changes.
- _passThrough = value;
- if (_passThrough)
+ field = value;
+ if (field)
{
_scheduledAction = scheduler.ScheduleInterpolatedLightTransition(InputLightSourceParameters,
- _inputLightDestinationParameters, _inputStartOfTransition, _inputEndOfTransition, SetOutputInternal);
+ _inputLightDestinationParameters, _inputStartOfTransition, _inputEndOfTransition,
+ SetOutputInternal);
}
}
}
diff --git a/src/CodeCasa.AutomationPipelines/CodeCasa.AutomationPipelines.csproj b/src/CodeCasa.AutomationPipelines/CodeCasa.AutomationPipelines.csproj
index 804a41c..085a7f0 100644
--- a/src/CodeCasa.AutomationPipelines/CodeCasa.AutomationPipelines.csproj
+++ b/src/CodeCasa.AutomationPipelines/CodeCasa.AutomationPipelines.csproj
@@ -40,9 +40,7 @@
-
- <_Parameter1>CodeCasa.AutomationPipelines.Tests
-
+
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 fdd0968..f82839e 100644
--- a/src/CodeCasa.NetDaemon.Extensions.Observables/CodeCasa.NetDaemon.Extensions.Observables.csproj
+++ b/src/CodeCasa.NetDaemon.Extensions.Observables/CodeCasa.NetDaemon.Extensions.Observables.csproj
@@ -36,10 +36,10 @@
+
-
diff --git a/src/CodeCasa.NetDaemon.Notifications.InputSelect/CodeCasa.NetDaemon.Notifications.InputSelect.csproj b/src/CodeCasa.NetDaemon.Notifications.InputSelect/CodeCasa.NetDaemon.Notifications.InputSelect.csproj
index d0e0013..05c10c7 100644
--- a/src/CodeCasa.NetDaemon.Notifications.InputSelect/CodeCasa.NetDaemon.Notifications.InputSelect.csproj
+++ b/src/CodeCasa.NetDaemon.Notifications.InputSelect/CodeCasa.NetDaemon.Notifications.InputSelect.csproj
@@ -36,11 +36,9 @@
-
+
-
- <_Parameter1>NetDaemon.Notifications.InputSelect.Tests
-
+
diff --git a/tests/CodeCasa.AutomationPipelines.Lights.Tests/CodeCasa.AutomationPipelines.Lights.Tests.csproj b/tests/CodeCasa.AutomationPipelines.Lights.Tests/CodeCasa.AutomationPipelines.Lights.Tests.csproj
new file mode 100644
index 0000000..14cdcc3
--- /dev/null
+++ b/tests/CodeCasa.AutomationPipelines.Lights.Tests/CodeCasa.AutomationPipelines.Lights.Tests.csproj
@@ -0,0 +1,22 @@
+
+
+
+ net10.0
+ latest
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/CodeCasa.AutomationPipelines.Lights.Tests/FactoryNodeTests.cs b/tests/CodeCasa.AutomationPipelines.Lights.Tests/FactoryNodeTests.cs
new file mode 100644
index 0000000..4121faa
--- /dev/null
+++ b/tests/CodeCasa.AutomationPipelines.Lights.Tests/FactoryNodeTests.cs
@@ -0,0 +1,135 @@
+namespace CodeCasa.AutomationPipelines.Lights.Tests;
+
+using AutomationPipelines;
+using Nodes;
+
+[TestClass]
+public sealed class FactoryNodeTests
+{
+ [TestMethod]
+ public void FactoryNode_TransformsInput()
+ {
+ // Arrange
+ const string inputValue = "Test";
+ const string expectedOutput = "Test_Transformed";
+ string? emittedOutput = null;
+
+ var factoryNode = new FactoryNode(input => $"{input}_Transformed");
+ factoryNode.OnNewOutput.Subscribe(o => emittedOutput = o);
+
+ // Act
+ factoryNode.Input = inputValue;
+
+ // Assert
+ Assert.AreEqual(expectedOutput, emittedOutput);
+ Assert.AreEqual(expectedOutput, factoryNode.Output);
+ }
+
+ [TestMethod]
+ public void FactoryNode_HandlesNullInput()
+ {
+ // Arrange
+ string? emittedOutput = null;
+ var factoryNode = new FactoryNode(input => input ?? "NULL");
+ factoryNode.OnNewOutput.Subscribe(o => emittedOutput = o);
+
+ // Act
+ factoryNode.Input = null;
+
+ // Assert
+ Assert.AreEqual("NULL", emittedOutput);
+ Assert.AreEqual("NULL", factoryNode.Output);
+ }
+
+ [TestMethod]
+ public void FactoryNode_ProducesNullOutput()
+ {
+ // Arrange
+ var emittedOutput = "NotNull";
+ var factoryNode = new FactoryNode(_ => null);
+ factoryNode.OnNewOutput.Subscribe(o => emittedOutput = o);
+
+ // Act
+ factoryNode.Input = "Test";
+
+ // Assert
+ Assert.IsNull(emittedOutput);
+ Assert.IsNull(factoryNode.Output);
+ }
+
+ [TestMethod]
+ public void FactoryNode_MultipleInputs()
+ {
+ // Arrange
+ var outputs = new List();
+ var factoryNode = new FactoryNode(input => $"{input}_transformed");
+ factoryNode.OnNewOutput.Subscribe(o => outputs.Add(o));
+
+ // Act
+ factoryNode.Input = "First";
+ factoryNode.Input = "Second";
+ factoryNode.Input = "Third";
+
+ // Assert
+ CollectionAssert.AreEqual(
+ new[] { "First_transformed", "Second_transformed", "Third_transformed" },
+ outputs);
+ Assert.AreEqual("Third_transformed", factoryNode.Output);
+ }
+
+ [TestMethod]
+ public void FactoryNode_WithIntegerTransformation()
+ {
+ // Arrange
+ var emittedOutputs = new List();
+ var factoryNode = new FactoryNode(input => input * 2);
+ factoryNode.OnNewOutput.Subscribe(o => emittedOutputs.Add(o));
+
+ // Act
+ factoryNode.Input = 5;
+ factoryNode.Input = 10;
+ factoryNode.Input = 0;
+
+ // Assert
+ CollectionAssert.AreEqual(new[] { 10, 20, 0 }, emittedOutputs);
+ Assert.AreEqual(0, factoryNode.Output);
+ }
+
+ [TestMethod]
+ public void FactoryNode_OutputNotificationCalledForEachInput()
+ {
+ // Arrange
+ var outputNotificationCount = 0;
+ var factoryNode = new FactoryNode(input => input);
+ factoryNode.OnNewOutput.Subscribe(_ => outputNotificationCount++);
+
+ // Act
+ factoryNode.Input = "Test1";
+ factoryNode.Input = "Test2";
+ factoryNode.Input = "Test3";
+
+ // Assert
+ Assert.AreEqual(3, outputNotificationCount);
+ }
+
+ [TestMethod]
+ public void FactoryNode_ChainedWithOtherNodes()
+ {
+ // Arrange
+ string? finalOutput = null;
+ var pipeline = new Pipeline();
+ pipeline.OnNewOutput.Subscribe(o => finalOutput = o);
+
+ var factoryNode1 = new FactoryNode(input => $"{input}_1");
+ var factoryNode2 = new FactoryNode(input => $"{input}_2");
+
+ // Act
+ pipeline.SetDefault("Test");
+ pipeline.RegisterNode(factoryNode1);
+ pipeline.RegisterNode(factoryNode2);
+
+ // Assert
+ Assert.AreEqual("Test_1_2", finalOutput);
+ Assert.AreEqual("Test_1_2", pipeline.Output);
+ }
+}
diff --git a/tests/CodeCasa.AutomationPipelines.Lights.Tests/MSTestSettings.cs b/tests/CodeCasa.AutomationPipelines.Lights.Tests/MSTestSettings.cs
new file mode 100644
index 0000000..aaf278c
--- /dev/null
+++ b/tests/CodeCasa.AutomationPipelines.Lights.Tests/MSTestSettings.cs
@@ -0,0 +1 @@
+[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)]