A smart home implementation example using C# and NetDaemon.
This repository explores creative and powerful ways to use a rich programming language like C# for home automation. From custom logic to seamless integrations, you'll find practical examples and unique ideas to elevate your smart home setup. Stay tuned for ongoing updates and new features!
One of the great advantages of using a general-purpose programming language like C# for home automations is the ability to introduce your own architectural patterns. This chapter highlights some of the patterns used in this example project.
I've never been a fan of large tablets that display every available entity. Part of making a home "smart" is tailoring it to show only the information the inhabitants actually care about. That’s why I opted for an NSPanel Pro instead.
It was a fun challenge to get it working in a smooth and intuitive way. Rather than using a standard Home Assistant dashboard, I built a custom dashboard using Blazor. Here’s a preview:
The source code for this dashboard is available in this repository.
- Custom webview Using the posts from Blakadder
- Proximity detection: Managed by the Automate app, which calls a Home Assistant webhook. The webhook triggers an automation that the panel subscribes to.
- Blazor Dashboard: The razor page/components can be found here: Components.
- Panel state: Stored in an input select value and managed in LivingRoomPanelNavigation.cs.
- Google timers & alarms: Implemented via the HACS integration ha-google-home.
- Interactive notifications: Powered by Input Select Notifications.
This demo shows how to use a custom compound entity to group related properties and services for a person.
In this example, the Jasper
class provides Jasper’s name and location and making it easy to trigger context-aware notifications to his phone:
[NetDaemonApp]
internal class OfficeLightsNotifications
{
public OfficeLightsNotifications(
LightEntities lightEntities,
Jasper jasper)
{
var notificationId = $"{nameof(OfficeLightsNotifications)}_Notification"; // Note: Using an ID that is consistent between runs also ensures that old notifications are removed/replaced on phones when the app is reloaded.
var officeLights = lightEntities.OfficeLights.ToOnOffObservable();
var jasperHome = jasper.CreateHomeObservable();
// Only notify Jasper if he is at home and the lights are on.
jasperHome.And(officeLights).SubscribeOnOff(
() =>
{
jasper.Phone.Notify(new AndroidNotificationConfig
{
Message = $"Hey {jasper.Name}, the office lights are on!",
StatusBarIcon = "mdi:lightbulb",
Actions =
[
new(() => lightEntities.OfficeLights.TurnOff(), "Click here to turn them off.")
]
}, notificationId);
},
() => jasper.Phone.RemoveNotification(notificationId));
}
}
- The code from this
NetDaemonApp
: OfficeLightsNotifications.cs - The custom Jasper entity: Jasper.cs
This project showcases the use of phone notification, built with the CodeCasa.NetDaemon.Notifications.Phone
library from NetDaemon.Utils
.
Here’s a preview of the notifications in action:
For detailed usage and setup instructions, see the CodeCasa.NetDaemon.Notifications.Phone
documentation.
- The
NetDaemonApp
demo code: PhoneDemoNotifications.cs
This project also showcases rich notifications using an input select entity, built with the CodeCasa.NetDaemon.Notifications.InputSelect
library from NetDaemon.Utils
.
Here’s a preview of the notifications in action:
For detailed usage and setup instructions, see the CodeCasa.NetDaemon.Notifications.InputSelect
documentation.
- The
NetDaemonApp
demo code: DashboardDemoNotifications.cs - The Blazor component: Notifications.razor
This automation uses the AutomationPipelines
library to handle complex logic in a modular, layered way.
Rather than implementing behavior directly in a single class, logic is split into small, independent pipeline nodes. Each node can contribute to or override the final outcome based on its own conditions. This makes the automation easier to reason about, test, and extend.
Below is the setup used in the BackyardStringLightsPipeline
app:
backyardPorchStringLightsPipeline
.SetDefault(false)
.RegisterNode(new LightStringRoutineNode<bool>(scheduler, true, TimeSpan.Zero))
.RegisterNode<BackyardStringLightsEnergySavingNode>()
.SetOutputHandler(b => UpdateLightState(lightEntities.BackyardPorchStringLights, b));
In this example:
- The pipeline starts with a default state of false (lights off).
- The first node (LightStringRoutineNode) schedules the lights to turn on during morning and evening hours.
- The second node (
BackyardStringLightsEnergySavingNode
) can turn them off again if all curtains are closed. - Finally,
SetOutputHandler
applies the resulting output to the actual light entity.
This project contains the NetDaemon automations. It runs as a console application and can be hosted as a container.
A Blazor-based web dashboard that demonstrates how to integrate with Home Assistant. This project showcases how to build responsive, interactive UIs that control and reflect your smart home’s state in real-time.
NetDaemon can auto-generate strongly-typed classes based on the entities in your Home Assistant configuration. For this demo, a curated selection of generated code is included to illustrate how this feature simplifies development and enhances type safety.
These projects combine existing entities into compound entities or creates entirely new entities tailored to specific automation or dashboard scenarios. It also includes helper constants to simplify and standardize usage across automations and dashboards.
A collection of utility classes for working with NetDaemon entities. These utilities are tailored to the use-cases of this project but may be useful for similar implementations in other projects.