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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CodeCasa.sln
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeCasa.AutomationPipeline
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeCasa.Lights.NetDaemon", "src\CodeCasa.Lights.NetDaemon\CodeCasa.Lights.NetDaemon.csproj", "{FA32051C-0DFF-4FDE-9D9D-60F4ABA4D1A8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeCasa.AutomationPipelines.Lights", "src\CodeCasa.AutomationPipelines.Lights\CodeCasa.AutomationPipelines.Lights.csproj", "{A66370F1-3944-4EAA-82EF-6A321BF273D1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeCasa.AutomationPipelines.Lights.NetDaemon", "src\CodeCasa.AutomationPipelines.Lights.NetDaemon\CodeCasa.AutomationPipelines.Lights.NetDaemon.csproj", "{039D5B9D-F313-4DC5-9A12-FF2A1FDEB15C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -89,6 +93,14 @@ Global
{FA32051C-0DFF-4FDE-9D9D-60F4ABA4D1A8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FA32051C-0DFF-4FDE-9D9D-60F4ABA4D1A8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FA32051C-0DFF-4FDE-9D9D-60F4ABA4D1A8}.Release|Any CPU.Build.0 = Release|Any CPU
{A66370F1-3944-4EAA-82EF-6A321BF273D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A66370F1-3944-4EAA-82EF-6A321BF273D1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A66370F1-3944-4EAA-82EF-6A321BF273D1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A66370F1-3944-4EAA-82EF-6A321BF273D1}.Release|Any CPU.Build.0 = Release|Any CPU
{039D5B9D-F313-4DC5-9A12-FF2A1FDEB15C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{039D5B9D-F313-4DC5-9A12-FF2A1FDEB15C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{039D5B9D-F313-4DC5-9A12-FF2A1FDEB15C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{039D5B9D-F313-4DC5-9A12-FF2A1FDEB15C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
4 changes: 0 additions & 4 deletions src/CodeCasa.Abstractions/CodeCasa.Abstractions.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,5 @@
<PackagePath>\</PackagePath>
</None>
</ItemGroup>

<ItemGroup>
<PackageReference Include="NetDaemon.HassModel" Version="25.48.0" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Title>CodeCasa.AutomationPipelines.Lights.NetDaemon</Title>
<Authors>Jasper Lammers</Authors>
<Company>DevJasper</Company>
<Description>NetDaemon integration for light automation pipelines, enabling advanced reactive light control through CodeCasa.AutomationPipelines and CodeCasa.Lights abstractions.</Description>
<RepositoryUrl>https://github.com/DevJasperNL/CodeCasa</RepositoryUrl>
<PackageTags>NetDaemon;Light Control;Lights;Home Automation;Home Assistant;HA Integration;Smart Home;Automation;Pipeline;Pipelines;Node;Nodes;Reactive;Rx;Reactive Extensions;C#;.NET;DotNet;Extension Methods</PackageTags>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageId>CodeCasa.AutomationPipelines.Lights.NetDaemon</PackageId>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<PackageIcon>ccnd_icon.png</PackageIcon>
<PackageReleaseNotes>https://github.com/DevJasperNL/CodeCasa/releases</PackageReleaseNotes>
</PropertyGroup>

<ItemGroup>
<None Include="..\..\LICENSE">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
<None Include="..\..\README.md">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
<None Include="..\..\img\ccnd_icon.png">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\CodeCasa.AutomationPipelines.Lights\CodeCasa.AutomationPipelines.Lights.csproj" />
<ProjectReference Include="..\CodeCasa.Lights.NetDaemon\CodeCasa.Lights.NetDaemon.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using CodeCasa.AutomationPipelines.Lights.Pipeline;
using CodeCasa.Lights.NetDaemon.Extensions;
using NetDaemon.HassModel.Entities;

namespace CodeCasa.AutomationPipelines.Lights.NetDaemon.Extensions
{
/// <summary>
/// Extension methods for <see cref="LightPipelineFactory"/> to work with NetDaemon light entities.
/// </summary>
public static class LightPipelineFactoryExtensions
{
/// <summary>
/// Sets up a light pipeline for a NetDaemon light entity.
/// </summary>
/// <param name="lightPipelineFactory">The light pipeline factory.</param>
/// <param name="lightEntity">The NetDaemon light entity to set up the pipeline for.</param>
/// <param name="pipelineBuilder">An action to configure the pipeline behavior.</param>
/// <returns>An async disposable representing the created pipeline(s) that can be disposed to clean up resources.</returns>
public static IAsyncDisposable SetupLightPipeline(this LightPipelineFactory lightPipelineFactory, ILightEntityCore lightEntity,
Action<ILightTransitionPipelineConfigurator> pipelineBuilder)
{
return lightPipelineFactory.SetupLightPipeline(lightEntity.AsLight(), pipelineBuilder);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Title>CodeCasa.AutomationPipelines.Lights</Title>
<Authors>Jasper Lammers</Authors>
<Company>DevJasper</Company>
<Description>Light automation pipeline extensions for CodeCasa.AutomationPipelines, providing composable and reactive light control logic.</Description>
<RepositoryUrl>https://github.com/DevJasperNL/CodeCasa</RepositoryUrl>
<PackageTags>Home Automation;Home Assistant;HA Integration;Smart Home;Automation;Lights;Light;Pipeline;Pipelines;Node;Nodes;Reactive;Rx;Reactive Extensions;C#;.NET;DotNet;Extension Methods</PackageTags>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageId>CodeCasa.AutomationPipelines.Lights</PackageId>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<PackageIcon>cc_icon.png</PackageIcon>
<PackageReleaseNotes>https://github.com/DevJasperNL/CodeCasa/releases</PackageReleaseNotes>
</PropertyGroup>

<ItemGroup>
<None Include="..\..\LICENSE">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
<None Include="..\..\README.md">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
<None Include="..\..\img\cc_icon.png">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>

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

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
namespace CodeCasa.AutomationPipelines.Lights
{
/// <summary>
/// A composite disposable that manages both synchronous and asynchronous disposable resources.
/// </summary>
public sealed class CompositeAsyncDisposable : IAsyncDisposable
{
private readonly List<IAsyncDisposable> _asyncDisposables = new();
private readonly List<IDisposable> _disposables = new();
private bool _disposed;

/// <summary>
/// Adds an asynchronous disposable resource to be disposed when this composite is disposed.
/// </summary>
/// <param name="asyncDisposable">The asynchronous disposable resource to add.</param>
public void Add(IAsyncDisposable asyncDisposable)
{
_asyncDisposables.Add(asyncDisposable);
}

/// <summary>
/// Adds a synchronous disposable resource to be disposed when this composite is disposed.
/// </summary>
/// <param name="disposable">The synchronous disposable resource to add.</param>
public void Add(IDisposable disposable)
{
_disposables.Add(disposable);
}

/// <summary>
/// Adds a range of synchronous disposable resources to be disposed when this composite is disposed.
/// </summary>
/// <param name="disposables">The collection of synchronous disposable resources to add.</param>
public void AddRange(IEnumerable<IDisposable> disposables)
{
_disposables.AddRange(disposables);
}

/// <summary>
/// Disposes all managed asynchronous and synchronous resources.
/// </summary>
public async ValueTask DisposeAsync()
{
if (_disposed)
{
return;
}
_disposed = true;

foreach (var d in _asyncDisposables)
{
await d.DisposeAsync();
}
foreach (var d in _disposables)
{
d.Dispose();
}
}
}
}
60 changes: 60 additions & 0 deletions src/CodeCasa.AutomationPipelines.Lights/CompositeHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using CodeCasa.Lights;
using CodeCasa.Lights.Extensions;

namespace CodeCasa.AutomationPipelines.Lights
{
internal static class CompositeHelper
{
public static string[] ValidateLightsSupported(IEnumerable<string> lightIds, IEnumerable<string> supportedLightIds)
{
var supportedLightIdsArray = supportedLightIds.ToArray();
var lightIdsArray = lightIds.ToArray();
if (!lightIdsArray.Any())
{
throw new ArgumentException("At least one id should be provided.", nameof(lightIdsArray));
}

var unsupportedLightIds = lightIdsArray
.Where(id => !supportedLightIdsArray.Contains(id))
.ToArray();

if (unsupportedLightIds.Any())
{
throw new InvalidOperationException(
$"The following lights are not supported: {string.Join(", ", unsupportedLightIds)}.");
}

return lightIdsArray.ToArray();
}

public static void ValidateLightSupported(IEnumerable<string> lights, string supportedLightId)
{
var lightsArray = lights.ToArray();
if (!lightsArray.Any())
{
throw new ArgumentException("At least one id should be provided.", nameof(lightsArray));
}

var unsupportedLightIds = lightsArray
.Where(id => id != supportedLightId)
.ToArray();

if (unsupportedLightIds.Any())
{
throw new InvalidOperationException(
$"The following lights are not supported: {string.Join(", ", unsupportedLightIds)}.");
}
}

public static string[] ResolveGroupsAndValidateLightsSupported(IEnumerable<ILight> lights, IEnumerable<string> supportedLightIds)
{
return ValidateLightsSupported(lights.SelectMany(le => le.Flatten()).Select(l => l.Id).Distinct(), supportedLightIds);
}


public static void ResolveGroupsAndValidateLightSupported(IEnumerable<ILight> lights, string supportedLightId)
{
ValidateLightSupported(lights.SelectMany(le => le.Flatten()).Select(l => l.Id).Distinct(), supportedLightId);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using CodeCasa.Lights;

namespace CodeCasa.AutomationPipelines.Lights.Context;

/// <summary>
/// Represents the context for a light pipeline, providing access to the service provider and the light being controlled.
/// </summary>
public interface ILightPipelineContext
{
/// <summary>
/// Gets the service provider instance used to resolve dependencies in the pipeline.
/// </summary>
IServiceProvider ServiceProvider { get; }

/// <summary>
/// Gets the light being controlled by the pipeline.
/// </summary>
ILight Light { get; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using CodeCasa.Lights;

namespace CodeCasa.AutomationPipelines.Lights.Context;

/// <inheritdoc />
public class LightPipelineContext : ILightPipelineContext
{
/// <summary>
/// Initializes a new instance of the <see cref="LightPipelineContext"/> class.
/// </summary>
/// <param name="serviceProvider">The service provider instance used to resolve dependencies in the pipeline.</param>
/// <param name="light">The light being controlled by the pipeline.</param>
internal LightPipelineContext(IServiceProvider serviceProvider, ILight light)
{
ServiceProvider = serviceProvider;
Light = light;
}

/// <inheritdoc />
public IServiceProvider ServiceProvider { get; }

/// <inheritdoc />
public ILight Light { get; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
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;
}
}
}
Loading