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
6 changes: 6 additions & 0 deletions CodeCasa.sln
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeCasa.Lights.NetDaemon.S
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
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeCasa.Notifications.Lights", "src\CodeCasa.Notifications.Lights\CodeCasa.Notifications.Lights.csproj", "{B79CCDE0-2EB5-453B-B42C-B52014D8F3D6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -113,6 +115,10 @@ Global
{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
{B79CCDE0-2EB5-453B-B42C-B52014D8F3D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B79CCDE0-2EB5-453B-B42C-B52014D8F3D6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B79CCDE0-2EB5-453B-B42C-B52014D8F3D6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B79CCDE0-2EB5-453B-B42C-B52014D8F3D6}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
</PropertyGroup>

<ItemGroup>
<InternalsVisibleTo Include="CodeCasa.Notifications.Lights" />
<InternalsVisibleTo Include="CodeCasa.AutomationPipelines.Lights.Tests" />
</ItemGroup>

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

<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Title>CodeCasa.Notifications.Lights</Title>
<Authors>Jasper Lammers</Authors>
<Company>DevJasper</Company>
<Description>Light notification system for CodeCasa, enabling visual alerts and notifications using smart lights.</Description>
<RepositoryUrl>https://github.com/DevJasperNL/CodeCasa</RepositoryUrl>
<PackageTags>Home Automation;Home Assistant;HA Integration;Smart Home;Automation;Lights;Light;Notification;Notifications;Alert;Alerts;C#;.NET;DotNet</PackageTags>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageId>CodeCasa.Notifications.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.AutomationPipelines.Lights\CodeCasa.AutomationPipelines.Lights.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using CodeCasa.AutomationPipelines;
using CodeCasa.AutomationPipelines.Lights.Context;
using CodeCasa.Lights;

namespace CodeCasa.Notifications.Lights.Config;

/// <summary>
/// Configuration for a light notification.
/// </summary>
public class LightNotificationConfig
{
/// <summary>
/// Gets or sets the priority of the notification.
/// Higher values indicate higher priority.
/// </summary>
public int Priority { get; set; }

/// <summary>
/// Gets the type of the pipeline node associated with the notification.
/// </summary>
public Type? NodeType { get; }

/// <summary>
/// Gets the factory function to create the pipeline node associated with the notification.
/// </summary>
public Func<ILightPipelineContext, IPipelineNode<LightTransition>>? NodeFactory { get; }

internal LightNotificationConfig(Type nodeType, int priority)
{
NodeType = nodeType;
Priority = priority;
}

internal LightNotificationConfig(Func<ILightPipelineContext, IPipelineNode<LightTransition>> nodeFactory, int priority)
{
NodeFactory = nodeFactory;
Priority = priority;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using CodeCasa.AutomationPipelines;
using CodeCasa.AutomationPipelines.Lights.Context;
using CodeCasa.AutomationPipelines.Lights.Extensions;
using CodeCasa.AutomationPipelines.Lights.Pipeline;
using CodeCasa.Lights;
using System.Reactive.Linq;

namespace CodeCasa.Notifications.Lights.Extensions
{
/// <summary>
/// Extension methods for configuring light transition pipelines with notifications.
/// </summary>
public static class LightTransitionPipelineConfiguratorExtensions
{
/// <summary>
/// Adds light notifications to the pipeline.
/// </summary>
/// <param name="configurator">The pipeline configurator.</param>
/// <param name="lightNotificationManagerContext">The context providing light notifications.</param>
/// <returns>The updated pipeline configurator.</returns>
public static ILightTransitionPipelineConfigurator AddNotifications(
this ILightTransitionPipelineConfigurator configurator, LightNotificationManagerContext lightNotificationManagerContext)
{
return configurator.AddReactiveNode(c =>
{
c.AddNodeSource(lightNotificationManagerContext.LightNotifications.Select(lnc =>
{
if (lnc == null)
{
return new Func<ILightPipelineContext, IPipelineNode<LightTransition>?>(_ => null);
}

if (lnc.NodeFactory != null)
{
return lnc.NodeFactory;
}

if (lnc.NodeType == null)
{
throw new InvalidOperationException("Both NodeFactory and NodeType are null.");
}

return ctx => (IPipelineNode<LightTransition>)ctx.ServiceProvider.CreateInstanceWithinContext(lnc.NodeType, ctx);
}));
});
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using Microsoft.Extensions.DependencyInjection;

namespace CodeCasa.Notifications.Lights.Extensions;

/// <summary>
/// Extension methods for setting up light notification services in an <see cref="IServiceCollection" />.
/// </summary>
public static class ServiceCollectionExtensions
{
/// <summary>
/// Adds light notification services to the specified <see cref="IServiceCollection" />.
/// </summary>
/// <param name="serviceCollection">The <see cref="IServiceCollection" /> to add services to.</param>
/// <returns>The <see cref="IServiceCollection" /> so that additional calls can be chained.</returns>
public static IServiceCollection AddLightNotifications(this IServiceCollection serviceCollection)
{
return serviceCollection
.AddSingleton<LightNotificationManager>()
.AddTransient<LightNotificationManagerContext>()
.AddTransient<LightNotificationContext>();
}
}
19 changes: 19 additions & 0 deletions src/CodeCasa.Notifications.Lights/LightNotification.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
namespace CodeCasa.Notifications.Lights
{
/// <summary>
/// Represents a light notification.
/// </summary>
/// <param name="id">The unique identifier for the notification.</param>
/// <param name="disposable">The disposable object associated with the notification.</param>
public class LightNotification(string id, IDisposable disposable) : IDisposable
{
/// <summary>
/// Gets the unique identifier for the notification.
/// This ID can be used to update or cancel the notification later.
/// </summary>
public string Id { get; } = id;

/// <inheritdoc />
public void Dispose() => disposable.Dispose();
}
}
113 changes: 113 additions & 0 deletions src/CodeCasa.Notifications.Lights/LightNotificationContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
using CodeCasa.AutomationPipelines;
using CodeCasa.AutomationPipelines.Lights.Context;
using CodeCasa.Lights;
using CodeCasa.Notifications.Lights.Config;

namespace CodeCasa.Notifications.Lights
{
/// <summary>
/// Context for managing light notifications.
/// </summary>
/// <param name="lightNotificationManager">The manager responsible for handling light notifications.</param>
public class LightNotificationContext(LightNotificationManager lightNotificationManager)
{
/// <summary>
/// Notifies with a new light notification, replacing an existing one.
/// </summary>
/// <typeparam name="TNode">The type of the pipeline node representing the notification behavior.</typeparam>
/// <param name="lightNotificationToReplace">The existing light notification to replace.</param>
/// <returns>The created or updated light notification.</returns>
public LightNotification Notify<TNode>(LightNotification lightNotificationToReplace) where TNode : IPipelineNode<LightTransition> => Notify<TNode>(lightNotificationToReplace, 0);

/// <summary>
/// Notifies with a new light notification with a specific priority, replacing an existing one.
/// </summary>
/// <typeparam name="TNode">The type of the pipeline node representing the notification behavior.</typeparam>
/// <param name="lightNotificationToReplace">The existing light notification to replace.</param>
/// <param name="priority">The priority of the notification.</param>
/// <returns>The created or updated light notification.</returns>
public LightNotification Notify<TNode>(LightNotification lightNotificationToReplace, int priority) where TNode : IPipelineNode<LightTransition>
{
return Notify<TNode>(lightNotificationToReplace.Id, priority);
}

/// <summary>
/// Notifies with a new light notification using a specific ID.
/// </summary>
/// <typeparam name="TNode">The type of the pipeline node representing the notification behavior.</typeparam>
/// <param name="notificationId">The unique identifier for the notification.</param>
/// <returns>The created or updated light notification.</returns>
public LightNotification Notify<TNode>(string notificationId) where TNode : IPipelineNode<LightTransition> => Notify<TNode>(notificationId, 0);

/// <summary>
/// Notifies with a new light notification with a specific priority and ID.
/// </summary>
/// <typeparam name="TNode">The type of the pipeline node representing the notification behavior.</typeparam>
/// <param name="notificationId">The unique identifier for the notification.</param>
/// <param name="priority">The priority of the notification.</param>
/// <returns>The created or updated light notification.</returns>
public LightNotification Notify<TNode>(string notificationId, int priority) where TNode : IPipelineNode<LightTransition>
{
return lightNotificationManager.Notify(new LightNotificationConfig(typeof(TNode), priority), notificationId);
}

/// <summary>
/// Notifies with a new light notification using a factory, replacing an existing one.
/// </summary>
/// <param name="lightNotificationToReplace">The existing light notification to replace.</param>
/// <param name="nodeFactory">The factory function to create the pipeline node.</param>
/// <returns>The created or updated light notification.</returns>
public LightNotification Notify(LightNotification lightNotificationToReplace, Func<ILightPipelineContext, IPipelineNode<LightTransition>> nodeFactory) => Notify(lightNotificationToReplace, nodeFactory, 0);

/// <summary>
/// Notifies with a new light notification using a factory and specific priority, replacing an existing one.
/// </summary>
/// <param name="lightNotificationToReplace">The existing light notification to replace.</param>
/// <param name="nodeFactory">The factory function to create the pipeline node.</param>
/// <param name="priority">The priority of the notification.</param>
/// <returns>The created or updated light notification.</returns>
public LightNotification Notify(LightNotification lightNotificationToReplace, Func<ILightPipelineContext, IPipelineNode<LightTransition>> nodeFactory, int priority)
{
return Notify(lightNotificationToReplace.Id, nodeFactory, priority);
}

/// <summary>
/// Notifies with a new light notification using a factory and specific ID.
/// </summary>
/// <param name="notificationId">The unique identifier for the notification.</param>
/// <param name="nodeFactory">The factory function to create the pipeline node.</param>
/// <returns>The created or updated light notification.</returns>
public LightNotification Notify(string notificationId, Func<ILightPipelineContext, IPipelineNode<LightTransition>> nodeFactory) => Notify(notificationId, nodeFactory, 0);

/// <summary>
/// Notifies with a new light notification using a factory, specific priority, and ID.
/// </summary>
/// <param name="notificationId">The unique identifier for the notification.</param>
/// <param name="nodeFactory">The factory function to create the pipeline node.</param>
/// <param name="priority">The priority of the notification.</param>
/// <returns>The created or updated light notification.</returns>
public LightNotification Notify(string notificationId, Func<ILightPipelineContext, IPipelineNode<LightTransition>> nodeFactory, int priority)
{
return lightNotificationManager.Notify(new LightNotificationConfig(nodeFactory, priority), notificationId);
}

/// <summary>
/// Removes a specific light notification.
/// </summary>
/// <param name="notificationToRemove">The light notification to remove.</param>
public void RemoveNotification(LightNotification notificationToRemove)
{
RemoveNotification(notificationToRemove.Id);
}

/// <summary>
/// Removes a light notification by its ID.
/// </summary>
/// <param name="id">The unique identifier of the notification to remove.</param>
/// <returns>True if the notification was successfully removed; otherwise, false.</returns>
public bool RemoveNotification(string id)
{
return lightNotificationManager.Remove(id);
}
}
}
Loading