From 73049e4f210b292a0e04a55e41156ec88be18f00 Mon Sep 17 00:00:00 2001 From: Jasper Date: Wed, 5 Aug 2026 11:00:29 +0200 Subject: [PATCH 1/2] Using group node output rather than setting lights directly. --- .../Nodes/GroupNode.cs | 13 ++-- .../Nodes/GroupNodeContext.cs | 76 +++++++++---------- 2 files changed, 46 insertions(+), 43 deletions(-) diff --git a/src/CodeCasa.AutomationPipelines.Lights/Nodes/GroupNode.cs b/src/CodeCasa.AutomationPipelines.Lights/Nodes/GroupNode.cs index 89c59e3..319ccdd 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/Nodes/GroupNode.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/Nodes/GroupNode.cs @@ -5,11 +5,9 @@ namespace CodeCasa.AutomationPipelines.Lights.Nodes; internal class GroupNode : PipelineNode { private readonly GroupNodeContext _groupNodeContext; - private readonly ILight _light; - public GroupNode(ILight light, GroupNodeContext groupNodeContext) + public GroupNode(GroupNodeContext groupNodeContext) { - _light = light; _groupNodeContext = groupNodeContext; Name = "Group Node"; } @@ -19,14 +17,19 @@ protected override void InputReceived(LightTransition? input) { if (input != null) { - _groupNodeContext.Process(_light, input); + _groupNodeContext.Process(this, input); } } + internal void SetOutput(LightTransition? output) + { + Output = output; + } + /// public override async ValueTask DisposeAsync() { - _groupNodeContext.Unregister(_light); + _groupNodeContext.Unregister(this); await base.DisposeAsync(); } } \ No newline at end of file diff --git a/src/CodeCasa.AutomationPipelines.Lights/Nodes/GroupNodeContext.cs b/src/CodeCasa.AutomationPipelines.Lights/Nodes/GroupNodeContext.cs index 48d8447..2d7a7ed 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/Nodes/GroupNodeContext.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/Nodes/GroupNodeContext.cs @@ -9,26 +9,26 @@ internal class GroupNodeContext(IScheduler scheduler, ILogger _groups = new(); private readonly Lock _lock = new(); - public void Register(ILight light, ILight lightGroup, TimeSpan groupDuration, EqualityComparer equalityComparer) + public void Register(GroupNode groupNode, ILight lightGroup, TimeSpan groupDuration, EqualityComparer equalityComparer) { lock (_lock) { var existingGroup = _groups.FirstOrDefault(g => g.LightGroup == lightGroup); if (existingGroup == null) { - existingGroup = new GroupInfo(lightGroup, light, equalityComparer, groupDuration, scheduler, logger); + existingGroup = new GroupInfo(lightGroup, groupNode, equalityComparer, groupDuration, scheduler, logger); _groups.Add(existingGroup); } else { - existingGroup.AddMember(light); + existingGroup.AddMember(groupNode); } } } - public void Process(ILight light, LightTransition transition) + public void Process(GroupNode groupNode, LightTransition transition) { - var inputInfo = new InputInfo(DateTime.UtcNow, light, transition); + var inputInfo = new InputInfo(DateTime.UtcNow, groupNode, transition); lock (_lock) { foreach (var group in _groups) @@ -39,13 +39,13 @@ public void Process(ILight light, LightTransition transition) } - public void Unregister(ILight light) + public void Unregister(GroupNode groupNode) { lock (_lock) { foreach (var group in _groups.ToArray()) { - if (group.RemoveMember(light)) + if (group.RemoveMember(groupNode)) { _groups.Remove(group); group.Dispose(); @@ -54,10 +54,10 @@ public void Unregister(ILight light) } } - internal class InputInfo(DateTime timestamp, ILight light, LightTransition lightTransition) + internal class InputInfo(DateTime timestamp, GroupNode groupNode, LightTransition lightTransition) { public LightTransition Transition { get; } = lightTransition; - public ILight Light { get; } = light; + public GroupNode GroupNode { get; } = groupNode; public DateTime Timestamp { get; } = timestamp; public bool HasExecuted { get; private set; } public void Execute() @@ -66,14 +66,14 @@ public void Execute() { return; } - Light.ApplyTransition(Transition); + GroupNode.SetOutput(Transition); HasExecuted = true; } } internal class GroupInfo( ILight lightGroup, - ILight firstGroupMember, + GroupNode firstGroupNode, IEqualityComparer equalityComparer, TimeSpan groupDuration, IScheduler scheduler, @@ -81,28 +81,28 @@ internal class GroupInfo( : IDisposable { public ILight LightGroup { get; } = lightGroup; - private readonly List _groupMembers = [firstGroupMember]; - private readonly Dictionary _groupInputs = new(); - private readonly Dictionary _scheduledWork = new(); + private readonly List _groupNodes = [firstGroupNode]; + private readonly Dictionary _groupInputs = new(); + private readonly Dictionary _scheduledWork = new(); private readonly Lock _lock = new(); - public void AddMember(ILight member) + public void AddMember(GroupNode groupNode) { lock (_lock) { - _groupMembers.Add(member); + _groupNodes.Add(groupNode); } } - public bool RemoveMember(ILight member) + public bool RemoveMember(GroupNode groupNode) { lock (_lock) { - _groupMembers.Remove(member); - _groupInputs.Remove(member); - CleanupScheduledWork(member); + _groupNodes.Remove(groupNode); + _groupInputs.Remove(groupNode); + CleanupScheduledWork(groupNode); - return !_groupMembers.Any(); + return !_groupNodes.Any(); } } @@ -110,7 +110,7 @@ public void Process(InputInfo inputInfo) { lock (_lock) { - if (!_groupMembers.Contains(inputInfo.Light)) + if (!_groupNodes.Contains(inputInfo.GroupNode)) { return; } @@ -119,14 +119,14 @@ public void Process(InputInfo inputInfo) CleanupExpiredInputs(inputInfo.Timestamp); // If there's an existing input for this light, execute it first - if (_groupInputs.TryGetValue(inputInfo.Light, out var existingInput)) + if (_groupInputs.TryGetValue(inputInfo.GroupNode, out var existingInput)) { existingInput.Execute(); - CleanupScheduledWork(inputInfo.Light); + CleanupScheduledWork(inputInfo.GroupNode); } // Add the new input - _groupInputs[inputInfo.Light] = inputInfo; + _groupInputs[inputInfo.GroupNode] = inputInfo; // Check if all group members now have matching transitions if (AllMembersHaveMatchingTransitions(inputInfo.Transition)) @@ -144,15 +144,15 @@ public void Process(InputInfo inputInfo) { lock (_lock) { - if (_groupInputs.TryGetValue(inputInfo.Light, out var info) && !info.HasExecuted) + if (_groupInputs.TryGetValue(inputInfo.GroupNode, out var info) && !info.HasExecuted) { info.Execute(); - _groupInputs.Remove(inputInfo.Light); + _groupInputs.Remove(inputInfo.GroupNode); } - _scheduledWork.Remove(inputInfo.Light); + _scheduledWork.Remove(inputInfo.GroupNode); } }); - _scheduledWork[inputInfo.Light] = scheduledWork; + _scheduledWork[inputInfo.GroupNode] = scheduledWork; } } @@ -164,24 +164,24 @@ private void CleanupExpiredInputs(DateTime currentTime) if (info.HasExecuted) { // This can occur if the light is in multiple groups at once. - _groupInputs.Remove(info.Light); - CleanupScheduledWork(info.Light); + _groupInputs.Remove(info.GroupNode); + CleanupScheduledWork(info.GroupNode); } else if (info.Timestamp + groupDuration < currentTime) { // We waited long enough for this light to be part of the group, // but it never received a transition that matched the other lights in the group. info.Execute(); - _groupInputs.Remove(info.Light); - CleanupScheduledWork(info.Light); + _groupInputs.Remove(info.GroupNode); + CleanupScheduledWork(info.GroupNode); } } } private bool AllMembersHaveMatchingTransitions(LightTransition transition) { - // We need inputs from ALL group members - if (_groupInputs.Count != _groupMembers.Count) + // We need inputs from ALL group nodes + if (_groupInputs.Count != _groupNodes.Count) { return false; } @@ -190,12 +190,12 @@ private bool AllMembersHaveMatchingTransitions(LightTransition transition) return _groupInputs.Values.All(info => equalityComparer.Equals(info.Transition, transition)); } - private void CleanupScheduledWork(ILight light) + private void CleanupScheduledWork(GroupNode groupNode) { - if (_scheduledWork.TryGetValue(light, out var disposable)) + if (_scheduledWork.TryGetValue(groupNode, out var disposable)) { disposable.Dispose(); - _scheduledWork.Remove(light); + _scheduledWork.Remove(groupNode); } } From a3b133d3a341c3254ebfc1a8aad61eef8aeba45b Mon Sep 17 00:00:00 2001 From: Jasper Date: Wed, 5 Aug 2026 11:07:18 +0200 Subject: [PATCH 2/2] Fixing build errors --- .../Pipeline/LightPipelineFactory.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CodeCasa.AutomationPipelines.Lights/Pipeline/LightPipelineFactory.cs b/src/CodeCasa.AutomationPipelines.Lights/Pipeline/LightPipelineFactory.cs index 55a56cb..257a913 100644 --- a/src/CodeCasa.AutomationPipelines.Lights/Pipeline/LightPipelineFactory.cs +++ b/src/CodeCasa.AutomationPipelines.Lights/Pipeline/LightPipelineFactory.cs @@ -126,10 +126,10 @@ internal Dictionary> CreateLightPipelines