├── .devcontainer.json ├── .gitattributes ├── .github ├── FUNDING.yml └── dependabot.yml ├── .gitignore ├── .nvmrc ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── docs ├── community │ └── sponsor.md ├── developer │ ├── architecture.md │ ├── index.md │ ├── unit_test.md │ ├── vs.md │ └── vscode.md ├── get_started.md └── user │ ├── advanced │ ├── application_lifecycle.md │ ├── async_features.md │ ├── calling_ha_api.md │ ├── client.md │ └── dev_workflow.md │ ├── app_model │ ├── advanced_config.md │ ├── app_model.md │ ├── custom_config.md │ ├── custom_logging.md │ ├── instancing_apps.md │ └── moving_from_v3.md │ ├── extensions │ ├── mqttEntityManager.md │ ├── scheduling.md │ └── tts.md │ ├── hass_model │ ├── hass_model.md │ ├── hass_model_codegen.md │ ├── hass_model_events.md │ ├── hass_model_generated_entities.md │ ├── hass_model_generated_service.md │ ├── hass_model_integration_servicecallback.md │ ├── hass_model_notifications.md │ ├── hass_model_registry.md │ ├── hass_model_service_with_returnvalue.md │ ├── hass_model_subscribe_to_triggers.md │ └── hass_model_working_with_entities.md │ ├── index.md │ ├── started │ ├── assets │ │ ├── add_on_configuration.png │ │ └── netdaemon_add_on.png │ ├── development_source_deploy.md │ ├── development_tools.md │ ├── example.md │ ├── get_started.md │ ├── installation.md │ └── integration.md │ ├── support_policy.md │ ├── troubleshooting │ ├── call_service.md │ └── unauthorized.md │ ├── tutorials │ ├── publish_script.md │ └── webhost.md │ └── whats_new.md ├── docusaurus.config.js ├── package.json ├── sidebars.js ├── src ├── css │ └── custom.css └── pages │ ├── index.js │ └── styles.module.css └── static ├── _redirects └── img ├── C_Sharp_logo.svg ├── codegen.png ├── docs ├── dev │ └── overall_architecture.png ├── installation │ ├── folder_structure_netdaemon.png │ ├── folderstructure_v3.png │ └── netdaemon_add_on.png ├── started │ ├── daemon.png │ ├── daemon3.png │ ├── daemon_addon_config.png │ ├── netdaemonfolder.png │ ├── newrepo.png │ ├── rootdir.png │ └── vs_publish_config.jpg ├── trouble │ └── listen_events.png └── tutorials │ ├── webhost_1_output.png │ ├── webhost_2_publish.png │ ├── webhost_3_publish_dlg.png │ └── webhost_4_port.png ├── favicon.ico ├── favicon.png ├── hacs.png ├── hacs.psd ├── hacs.svg ├── hass.png ├── hass.psd ├── logo.svg ├── undraw_docusaurus_mountain.svg ├── undraw_docusaurus_react.svg └── undraw_docusaurus_tree.svg /.devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "NetDaemon Documentation", 3 | "image": "mcr.microsoft.com/vscode/devcontainers/javascript-node:20-bullseye", 4 | "forwardPorts": [ 5 | 3000 6 | ], 7 | "postCreateCommand": "yarn install", 8 | "customizations": { 9 | "vscode": { 10 | "extensions": [ 11 | "dbaeumer.vscode-eslint", 12 | "esbenp.prettier-vscode"], 13 | "settings": { 14 | "terminal.integrated.shell.linux": "/bin/bash", 15 | "editor.defaultFormatter": "esbenp.prettier-vscode", 16 | "editor.formatOnSave": false, 17 | "[javascript]": { 18 | "editor.defaultFormatter": "esbenp.prettier-vscode", 19 | "editor.formatOnSave": false 20 | } 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | 2 | # These are supported funding model platforms 3 | 4 | github: [helto4real] 5 | patreon: # Replace with a single Patreon username 6 | open_collective: # Replace with a single Open Collective username 7 | ko_fi: # Replace with a single Ko-fi username 8 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 9 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 10 | liberapay: # Replace with a single Liberapay username 11 | issuehunt: # Replace with a single IssueHunt username 12 | otechie: # Replace with a single Otechie username 13 | custom: ['https://www.buymeacoffee.com/ij1qXRM6E'] 14 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | ignore: 9 | - dependency-name: react 10 | versions: 11 | - 17.0.1 12 | - 17.0.2 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn.lock 21 | yarn-error.log* 22 | package-lock.json -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 20 2 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. 8 | 9 | ## Our Standards 10 | 11 | Examples of behavior that contributes to a positive environment for our community include: 12 | 13 | * Demonstrating empathy and kindness toward other people 14 | * Being respectful of differing opinions, viewpoints, and experiences 15 | * Giving and gracefully accepting constructive feedback 16 | * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience 17 | * Focusing on what is best not just for us as individuals, but for the overall community 18 | 19 | Examples of unacceptable behavior include: 20 | 21 | * The use of sexualized language or imagery, and sexual attention or 22 | advances of any kind 23 | * Trolling, insulting or derogatory comments, and personal or political attacks 24 | * Public or private harassment 25 | * Publishing others' private information, such as a physical or email 26 | address, without their explicit permission 27 | * Other conduct which could reasonably be considered inappropriate in a 28 | professional setting 29 | 30 | ## Enforcement Responsibilities 31 | 32 | Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. 33 | 34 | Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. 35 | 36 | ## Scope 37 | 38 | This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. 39 | 40 | ## Enforcement 41 | 42 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at hi@hacs.xyz. All complaints will be reviewed and investigated promptly and fairly. 43 | 44 | All community leaders are obligated to respect the privacy and security of the reporter of any incident. 45 | 46 | ## Enforcement Guidelines 47 | 48 | Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: 49 | 50 | ### 1. Correction 51 | 52 | **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. 53 | 54 | **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. 55 | 56 | ### 2. Warning 57 | 58 | **Community Impact**: A violation through a single incident or series of actions. 59 | 60 | **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. 61 | 62 | ### 3. Temporary Ban 63 | 64 | **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. 65 | 66 | **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. 67 | 68 | ### 4. Permanent Ban 69 | 70 | **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. 71 | 72 | **Consequence**: A permanent ban from any sort of public interaction within the project community. 73 | 74 | ## Attribution 75 | 76 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, 77 | available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 78 | 79 | Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). 80 | 81 | [homepage]: https://www.contributor-covenant.org 82 | 83 | For answers to common questions about this code of conduct, see the FAQ at 84 | https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 NetDaemon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NetDaemon documentation 2 | 3 | [![Netlify Status](https://api.netlify.com/api/v1/badges/0a7cbd16-7d93-4908-b754-32cd8c66e9a5/deploy-status)](https://app.netlify.com/sites/netdaemon/deploys) 4 | 5 | This documentation site for NetDaemon is build using [Docusaurus 2](https://v2.docusaurus.io/), a modern static website generator. 6 | 7 | The documentation site for NetDaemon is hosted on [netlify](https://www.netlify.com/) 8 | 9 | ## Development 10 | 11 | _It is recomended that you use the provided devcontainer when making changes to the documentation._ 12 | 13 | To do this, ensure that you have [docker](https://docs.docker.com/get-docker/) installed locally and that you are using a 14 | code editor that can develop within a container. 15 | 16 | A great (and free!) option is to use Visual Studio Code and its 17 | [Dev Containers extension](https://code.visualstudio.com/docs/devcontainers/containers). 18 | 19 | Using your code editor, open up this repo inside the container - if you are using VS Code and have the extension installed then just open up the folder and it should prompt you to open it as a container instead. 20 | 21 | ### Building the code within the container 22 | 23 | Connect to the console within the container. If you are using VS Code then just go to the `TERMINAL` window and you should already have a console open. 24 | 25 | If you're not using VS Code then find the container within Docker and start a command shell within it. 26 | 27 | In either case, make sure you are sitting in the `/workspaces/netdaemon-docs` folder and then run `yarn start` - it should look like this: 28 | 29 | ```bash 30 | root ➜ /workspaces/netdaemon-docs (readme-containers ✗) $ yarn start 31 | yarn run v1.22.17 32 | $ docusaurus start --host 0.0.0.0 33 | [INFO] Starting the development server... 34 | Warn: `blogDir` doesn't exist: "/workspaces/netdaemon-docs/blog". 35 | [SUCCESS] Docusaurus website is running at: http://localhost:3000/ 36 | 37 | ✔ Client 38 | Compiled successfully in 23.06s 39 | 40 | client (webpack 5.96.1) compiled successfully 41 | ``` 42 | 43 | _Note that yarn can take a long time to start, during which time it appears to be doing nothing - have patience!_ 44 | 45 | 46 | Once Yarn has started you can connect to the newly built docs via your browser at http://localhost:3000/ (Docker automatically maps the port - just make sure that the port matches what is displayed in the terminal output). 47 | 48 | If there is a problem with your docs (syntax error in a file, missing files etc.) then yarn should fail to start and tell you what's wrong. **It is essential that you fix all build issues** before submitting a pull-request, as otherwise GitHub won't be able to deploy the code either. 49 | 50 | 51 | 52 | ### Dynamic refresh of docs (maybe) 53 | 54 | In theory, yarn should be able to tell when you make changes to the doc files and dynamically reflect the changes immediately in the live view. 55 | 56 | In practise this doesn't seem to work for many people (investigations welcome!) so if you don't get dynamic changes then you'll need to kill yarn (`ctrl-c` in the terminal) and restart it. 57 | 58 | Don't forget that you'll likely need to F5 your browser to force it to reload the changes. 59 | 60 | 61 | ### Forcing a clean up 62 | 63 | If you are having problems with cached files etc. within the container you can force a rebuild by typing this into the terminal: 64 | 65 | `rm -rf node_modules && yarn install` 66 | 67 | 68 | ## Submitting a pull request 69 | 70 | Finally, once you've verified that the docs code builds correctly locally you can submit a pull request. The PR on GitHub will have a series of checks, including one for "deploy-preview" - clicking the "details" link on that check should point you to a GitHub-hosted version of your docs so you (and reviewers) can do a check of the generated documentation. 71 | 72 | -------------------------------------------------------------------------------- /docs/community/sponsor.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: sponsor 3 | title: Sponsor & support the NetDaemon project 4 | --- 5 | 6 | 7 | NetDeamon has been a passion project, crafted by a small yet dedicated team along with the invaluable support of our fantastic community. 8 | 9 | NetDeamon will always be free and will be continued to be released with nothing less than "MIT" licensing. However if you're as fond of NetDeamon as we are and would like to express your appreciation through a small donation, please consider using one of the links below. 10 | 11 | Your contribution will go towards covering essential infrastructure costs such as hosting fees and the like. 12 | 13 | 14 | - [GitHub sponsoring](https://github.com/sponsors/helto4real) 15 | - [Buy me a coffee](https://www.buymeacoffee.com/ij1qxrm6e) 16 | 17 | -------------------------------------------------------------------------------- /docs/developer/architecture.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: architecture 3 | title: Architecture of NetDaemon 4 | --- 5 | 6 | ![](/img/docs/dev/overall_architecture.png) 7 | 8 | ## Namespaces in NetDaemon github project 9 | 10 | | Namespace | Description | 11 | | ------------- | ----------------- | 12 | | Client | Handles basic API to connect and communicate with Home Assistant. Maintains websocket connection and provides an convenient interface for API calls. Normally not used by user apps but can if advanced options are needed. | 13 | | HassModel | Handles an entity model and service calls. It caches all entity states for fast access. This namespace also contains the code generator. This is typically injected IHaContext or more specific entities and services in user apps to interact with Home Assistant. This namespace also have components that support service callbacks using NetDaemon integration. | 14 | | AppModel | Handles application life cycle and application configuration. Instance and disposes applications. This namespace also contains the compiler of source deployed apps. | 15 | | Runtime | NetDaemon runtime that connect everything together. The runtime also handles app state by creating input_booleans per app. | 16 | | Host | Default host in all Docker containers. This is used when users use source deployment. It has all dependencies needed for basic operations and all extensions. Users that deploy compiled projects use their own and use the `Runtime` to run NetDaemon | 17 | | Extensions | The extension namespace contains various extensions that is not part of core features. Logging provides default Serilog logging to ND. Scheduling provides convenient scheduling features. Tts provides a convenient interface to do text to speech and finally MqttEntityManager provides features to create entities using the MQTT features in HA. | 18 | 19 | ## Design of NetDaemon 20 | 21 | NetDaemon relies heavily on IObservables to handle events from Home Assistant. An easy way to access all events is to inject `IObservable` using DI. You may need to inject IHomeAssistantRunner or IHomeAssistantConnection depending on need. If you want to see how these are used please see the implementation of `NetDaemon.HassModel.Internal.EntityStateCache class` 22 | 23 | NetDaemon utilize dependency injection through constructor injection a lot. Devs that contributes to NetDaemon need to know or learn DI. 24 | 25 | One important separation of concerns is the AppModel. We totally separate the application life cycle and configuration from other parts of NetDaemon. We might see other application models in the future. 26 | 27 | ## Core interfaces in Client 28 | 29 | ### IHomeAssistantRunner 30 | 31 | This interface can be injected through DI and used to maintain a connection to Home Assistant. It will reconnect when needed. It also provides IObservables when connection connects and disconnects. `CurrentConnection` always has current connected connection or null if disconnected. 32 | 33 | ### IHomeAssistantConnection 34 | 35 | This interface can be injected through DI and contains the current connected Home Assistant API connection instance. It has functions to send low level commands and returns results. If you need to add features that is not currently supported by NetDaemon this is the interface to extend. 36 | 37 | All events from Home Assistant are available as an IObservable called `OnHomeAssistantEvent` 38 | 39 | ## Core interfaces in HassModel 40 | 41 | ### IHaContext 42 | 43 | This interface has convenient functions for interacting with Home Assistant. The functions are more "send and forget" and the async code is hidden from user. 44 | 45 | ### EntityModel 46 | 47 | The model for different types of entities you can find in the `NetDaemon.HassModel.Entities` namespace. We support both text and numeric representation of entities. 48 | 49 | ## Core interfaces in AppModel 50 | 51 | ### IApplication 52 | 53 | Represents an application and current state (Enabled/Disabled/Running/Error). 54 | 55 | ### IAppStateManager 56 | 57 | This interface is used to manage application state. The current implementation in the `Runtime` is to manage state using Home Assistant `input_boolean` helpers. You can add custom state management by adding a singleton to DI `.AddSingleton(s => s.GetRequiredService());` 58 | 59 | ### IAsyncInitializable 60 | 61 | Makes it possible for user apps to get a async initialization callback from the runtime. 62 | 63 | ### IAppModelContext 64 | 65 | Manage all currently discovered applications. 66 | 67 | ## Deployment options 68 | 69 | NetDaemon can be deployed using source code or just compile the template project and use any kind of host. 70 | -------------------------------------------------------------------------------- /docs/developer/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: developer 3 | title: Development 4 | --- 5 | 6 | This is the developer docs for NetDaemon project. If you are looking for documentation how to make apps to control your home, please see [the docs here](https://netdaemon.xyz). 7 | 8 | In the developer pages you find useful information if you want to start contributing to NetDaemon project. 9 | 10 | Thank you for considering contributing to this projects. This is most welcome! 11 | 12 | ## Get things going 13 | 14 | 1. Fork and clone your copy of the `net-daemon/netdaemon` project 15 | 2. Get it up and running and debugging in your prefered development environment. VSCode, Visual Studio and JetBrains Rider are supported for now. Please see specific docs for each of the development tools 16 | 3. Make sure you write tests. If you want to do a big design change, make Issue and describe your suggestion and ping the [discord group](https://discord.gg/K3xwfcX) to get the discussions going. 17 | 4. Write tests that covers your added featureset or change the existing tests as needed. Do not exclude failing tests! 18 | 5. Make PR and it will get reviewed and merged if tests is ok 19 | 20 | -------------------------------------------------------------------------------- /docs/developer/unit_test.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: unit_test 3 | title: Unit testing and quality control 4 | --- 5 | 6 | We expect every contributor to make good tests for each contribution. Easiest is to look att the current tests in the project you are contributing in and try to maintain the current style. 7 | 8 | PR:s that decrease test coverage in any significant way will not be accepted. 9 | 10 | We do code coverage analysis as part of the PR process so it should be quite easy to see if your tests cover. 11 | 12 | As part of PR build process we also check for warnings. Each warning found is treated as error. This will help maintain code quality over time. 13 | -------------------------------------------------------------------------------- /docs/developer/vs.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: vs 3 | title: Visual Studio 4 | --- 5 | 6 | 7 | 8 | ## Visual studio on Windows host os 9 | 10 | Visual Studio 2019 community or newer is recommended. You need to have dotnet 5 installed. [Download .NET 5 here](https://dotnet.microsoft.com/download/dotnet/5.0) or check the minimal version of Visual Studio supported. 11 | 12 | Fork and clone your net-daemon/netdaemon. 13 | 14 | ### Debug 15 | 16 | The debugging can be pointed to the `exampleapps` folder. please turn off the entity generation by `"generate_entities": false`. 17 | 18 | ### Setup the configuration 19 | 20 | ## Setup the environment vars 21 | Easiest is to setup environment varables for your Home Assistant instance in the host. The `HOMEASSISTANT__HOST`, `HOMEASSISTANT__TOKEN` is required to debug. You may have to restart Visual Studio after setting the environment vars. 22 | 23 | | Environment variable | Description | 24 | | ------ | ------ | 25 | | HOMEASSISTANT__TOKEN | Token secret to access the HA instance| 26 | | HOMEASSISTANT__HOST | The ip or hostname of HA | 27 | | HOMEASSISTANT__PORT | The port of home assistant (defaults to 8123 if not specified) | 28 | | NETDAEMON__GENERATEENTITIES | Generate entities, recommed set false unless debugging| 29 | | NETDAEMON__APPSOURCE | The folder/project/dll where it will find daemon. Set this to empty `""` to debug apps local. If needed to debug the dynamic source compilation, set to `/workspaces/netdaemon/Service/apps` 30 | 31 | Use `src/Service/apps` as starting point to debug your stuff! 32 | ``` 33 | 34 | ## Visual studio using containers 35 | 36 | Will be supported at a later time -------------------------------------------------------------------------------- /docs/developer/vscode.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: vscode 3 | title: VS Code 4 | --- 5 | 6 | For Visual Studio Code the recommenced way is to use the Dev Container to edit and debug NetDaemon core projects. 7 | 8 | ## VS Code Dev Container 9 | 10 | Fork and clone your net-daemon/netdaemon. Make you have remote development extensions installed. Open the NetDaemon folder. Run the `Reopen in container` task. When VSCode is finished you are now ready to code. 11 | 12 | ### Debug 13 | 14 | The debugging can be pointed to the `exampleapps`folder. 15 | 16 | ### Setup the configuration 17 | 18 | ## Setup the environment vars 19 | 20 | Easiest is to setup environment variables for your Home Assistant instance in the host. The `HOMEASSISTANT__HOST`, `HOMEASSISTANT__TOKEN` is required to debug. You may have to restart VSCode after setting the environment vars. 21 | 22 | | Environment variable | Description | 23 | | ------ | ------ | 24 | | HOMEASSISTANT__TOKEN | Token secret to access the HA instance| 25 | | HOMEASSISTANT__HOST | The ip or hostname of HA | 26 | | HOMEASSISTANT__PORT | The port of home assistant (defaults to 8123 if not specified) | 27 | | NETDAEMON__GENERATEENTITIES | Generate entities for V2 API, recommend set false unless debugging| 28 | | NETDAEMON__APPSOURCE | The folder/project/dll where it will find daemon. Set this to empty `""` to debug apps local. If needed to debug the dynamic source compilation, set to `/workspaces/netdaemon/Service/apps` 29 | 30 | Use `src/Service/apps` as starting point to debug your stuff! 31 | -------------------------------------------------------------------------------- /docs/get_started.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: get_started 3 | title: Getting started with NetDaemon 4 | --- 5 | 6 | ## What do I need to get started? 7 | 8 | Here is what you will need to start developing apps to automate your home with NetDaemon: 9 | 10 | - The hostname/ip-address and port of your Home Assistant instance 11 | - We recommend using [git](https://git-scm.com/) as a way to version control your apps. 12 | - Create an access token to use for authorization. The long lived access token is created under your profile in the Home Assistant GUI. Make sure the user account for this access token has Administrator privileges in Home Assistant 13 | - Have a .NET development environment. You can edit .cs files in any tool, but using a development environment is recommended 14 | - [Visual Studio 2022](https://visualstudio.microsoft.com/vs/), [Visual Studio Code](https://code.visualstudio.com) or [JetBrains Rider](https://www.jetbrains.com/rider/) 15 | - [.NET 9 SDK](https://dotnet.microsoft.com/download/dotnet/9.0) installed (if running in a VS Code Dev Container, it is pre-installed) 16 | - [Docker](https://www.docker.com/) installed if you want to use a Dev Container in VS Code (recommended) 17 | 18 | Now you have all you need to develop an app, lets just do that by moving to app development. 19 | -------------------------------------------------------------------------------- /docs/user/advanced/application_lifecycle.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: application_lifecycle 3 | title: Application lifecycle 4 | --- 5 | 6 | This topic describes the lifecycle of a NetDaemon app, from instansiation to disposal. Understanding the life-cycle is 7 | important for writing efficient and reliable apps for NetDaemon. 8 | 9 | ## Lifecycle of a NetDaemon app 10 | 11 | The main phases of the life-cycle of a NetDaemon app are: 12 | ```mermaid 13 | graph TD 14 | A(Instantiating) --> B(Async initialization) 15 | B --> C(Running) 16 | C --> D(Disposing) 17 | D --> E((Stopped)) 18 | ``` 19 | 20 | ## Instantiating 21 | 22 | When NetDaemon starts it searches for all apps and loads and instantiates them. The app's constructor is called. 23 | 24 | _You should never do any blocking operations in the constructor, as it will block the startup of NetDaemon._ 25 | 26 | ### Constructor injection 27 | 28 | The constructor is the place to inject dependencies, such as the `IHaContext` or `ILogger`. 29 | 30 | __Example of a constructor injecting different dependencies:__ 31 | ```csharp 32 | [NetDaemonApp] 33 | public class MyFirstApp : NetDaemonApp 34 | { 35 | private readonly IHaContext _haContext; 36 | private readonly ILogger _logger; 37 | 38 | public MyFirstApp(IHaContext haContext, ILogger logger) 39 | { 40 | _haContext = haContext; 41 | _logger = logger; 42 | // Do some initialization of subscriptions to events etc. here 43 | } 44 | } 45 | ``` 46 | 47 | ## Async initialization 48 | 49 | If you need to do some async initialization after the application is instanced and before it is running, 50 | you can implement the `IAsyncInitializable` interface. The `InitializeAsync` method is called after 51 | the app is instantiated. 52 | 53 | This is a good place to do async initialization, like init async libraries or creating long-running tasks. 54 | You can of course do non async initialization here as long as it is not blocking we do recommend using 55 | the constructor for non async initialization. 56 | 57 | _You should never do any blocking operations in initialization phase, as it will block NetDaemon._ 58 | 59 | 60 | __Example of initialization using the `IAsyncInitializable`:__ 61 | ```csharp 62 | [NetDaemonApp] 63 | public class AsyncUsingApp : IAsyncInitializable 64 | { 65 | private Task? _longRunningTask; 66 | 67 | public async Task InitializeAsync(CancellationToken cancellationToken) 68 | { 69 | // Always pass the cancellationToken to async methods since it will be cancelled 70 | // when NetDaemon is stopping and disposing the app 71 | var result = await SomeInitAsync(cancellationToken); 72 | _longRunningTask = Task.Run(() => DoSomeLongRunningTask(cancellationToken)); 73 | } 74 | } 75 | ``` 76 | 77 | ## Running 78 | 79 | After the instantiating and eventual initialization is done, the app is running. This is where the app is listening 80 | to events and performing its tasks as you configured. 81 | 82 | ## Disposing 83 | 84 | There are several reasons for an app to be stopped and disposed: 85 | - NetDaemon runtime is stopping. 86 | - The app is disabled through the Home Assistant UI using the apps input_boolean. 87 | - NetDaemon loose connection to Home Assistant, like if Home Assistant is restarted, then NetDaemon will stop and try to reconnect. 88 | 89 | When NetDaemon is stopping the app, it will dispose the apps. If your app has implemented the `IAsyncDisposable` interface, 90 | or the `IDisposable` interface, the `DisposeAsync` or `Dispose` method is called automatically by NetDaemon when the 91 | app is being disposed. 92 | 93 | The disposal phase looks like this: 94 | - Runtime stops new events from being processed. 95 | - The `DisposeAsync` or `Dispose` method is called on the app. 96 | - The runtime clean up the application resources. 97 | 98 | This order is very important to understand. You can never subscribe to events in this phase since the runtime stops 99 | processing new events. You can however call services. Keep in mind that if the app is stopped due to Home Assistant 100 | loosing connection, the services will not be called and errors will be logged. 101 | 102 | _You should never do any blocking operations in dispose phase, as it will block NetDaemon from stopping._ 103 | 104 | __Example of using the `IAsyncDisposable`:__ 105 | ```csharp 106 | [NetDaemonApp] 107 | public class DisposbleApp : IAsyncDisposable 108 | { 109 | private readonly IHaContext _haContext; 110 | public DisopsableApp(IHaContext haContext) 111 | { 112 | _haContext = haContext; 113 | } 114 | 115 | // Get called when app is being disposed 116 | public async ValueTask DisposeAsync() 117 | { 118 | // Do some async clean-up here. 119 | // Do not implement both IDisposable and IAsyncDisposable, choose one of the two depend on your needs 120 | } 121 | } 122 | ``` 123 | ## Stopped 124 | 125 | After the application is disposed NetDaemon will clean up all resources and the app is in a stopped state. It will not be started again 126 | unless NetDaemon is restarted or the app is enabled again. 127 | 128 | ## Important notes on app life-cycle and subscriptions 129 | 130 | There are some situations where you need to control the behavior of the subscription to events when the app is being disposed. 131 | In most cases the subscriptions will just be stopped when the app is being disposed, but there are some cases where you want 132 | to control the behavior. 133 | 134 | For example the usage of `Throttle` on the observable events. It´s default behavior is to call the action when the observable 135 | is completed (when the app is being disposed). This is not always the desired behavior. You can control this by: 136 | - Using the `WhenStateIsFor` extension method as a replacement. Most use-cases this works better and will never call the action 137 | when the app is being disposed. 138 | - Using the `IgnoreOnComplete` extension method. It will never call the action when the app is being disposed. 139 | 140 | __Example of prohibit action being called when app is disposed using `IgnoreOnComplete` extension method:__ 141 | ```csharp 142 | [NetDaemonApp] 143 | public class IgnoreOnCompleteApp : NetDaemonApp 144 | { 145 | public IgnoreOnCompleteApp(IHaContext haContext, ILogger logger) 146 | { 147 | _entities.Light.TomasRoom? 148 | .StateChanges() 149 | .Where(e => 150 | e.New.IsOn() 151 | ) 152 | // Order is important! Needs to be called before Throttle 153 | .IgnoreOnComplete() 154 | .Throttle(TimeSpan.FromMinutes(15)) 155 | .Subscribe(s => 156 | { 157 | // Do something like call a service 158 | // This service will not be called on dispose of the app 159 | }); 160 | } 161 | } 162 | ``` 163 | -------------------------------------------------------------------------------- /docs/user/advanced/async_features.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: async_features 3 | title: Async features of NetDaemon 4 | --- 5 | 6 | The NetDaemon API is built to have synchronous behavior for users, but the underlying implementation is asynchronous. This makes the API easer to use in most situations. Sometimes you do need to call async functions, like when working with a database. NetDaemon provides features to do this easily. 7 | 8 | ## Async lifetime 9 | 10 | Apps that implement the `IAsyncInitializable` interface get a callback to execute asynchronous code after the app is instantiated: 11 | 12 | ```csharp 13 | [NetDaemonApp] 14 | public class AsyncUsingApp : IAsyncInitializable, IAsyncDisposable 15 | { 16 | private readonly IHomeAssistantApiManager _apiManager; 17 | public AsyncUsingApp(IHomeAssistantApiManager apiManager) 18 | { 19 | _apiManager = apiManager; 20 | } 21 | 22 | public async Task InitializeAsync(CancellationToken cancellationToken) 23 | { 24 | // Use your extension method defined below 25 | var result = await SomeFunctionAsync("a string", cancellationToken); 26 | } 27 | 28 | // Get called when app is being disposed 29 | public async ValueTask DisposeAsync() 30 | { 31 | // Do some async dispose here. Do not implement both IDisposable and IAsyncDisposable, choose one of the two depend on your needs 32 | } 33 | } 34 | ``` 35 | 36 | ## Using async methods in observable 37 | 38 | NetDaemon provides some convenient ways to call async methods in a safe way that guarantees exceptions will be logged: 39 | 40 | ```csharp 41 | 42 | [NetDaemonApp] 43 | public class UseAsyncMethodsApp 44 | { 45 | public UseAsyncMethodsApp(IHaContext ha, ILogger logger) 46 | { 47 | _entities.Light.TomasRoom? 48 | .StateChanges() 49 | .Where(e => 50 | e.New.IsOn() 51 | ) 52 | .SubscribeAsync(async s => 53 | { 54 | await Task.Delay(100); 55 | }, 56 | e => logger.LogError(e, "fail!")); // If you do not provide error handling default will log anyway 57 | } 58 | } 59 | 60 | ``` 61 | 62 | The two provided async extensions to `IObservable` are: 63 | 64 | | Method | Description | 65 | | ------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 66 | | SubscribeAsync | Calls async methods in a safe way. Guaranteed to be logged. If you wait on a `Task` in that method it will block that subscription until returned. | 67 | | SubscribeAsyncConcurrent | Calls async methods concurrently. Order of handling events are not guaranteed. Blocking waits will still be able to handle new events. | 68 | -------------------------------------------------------------------------------- /docs/user/advanced/calling_ha_api.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: call_ha_api 3 | title: Calling Home Assistant API 4 | --- 5 | 6 | NetDaemon provides an interface that helps you make Home Assistant API calls. 7 | 8 | To use it, inject the `IHomeAssistantApiManager` interface in your apps. 9 | 10 | ```csharp 11 | [NetDaemonApp] 12 | public class UsingApiCallsApp : IAsyncInitializable 13 | { 14 | private readonly IHomeAssistantApiManager _apiManager; 15 | public UsingApiCallsApp(IHomeAssistantApiManager apiManager) 16 | { 17 | _apiManager = apiManager; 18 | } 19 | 20 | public async Task InitializeAsync(CancellationToken cancellationToken) 21 | { 22 | // Use your extension method defined below 23 | var entityState = await _apiManager.MyGetEntityStateAsync("light.my_light", cancellationToken); 24 | } 25 | } 26 | ``` 27 | 28 | It is nice to add extension methods onto `IHomeAssistantApiManager` as shown below: 29 | 30 | ```csharp 31 | /// 32 | /// Get the current state for a entity 33 | /// 34 | /// ApiManager to extend 35 | /// Id of the event 36 | /// Token to cancel async operation 37 | public static async Task MyGetEntityStateAsync(this IHomeAssistantApiManager apiManager, string entityId, 38 | CancellationToken cancellationToken) 39 | { 40 | var apiUrl = $"states/{HttpUtility.UrlEncode(entityId)}"; 41 | 42 | return await apiManager.GetApiCallAsync(apiUrl, cancellationToken); 43 | } 44 | ``` 45 | 46 | There is also the ability to post data and receive a result: 47 | 48 | ```csharp 49 | /// 50 | /// Get the current state for a entity 51 | /// 52 | /// ApiManager to extend 53 | /// Id of the event 54 | /// The state to set 55 | /// Attributes 56 | /// Token to cancel async operation 57 | /// 58 | /// This sets the state of a device within Home Assistant 59 | /// and will not communicate with the actual device. To communicate with the device 60 | /// use service calls. To persist devices use the NetDaemon integrations and its service calls. 61 | /// 62 | public static async Task MySetEntityStateAsync(this IHomeAssistantApiManager apiManager, string entityId, 63 | string state, object? attributes, CancellationToken cancellationToken) 64 | { 65 | var apiUrl = $"states/{HttpUtility.UrlEncode(entityId)}"; 66 | 67 | var data = new 68 | { 69 | state, attributes 70 | }; 71 | return await apiManager.PostApiCallAsync(apiUrl, cancellationToken, data); 72 | } 73 | ``` 74 | -------------------------------------------------------------------------------- /docs/user/advanced/client.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: advanced_client 3 | title: Client API 4 | --- 5 | While the recommended way to interact with Home Assistant is through the AppModel and HassModel API, there are scenarios where direct interaction with Home Assistant is required. The Client APIs offer a convenient method for lower-level interactions with Home Assistant. 6 | 7 | ## IHomeAssistantConnection 8 | You can access the client API through the `IHomeAssistantConnection` interface on the existing connection. 9 | This can be injected into a NetDaemon app's constructor. Most methods in this interface are asynchronous, so you need to implement 10 | the `IAsyncInitializable` interface to use them in the `InitializeAsync` method. 11 | Refer to how to [use async features](/user/advanced/async_features.md) for more details on using 12 | async in the subscribe method. Don't forget to include the `NetDaemon.Client.HomeAssistant.Extensions` namespace to access extended features. 13 | 14 | ```csharp 15 | using NetDaemon.AppModel; 16 | using NetDaemon.Client; 17 | using NetDaemon.Client.HomeAssistant.Extensions; 18 | 19 | namespace Apps; 20 | 21 | [NetDaemonApp] 22 | public sealed class HelperApp(IHomeAssistantConnection conn) : IAsyncInitializable 23 | { 24 | private readonly IHomeAssistantConnection _conn = conn; 25 | 26 | public async Task InitializeAsync(CancellationToken cancellationToken) 27 | { 28 | // Use the connection to call Home Assistant features here... 29 | } 30 | } 31 | 32 | ``` 33 | ## IHomeAssistantRunner 34 | 35 | If you need to implement a solution that only requires connecting to Home Assistant and utilizing basic NetDaemon features without the full 36 | app model, you can use the `IHomeAssistantRunner` interface. This interface handles connection and reconnection to Home Assistant. 37 | 38 | Add the client NuGet package to your application and use the `HomeAssistantRunner` class to connect to Home Assistant. 39 | The `AddHomeAssistantClient` extension method on the `IServiceCollection` must be used to add the client to dependency injection. 40 | 41 | Example: Implementation of a background service that uses the `IHomeAssistantRunner` to connect to Home Assistant and subscribe to all events. 42 | 43 | ```csharp 44 | internal sealed class MyOwnService : BackgroundService 45 | { 46 | private const int TimeoutInSeconds = 5; 47 | 48 | private readonly IHomeAssistantRunner _homeAssistantRunner; 49 | 50 | private readonly IHostApplicationLifetime _hostLifetime; 51 | 52 | private CancellationToken? _cancelToken; 53 | private IHomeAssistantConnection? _connection; 54 | 55 | public MyOwnService( 56 | IHostApplicationLifetime hostLifetime, 57 | IHomeAssistantRunner homeAssistantRunner, 58 | { 59 | _hostLifetime = hostLifetime; 60 | _homeAssistantRunner = homeAssistantRunner; 61 | 62 | // When ever the client is connected it will trigger the OnConnect 63 | homeAssistantRunner.OnConnect 64 | .Select(async s => await OnHomeAssistantClientConnected(s).ConfigureAwait(false)) 65 | .Subscribe(); 66 | 67 | // When ever the client is disconnected it will trigger the OnDisconnect 68 | homeAssistantRunner.OnDisconnect.Subscribe(OnHomeAssistantClientDisconnected); 69 | } 70 | 71 | protected override async Task ExecuteAsync(CancellationToken stoppingToken) 72 | { 73 | _cancelToken = stoppingToken; 74 | await _homeAssistantRunner.RunAsync( 75 | "localhost", 76 | 8123, 77 | false, 78 | "YOUR_LONG_LIVED_ACCESS_TOKEN", 79 | TimeSpan.FromSeconds(TimeoutInSeconds), 80 | stoppingToken).ConfigureAwait(false); 81 | 82 | // Stop application if this is exited and use _cancelToken as token 83 | _hostLifetime.StopApplication(); 84 | } 85 | 86 | private async Task OnHomeAssistantClientConnected(IHomeAssistantConnection connection) 87 | { 88 | // First you need to tell Home Assistant to subscribe to events 89 | var hassEvents = await connection.SubscribeToHomeAssistantEventsAsync(null, _cancelToken ?? CancellationToken.None).ConfigureAwait(false); 90 | 91 | // We subscribe to the events that Home Assistant sends and handle them in the HandleEvent method 92 | hassEvents.Subscribe(HandleEvent); 93 | 94 | } 95 | 96 | private void OnHomeAssistantClientDisconnected(DisconnectReason reason) 97 | { 98 | // Here you would typically cancel and dispose any functions 99 | // using the connection 100 | if (_connection is not null) _connection = null; 101 | } 102 | 103 | private void HandleEvent(HassEvent hassEvent) 104 | { 105 | switch (hassEvent.EventType) 106 | { 107 | case "state_changed": 108 | var state = hassEvent.ToStateChangedEvent(); 109 | // Do something with the state 110 | break; 111 | } 112 | } 113 | } 114 | ``` 115 | -------------------------------------------------------------------------------- /docs/user/advanced/dev_workflow.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: dev_workflow3 3 | title: Development workflow 4 | --- 5 | 6 | ## Development workflow 7 | 8 | ## Test individual applications in development environment 9 | 10 | Over time a single NetDaemon solution might contain a large number of applications. While you are working on one NetDaemon app in your development environment you might not want to run all the NetDaemon applications each time you run it for testing or debugging. 11 | 12 | You can do this by applying the `[Focus]` Attribute to the app class(es) you want to test (The `[NetDaemonApp]` is still required). If one or more apps have this attribute set NetDaemon will only launch these applications and ignore all others. 13 | 14 | ```csharp 15 | [Focus] 16 | [NetDaemonApp] 17 | public class HelloWorldApp 18 | { 19 | public HelloWorldApp(IHaContext ha) 20 | { 21 | // I am the only app started 22 | } 23 | } 24 | ``` 25 | 26 | :::note 27 | 28 | This will only apply to the `Development` environment. In all other environments this attribute is ignored. To set the environment to `Development` while debugging make sure the environment variable `DOTNET_ENVIRONMENT` or `ASPNETCORE_ENVIRONMENT` is set to `Development` in the project or startup settings of your IDE. 29 | 30 | ::: 31 | 32 | ### Additional information on development environment settings 33 | 34 | This [Microsoft article](https://docs.microsoft.com/aspnet/core/fundamentals/environments) 35 | gives more detail on how the application can be configured for a Development or non-Development environment. 36 | 37 | If you find that the NetDaemon apps that you have deployed to your Home Assistant system are running in Development 38 | mode (because, for example, the `[Focus]` attribute is being enforced in production) then check if the `DOTNET_ENVIRONMENT` or `ASPNETCORE_ENVIRONMENT` variables are being set. 39 | 40 | :::tip 41 | 42 | A cause of this can be if you've deployed the `Properties/launchSettings.json` file to Home Assistant: 43 | this file should not be deployed into production. 44 | 45 | ::: 46 | -------------------------------------------------------------------------------- /docs/user/app_model/advanced_config.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: app_model_advanced_config 3 | title: Advanced configuration 4 | --- 5 | 6 | You can use complex data types for configuration in YAML instancing. Make sure you are not using immutable data types like strings or read-only collections without making them nullable. For collections we recommend using `IList` that are instanced with `List` if you do not want to be nullable. 7 | 8 | ### Advanced configuration 9 | 10 | Examples of configurations: 11 | 12 | | YAML type | .NET type | 13 | | --------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | 14 | | *Scalar*
AString: hello world
AnInt: 10
ABool: true |
string? AString \{ get; set; \}
int? AnInt \{get;set;\}
bool? ABool \{ get; set; \} | 15 | | *Sequences*
SimpleList:
- Hello
- World | IList<string> SimpleList \{ get; set; \}
Sequences can also be IEnumerable<type>, but must then be nullable | 16 | 17 | #### Example of complex data types support 18 | 19 | This example shows an example of how to use complex configuration options. 20 | 21 | ```yaml 22 | 23 | ComplexConfig: # This should have the same name as class 24 | AString: hello world 25 | AnInt: 10 26 | ABool: true 27 | AStringList: 28 | - this 29 | - is 30 | - cool! 31 | Devices: 32 | - name: tv 33 | commands: 34 | - name: command1 35 | data: some code 36 | - name: command2 37 | data: some code2"; 38 | ``` 39 | 40 | ```csharp 41 | // The app using the config 42 | [NetDaemonApp] 43 | public class ComplexConfigApp 44 | { 45 | 46 | public ComplexConfigApp(IAppConfig config) 47 | { 48 | ComplexConfig complexConfig = config.Value; 49 | 50 | // Use config 51 | foreach(var device in complexConfig.Devices) 52 | { 53 | // Do something useful or fun 54 | } 55 | } 56 | } 57 | 58 | public class ComplexConfig 59 | { 60 | public string? AString { get; set; } 61 | public int? AnInt { get; set; } 62 | public bool? ABool { get; set; } 63 | public IList AStringList { get; set; } 64 | public IEnumerable? Devices { get; set; } 65 | } 66 | 67 | public class Device 68 | { 69 | public string? name { get; set; } 70 | public IEnumerable? commands { get; set; } 71 | } 72 | public class Command 73 | { 74 | public string? name { get; set; } 75 | public string? data { get; set; } 76 | } 77 | 78 | ``` 79 | 80 | ### Troubleshooting 81 | 82 | #### Issue 83 | 84 | My config properties are all null. 85 | 86 | #### Check 87 | 88 | Ensure your YAML file is being copied to the output folder. 89 | 90 | #### Fix 91 | 92 | Ensure the following is in your .csproj file to do this automatically for all YAML files: 93 | 94 | ```xml 95 | 96 | 97 | Always 98 | Always 99 | 100 | 101 | ``` 102 | 103 | Alternatively, set Copy to Output Directory to Copy Always. 104 | 105 | ![image](https://user-images.githubusercontent.com/6813309/201219449-495d0015-a08a-4651-9db0-e445ea4e6e53.png) 106 | -------------------------------------------------------------------------------- /docs/user/app_model/app_model.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: app_model 3 | title: AppModel API 4 | --- 5 | 6 | The application model is the information and services that are handled by the NetDaemon runtime related to the application instance. Instancing an app is the first step to starting your automations in NetDaemon. 7 | 8 | ## Instancing applications 9 | 10 | To instance an app you just have to use the `[NetDaemonApp]` attribute on any class and the NetDaemon runtime will instance it and run it. 11 | -------------------------------------------------------------------------------- /docs/user/app_model/custom_config.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: app_model_custom_config 3 | title: Custom config 4 | --- 5 | This page describes how to create and configure a custom configuration provider. If you are happy with the standard config providers described in the [Instance applications](/user/app_model/instancing_apps.md) page then you don't need to read this page. 6 | 7 | ### IOptions and ini Files 8 | 9 | The default Microsoft implementation of config comes via the `Microsoft.Extensions.Options.IOptions` interface that can be configured at application startup and then injected into subsequent classes. 10 | 11 | If you would prefer to use ini files for your config instead of YAML files you can use a [ini file config provider](https://docs.microsoft.com/dotnet/api/microsoft.extensions.configuration.ini.iniconfigurationprovider) (or any other IConfigurationProvider). 12 | 13 | ### How the default config is configured 14 | 15 | Within `program.cs` the `HostBuilder` startup instructs the framework to use the default NetDaemon configuration defined inside the core NetDaemon app: 16 | 17 | ```csharp 18 | try 19 | { 20 | await Host.CreateDefaultBuilder(args) 21 | .UseDefaultNetDaemonLogging() 22 | .UseNetDaemonAppSettings()// <----- 23 | .UseNetDaemon() 24 | .Build() 25 | .RunAsync(); 26 | /// ... 27 | } 28 | ``` 29 | 30 | This configuration builder uses the default [config source](https://docs.microsoft.com/dotnet/api/microsoft.extensions.hosting.host.createdefaultbuilder#microsoft-extensions-hosting-host-createdefaultbuilder(system-string())) as well as a YAML file source custom to NetDaemon, as described in the [Instance applications](user/app_model/instancing_apps.md) page. 31 | 32 | To modify the config behavior we can create our own config provider within `program.cs`. 33 | 34 | :::note 35 | 36 | An in-depth description of host builders is beyond the scope of this article, but you can find more information on the [Microsoft Learn](https://docs.microsoft.com/aspnet/core/fundamentals/host/generic-host)) 37 | 38 | ::: 39 | 40 | ### Step 1 - Change program.cs to add the ini file config provider 41 | 42 | Go to `program.cs` and remove or comment out the call to `UseNetDaemonAppSettings()` and add the configuration provider and the NetDaemon config objects so that it now looks like this: 43 | 44 | ```csharp 45 | try 46 | { 47 | await Host.CreateDefaultBuilder(args) 48 | .UseDefaultNetDaemonLogging() 49 | //.UseNetDaemonAppSettings()// <----- 50 | .ConfigureAppConfiguration((context, config) => config.AddIniFile("appsettings.ini", true, true))// <----- 51 | .ConfigureServices((context, services) =>// <----- 52 | services.ConfigureNetDaemonServices(context.Configuration)// <----- 53 | )// <----- 54 | .UseNetDaemon() 55 | .Build() 56 | .RunAsync(); 57 | /// ... 58 | } 59 | ``` 60 | 61 | Note that there may already be a `ConfigureServices` call in your `program.cs`. If one exists simply add this call inside the configure services (keeping what you have as well). 62 | 63 | ### Step 2 - Create a appsettings.ini file 64 | 65 | You now need to create a ini file. In the previous step we set it up as `appsettings.ini`, so we should make a new file named `appsettings.ini` next to your `program.cs`. In this file you can put any custom config you want and it will be available in the app through dependency injection with IOptions. 66 | 67 | Here is an example `appsettings.ini`: 68 | 69 | ```ini 70 | MyConfigValue=My Super Awesome value 71 | ``` 72 | 73 | ### Step 3 - Include appsettings.ini in build output 74 | 75 | In your `.csproj` file add the following to tell dotnet build to copy the file from the project into the output directory so that it exists at runtime. 76 | 77 | ```xml 78 | 79 | 80 | Always 81 | 82 | 83 | ``` 84 | 85 | ### Step 4 - Create our config class 86 | 87 | Just like with the the original YAML config we need to make a class that will be used for us to access our values. This class can be placed anywhere as long as it is accessible from your program.cs file. 88 | 89 | ```csharp 90 | public class ConfigExample 91 | { 92 | public string MyConfigValue { get; set; } 93 | } 94 | ``` 95 | 96 | ### Step 5 - Register your config class 97 | 98 | Now that we created the class we will use to access the configuration we need to tell the app to register it. Back in our `program.cs` file we will add a line to `ConfigureServices`: 99 | 100 | ```csharp 101 | try 102 | { 103 | await Host.CreateDefaultBuilder(args) 104 | .UseDefaultNetDaemonLogging() 105 | //.UseNetDaemonAppSettings() 106 | .ConfigureAppConfiguration((context, config) => config.AddIniFile("appsettings.ini", true, true)) 107 | .ConfigureServices((context, services) => 108 | services.ConfigureNetDaemonServices(context.Configuration) 109 | .Configure(context.Configuration)// <----- 110 | ) 111 | .UseNetDaemon() 112 | .Build() 113 | .RunAsync(); 114 | /// ... 115 | } 116 | ``` 117 | 118 | ### Step 6 - Use the config 119 | 120 | Inside the constructor of any class you want dependency injected (service, app, ect.) add a parameter for `IOptions`. Now we can use our config however we please. 121 | 122 | ```csharp 123 | [NetDaemonApp] 124 | public class MyFancyApp 125 | { 126 | 127 | public MyFancyApp(IOptions config, ILogger logger) 128 | { 129 | logger.LogInformation($"My config value is {config.MyConfigValue}"); 130 | } 131 | } 132 | ``` 133 | 134 | `MyFancyApp` simply reads the config we set up and writes it to the configured logger(s). When run it would log "My config value is My Super Awesome Value" 135 | 136 | ### Additional options / other config providers 137 | 138 | You are free to add your own custom ConfigProviders or any system that implements the `Microsoft.Extensions.Configuration.IConfigurationProvider` interface. 139 | -------------------------------------------------------------------------------- /docs/user/app_model/custom_logging.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: app_model_custom_logging 3 | title: Custom logging 4 | --- 5 | This page describes how to create and configure a custom logger. If you are happy with the standard logging described in the [Instance applications](/user/app_model/instancing_apps.md) page then this information probably won't be of interest to you. 6 | 7 | ### ILogger and Serilog 8 | 9 | The default Microsoft implementation of logging comes via the `Microsoft.Extensions.Logging.ILogger` interface that can be configured at application startup and then injected into subsequent classes. 10 | 11 | If you are familiar with .NET logging then you may want to go ahead and configure the standard Microsoft logging provider into the application, or you can use [Serilog](https://serilog.net/), which is what we provide in the template application. 12 | 13 | ### How the default logger is configured 14 | 15 | Within `program.cs` the `HostBuilder` startup instructs the framework to use the default NetDaemon logging configuration defined inside the core NetDaemon app: 16 | 17 | ```csharp 18 | try 19 | { 20 | await Host.CreateDefaultBuilder(args) 21 | .UseDefaultNetDaemonLogging() // <----- 22 | .UseNetDaemon() 23 | .Build() 24 | .RunAsync(); 25 | /// ... 26 | } 27 | ``` 28 | 29 | This configuration builder creates a Console Sink logger with the minimum logging level defined in `appsettings.json`, as described in the [Instance applications](user/app_model/instancing_apps.md) page. 30 | 31 | To modify the logging behavior we can create our own logging configuration and replace the default one within `program.cs`. 32 | 33 | :::note 34 | 35 | An in-depth description of host builders is beyond the scope of this article, but you can find more information on [Microsoft Learn](https://learn.microsoft.com/aspnet/core/fundamentals/host/generic-host). 36 | 37 | ::: 38 | 39 | ### Step 1 - create a new logging configuration class 40 | 41 | Create a new class file and place it within your app folder (at the `daemonapp` root, alongside `program.cs` is a good place). In this sample we're calling it `CustomLoggingProvider`: 42 | 43 | ```csharp 44 | using Microsoft.Extensions.Configuration; 45 | using Microsoft.Extensions.Hosting; 46 | using Serilog; 47 | 48 | namespace HomeAssistantGenerated.Logging; 49 | 50 | public static class CustomLoggingProvider 51 | { 52 | /// 53 | /// Adds standard Serilog logging configuration, from appsettings, as per: 54 | /// https://github.com/datalust/dotnet6-serilog-example 55 | /// 56 | /// 57 | public static IHostBuilder UseCustomLogging(this IHostBuilder builder) 58 | { 59 | var configuration = new ConfigurationBuilder() 60 | .AddJsonFile("appsettings.json") 61 | .Build(); 62 | 63 | var logger = new LoggerConfiguration() 64 | .ReadFrom.Configuration(configuration) 65 | .CreateLogger(); 66 | 67 | return builder.UseSerilog(logger); 68 | } 69 | } 70 | ``` 71 | 72 | Here we're stating that its entire behavior will be found in the `appsettings.json` file, which is described in more detail in step 3. 73 | 74 | ### Step 2 - change program.cs to call the new configuration 75 | 76 | Go back to `program.cs` and remove or comment out the call to `UseDefaultNetDaemonLogging()` so that it now looks like this: 77 | 78 | ```csharp 79 | try 80 | { 81 | Environment.CurrentDirectory = AppDomain.CurrentDomain.BaseDirectory; // <-- Add if not present 82 | 83 | await Host.CreateDefaultBuilder(args) 84 | //.UseDefaultNetDaemonLogging() // <-- Remove 85 | .UseCustomLogging() // <-- Add 86 | .UseNetDaemon() 87 | .Build() 88 | .RunAsync(); 89 | //... 90 | ``` 91 | 92 | Note that there may be a line in the example above that is not in your `program.cs`: 93 | 94 | ```csharp 95 | Environment.CurrentDirectory = AppDomain.CurrentDomain.BaseDirectory; 96 | ``` 97 | 98 | If you plan to log to files then it is strongly recommended that you ensure this line is present _before_ you configure logging. This ensures that the log files are written relative the application's launch folder. 99 | 100 | ### Step 3 - Update appsettings.json 101 | 102 | You now need to modify the logging configuration in `appsettings.json`. The first step is to rename the existing `"Logging"` high-level object to `"Serilog"`. Note that we decided to retain the `Serilog` name to keep the configuration consistent with the official documentation. 103 | 104 | Here is the original `appsettings.json`: 105 | 106 | ```json 107 | { 108 | "Logging": { 109 | "LogLevel": { 110 | "Default": "Debug", 111 | "Microsoft": "Warning" 112 | }, 113 | "ConsoleThemeType": "Ansi" 114 | }, 115 | "HomeAssistant": { 116 | "Host": "localhost", 117 | "Port": 8123, 118 | "Ssl": false, 119 | "Token": "enter_token_here" 120 | }, 121 | "NetDaemon": { 122 | "ApplicationConfigurationFolder": "./apps" 123 | } 124 | } 125 | ``` 126 | 127 | You need to remove the top `"Logging"` section and replace it with a new `"Serilog"` section. Please refer to the original [Serilog documentation](https://github.com/serilog/serilog/wiki/Configuration-Basics) for more detail, but here's an example configuration to get started with: 128 | 129 | ```json 130 | { 131 | "Serilog": { 132 | "MinimumLevel": { 133 | "Default": "Warning", 134 | "Override": { 135 | "System": "Information", 136 | "Microsoft": "Information", 137 | "System.Net.Http.HttpClient": "Warning", 138 | "daemonapp.apps.ScottHome": "Verbose" 139 | } 140 | }, 141 | "WriteTo": [ 142 | { 143 | "Name": "File", 144 | "Args": { 145 | "path": "logs/log-.txt", 146 | "rollingInterval": "Day" 147 | } 148 | }, 149 | { 150 | "Name": "Console", 151 | "Args": { 152 | "theme": "Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme::Code, Serilog.Sinks.Console", 153 | "outputTemplate": "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} {NewLine}{Exception}" 154 | } 155 | } 156 | ] 157 | }, 158 | "HomeAssistant": { // Rest of your config starts here... 159 | ``` 160 | 161 | In order, this configuration performs the following: 162 | 163 | * Sets the default log level to `Warning` 164 | * Sets four over-ridden namespaces, which will have their own minimum level: 165 | * Any scoped logger for a namespace beginning `System.` or `Microsoft.` will default to `Information` 166 | * The `HttpClient` will default to `Warning` (hint: set this to `Verbose` to see HTTP interactions with the HA server, although be careful that this will also log your authentication key!) 167 | * My own apps (namespace = `daemonapp.apps.ScottHome`) will default to the most detailed logging levels - replace this with the namespace for your apps 168 | * Two "sinks" are configured, via the `WriteTo` array: 169 | * A file log will create a new log file based on the date, and roll this over each new day 170 | * A console log will write to both the HA and dev console logs (note that this is mandatory if you want to see your NetDaemon logs inside Home Assistant) 171 | 172 | You should have at least one log "sink" configured, and Serilog offers [many options](https://github.com/serilog/serilog/wiki/Provided-Sinks) including writing to remote logging services. 173 | 174 | The example has a write-to-console sink and a file-sink, but feel free to remove the file-sink if you don't need it. 175 | 176 | ### Additional options / other loggers 177 | 178 | For more advanced configuration please see the [Serilog documentation](https://github.com/serilog/serilog/wiki/Getting-Started). You are also free to configure your own logger such as Microsoft's default implementation, or any system that implements the `Microsoft.Extensions.Logging.ILogger` interface. 179 | -------------------------------------------------------------------------------- /docs/user/app_model/instancing_apps.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: app_model_instancing 3 | title: Instance applications 4 | --- 5 | Instancing applications can be done in several ways. Here are the ones supported by NetDaemon. 6 | 7 | ### Instancing an application using an attribute 8 | 9 | Instance any class with the `[NetDaemonApp]` attribute: 10 | 11 | ```csharp 12 | [NetDaemonApp] 13 | public class MyApp 14 | { 15 | 16 | } 17 | ``` 18 | 19 | If you need async initialization to be called after the app is instanced you can use the interface `IAsyncInitializable`. 20 | 21 | ```csharp 22 | [NetDaemonApp] 23 | public class MyAsyncInitializableApp : IAsyncInitializable 24 | { 25 | // async 26 | public Task InitializeAsync() 27 | { 28 | // do some async initialization here 29 | } 30 | } 31 | ``` 32 | 33 | ### Instancing an application configuration using yaml 34 | 35 | You can use a YAML configuration file to provide configuration details that are injected into the constructor using the `IAppConfig<>` interface. 36 | 37 | This is a sample YAML config file, C# config class, and app class with config injected into the constructor: 38 | 39 | ```yaml 40 | LightConfig: # This is the fully qualified name of the config class 41 | TheLight: light.kitchenlightwindow # this is example of custom configuration 42 | ``` 43 | 44 | ```c# 45 | public class LightConfig 46 | { 47 | // This property will be automapped with ´the_light´ config 48 | // Using autogenerated entity classes in HassModel 49 | public LightEntity? TheLight { get; set; } 50 | } 51 | 52 | public class LightManager 53 | { 54 | /// 55 | /// Initialize, is automatically run by the daemon 56 | /// 57 | public LightManager(IAppConfig config) 58 | { 59 | // Turn on the light when loading 60 | config.Value.TheLight?.TurnOn(); 61 | } 62 | } 63 | ``` 64 | 65 | ### Logging 66 | 67 | NetDaemon can inject a logger into your application instance when you specify a _scoped_ `ILogger` parameter in your application's constructor. 68 | 69 | For example, if your application is called `FrontDoorLocker` then specifying a parameter of type `ILogger` into the app's constructor will ensure that an appropriate logger is injected. In this example the `FrontDoorLocker` constructor stores the logger into a class field so that it can be used by subsequent methods: 70 | 71 | ```csharp 72 | 73 | private readonly ILogger _logger; 74 | 75 | public FrontDoorLocker(IHaContext ha, ILogger logger) 76 | { 77 | _ha = ha; 78 | _logger = logger; 79 | 80 | _logger.LogInformation($"{nameof(FrontDoorLocker)} started"); 81 | // ... // 82 | } 83 | ``` 84 | 85 | #### Log levels in brief 86 | 87 | The logging subsystem ([Serilog](https://serilog.net/)) defines several levels of log events. From low to high these are `Verbose`, `Debug`, `Information`, `Warning`, `Error` and `Fatal`. You can set the minimum level that you wish to log which means that only events at that level or higher will be logged. 88 | 89 | NetDaemon defaults to the `Debug` level, but you can override this in your `appsettings.json` file: 90 | 91 | ```json 92 | { 93 | "Logging": { 94 | "MinimumLevel": "Debug" 95 | }, 96 | ... 97 | } 98 | ``` 99 | 100 | Within your application you will use one of the `_logger` methods to write a message at the desired level. 101 | 102 | :::note 103 | 104 | `ILogger` is a Microsoft-defined interface and there is (oddly!) not an exact match of `ILogger` methods to Serilog levels. 105 | 106 | ::: 107 | 108 | ```csharp 109 | // See code sample above for setting up the logger in your constructor 110 | 111 | _logger.LogTrace("This is at Verbose level"); 112 | _logger.LogDebug("This is at Debug level"); 113 | _logger.LogInformation("This is at Information level"); 114 | _logger.LogWarning("This is at Warning level"); 115 | _logger.LogError("This is at Error level"); 116 | _logger.LogCritical("This is at Fatal level"); 117 | 118 | ``` 119 | 120 | #### Viewing log messages 121 | 122 | Within the development environment you can view log messages inside the console view (or the container console view if you're debugging in containers). 123 | 124 | When your application is deployed to your Home Assistant production environment then you can view the logs in Home Assistant by clicking on: **Settings -> Add-ons -> NetDaemon VX.X (.NET X) -> Log** 125 | 126 | :::note 127 | 128 | Logs are cleared each time you restart the NetDaemon add-on (or the Home Assistant server). 129 | 130 | ::: 131 | 132 | #### More advanced logging 133 | 134 | One of the goals of the NetDaemon template app is that it should work out of the box with minimal configuration. For that reason, the template ships with a "Default NetDaemon Logging Configurator", which supports all of the functionality described on this page, but nothing more. 135 | 136 | If you are familiar with Serilog or other .NET loggers and want to use more advanced techniques such as writing to log files or to remote log servers then please read the [Custom logging](user/app_model/custom_logging.md) page, which shows how to create and configure a custom logger. 137 | -------------------------------------------------------------------------------- /docs/user/app_model/moving_from_v3.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: app_model_moving_from_v3 3 | title: Moving from NetDaemon version 3 4 | --- 5 | 6 | This documentation how to upgrade and move to current .NET 7 based NetDaemon runtime v3.x to .NET 9 based NetDaemon runtime 5. There are some breaking issues but should be quite straight forward upgrade your applications and runtime environment. 7 | 8 | ### Nuget packages 9 | 10 | We have removed the `JoySoftware` from the nuget identity and all package now starting with `NetDaemon`. There are no company behind NetDaemon and the naming could lead to that conclusion. 11 | 12 | We also moved the source deploy features to it's own nuget package. This will make the size of app builds a lot smaller for users that uses the recommended compiled deploy option. 13 | 14 | # Development environment 15 | 16 | :::info 17 | 18 | We try to always keep NetDaemon up-to-date with the latest versions of .NET. Since we are a small team of maintainers we can not maintain more than one version at a time. This is why new features never is added to old versions. We recommend users to update their apps when ever a new major version of NetDaemon. 19 | 20 | ::: 21 | 22 | The following changes have to be executed in order to develop your apps for NetDaemon 5 and .NET 9. 23 | 24 | ## 1. Update nuget packages 25 | 26 | In this release we decided to change naming of NetDaemon nuget packages. The names are basically the same but without the `JoySoftware`. The reason is we needed to make sure we got the ownership of the NetDaemon id on our nuget packages for security reasons. 27 | 28 | 1. In all .NET project files `*.csproj`, rename all nuget references that starts with `JoySoftware.NetDaemon` to `NetDaemon`. For users that uses the source deploy option you will have to add the`NetDaemon.AppModel.SourceDeployedApps` nuget package! 29 | 2. Change the target framework to 9.0 30 | 3. Update Microsoft .NET references in your C# files to the corresponding .NET 9 versions 31 | 32 | ### 2. Update docker containers and add-ons 33 | 34 | The docker container, change the name from `netdaemon3` to `netdaemon5`. All other settings are the same. 35 | 36 | If you are using the addon write down the current settings in your current 3.x based addon and install the version 5 version and update settings as needed. In version 4 the default path had changed from `config/netdaemon3` to `config/netdaemon5`. 37 | 38 | ### 3. Update the code generator 39 | 40 | As some of the namespaces have changed you should update your locally installed version of the [Code generator](/user/hass_model/hass_model_codegen.md) by running the following command: 41 | 42 | ```bash 43 | dotnet tool install -g NetDaemon.HassModel.CodeGen 44 | ``` 45 | 46 | :::warning 47 | 48 | You may need to uninstall the old code generator by using the `dotnet tool uninstall -g JoySoftware.NetDaemon.HassModel.CodeGen` command. Since we have changed 49 | the namespace there will be conflicting tool command names. 50 | 51 | ::: 52 | 53 | ...and then re-run the generator to create an updated version of `HomeAssistantGenerated.cs` 54 | 55 | ### 4. Update the CLI tool 56 | 57 | As some of the namespaces have changed you should first uninstall the cli tool by `dotnet new uninstall JoySoftware.NetDaemon.Templates.Project` 58 | and then install the new too by 59 | 60 | ```bash 61 | dotnet new --install NetDaemon.Templates.Project 62 | 63 | ``` 64 | 65 | 66 | -------------------------------------------------------------------------------- /docs/user/extensions/scheduling.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: extensions_scheduling 3 | title: Scheduling 4 | --- 5 | 6 | Many applications require some sort of scheduled or periodic tasks to be executed, such as switching off lights at a specific time. 7 | 8 | There are several ways to do this in .NET that may or may not be suitable for use in NetDaemon apps. NetDaemon has an extension that supports scheduling based on the standard `System.Reactive.Concurrency.IScheduler` interface. It is recommended to use this extension for scheduling tasks as it will automatically cancel them when an app is stopped, and it adds support for Cron expressions. 9 | 10 | ### Setup 11 | 12 | If you use the NetDaemon project template you will already have the scheduler available and you can skip this setup. 13 | 14 | To set up the scheduler manually you should: 15 | 16 | * Include the NetDaemon.Extensions.Scheduling nuget package 17 | 18 | ```ps 19 | Install-Package NetDaemon.Extensions.Scheduling 20 | ``` 21 | 22 | * Make sure to call `.AddNetDaemonScheduler()` on the `ServiceCollection` in the hosts Program.cs 23 | 24 | ```csharp 25 | .ConfigureServices((_, services) => 26 | services 27 | .AddAppsFromAssembly(Assembly.GetExecutingAssembly()) 28 | .AddNetDaemonStateManager() 29 | .AddNetDaemonScheduler() 30 | ``` 31 | 32 | ### Injecting the scheduler 33 | 34 | You can get an instance of the `IScheduler` interface bye simply injecting it into your apps constructor: 35 | 36 | ```csharp 37 | using System.Reactive.Concurrency.Scheduler; 38 | 39 | [NetDaemonApp] 40 | class MyApp(IScheduler scheduler) 41 | { } 42 | ``` 43 | 44 | The scheduler you will receive is based on the `System.Reactive.Concurrency.DefaultScheduler.Instance`. This scheduler is however wrapped with additional behavior that will make sure that any tasks you schedule on this scheduler will be cancelled when the application is stopped. It will also log exceptions from scheduled tasks to the configured `ILogger`. 45 | 46 | :::note 47 | 48 | __IScheduler.Now always returns UTC. Use `Now.LocalDateTime` to get the current local time.__ 49 | 50 | ::: 51 | 52 | ### Using the Scheduler 53 | 54 | The `System.Reactive.Concurrency.Scheduler` namespace provides several extension methods for `IScheduler` that allow you to schedule tasks at a specific time, after a specific `TimeSpan`, or periodically. You can use these framework provided methods directly on the scheduler you received via the constructor and they will be scheduled using the cancellation and logging behavior. 55 | 56 | For example, to turn off the lights in my living in 2 minutes from now I can use: 57 | 58 | ```csharp 59 | scheduler.Schedule(TimeSpan.FromMinutes(2), () => entities.Light.Living.TurnOff()); 60 | ``` 61 | 62 | Scheduling periodic jobs using the default scheduling methods have some limitations: 63 | 64 | * Setting up more advanced schedules can be complicated. 65 | * Daylight savings time can cause problems when running jobs at a specific time of a day. 66 | * When a job throws an exception it will be logged, but subsequent jobs of this schedule will not be executed. 67 | 68 | As a more convenient way to schedule periodic tasks, the NetDaemon Scheduling Extensions provides an extension method `ScheduleCron()`. It can be used like this: 69 | 70 | ```csharp 71 | public CronSchedulingApp(IHaContext ha, IScheduler scheduler) 72 | { 73 | var entities = new Entities(ha); 74 | scheduler.ScheduleCron("45 23 * * *", () => entities.Light.Living.TurnOff()); 75 | } 76 | ``` 77 | 78 | Which will turn off the living room light at 23:45 each day. 79 | 80 | The first argument of this method is a [Cron expression](https://en.wikipedia.org/wiki/Cron) that describes the pattern of the schedule. These expressions can meet a large variety of scheduling demands. This Cron expression will be evaluated using the local timezone that is setup for your environment. 81 | 82 | The `ScheduleCron()` extension method uses [Cronos](https://github.com/HangfireIO/Cronos) to parse your Cron expression. See its docs for the exact specification. 83 | 84 | ## Unit testing scheduling apps 85 | 86 | Timing in some apps can be pretty complicated. It can therefore be useful to create unit tests for your schedules. For this purpose there is a special version of the `IScheduler` interface implemented by `Microsoft.Reactive.Testing.TestScheduler` in the `Microsoft.Reactive.Testing` nuget package. 87 | 88 | This scheduler allows you to do time traveling in unit tests: 89 | 90 | ```csharp 91 | [Fact] 92 | public void TestCron() 93 | { 94 | var haContextMoq = new Mock(); 95 | 96 | var testScheduler = new Microsoft.Reactive.Testing.TestScheduler(); 97 | testScheduler.AdvanceTo(new DateTime(2020, 2, 1, 23, 44, 0).ToUniversalTime().Ticks); 98 | 99 | var app = new CronSchedulingApp(haContextMoq.Object, testScheduler); 100 | 101 | haContextMoq.VerifyNoOtherCalls(); 102 | testScheduler.AdvanceBy(TimeSpan.FromMinutes(1).Ticks); 103 | 104 | haContextMoq.Verify(h => h.CallService("light", "turn_off", 105 | It.Is(s => s.EntityIds!.Single() == "light.living"), 106 | It.IsAny())); 107 | } 108 | ``` 109 | 110 | The `TestScheduler` is initially setup to `23:44` local time, just before the lights are supposed to be turned off. The test code then creates an instance of our `CronSchedulingApp` and passes it a mock of the `IHaContext` and the `TestScheduler`. Initially no calls should be made on the `IHaContext`. But when the `TestScheduler` advances 1 minute we expect the app to call `CallService` on the `IHaContext` to turn off the lights in the living room. 111 | -------------------------------------------------------------------------------- /docs/user/extensions/tts.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: extensions_tts 3 | title: Text to speech 4 | --- 5 | 6 | NetDaemon provides an extension that adds more features for text to speech. If you do not need these extra features you can use the code generator to get a nice API to use text to speech functionality. 7 | 8 | The main feature of this extension is that it queues all messages in order and waits for each message to be completed before sending next one to provided media player. 9 | 10 | The following example shows how to use the TTS service in your own apps: 11 | 12 | ```csharp 13 | 14 | [NetDaemonApp] 15 | public class MyTtsApp 16 | { 17 | public MyTtsApp(ITextToSpeechService tts) 18 | { 19 | // This uses the google service you may use some other like google cloud version, google_cloud_say 20 | tts.Speak("media_player.kitchen", "Hello this is first queued message", "google_say"); 21 | tts.Speak("media_player.kitchen", "Hello this is second queued message", "google_say"); 22 | } 23 | } 24 | 25 | ``` 26 | 27 | If you use source deployment with the NetDaemon add-on or Docker image this dependency is already included. If you are missing it add: 28 | 29 | ```cmd 30 | dotnet add package NetDaemon.Extensions.Tts 31 | ``` 32 | 33 | And add the tts service to the host in `program.cs`: 34 | 35 | ```csharp 36 | await Host.CreateDefaultBuilder(args) 37 | .UseNetDaemonTextToSpeech() 38 | ``` 39 | -------------------------------------------------------------------------------- /docs/user/hass_model/hass_model.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: hass_model 3 | title: HassModel API 4 | --- 5 | 6 | # HassModel API 7 | 8 | NetDaemon's HassModel API is the way to handle automations in Home Assistant. The generated code is very useful since entity classes are generated for all the domains and extension methods for all services. 9 | 10 | -------------------------------------------------------------------------------- /docs/user/hass_model/hass_model_codegen.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: hass_model_codegen 3 | title: Using the Code generator 4 | --- 5 | 6 | NetDaemon has a generator that creates code based on the entities and services in Home Assistant. This makes it possible to navigate all entities and services using intellisense. 7 | 8 | ## Use as local tool 9 | 10 | If you are using the template project you can install the code generator as a local tool. The advantage is that you will have a update script that keeps your tool version and NetDaemon versions in sync. If you encounter an error adding the local tool, please make a fresh project template using the latest version of the cli tool and move your apps to that version. You do not have to install the tool in this case. 11 | 12 | Run the tool using: 13 | 14 | ```cmd 15 | dotnet tool run nd-codegen 16 | ``` 17 | 18 | You can use following command to keep it up to date with the latest version: 19 | 20 | ```cmd 21 | dotnet tool update NetDaemon.HassModel.CodeGen 22 | ``` 23 | 24 | Or use the convenience script `update_all_dependencies.ps1` to update all tools and nuget packages (only available in later versions of the template). 25 | 26 | ## Use as global tool 27 | 28 | The code generator is installed as a [.NET global tool](https://docs.microsoft.com/dotnet/core/tools/global-tools) via the following command: 29 | 30 | ```cmd 31 | dotnet tool install -g NetDaemon.HassModel.CodeGen 32 | ``` 33 | 34 | After it is installed as a global tool it can be run with the command: 35 | 36 | ```cmd 37 | nd-codegen 38 | ``` 39 | 40 | You can use the following command to keep it up to date with the latest version: 41 | 42 | ```cmd 43 | dotnet tool update -g NetDaemon.HassModel.CodeGen 44 | ``` 45 | 46 | **Make sure the version of the codegen tool and your nuget packages `NetDaemon.*` have the same version.** 47 | 48 | You can check the installed version of the codegen tool with: 49 | 50 | ```cmd 51 | dotnet tool list -g 52 | ``` 53 | 54 | When the tool is run from the folder of your NetDaemon project that contains an `appsettings.json` or `appsettings.development.json` file it will automatically use the connection settings from that config file to connect to Home Assistant. It will output a single file `HomeAssistantGenerated.cs` that contains all the generated code. 55 | 56 | The generated code contains the following: 57 | 58 | * A class `Entities` that provides properties to navigate to all existing entities in Home Assistant via intellisense 59 | * A record derived from `Entity` for each domain that has entities in Home Assistant 60 | * A record with all the attributes of all entities in a specific domain, so they can be accessed via typed properties 61 | * A class `Services` that provides access to all services in Home Assistant via their domain 62 | * Extension methods for each service that takes an entity as a target 63 | 64 | The code generator also generates an extension method `AddHomeAssistantGenerated()` that injects different classes to be used in dependency injection for your convenience. Add it to the `program.cs` like this: 65 | 66 | ```csharp 67 | .ConfigureServices((_, services) => 68 | services 69 | .AddAppsFromAssembly(Assembly.GetExecutingAssembly()) 70 | .AddNetDaemonStateManager() 71 | .AddNetDaemonScheduler() 72 | .AddHomeAssistantGenerated() 73 | ``` 74 | 75 | ## Command line arguments 76 | 77 | The settings for the code generator can also be set from the command line. This will override the settings from a configuration file if it is present. 78 | 79 | | switch | | 80 | |--- |--| 81 | | -host | Host of the NetDaemon instance 82 | | -port | Port of the NetDaemon instance 83 | | -ssl | `true` to connect over ssl; otherwise `false` 84 | | -token | A long lived Home Assistant token 85 | | -o | The output file to generate 86 | | -ns | The namespace to use for the generated code 87 | 88 | -------------------------------------------------------------------------------- /docs/user/hass_model/hass_model_events.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: hass_model_events 3 | title: Home Assistant events 4 | --- 5 | 6 | Home Assistant has a generic event mechanism. Using HassModel you can respond to events as well as trigger events in Home Assistant. 7 | 8 | ## Respond to Events 9 | 10 | All events in Home Assistant are made available to your apps as an `IObservable` via the `Events` property of the `IHaContext` interface. 11 | 12 | ```csharp 13 | ha.Events.Where(e => e.EventType == "zha_event").Subscribe(e => { /*...*/ }); 14 | ``` 15 | 16 | The event has a `public JsonElement? DataElement` property that contains the payload of the event. 17 | 18 | To make it easy to listen to a specific type of event and handle the payload via a typed class you can use the `Filter` extension method. It will filter events on the event_type and deserialize the payload into a specified type ready for use: 19 | 20 | ```csharp 21 | ha.Events.Filter("zha_event") 22 | .Where(e => e.Data?.DeviceIeee == id && e.Data.Command == "shake") 23 | .Subscribe(HandleCubeShaked); 24 | 25 | 26 | record ZhaEventData 27 | { 28 | [JsonPropertyName("device_ieee")] public string? DeviceIeee { get; init; } 29 | [JsonPropertyName("unique_id")] public string? UniqueId { get; init; } 30 | [JsonPropertyName("endpoint_id")] public int? EndpointId { get; init; } 31 | [JsonPropertyName("cluster_id")] public int? ClusterId { get; init; } 32 | [JsonPropertyName("command")] public string? Command { get; init; } 33 | [JsonPropertyName("args")] public JsonElement? Args { get; init; } 34 | } 35 | ``` 36 | 37 | ## Triggering Events 38 | 39 | Sending an event is also very easy. `IHaContext` has a `SendEvent` method with just two arguments, the event_type and the data to send 40 | 41 | ```csharp 42 | ha.SendEvent("custom_event", new { mode="Standby", duration = 200 }); 43 | ``` 44 | 45 | The payload wil be JSON serialized using `System.Text.Json` it can be an anonymous type or any other type that serializes to the desired JSON payload. 46 | -------------------------------------------------------------------------------- /docs/user/hass_model/hass_model_generated_entities.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: hass_model_generated_entities 3 | title: Using generated entities 4 | --- 5 | 6 | After generating code from Home Assistant with `nd-codegen` you can use a strongly typed API to work with entities in Home Assistant. The Entities can be injected into your app like this: 7 | 8 | 9 | ```csharp 10 | [NetDaemonApp] 11 | class MyApp 12 | { 13 | public MyApp(Entities entities) 14 | { 15 | LightEntity livingRoomLight = entities.Light.LivingRoomLight; 16 | // .. 17 | } 18 | } 19 | ``` 20 | 21 | 22 | `Entities` is a generated class that provides access to all your entities in Home Assistant. The object returned by `Entities.Light.LivingRoomLight` is of type `LightEntity` which is also generated. This specific instance represents the `light.living_room_light` entity in Home Assistant. 23 | 24 | 25 | It is also possible to directly inject eg the LightEntities class 26 | 27 | 28 | ```csharp 29 | [NetDaemonApp] 30 | class MyApp 31 | { 32 | public MyApp(LightEntities lightEntities) 33 | { 34 | LightEntity livingRoomLight = lightEntities.LivingRoomLight; 35 | // .. 36 | } 37 | } 38 | ``` 39 | 40 | 41 | :::note 42 | 43 | To inject the generated types, make sure you call `AddHomeAssistantGenerated()` in your startup code. 44 | 45 | ::: 46 | 47 | 48 | Alternatively you can create an instance of the generated `Entities` class via its constructor that takes the IHaContext. 49 | ```csharp 50 | _myEntities = new Entities(haContext); 51 | 52 | // .. 53 | 54 | LightEntity livingRoomLight = _myEntities.Light.LivingRoomLight; 55 | ``` 56 | 57 | 58 | The LightEntity class provides strongly typed access to the state and attributes of the entity like: 59 | 60 | ```csharp 61 | if (livingRoomLight.State == "on" && livingRoomLight.Attributes.Brightness > 100) // ... 62 | ``` 63 | 64 | :::tip 65 | 66 | You can also check for "on" or "off" states using the Extension Methods `IsOn()` and `IsOff()` 67 | 68 | ::: 69 | 70 | It also allows you to respond to state change events of the entity via the StateAllChanges and StateChanges methods. 71 | 72 | ```csharp 73 | kitchenLight.StateAllChanges() 74 | .Where(s => s.Old?.Attributes?.Brightness < 128 75 | && s.New?.Attributes?.Brightness >= 128) 76 | .Subscribe(e => HandleBrightnessUp()); 77 | ``` 78 | 79 | The code generator will create a class for each domain in Home Assistant that has entities. As an example, for the `climate` domain there is a class `ClimateEntity` and for the `sensor` domain there is a class `SensorEntity`. These classes all derive from the built-in base class `Entity`. 80 | -------------------------------------------------------------------------------- /docs/user/hass_model/hass_model_generated_service.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: hass_model_generated_service 3 | title: Using generated services 4 | --- 5 | 6 | After generating code from Home Assistant with `nd-codegen` you can use a strongly typed API to call services in Home Assistant. This can be done via the generated entity classes or by using the generated `Services` class. 7 | 8 | ## Call services via typed entity classes 9 | 10 | The code generator generates a class for each domain that has entities in Home Assistant. For each service in Home Assistant that accepts an entity of that domain as a target, there is also a generated extension method for that `Entity` class. This allows services to be called like this: 11 | 12 | ```csharp 13 | 14 | [NetDaemonApp] 15 | class MyApp 16 | { 17 | public MyApp(Entities entities) 18 | { 19 | entities.Light.LivingRoomLight.TurnOn(brightness: 100, colorTemp: 80); 20 | 21 | entities.Climate.AtticHeater.SetHvacMode(hvacMode:"off") 22 | } 23 | } 24 | 25 | ``` 26 | 27 | :::note 28 | 29 | To inject the generated types, make sure you call `AddHomeAssistantGenerated()` in your startup code. 30 | 31 | ::: 32 | 33 | 34 | 35 | The service methods have parameters that corresponds to the fields that are required by the service. Optional fields are provided as optional parameters so they can be skipped by using named parameters for the ones that are used. 36 | 37 | ## Using the generated Services class 38 | 39 | The generated code also contains a class `Services`. This class provides access to all the services in Home Assistant. The `Services` class needs an `IHaContext` in its constructor to access Home Assistant. From there, the services are available as methods grouped by their domain. 40 | 41 | Example: 42 | 43 | ```csharp 44 | [NetDaemonApp] 45 | class MyApp 46 | { 47 | public MyApp(Services services) 48 | { 49 | services.PersistentNotification.Create( 50 | message: "NetDaemon Application started", 51 | title: "Awesome!"); 52 | 53 | services.Climate.SetTemperature( 54 | ServiceTarget.FromEntity("climate.bathroom"), 55 | temperature: 20.5); 56 | 57 | services.Light.TurnOn(ServiceTarget.FromEntities("light.livingroom_light", "light.diner"), 58 | brightness: 100, 59 | colorTemp: 80); 60 | } 61 | } 62 | ``` 63 | 64 | Just as with the extension methods on the `Entity` classes, these methods have parameters that correspond to the fields of the service in Home Assistant. 65 | 66 | If the service requires a target, the generated method will also contain a parameter of type 'ServiceTarget' that can be used to pass one or more entities, areas or devices. 67 | 68 | 69 | Alternatively you can create an instance of the generated `Services` class via its constructor that takes the IHaContext. 70 | ```csharp 71 | var services = new Services(haContext); 72 | 73 | services.PersistentNotification.Create( 74 | message: "NetDaemon Application started", 75 | title: "Awesome!"); 76 | ``` 77 | 78 | 79 | ## Use the generated services with return values 80 | 81 | Some Home Assistant services can return values. The generated services have a async method that returns a `JsonElement` that you can parse or serialize 82 | to a class. This means that you will have to use NetDaemon's [async features](user/advanced/async_features.md) to call the services and wait for the result. 83 | 84 | :::info 85 | Tip! Use the logger to log the returned `JsonElement` to see the structure of the returned data and create a class/record that matches the structure. 86 | ::: 87 | 88 | ### Process to use the generated services with return values: 89 | 90 | 1. First call the service log the returned `JsonElement` to see the structure of the returned data. For example, if you call the service `weather.get_forecasts` 91 | in this case the it returns a structure that looks like: 92 | ```json 93 | { 94 | "weather.smhi_hemma": { 95 | "forecast": [ 96 | { 97 | "datetime": "2024-06-09T16:00:00", 98 | "condition": "rainy", 99 | "wind_bearing": 54, 100 | "cloud_coverage": 100, 101 | "temperature": 11.0, 102 | "templow": 11.0, 103 | "pressure": 989.0, 104 | "wind_gust_speed": 12.96, 105 | "wind_speed": 6.12, 106 | "precipitation": 0.4, 107 | "humidity": 87 108 | } 109 | ] 110 | } 111 | } 112 | ``` 113 | 2. Make a class/record that matches the structure of the returned data. 114 | 3. Deserialize with the proper casing options to match the json structure. 115 | 116 | A complete example of how to use the generated services with return values: 117 | 118 | ```csharp 119 | using System.Threading; 120 | using System.Threading.Tasks; 121 | using System.Text.Json; 122 | 123 | public record WeaterForecastItem 124 | { 125 | public DateTime Datetime { get; init; } 126 | public string Condition { get; init; } 127 | public int WindBearing { get; init; } 128 | public int CloudCoverage { get; init; } 129 | public float Temperature { get; init; } 130 | public float Templow { get; init; } 131 | public float Pressure { get; init; } 132 | public float WindGustSpeed { get; init; } 133 | public float WindSpeed { get; init; } 134 | public float Precipitation { get; init; } 135 | public int Humidity { get; init; } 136 | } 137 | 138 | [NetDaemonApp] 139 | public class UseServiceWithReturnValueApp( 140 | Entities entities, 141 | Services services, 142 | ILogger logger) : IAsyncInitializable 143 | { 144 | // Snake-case json options 145 | private readonly JsonSerializerOptions _jsonOptions = new JsonSerializerOptions 146 | { 147 | PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower 148 | }; 149 | 150 | public async Task InitializeAsync(CancellationToken cancellationToken) 151 | { 152 | // Get the forecasts from SmhiHemma entity using the code generated entities 153 | var forecastResult = await entities.Weather.SmhiHemma.GetForecastsAsync(type: "hourly"); 154 | // Log the forecast to look for the structure of the result 155 | // Remove when you created the correct class/record for deserialization 156 | logger.LogInformation("Forecast: {forecast}", forecastResult); 157 | 158 | // Find the forecast property and deserialize it to a list of WeaterForecastItem 159 | var forecasts = forecastResult?.GetProperty(entities.Weather.SmhiHemma.EntityId) 160 | .GetProperty("forecast"); 161 | if (forecasts is not null) 162 | { 163 | var forecastItems = forecasts.Value.Deserialize>(_jsonOptions); 164 | 165 | logger.LogInformation("Forecast items: {forecastItems}", forecastItems); 166 | } 167 | // You can return multiple values from a service call 168 | var multipleEntitiesForecastResult = await services.Weather 169 | .GetForecastsAsync(ServiceTarget.FromEntities("weather.smhi_hemma", "weather.test"), "hourly"); 170 | logger.LogInformation("Muliple returns {multiple}", multipleEntitiesForecastResult); 171 | // Do something useful with the result of multiple forecasts from multiple entities 172 | } 173 | } 174 | ``` 175 | -------------------------------------------------------------------------------- /docs/user/hass_model/hass_model_integration_servicecallback.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: hass_model_integration_servicecallback 3 | title: Callback services 4 | --- 5 | 6 | From NetDaemon you can register services in Home Assistant and get a callback whenever this service is triggered. For example by an automation or script that runs inside Home Assistant 7 | 8 | This feature requires you to install the [NetDaemon integration](user/started/integration.md) and use the NetDaemon.HassModel.Integration nuget package: 9 | 10 | ```shell 11 | PM> Install-Package NetDaemon.HassModel.Integration 12 | ``` 13 | 14 | Then all you have to do is call the `RegisterServiceCallBack()` extension method on the `IHaContext` and pass a name for the service and a callback. 15 | 16 | ```csharp 17 | ha.RegisterServiceCallBack("callback_demo", 18 | e => _logger.LogInformation("Service called action: {Action} value: {value}", e?.action, e?.value)); 19 | 20 | // ... 21 | record ServiceData(string? action, int value); 22 | ``` 23 | 24 | The `T` parameter specifies the type that is used to deserialize the arguments from the service call. It will be passed to the callback delegate with the deserialized data. 25 | 26 | To test the service, run an app with this code and then go to the [developer tools in your Home Assistant](https://my.home-assistant.io/redirect/developer_call_service/?service=netdaemon.callback_demo), and post the folowing YAML: 27 | 28 | ```yaml 29 | service: netdaemon.callback_demo 30 | data: 31 | action: heat 32 | value: 22 33 | ``` 34 | 35 | This will then trigger the callback inside the NetDaemon app. 36 | -------------------------------------------------------------------------------- /docs/user/hass_model/hass_model_notifications.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: hass_model_notifications 3 | title: Home Assistant notifications 4 | --- 5 | 6 | Notifications and actionable notifications are an integral part of Home Assistant. Sending Notifications to a particular receiver can be done via the Notify service. 7 | 8 | ```csharp 9 | var services = new Services(ha); 10 | services.Notify.MobileApp("Test message"); 11 | ``` 12 | 13 | Sending an actionable event involves sending a bit more data and responding to a specific event. 14 | The example below is written with anonymous objects for brevity, but can be made type-safe using custom types. 15 | 16 | ```csharp 17 | var services = new Services(ha); 18 | ha.Events.Where(e => e.EventType == "mobile_app_notification_action") 19 | .Subscribe(e => 20 | { 21 | logger.LogInformation("Received event" + e.DataElement.ToString()); 22 | }); 23 | 24 | services.Notify.MobileApp( 25 | "hallo", data: 26 | new { 27 | actions = new [] 28 | { 29 | new { 30 | action = "ALARM", 31 | title = "Sound Alarm", 32 | } 33 | } 34 | } 35 | ); 36 | ``` 37 | 38 | It is advisable to use a more unique action value as per the [Home Assistant](https://companion.home-assistant.io/docs/notifications/actionable-notifications/) documentation. 39 | -------------------------------------------------------------------------------- /docs/user/hass_model/hass_model_registry.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: hass_model_registry 3 | title: Registry API 4 | --- 5 | 6 | The Registry API let you handle entities, floors, areas, labels and devices in Home Assistant. 7 | 8 | 9 | :::note 10 | 11 | This is an beta feature and you need version 24.20.0 or later of NetDaemon to use it. 12 | It will be released evenutally in a stable version but expext the API to change before release. 13 | The docs will be updated with more examples and details when the API is stable. 14 | ::: 15 | 16 | To use the new API you need to inject the new `IHaRegistry` interface in your app. 17 | 18 | See the example app below for how to use the new API: 19 | ```csharp 20 | [NetDaemonApp] 21 | public sealed class RegistryApp 22 | { 23 | public RegistryApp(IHaRegistry haRegistry) 24 | { 25 | var floor = haRegistry.GetFloor("upstairs"); 26 | var upstairsAreas = floor.Areas; 27 | var upstairsBooleans = upstairsAreas 28 | .SelectMany(n => n.Entities 29 | .Where(x => x.EntityId.StartsWith("input_boolean."))); 30 | 31 | upstairsBooleans.ToList().ForEach(x => x.CallService("toggle")); 32 | } 33 | } 34 | ``` 35 | 36 | You can also use the newly added properties in the ServiceTarget class to target floor, areas, labels 37 | and devices in your service calls. We will eventually make a nicer API for this but for now 38 | you can use the ServiceTarget class directly. 39 | 40 | ```csharp 41 | [NetDaemonApp] 42 | public sealed class TestServiceTargetApp 43 | { 44 | public TestServiceTargetApp(IHaContext ha) 45 | ha.CallService("input_boolean", "toggle", new ServiceTarget{ FloorIds = ["upstairs"] }); 46 | } 47 | } 48 | ``` 49 | -------------------------------------------------------------------------------- /docs/user/hass_model/hass_model_service_with_returnvalue.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: hass_model_service_with_returnvalue 3 | title: Service with return value 4 | --- 5 | 6 | For newer versions of Home Assistant, you can now return values some services. This is useful when you want to get a response from a service call. 7 | 8 | Please check the Home Assistant documentation for more information on how to use this feature and what services support it. 9 | 10 | The following example list events from the calendar component in Home Assistant: 11 | 12 | ```csharp 13 | using System.Text.Json; 14 | using Microsoft.Extensions.Logging; 15 | using NetDaemon.AppModel; 16 | using NetDaemon.HassModel; 17 | using NetDaemon.HassModel.Entities; 18 | 19 | namespace Apps; 20 | 21 | [NetDaemonApp] 22 | public sealed class ServiceApp : IAsyncInitializable 23 | { 24 | private readonly IHaContext _ha; 25 | private readonly ILogger _logger; 26 | private readonly JsonSerializerOptions _jsonOptions = new() { PropertyNameCaseInsensitive = true }; 27 | 28 | public ServiceApp(IHaContext ha, ILogger logger) 29 | { 30 | _ha = ha; 31 | _logger = logger; 32 | } 33 | 34 | public async Task InitializeAsync(CancellationToken cancellationToken) 35 | { 36 | var result = await _ha.CallServiceWithResponseAsync("calendar", "list_events", ServiceTarget.FromEntity("calendar.cal"), 37 | data: new { start_date_time = "2023-07-21 00:00:00", end_date_time = "2023-07-22 03:00:00"}); 38 | 39 | if (result is not null) 40 | { 41 | var events = result.Value.Deserialize(_jsonOptions); 42 | if (events is null) 43 | _logger.LogWarning("No results!"); 44 | else 45 | _logger.LogInformation("Events: {Events}", events); 46 | } 47 | } 48 | } 49 | public record CalendarEvents(IEnumerable Events); 50 | public record CalendarEvent(DateTime Start, DateTime End, string Summary, string Description); 51 | ``` 52 | -------------------------------------------------------------------------------- /docs/user/hass_model/hass_model_subscribe_to_triggers.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: hass_model_subscribe_to_triggers 3 | title: Subscribe to triggers 4 | --- 5 | 6 | NetDaemon HassModel provides an API to allow subscription to triggers. 7 | 8 | :::warning 9 | Please consider trigger support beta and API:s can change upon release. 10 | ::: 11 | 12 | :::note 13 | 14 | We have no means to make code generation objects to use with this feature. This means you will have to call the subscription with anonymous objects in the same structure that is [documented here](https://www.home-assistant.io/docs/automation/trigger/). You will also need to log the websocket raw message for response message in order to parse results correctly with your custom record class. Luckily we have a way to get the raw event message from Home Assistant with an convenient interface. 15 | 16 | We might add record classes to use for responses at a later time. 17 | ::: 18 | 19 | ## How to subscribe to a trigger 20 | 21 | You can inject the `ITriggerManager` interface to use the trigger subscription feature. The `RegisterTrigger` returns a `IObservable` that you can deserialize to a POCO. You can also use `RegisterTrigger` to have the result return a `IObservable` that deserialize to POCO for you. 22 | 23 | ```csharp 24 | public TheApp(ITriggerManager triggerManager) 25 | { 26 | var triggerObservable = triggerManager.RegisterTrigger( 27 | new 28 | { 29 | platform = "state", 30 | entity_id = new string[] { "media_player.vardagsrum" }, 31 | from = new string[] { "idle", "playing" }, 32 | to = "off" 33 | }); 34 | triggerObservable.Subscribe(n => 35 | // Do some magic here 36 | ); 37 | } 38 | 39 | ``` 40 | 41 | You always need to provide the platform attribute. The id can be set to a unique text or if not set, Home assistant will set one. Everything else is dependent on the platform/type of trigger. 42 | 43 | Below is an example using a record class representing the response for the trigger subscription. This example also sets an id for the trigger. 44 | 45 | ```csharp 46 | // Record representing result from subscription 47 | record TimePatternResult(string id, string alias, string platform, DateTimeOffset now, string description); 48 | // ... ctr of your app 49 | public TimeApp(ITriggerManager triggerManager) 50 | { 51 | var timePatternTriggerObservable = triggerManager.RegisterTrigger(new 52 | { 53 | platform = "time_pattern", 54 | id = "some id", 55 | seconds = "/1" 56 | }); 57 | 58 | var disposedSubscription = timePatternTriggerObservable.Subscribe(n => 59 | _logger.LogCritical("Got trigger message: {Message}", n) 60 | ); 61 | } 62 | ``` 63 | 64 | ## How to check the trigger result data structure 65 | 66 | The easiest way is to inject a NetDaemon client interface to log all messages that are received after you use the `RegisterTrigger` function. 67 | 68 | ```csharp 69 | public CheckResultFormatApp(ILogger logger, ITriggerManager triggerManager, IHomeAssistantRunner runner) 70 | { 71 | _logger = logger; 72 | 73 | // This interface in the client project will allow subscribe to all raw Home Assistant websocket messages. 74 | var haMessages = (IHomeAssistantHassMessages)runner.CurrentConnection!; 75 | 76 | haMessages.OnHassMessage.Subscribe(m => logger.LogInformation("{Message}", m)); 77 | 78 | // Do your trigger magic... and the previous subscription will log every message from this trigger. 79 | var triggerObservable = triggerManager.RegisterTrigger( 80 | new 81 | { 82 | platform = "state", 83 | entity_id = new string[] { "media_player.vardagsrum" }, 84 | from = new string[] { "idle", "playing" }, 85 | to = "off" 86 | }); 87 | ``` 88 | 89 | Now it should log every message received. Check for the `TriggerElement` property. It has the response. It is recommended to use a single app for this or use `[Focus]` flag so you do not get flooded with messages. 90 | 91 | From the results it should be fairly easy to construct a record that allows for deserializing the result and then use the `RegisterTrigger` method instead. 92 | 93 | ## Example of using a MQTT trigger button 94 | 95 | If you are using Zigbee2Mqtt newer versions it will only support using triggers to get actions on buttons. Below is an example 96 | of a extension method that will subscribe to button events using MQTT triggers: 97 | 98 | ```csharp 99 | public static class TriggerManagerExtensions 100 | { 101 | public static IObservable RegisterMqttButtonhActionTrigger(this ITriggerManager triggerManager, object mqttDeviceName) 102 | { 103 | var triggerTopic = triggerManager.RegisterTrigger(new 104 | { 105 | platform = "mqtt", 106 | topic = $"zigbee2mqtt/{mqttDeviceName}/action" 107 | }); 108 | return triggerTopic.Select(e => e.GetProperty("payload").GetString()); 109 | } 110 | } 111 | ``` 112 | :::note 113 | 114 | Add proper error handling as you see fit. 115 | 116 | ::: 117 | 118 | Usage: 119 | ```csharp 120 | [NetDaemonApp] 121 | public class TestDeviceTriggerApp 122 | { 123 | public TestDeviceTriggerApp(ITriggerManager triggerManager, ILogger logger) 124 | { 125 | var trigger = triggerManager.RegisterMqttButtonActionTrigger("my_button_mqtt_device_name"); 126 | trigger.Subscribe(e => 127 | { 128 | logger.LogInformation("Received event {event}", e); 129 | }); 130 | } 131 | } 132 | ``` 133 | -------------------------------------------------------------------------------- /docs/user/hass_model/hass_model_working_with_entities.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: hass_model_working_with_entities 3 | title: Working with entities 4 | --- 5 | 6 | # Working with Entities 7 | 8 | Entities are the core data structures of Home Assistant. In NetDaemon an entity is represented by the `Entity` class. 9 | 10 | The `Entity` class provides access to the entity's current state, attributes, and state change events. It also provides a way to call services that take the entity as a target. 11 | 12 | ## Accessing Entities 13 | 14 | The basic way to create an instance of an `Entity` is by by using the `Entity()` method on `IHaContext`: 15 | 16 | ```csharp 17 | var atticLight = haContext.Entity("light.attic"); 18 | ``` 19 | 20 | The `Entity` class also has a public constructor that takes a `IHaContext` and the entityId. Using the `Entity()` method on `IHaContext` however, will create an instance of a specific generated Entity Type based on the provided entityId if available. 21 | 22 | :::note 23 | 24 | This does not create a new entity in Home Assistant. It creates an instance of the NetDaemon `Entity` class to interact with an existing Home Assistant entity. 25 | 26 | ::: 27 | 28 | 29 | When using the code generator all discovered entities in your Home Assistant will be generated as strongly typed properties that can be accessed like this: 30 | 31 | ```csharp 32 | var myEntities = new Entities(haContext); 33 | 34 | var atticLight = myEntities.Light.Attic; 35 | ``` 36 | 37 | ## State 38 | 39 | Entities in Home Assistant have a state property and a set of attributes. NetDaemon makes these available via the `Entity.State` and `Entity.Attributes` properties. 40 | 41 | :::note 42 | 43 | Accessing these properties does *NOT* involve calling Home Assistant to retrieve the current state. Instead NetDaemon subscribes to Home Assistant's state_changed event when it starts and keeps an internal cache of the states of all entities in memory. 44 | 45 | ::: 46 | 47 | The current state value of an entity can be retrieved as a string like this: 48 | 49 | ```csharp 50 | var state = atticLight.State; 51 | ``` 52 | 53 | Some entities, like many sensors, have numeric state values. You can create a `NumericEntity` and use its `State` property of type `double` to avoid manual parsing: 54 | 55 | ```csharp 56 | var temperatureSensor = new NumericEntity(haContext, "sensor.bathroom_temperature"); 57 | 58 | if (temperatureSensor.State > 20.5) // ... 59 | ``` 60 | 61 | ## Attributes 62 | 63 | The `Entity` class also provides access to the entity's attributes. Each entity or set of entities can have its own set of attributes with their own data types. The generic `Entity` class provides a convenient way to access them in a type safe manner. 64 | 65 | For instance a zone entity has the following attributes: 66 | 67 | ```yaml 68 | latitude: 40.657722371758105 69 | longitude: -74.10552978515626 70 | radius: 12280.977677710147 71 | passive: false 72 | editable: true 73 | friendly_name: New York 74 | icon: mdi:map-marker 75 | ``` 76 | 77 | To use these attributes with type safety you can provide a class that can JSON-deserialize the attributes into properties: 78 | 79 | ```csharp 80 | record ZoneAttributes 81 | { 82 | public double latitude { get; init; } 83 | public double longitude { get; init; } 84 | public double radius { get; init; } 85 | public string? friendly_name { get; init; } 86 | public string? icon { get; init; } 87 | } 88 | ``` 89 | 90 | It can then be used like this: 91 | 92 | ```csharp 93 | var zone = new Entity(haContext, "zone.new_york"); 94 | 95 | double latitude = zone.Attributes.latitude; 96 | double longitude = zone.Attributes.longitude; 97 | ``` 98 | 99 | When using the code generator, a class derived from `Entity` will be generated for each domain in Home Assistant. Each generated class contains all of the unique entity attributes for that domain. 100 | 101 | ## State Changes 102 | 103 | The API uses a fluent syntax to handle state changes. This is based on .Net's Reactive Extensions. 104 | 105 | :::note 106 | 107 | If you are unfamiliar with reactive programming it is strongly recommended to read about the basics using one of these resources: 108 | 109 | - [Lee Campbell - Introduction to Rx](http://introtorx.com/) 110 | - [ReactiveX - Introduction](https://reactivex.io/intro.html) 111 | - [Microsoft Learn - Reactive Extensions](https://learn.microsoft.com/previous-versions/dotnet/reactive-extensions/hh242985(v=vs.103)) 112 | - [Reactive Extensions on GitHub - A Brief Intro](https://github.com/dotnet/reactive#a-brief-intro) 113 | 114 | ::: 115 | 116 | Let´s start with a basic example. If a motion sensor's state turns to `"on"`, turn on a light. 117 | 118 | ```csharp 119 | myEntities.binary_sensor.my_motion_sensor 120 | .StateChanges() 121 | .Where(e.New?.State == "on") 122 | .Subscribe(s => myEntities.light.Attic.TurnOn()); 123 | ``` 124 | 125 | So what is going on here? Let's step through the lines: 126 | 127 | 1. `myEntities.binary_sensor.my_motion_sensor` selects the entity you want track changes from. 128 | 2. `.StateChanges()` creates a `IObservable` from `System.Reactive`, where `T` is a NetDaemon `StateChange`. 129 | 3. `.Where(e.New?.State == "on")` uses a predicate to filter state changes to only react those where the `Entity`'s new `State` equals `"on"`. 130 | 4. `.Subscribe(s => myEntities.light.Attic.TurnOn());` subscribes to the filtered `IObservable` to react to state changes with the provided lambda. In this case it calls the generated service to turn on a light using Home Assistant. 131 | 132 | :::tip 133 | 134 | To get all changes including attributes, use `StateAllChanges` instead of `StateChanges`. 135 | 136 | ::: 137 | 138 | :::tip 139 | 140 | To get the initial state of an entity upon subscribing, use `StateAllChangesWithCurrent` or `StateChangesWithCurrent`. 141 | 142 | ::: 143 | 144 | If the old state is relevant then the filter could be modified like this: 145 | 146 | ```csharp 147 | myEntities.BinarySensor.MyMotionSensor 148 | .StateChanges() 149 | .Where(e => e.New?.State == "on" && e.Old?.State == "off") 150 | .Subscribe(s => myEntities.Light.Attic.TurnOn()); 151 | ``` 152 | 153 | You can use any combination of state and attributes, or even external methods in the lambda expressions. For example: 154 | 155 | ``` 156 | When the Sun's new elevation is below 3.0, 157 | and it is not rising, 158 | and the old elevation is above 3.0, 159 | then we should turn on a light. 160 | ``` 161 | 162 | This uses `StateAllChanges` so we can observe attribute changes: 163 | 164 | ```csharp 165 | myEntities.Sun.Sun 166 | .StateAllChanges() 167 | .Where(e => 168 | e.New?.Attributes.Elevation <= 3.0 && 169 | e.New?.Attributes.Rising == false && 170 | e.Old?.Attributes.Elevation > 3.0) 171 | .Subscribe(s => 172 | { 173 | // Do some logic and stuff here. 174 | myEntities.light.attic.TurnOn(); 175 | }); 176 | ``` 177 | 178 | You may want to wait for an entity to remain in a state for a specific amount of time before reacting. An example would be waiting for a motion sensor to be in the `"off"` state for 5 minutes. The `WhenStateIsFor` extension method can be used here: 179 | 180 | ```csharp 181 | myEntities.BinarySensor.MyMotionSensor 182 | .StateChanges() 183 | .WhenStateIsFor(s => s?.State == "off", TimeSpan.FromMinutes(5), scheduler) 184 | .Subscribe(s => myEntities.Light.Attic.TurnOff()); 185 | ``` 186 | 187 | `WhenStateIsFor` takes a predicate as its first argument, and a `TimeSpan` as the second argument. The predicate declares the entity state we want to react to, and the `TimeSpan` declares how long we want the entity to be in that state before reacting. 188 | 189 | As a thrid argument we want to pass an instance that implements `IScheduler`. No need to call any methods on the instance before passing it as an argument. Passing a scheudler ensures that the events stops when the app is stopped. It's also useful to inject a TestScheduler for unit tests. 190 | To learn more about Scheduler please see [scheduling](user/extensions/scheduling.md). 191 | 192 | In this case we want to wait for a motion sensor's state to change to `"off"`, remain in that state for 5 minutes, and then turn off a light. Each time the state changes the wait resets. 193 | 194 | :::note 195 | 196 | The predicate used by NetDaemon's `WhenStateIsFor` receives an `EntityState`, rather than a `StateChange` as used by `System.Reactive`'s `Where` used previously. 197 | 198 | ::: 199 | 200 | ## Call Services on an Entity 201 | 202 | Many services in Home Assistant take an entity ID as their target. When you have an instance of an entity you can use it directly to call such a service: 203 | 204 | ```csharp 205 | light1.CallService("turn_on", new { brightness = 100 } ); 206 | ``` 207 | 208 | The second argument is the data that will be JSON-serialized and passed with the service call. This can be any type as long as it has properties that match the arguments of the service. That includes strongly typed objects or anonymous ones. 209 | 210 | An `IEnumerable` of entities that have the same domain can also be the source of the service call: 211 | 212 | ```csharp 213 | var downstairsLights = new [] { 214 | haContext.Entity("light.living"), 215 | haContext.Entity("light.kitchen") }; 216 | 217 | downstairsLights.CallService("turn_on", new { brightness = 100 } ); 218 | ``` 219 | 220 | This will make a single call to Home Assistant's `light.turn_on` service on both entities. 221 | 222 | The code generator also creates `Entity` extension methods for all services that take a specific type of entity as their target. `LightEntity` for example has the `TurnOn` extension method: 223 | 224 | ```csharp 225 | myEntities.Light.Attic.TurnOn(transition: 50, brightnessPct: 50); 226 | ``` 227 | 228 | Extension methods are also created for the corresponding `IEnumerable`, such as for `IEnumerable`: 229 | 230 | ```csharp 231 | var downstairsLights = new [] { 232 | myEntities.Light.Living, 233 | myEntities.Light.Kitchen }; 234 | 235 | downstairsLights.TurnOn(transition: 50, brightnessPct: 50); 236 | ``` 237 | 238 | `CallService` and the generated extension methods are non-blocking fire-and-forget calls. They send a message to Home Assistant using a websocket connection. Your application code does not need to await this async call, NetDaemon does that internally and will log any exceptions to the configured logger. 239 | 240 | :::info 241 | 242 | Home Assistant service calls do not provide return values, they only change state and throw exceptions. NetDaemon does not currently provide an asynchronous service call method to react to these errors (such as `HomeAssistantError` and `ValueError`). 243 | 244 | It is usually the job of integrations to handle errors, retry attempts, and set the `available` property of entities. This is a part of their [Integration Quality Scale](https://developers.home-assistant.io/docs/integration_quality_scale_index/) score. 245 | 246 | ::: 247 | 248 | ## Subscribing safely 249 | 250 | NetDaemon is built on top of the Reactive extensions library, which is a powerful tool for handling asynchronous events. 251 | 252 | One of the perks using Reactive extensions are that unhandled exceptions will prohibit further subscriptions. This is handled by NetDaemon 253 | by providing a extension method `SubscribeSafe` that will catch any exceptions and log them to the log as default behavior. You can also provide a custom handler for the exceptio 254 | 255 | ```csharp 256 | myEntities.BinarySensor.MyMotionSensor 257 | .StateChanges() 258 | .SubscribeSafe(s => myEntities.Light.Attic.TurnOn()); 259 | ``` 260 | 261 | -------------------------------------------------------------------------------- /docs/user/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: index 3 | title: NetDaemon 4 | --- 5 | 6 | NetDaemon provides the capability to write home automations for Home Assistant in C#. Current version of the NetDaemon runtime is 5 and use .NET 9 and C# 13. 7 | 8 | ## About NetDaemon 9 | 10 | Runtime version 5 has NuGet version >= `24.47.0` and uses the Docker image `netdaemon/netdaemon5`. 11 | No additional features will be added to version 4. 12 | 13 | ## Migrating from version 3 14 | We are no longer supporting version 3 of the runtime. 15 | 16 | If you are a current user of version 3.x of NetDaemon runtime please upgrade, see [migrating from version 3 runtime](user/app_model/moving_from_v3.md) 17 | 18 | The daemon core runtime is distributed as a Home Assistant add-on or as Docker container. You can as an alternative deploy your own instance using the template project. Please see [the getting started](user/started/installation.md) documentation for setup. 19 | 20 | :::note 21 | 22 | You need to restart the add-on every time you change a file. C# needs to compile the changes. 23 | 24 | ::: 25 | 26 | ## Issues 27 | 28 | If you have issues or suggestions of improvements, please [add an issue](https://github.com/net-daemon/netdaemon/issues) 29 | 30 | ## Discuss the NetDaemon 31 | 32 | Please [join the Discord server](https://discord.gg/K3xwfcX) to get support or if you want to contribute and help others. 33 | 34 | If you prefer discussing using [github discussions](https://github.com/net-daemon/netdaemon/discussions) your are welcome to post questions and feature requests there. Bugs are reported as issues. 35 | -------------------------------------------------------------------------------- /docs/user/started/assets/add_on_configuration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/net-daemon/docs/08c5bff872a9cb20cdc2ccb7d9ffcb2f80a71731/docs/user/started/assets/add_on_configuration.png -------------------------------------------------------------------------------- /docs/user/started/assets/netdaemon_add_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/net-daemon/docs/08c5bff872a9cb20cdc2ccb7d9ffcb2f80a71731/docs/user/started/assets/netdaemon_add_on.png -------------------------------------------------------------------------------- /docs/user/started/development_source_deploy.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: development_source_deploy 3 | title: Get started with NetDaemon app development 4 | --- 5 | 6 | ### Deploy source files and configurations 7 | 8 | With this option you can deploy the actual source code and NetDaemon will 9 | compile them and use it dynamically. 10 | 11 | This option offers: 12 | 13 | - Easy access and editing your apps with integrated editor like VS Code server. 14 | - Limited DI support. 15 | - Does **not** support custom nuget packages. 16 | 17 | Deployment is done by copying source code and config files under your apps folder 18 | (.cs and .yaml ) to the destination. 19 | The provided debug project should never be copied. 20 | 21 | **We always recommend to deploy your binaries as described in the main getting started guide** 22 | 23 | #### Use the source code deployment template 24 | 25 | ```bash 26 | dotnet new --install NetDaemon.Templates.Project 27 | mkdir NetDaemonApps 28 | cd NetDaemonApps 29 | dotnet new nd-src-project 30 | ``` 31 | 32 | ### Source files and configurations 33 | 34 | **Do not copy project files. Only copy the contents under the `apps` folder!** 35 | Copy the content from the `apps` folder to to `/config/netdaemon5` if you are using 36 | add-on (note that you might have changed the destination in add-on config), 37 | or the destination folder you chose in the other hosting options. 38 | **Do not copy project files. Only copy the contents under the `apps` folder!** 39 | 40 | See [installation docs](user\started\installation.md) for how to configure 41 | different hosting options. 42 | -------------------------------------------------------------------------------- /docs/user/started/development_tools.md: -------------------------------------------------------------------------------- 1 | 2 | # Configure your development tool 3 | 4 | We support most popular options. We strongly recommend using containers to run 5 | and debug your apps but local compilation and debugging is also supported. 6 | We provide a Docker file in the project template to use as a start and a 7 | dev container for VSCode. 8 | 9 | ## Visual Studio 10 | 11 | You should be all set, so skip to step 3. 12 | :::note 13 | 14 | To use Visual Studio's publish feature first install & configure the [Samba share Home Assistant addon](https://github.com/home-assistant/addons/blob/52bafd68185080e9b1a1d6b6c501ab96705d73f9/samba/DOCS.md), then configure your VS publish options: 15 | 16 | ![Visual studio profile settings](/img/docs/started/vs_publish_config.jpg) 17 | 18 | Now you can publish to quickly deploy files, and restart NetDaemon to run them. 19 | ::: 20 | 21 | ## Visual Studio Code 22 | 23 | Dev Containers are the preferred way to develop your apps. 24 | This also requires Docker to be installed. You can also 25 | develop and debug directly on your dev machine without Docker. 26 | 27 | 1. Install [Remote Development extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.vscode-remote-extensionpack) 28 | in VS Code if you have not already. 29 | 2. Open folder, the newly cloned template 30 | 3. Run task: `Dev Containers: Open Folder in Container...`. Wait until it fully opened 31 | 32 | ## JetBrains Rider 33 | 34 | Just as with Visual Studio it is ready to go for local compile 35 | and debugging of your apps. 36 | 37 | Rider supports debugging of containerized ASP.Net Core apps from version 2018.2. 38 | 39 | Open the netdaemon_app folder in Rider and it should be able to build 40 | the projects immediately. A default execute and debug profile will be 41 | created however these will be executed as local processes. The preferred 42 | way to develop your app is to use a container, which requires Docker to be installed. 43 | To configure a container, perform the following steps: 44 | 45 | 1. Locate `Dockerfile` in the Solution Explorer window 46 | 2. Right click and select `Debug Dockerfile` 47 | 3. This will create a new profile called "DOCKERFILE" 48 | 4. To view progress, located "Docker" in the "Services" tab and double-click it 49 | 5. Like all container apps, the first build may take a few minutes - watch the progress in the "Services / Docker" tab 50 | 51 | Ensure that the "DOCKERFILE" profile is selected in the toolbar and then `Run` and `Debug` will execute within the container. 52 | 53 | ## Studio Code Server Addon 54 | 55 | ### Setup 56 | 57 | 1. In Home Assistant go to Configurations -> Add-ons, Backups & 58 | Supervisor -> Add-on Store -> Menu -> Repositories 59 | 2. Add the repository: [https://github.com/hassio-addons/repository](https://github.com/hassio-addons/repository) 60 | 3. Install the `Studio Code Server Addon` (a0d7b954_vscode) 61 | 4. In the add-on configuration Tab add the following config: 62 | 63 | ```yaml 64 | init_commands: 65 | - >- 66 | wget 67 | https://packages.microsoft.com/config/debian/12/packages-microsoft-prod.deb 68 | -O packages-microsoft-prod.deb 69 | - dpkg -i packages-microsoft-prod.deb 70 | - rm packages-microsoft-prod.deb 71 | - apt-get update 72 | - apt-get install -y apt-transport-https 73 | - apt-get update 74 | - apt-get install -y dotnet-sdk-9.0 75 | - dotnet tool install -g NetDaemon.HassModel.CodeGen 76 | packages: [] 77 | log_level: info 78 | config_path: / 79 | ``` 80 | --> The part in the `init_commands` will install .NET SDK 9.0 only in the 81 | Studio Code Server add-on (Docker Container) 82 | 5. Now you can start the Studio Code Server add-on by going the 83 | add-on information tab and pressing `OPEN WEB UI`. 84 | 85 | HINTS: 86 | 87 | - It is recommended to install the C# Extensions (ms-dotnettools.csharp) in Studio Code 88 | Server to get Semantic Highlighting and IntelliSense. 89 | - Open only the folder where the solution/project is located to ensure that the C# 90 | Extension works properly. 91 | -------------------------------------------------------------------------------- /docs/user/started/example.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: example 3 | title: Example app 4 | --- 5 | 6 | This application shows basic capabilities of the HassModel API of NetDaemon. It has a single file `ExampleApp.cs` that contains the app logic. This example uses the code generation capability of HassModel [explained here](user/hass_model/hass_model_codegen.md). 7 | 8 | ## ExampleApp.cs 9 | 10 | ```cs 11 | using System; 12 | using System.Reactive.Linq; 13 | using NetDaemon.AppModel; 14 | using NetDaemon.HassModel; 15 | using HomeAssistantGenerated; 16 | 17 | [NetDaemonApp] 18 | public class ExampleAppHaContext 19 | { 20 | public ExampleAppHaContext(IHaContext ha) 21 | { 22 | var entities = new Entities(ha); 23 | 24 | entities.BinarySensor.OfficeMotion 25 | .StateChanges() 26 | .Where(e => e.New.IsOn()) 27 | .Subscribe(_ =>entities.Light.Office.TurnOn()); 28 | } 29 | } 30 | ``` 31 | 32 | ## The NetDaemonAppAttribute 33 | 34 | ```cs 35 | [NetDaemonApp] 36 | ``` 37 | 38 | By decorating a class with the `NetDaemonAppAttribute` it is registered as an application to be loaded by NetDaemon. 39 | 40 | NetDaemon will create a helper in Home Assistant. By default, the helper is named: 41 | 42 | | Environment | Name | 43 | | --------------- | -------------------------------------------------------------------------| 44 | | Development | `dev_netdaemon_{namespace}_appname` | 45 | | Production | `netdaemon_{namespace}_appname` | 46 | 47 | The app name will be converted to snake casing (i.e: HelloWorld -> hello_world) 48 | 49 | When you want to choose your own name you can make use of the Id parameter like: 50 | 51 | ```cs 52 | [NetDaemonApp(Id = "appname")] 53 | ``` 54 | 55 | Netdaemon will replace \{namespace\}_appname with your given Id 56 | 57 | ## The constructor 58 | 59 | ```cs 60 | public ExampleAppHaContext(IHaContext ha) 61 | ``` 62 | 63 | When the application is (re-)started a new instance of the class is created by calling its constructor. This constructor will receive constructor arguments by using the standard .NET dependency injection mechanism. In this example the constructor receives an IHaContext interface which provides basic methods for interacting with Home Assistant. 64 | 65 | The constructor can be used to do initialization of your application. **Never block the constructor!** Typically here you configure what should happen when a state changes or run a function every minute for example. If you need to do asynchronous initialization of your application this can be done by implementing `IAsyncInitializable` 66 | 67 | **Example:** 68 | 69 | ```cs 70 | var entities = new Entities(ha); 71 | 72 | entities.BinarySensor.OfficeMotion 73 | .StateChanges() 74 | .Where(e => e.New.IsOn()) 75 | .Subscribe(_ => entities.Light.Office.TurnOn()); 76 | ``` 77 | 78 | | Function | Description | 79 | | --------------- | -------------------------------------------------------------------------| 80 | | new Entities(ha) | Creates an Instance of the generated Entities class that provides strongly typed access to all your HA entities | 81 | | entities.BinarySensor.OfficeMotion | Selects the `binary_sensor.office_motion` entity from Home Assistant | 82 | | StateChanges() | Respond to changes of the state of this motion sensor 83 | | Where | FIlter which state changes we are interested in, int this case when the sensor state becomes 'on' | 84 | | Subscribe | Calls any code/action when the criteria is met | 85 | | TurnOff() | Turns off the lights in the office using another generated entity and a generated service method | 86 | 87 | ## Creating and updating Home Assistant entities 88 | 89 | If you want to create entities from NetDaemon, set their state attributes, then check out the [MQTT Entity Manager documentation](user/extensions/mqttEntityManager.md) 90 | 91 | ## Real-world example apps 92 | 93 | Please check out the apps being developed for NetDaemon. Since documentation is still lacking behind it would be best to look at real code 😊 94 | 95 | | User | Description | Version | 96 | | --- | --- | --- | 97 | | [@helto4real](https://github.com/helto4real/NetDaemon3Automations) | Tomas NetDaemon apps running in production | 4 | 98 | | [@FrankBakkerNl](https://github.com/FrankBakkerNl/NetDaemonExample) | HassModel examples by Frank the author of HassModel API | 99 | | [@Kennetjuh](https://github.com/kennetjuh/NetDeamonImpl) | Kennetjuh's NetDaemon implementation | 5 | 100 | | [@vinnie1234](https://github.com/vinnie1234/HomeAutomation-NetDaemon) | Vincent's NetDaemon implementation | 5 | 101 | | [@leosperry](https://github.com/leosperry/NetDeamonImpl) | Leonard's NetDaemon implementation | 3 | 102 | | [@pockybum522](https://github.com/PockyBum522/netdaemon-home-assistant-apps) | David's NetDaemon apps as used Daily | 4 | 103 | | [@philippch](https://github.com/PhilippCh/HomeAutomations) | Philipp's apartment automated by NetDaemon | 4 | 104 | | [@DevJasperNL](https://github.com/DevJasperNL/CodeCasa) | Jasper's Code Casa 🏡
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 including:
  • Using Blazor to create a custom Home Assistant frontend
  • Using pipelines to seperate responsibilities
| 5 | 105 | 106 | > **Do you have a NetDaemon project you'd like to share? We'd love to see it! Feel free to contribute by adding your project. Learn how here: https://github.com/net-daemon/docs** 107 | 108 | ## Third party libraries 109 | 110 | The following libraries work well together with NetDaemon. 111 | 112 | | GitHub and NuGet | Description | Contributor(s) | 113 | | --- | --- | --- | 114 | | [NetDaemon.Extensions.Observables](https://github.com/DevJasperNL/CodeCasa.Libraries/tree/main/src/NetDaemon.Extensions.Observables) ([NuGet](https://www.nuget.org/packages/DevJasper.NetDaemon.Extensions.Observables/)) | Collection of extension methods meant to enhance NetDaemon entities with stateful and boolean observables allowing for a more intuitive and robust coding experience. | [@DevJasperNL](https://github.com/DevJasperNL) 115 | | [Automation Pipelines](https://github.com/DevJasperNL/CodeCasa.Libraries/tree/main/src/AutomationPipelines) ([NuGet](https://www.nuget.org/packages/AutomationPipelines/)) | Composable, reactive, and layered logic pipelines for C# automation. | [@DevJasperNL](https://github.com/DevJasperNL) 116 | -------------------------------------------------------------------------------- /docs/user/started/get_started.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: get_started 3 | title: Getting started with NetDaemon 4 | --- 5 | 6 | # Getting started 7 | 8 | This guide walks you through setting up your development environment for 9 | building NetDaemon apps. 10 | 11 | ## Prerequisites 12 | 13 | - Dotnet 9 SDK 14 | - Development environment 15 | - A Home Assistant long lived authorization token 16 | - Git (optional) 17 | - Docker (optional) 18 | 19 | ## NetDaemon development workflow 20 | 21 | The following workflow is the recommended approach 22 | for developing and deploying NetDaemon: 23 | 24 | 1. Install the NetDaemon CLI tools to access the project template. 25 | 2. Develop, build, compile, and test your NetDaemon apps locally using your preferred development environment. 26 | 3. Publish your project. 27 | 4. Deploy your project using one of the following options: 28 | - NetDaemon Home Assistant add-on 29 | - NetDaemon pre-built Docker container 30 | - Your own Docker container 31 | 32 | ### Install the NetDaemon .NET CLI tool and creating a project 33 | 34 | First, install the NetDaemon .NET CLI tool: 35 | 36 | ```bash 37 | dotnet new --install NetDaemon.Templates.Project 38 | ``` 39 | 40 | Once installed, create a new project using the template: 41 | 42 | ```csharp 43 | mkdir NetDaemonApps // Your project folder name 44 | cd NetDaemonApps 45 | dotnet new nd-project 46 | ``` 47 | 48 | *There is an deployment option where you use the source code 49 | to deploy NetDaemon apps, we do not recommend using it but see 50 | [souce deployment docs](development_source_deploy.md) 51 | for details* 52 | 53 | We assume you are familiar with your development environment, 54 | but for additional guidance and tips, see the 55 | [development tools tips & tricks page](development_tools.md). 56 | 57 | ### Configuring NetDaemon appsettings.json 58 | 59 | NetDaemon development environment needs to be configured to 60 | connect to Home Assistant. 61 | 62 | Edit the values in the `appsettings.json`. The following settings are mandatory: 63 | 64 | - `Host`: the IP address or hostname of your Home Assistant instance 65 | - `Port`: Defaults to 8123 66 | - `Token`: Your long-lived access token 67 | 68 | Example `appsettings.json` file: 69 | 70 | ```json 71 | { 72 | "Logging": { 73 | "LogLevel": { 74 | "Default": "Debug", 75 | "Microsoft": "Warning" 76 | }, 77 | "ConsoleThemeType": "Ansi" 78 | }, 79 | "HomeAssistant": { 80 | "Host": "home_assistant_host", 81 | "Port": 8123, 82 | "Ssl": false, 83 | "Token": "my_very_secret_token_from_homeassistant" 84 | }, 85 | "NetDaemon": { 86 | "ApplicationConfigurationFolder": "./apps" 87 | }, 88 | "CodeGeneration": { 89 | "Namespace": "HomeAssistantGenerated", 90 | "OutputFile": "HomeAssistantGenerated.cs", 91 | "UseAttributeBaseClasses" : "false" 92 | } 93 | } 94 | ``` 95 | 96 | :::info 97 | We recommend creating a development-specific configuration file named 98 | `appsettings.development.json`, which is automatically excluded from Git. 99 | This prevents accidentally exposing your secret token if you use an 100 | external git repository like GitHub. Remember to remove sensitive data 101 | from `appsettings.json` before push to remote repository! 102 | ::: 103 | 104 | ## Development and debugging NetDaemon apps 105 | 106 | Once configured, you can begin developing your NetDaemon applications. 107 | The project template includes example apps to help you get started with 108 | development and testing. 109 | 110 | Run and debug your applications while monitoring log output for errors. 111 | 112 | For a more powerful and convenient way to interact with Home Assistant 113 | entities and services, we recommend using the [code generator](../hass_model/hass_model_codegen.md) 114 | code generator to generate C# classes. 115 | 116 | ## Deploy NetDaemon apps 117 | 118 | Use `dotnet publish -c Release -o [your output directory]` 119 | Then, copy all contents from `[your_output_directory]` to the appropriate 120 | location on your Home Assistant host: 121 | 122 | - If using the NetDaemon add-on: /config/netdaemon5 123 | - If using another hosting option: Copy to your chosen destination folder 124 | 125 | For more details on different deployment options, see the [Installation Documentaiton](user\started\installation.md) 126 | 127 | ## Keep your dependencies and tools up-to-date 128 | 129 | The template project includes a PowerShell script to update NetDaemon 130 | and all dependent packages to their latest versions: 131 | 132 | ```bash 133 | update_all_dependencies.ps1 134 | ``` 135 | -------------------------------------------------------------------------------- /docs/user/started/installation.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: installation 3 | title: Install NetDaemon runtime 4 | --- 5 | 6 | 7 | You can deploy your NetDaemon apps in several ways: 8 | 9 | - Using the NetDaemon Home Assistant add-on 10 | - Using the NetDaemon Docker container 11 | - Custom deployment (will not be covered here) 12 | 13 | ## Deploying NetDAemon using the Home Assistant add-on 14 | 15 | Once added to Home Assistant, 16 | select one of the V5 versions. 17 | 18 | 1. Create a folder structure under your Home Assistant host configuration 19 | directory to store NetDaemon apps: 20 | 21 | ```text 22 | /config/netdaemon5 23 | ``` 24 | 25 | 2. Add the NetDaemon repository to the Home Assistant add-on store: 26 | 27 | ```text 28 | https://github.com/net-daemon/homeassistant-addon 29 | ``` 30 | 31 | 3. Install the NetDaemon add-on from Home Assistant add-on store: 32 | 33 | import NdAddOn from "./assets/netdaemon_add_on.png"; 34 | 35 | 36 | 37 | ### Publish and copy compiled NetDaemon apps 38 | 39 | Publish the .NET project created from the CLI tool: 40 | 41 | ```bash 42 | dotnet publish -c Release -o [outputdir] 43 | ``` 44 | 45 | Copy all files and folders **within** the `[outputdir]` 46 | to `/config/netdaemon5` on your Home Assistant host. 47 | 48 | For more details how to make this step easier, see the tutorial 49 | [Publish NetDeamon apps with Powershell](user/tutorials/publish_script.md). 50 | 51 | ### Configure the NetDaemon add-on 52 | 53 | In the add-on configuration, set `Application assembly` setting 54 | to the path for your project's entry assembly (DLL), 55 | relative to `/config/netdaemon5` folder. 56 | Optionally, specify a separate path for configurations. 57 | 58 | This is an example of how the configuration should should look like, 59 | please replace the assembly name with your own assembly name: 60 | 61 | import NdAddOnConfig from "./assets/add_on_configuration.png"; 62 | 63 | 64 | 65 | The name of the assembly may be different or current dotnet version might 66 | be different for you. Ensure you're using the latest NetDaemon version and 67 | the corresponding .NET SDK. Make sure to always use the latest NetDaemon 68 | release. 69 | 70 | ## Deploy using Docker and the official NetDaemon image 71 | 72 | If you're using Home Assistant Core without add-on support, 73 | deploying NetDaemon in a Docker container is a convenient alternative. 74 | 75 | :::info 76 | Note: Always use a specific version tag instead of latest 77 | to avoid potential breaking changes. 78 | [Find the version of the latest stable version here](https://github.com/net-daemon/netdaemon/releases) 79 | ::: 80 | 81 | ### Example Docker run configuration 82 | 83 | ```bash 84 | docker run -d \ 85 | --name netdaemon5 \ 86 | --restart=always \ 87 | -e HomeAssistant__Host=192.168.1.4 \ 88 | -e HomeAssistant__Token="eyJhbGciOiJIUz..." \ 89 | -e NetDaemon__ApplicationAssembly=NetDaemonApps.dll \ 90 | -e Logging__LogLevel__Default=Information \ 91 | -e TZ=Europe/Stockholm \ 92 | -v ~/netdaemon_config:/data \ 93 | netdaemon/netdaemon5 94 | ``` 95 | 96 | ### Example using docker-compose.yaml for 97 | 98 | ```yaml 99 | services: 100 | netdaemon: 101 | image: netdaemon/netdaemon5 # use netdaemon/netdaemon5:ver 102 | # for specific version 103 | container_name: netdaemon5 104 | restart: always 105 | environment: 106 | - HomeAssistant__Host=your_ip_or_hostname # use host.docker.internal if HA in container (see section below) 107 | - HomeAssistant__Token=your_token 108 | - NetDaemon__ApplicationAssembly=NetDaemonApps.dll 109 | - Logging__LogLevel__Default=Information # use Information/Debug/Trace/Warning/Error 110 | - TZ=Etc/UTC # Set your current timezone 111 | volumes: 112 | - /config/netdaemon:/data # replace /config/netdaemon 113 | # to your local folder 114 | # extra_hosts: # Enable if HA in 115 | # - "host.docker.internal:host-gateway" # container 116 | ``` 117 | 118 | ### Environment variables 119 | 120 | The Docker container needs these environment variables to run properly. 121 | 122 | | ENV | Description | 123 | | ------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 124 | | `HomeAssistant__Host` | The host that is running Home Assistant (defaults to `localhost`) | 125 | | `HomeAssistant__Port` | The port Home Assistant is running on (default to `8123`) | 126 | | `HomeAssistant__Ssl` | Set to True if SSL is used to access Home Assistant. | 127 | | `HomeAssistant__Token` | A Long Lived Access Token(LLAT) that NetDaemon can use for the communication with Home Assistant. | 128 | | `HomeAssistant__InsecureBypassCertificateErrors` | Ignores certificate errors. Please use at own risk. | 129 | | `NetDaemon__ApplicationAssembly` | DLL name for compiled deployments (omit for source deployments) `dllname.dll` | 130 | | `Logging__LogLevel__Default` | Defaults to Information, values are (Trace, Debug, Information, Warning, Error) | 131 | | `TZ` | You will need to set container time zone to make the scheduler work properly | 132 | | `NetDaemon__ApplicationConfigurationFolder` | If you want to select another folder for your YAML configurations. Standard is `/data` | 133 | 134 | ### Volumes 135 | 136 | | Vol | Description | 137 | | ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | 138 | | /data | The volume of the NetDaemon folder should be mapped to `/data` | 139 | 140 | ### Connecting to the Home Assistant container 141 | 142 | If you are trying trying to connecto to Home Assistant from the 143 | NetDaemon container, `localhost` will not resolve to the actual host 144 | machines loop-back ip, but rather the NetDaemons container's internal 145 | loopback ip. 146 | 147 | If Home Assistant is running either directly on the host or in a container using 148 | `network_mode: host` you will have to configure your NetDaemon container to 149 | resolve the actual ip. This can be achieved by setting the `HomeAssistant__Host` 150 | environment variable to `host.docker.internal` 151 | (and if on Linux, adding the field [extra_hosts](https://docs.docker.com/reference/compose-file/build/#extra_hosts) 152 | with the value `host.docker.internal:host-gateway`) 153 | For more info, see this post on [Stack Overflow](https://stackoverflow.com/questions/24319662/from-inside-of-a-docker-container-how-do-i-connect-to-the-localhost-of-the-mach). 154 | 155 | If Home Assistant is not using host mode networking, then the previous method should 156 | work, but you can also set the `HomeAssistant__Host` variable to the name of the 157 | HA container instead, without adding the `extra_hosts` field. 158 | Note, that the LLAT for the user must **not** have the setting 159 | "Local access only" in Home Assistant. 160 | -------------------------------------------------------------------------------- /docs/user/started/integration.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: integration 3 | title: Install NetDaemon integration 4 | --- 5 | 6 | The NetDaemon custom integration is optional but recommended 7 | to get access to all NetDaemon features. 8 | 9 | Using the NetDaemon custom integration enables you to 10 | 11 | - Use the service callbacks from Home Assistant to your code 12 | 13 | The integration requires NetDaemon 21.00.0 or higher to work. 14 | 15 | ## Installation 16 | 17 | We recommend to install the custom integration through [HACS](https://hacs.xyz/). 18 | 19 | 1. Add `https://github.com/net-daemon/integration` with the category "integration" as a custom repository in [HACS](https://hacs.xyz/docs/faq/custom_repositories) 20 | 2. Install it in HACS 21 | 3. Clear browser cache and restart Home Assistant 22 | 4. Go to Configuration -> Integrations -> + -> Search for "NetDaemon" and install it. 23 | 24 | ## Integration repo 25 | 26 | See [integration repo](https://github.com/net-daemon/integration) for details or reporting issues with the integrations 27 | -------------------------------------------------------------------------------- /docs/user/support_policy.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: user_support_policy 3 | title: Maintenance and support 4 | --- 5 | 6 | ## Support and maintenance policy 7 | 8 | We are a small team maintaining this project in our free time so we are happy to support and maintain the latest major version of the NetDaemon to the extent of our time and availability. 9 | We have no means to support older versions. We can **not** guarantee anything but we will do our best to help you out as long as this project is active. 10 | 11 | _These policies are guidelines, not guarantees, and could be changed at any time without prior notice._ 12 | 13 | ### What we support 14 | 15 | We offer support through the [Discord server](https://discord.gg/QwvWpy8M) and [GitHub issues](https://github.com/net-daemon/netdaemon/issues) for the latest major version of NetDaemon. 16 | We will try our best to provide bug fixes and security patches for the latest major version if we can but no guarantees. 17 | We do recommend using the Discord server for faster response times. 18 | 19 | ### What happens to older versions? 20 | 21 | You can continue to use older versions of NetDaemon, but we will not provide any support, bug fixes or security patches for them. We encourage you to upgrade to the latest version of NetDaemon to take advantage of the latest features and performance improvements. 22 | The nuget packages to all prior versions of the runtime will be deprecated to let you know that you should upgrade to the latest version. 23 | 24 | The add-on will remain for the two latest major versions but the oldest one will be deprecated and removed next major version. 25 | 26 | All docker/GitHub images will remain in the registries as long as we are allowed to have them there. 27 | 28 | ### How to upgrade to the latest version 29 | 30 | Below you have links to the migration guides for upgrading to the latest version of NetDaemon. If you are on a really old version we 31 | recommend to start a new project from the official CLI template and migrate the old apps one by one. 32 | 33 | [From V3->V4](user/app_model/moving_from_v3.md) (current version) 34 | 35 | ## Release policy 36 | 37 | ### Major releases of NetDaemon runtime 38 | 39 | We aim to always be on the latest major version of .NET to allow users to take advantage of the latest features and performance improvements of .NET 40 | as well as the newest language features of C#. We will try to release new versions of NetDaemon as soon as possible after a new version of .NET is released. 41 | 42 | Sometimes we might introduce breaking changes if neede in order to make the project easier to maintain or introduce new features. 43 | This will result in a new major version of NetDaemon. We will provide a migration guide for users to upgrade to the latest version. 44 | New versions will be announced on the [Discord server](https://discord.gg/QwvWpy8M) in good time before the release to give users time to prepare for the upgrade. 45 | 46 | ### Minor releases of NetDaemon runtime 47 | 48 | If the release only contains upgrades to a newer version of .NET and no other breaking changes, we will release a minor version of NetDaemon runtime. This will allow users to upgrade to the latest version of .NET without any breaking changes to their existing code. 49 | It will require users to update their .NET version to the latest version to use the latest version of NetDaemon. 50 | 51 | ### New features, bug fixes and security patches 52 | 53 | New features and bug fixes will be released continuously as soon as they are ready on the latest version of NetDaemon runtime. 54 | No new features, bug fixes and security patches will be released for older versions of NetDaemon runtime. Users are encouraged to upgrade to the latest version of NetDaemon runtime as soon as possible. 55 | New features and bug fixes does not mean new version of the runtime and are mostly not breaking. We will always provide information in the release notes 56 | if we have to introduce breaking changes. 57 | 58 | 59 | -------------------------------------------------------------------------------- /docs/user/troubleshooting/call_service.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: trouble_call_service 3 | title: Service calls 4 | --- 5 | 6 | ## CallService does not affect the device/entity/area 7 | 8 | There are a number of reasons service calls using `CallService` does not work when you use it indirectly through `TurnOn/TurnOff` methods. 9 | 10 | ### Problems with the actual device/entity 11 | 12 | Please check that the device/entity responds to service calls using the GUI. You can find the tool for calling services under the menu item `Developer Tools` and select the `SERVICES" tab. Call the service from there with the exact parameters you are using in NetDaemon. If this works as expected it is time to check what the actual service call is. 13 | 14 | ### Check the logs 15 | 16 | If you do faulty service calls it would normally log an error. Please check your add-on log, container log, or the log where you run the NetDaemon runtime. 17 | 18 | ### Check the service calls that are created from NetDaemon 19 | 20 | Home Assistant has a great feature to be able to track events in the GUI. Use menu item `Developer Tools` and select the `EVENTS` tab. Use the `Listen to events` section. Type in `call_service` in the form `Listening to` and press `START LISTENING`. The example image below shows how it could look when you get an event from turning on a light. 21 | 22 | ![](/img/docs/trouble/listen_events.png) -------------------------------------------------------------------------------- /docs/user/troubleshooting/unauthorized.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: unauthorized 3 | title: Unauthorized 4 | --- 5 | 6 | ## Debugging the Unauthorized message 7 | 8 | If you find yourself having setup NetDaemon and HomeAssistant, but you get `401`-message, there's a few things to check; 9 | 10 | 1. The environment settings for NetDaemon `HomeAssistant__Host`, `HomeAssistant__Port`, and `HomeAssistant__Token` must be set to point at the Home Assistant container. 11 | 1. Home Assistant has a [configuration.yaml](https://www.home-assistant.io/integrations/http/) file where it allows external calls. Set it up with the following section: 12 | 13 | ```yaml 14 | ... 15 | http: 16 | use_x_forwarded_for: true 17 | trusted_proxies: 18 | - 127.0.0.1 # Localhost 19 | - 192.168.1.0/24 # Local LAN subnet, if this is your local computer's network 20 | # If you have setup a Docker network, you can also add those to this list 21 | ... 22 | ``` 23 | 24 | 3. The user with which you generated a Long Lived Access Token must **not** have set the property "Local access only", which the administrator of the user can set. 25 | -------------------------------------------------------------------------------- /docs/user/tutorials/publish_script.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: publish_script 3 | title: Publish NetDaemon apps with PowerShell 4 | --- 5 | 6 | ## Audience 7 | - NetDaemon developers using Home Assistant OS on a system like Raspberry Pi, ODROID, etc. 8 | - Development under Windows (might work also for other OS but hasn't been tested) 9 | 10 | ## Goal 11 | Write an PowerShell script that deploys your apps to Home Assistant with the following steps: 12 | 13 | 1. Stop the NetDaemon AddOn 14 | 2. Delete the old installation 15 | 3. Publish the new installation 16 | 4. Restart the NetDaemon AddOn 17 | 18 | ## Prerequirements 19 | - [Samba share](https://github.com/home-assistant/addons/tree/master/samba) installed 20 | - [Home Assistant PowerShell Module](https://github.com/flemmingss/Home-Assistant-PowerShell-Module) 21 | 22 | After installing Samba share, please make sure that you can access your Home Assistant share from your machine: 23 | 1. Press Windows + R to open the Run dialog 24 | 2. In the Open Textbox write \\homeassistant.local\ (adapt if you use a different host name or use the ip adress) 25 | 3. You will be prompted to enter User name and Password. Provide your User and Password from your Samba share configuration. Don't forget to tick "Remember my credentials". 26 | 27 | The Home Assistant PowerShell Module uses the IP address of Home Assistant to start/stop the addons. It's a anyway a good idea to give a fix IP address to your Home Assistant system. 28 | 29 | ## The Script 30 | 31 | - Create a Powershell file like publish.ps1 in the same folder as your appsettings.json is located. 32 | - Create a subfolder "Home-Assistant" and copy the two files from the PowerShell Module into it. 33 | 34 | ```powershell 35 | $settings = (Get-Content appsettings.json | ConvertFrom-Json) 36 | 37 | #CHANGE ME 38 | $slug = 'c6a2317c_netdaemon5' # the slug can be found in the url of the browser when navigating to the NetDaemon addon 39 | 40 | $version = $slug.Split('_')[-1] # adapt if you are not using the default foldername for the addon 41 | $json = '{"addon": "' + $slug + '"}' 42 | $ip = $settings.HomeAssistant.Host 43 | $port = $settings.HomeAssistant.Port 44 | 45 | $token = $settings.HomeAssistant.Token 46 | 47 | # Point to the HA PowerSHell Module 48 | Unblock-File .\Home-Assistant\Home-Assistant.psd1 49 | Unblock-File .\Home-Assistant\Home-Assistant.psm1 50 | Import-Module .\Home-Assistant 51 | 52 | New-HomeAssistantSession -ip $ip -port $port -token $token 53 | 54 | Invoke-HomeAssistantService -service hassio.addon_stop -json $json 55 | 56 | Remove-Item -Recurse -Force \\$ip\config\$version\* 57 | dotnet publish -c Release HomeAssistant.csproj -o \\$ip\config\$version 58 | 59 | Invoke-HomeAssistantService -service hassio.addon_start -json $json 60 | ``` 61 | -------------------------------------------------------------------------------- /docs/user/tutorials/webhost.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: webhost 3 | title: Add webserver to NetDaemon 4 | --- 5 | 6 | ## Goal 7 | 8 | This guide shows how to tweak the original NetDaemon template in order to spin up a Kestrel webserver that hosts 9 | - a minimal API 10 | - a Web API controller 11 | - Blazor server pages (which can even be embedded in lovelace - at least locally.) 12 | 13 | You can easily update your existing ND solution by simply following the steps described in this document. 14 | 15 | ## Motivation 16 | There are scenarios where you want to provide data via RESTful services or WebSocket instead of storing everything in a HA entity. 17 | Or maybe you want to render complex UI without becoming a lovelace wizard, instead leveraging your .NET skills. 18 | Using Blazor Server and visualizing the output in a lovelace iframe-card might be an option. (Including hassle-free live updates, e.g. from your sensor data.) 19 | 20 | ## Steps 21 | 22 | Change the `SDK` in the `csproj` file to 23 | ```xml 24 | 25 | ``` 26 | 27 | In `program.cs`, change from `IHostBuilder` to the `WebApplicationBuilder`. 28 | The ND configuration remains the same. 29 | 30 | ```csharp 31 | var builder = WebApplication.CreateBuilder(args); 32 | 33 | builder.Host 34 | .UseNetDaemonAppSettings() 35 | .UseNetDaemonDefaultLogging() 36 | .UseNetDaemonRuntime() 37 | .UseNetDaemonTextToSpeech() 38 | .UseNetDaemonMqttEntityManagement() 39 | .ConfigureServices((_, services) => 40 | { 41 | services 42 | .AddAppsFromAssembly(Assembly.GetExecutingAssembly()) 43 | .AddNetDaemonStateManager() 44 | .AddNetDaemonScheduler() 45 | .AddHomeAssistantGenerated(); 46 | }); 47 | ``` 48 | 49 | Configuring MVC and Blazor services 50 | ```csharp 51 | // adding MVC / WebAPI controllers 52 | builder.Services.AddControllers(); 53 | 54 | // Blazor Server 55 | builder.Services.AddRazorPages(); 56 | builder.Services.AddServerSideBlazor(); 57 | ``` 58 | Configuring Kestrel Port and certificate 59 | ```csharp 60 | // Kestrel on port 10000 61 | // TODO: SSL 62 | builder.WebHost.ConfigureKestrel(options => 63 | { 64 | options.ListenAnyIP(10000); 65 | }); 66 | ``` 67 | Defining minimal API operations 68 | ```csharp 69 | // minimal API 70 | app.MapGet("/mini", () => "Hello from Minimal API!"); 71 | ``` 72 | Finally setting up Routing and Endpoints 73 | ```csharp 74 | // use controller routes 75 | app.MapControllers(); 76 | 77 | // configure Blazor 78 | app.UseStaticFiles(); 79 | app.UseRouting(); 80 | app.MapBlazorHub(); 81 | app.MapFallbackToPage("/_Host"); 82 | 83 | await app.RunAsync(); 84 | ``` 85 | 86 | Starting the project with F5 Debug mode runs the ND Apps. 87 | You can easily check the added functionality hosted on Kestrel from a browser. 88 | 89 | ![](/img/docs/tutorials/webhost_1_output.png) 90 | 91 | When adding Blazor artifacts from a different solution, search for all occurrences of the old project name and replace it with the new namespace. 92 | In case you still run into problems, close and reopen Visual Studio. 93 | 94 | In this demo, I just copied the components of the default Blazor project into the solution and modified only the `Counter.razor` file to show 95 | how easy it is to inject the ND services and use the generated HA classes. 96 | ```csharp 97 | @page "/counter" 98 | 99 | Counter 100 |

Demo

101 | 102 |

Sun: @Entities.Sun.Sun.State

103 |

UTC: @Entities.Sensor.TimeUtc.State

104 | 105 |

Current count: @currentCount

106 | 107 | 108 | @code { 109 | 110 | [Inject] 111 | public IScheduler Scheduler { get; set; } = null!; 112 | 113 | [Inject] 114 | public Entities Entities { get; set; } = null!; 115 | 116 | protected override void OnInitialized() 117 | { 118 | // update the counter automatically every second, using the wellknown IScheduler 119 | Scheduler.SchedulePeriodic(TimeSpan.FromSeconds(1), async () => 120 | { 121 | await InvokeAsync(() => 122 | { 123 | IncrementCount(); 124 | StateHasChanged(); 125 | }); 126 | }); 127 | } 128 | 129 | private int currentCount = 0; 130 | 131 | private void IncrementCount() 132 | { 133 | currentCount++; 134 | } 135 | } 136 | ``` 137 | 138 | Now let's deploy the whole thing. 139 | While it is perfectly fine to copy the binaries to `/config/netdaemon5` for a _normal_ NetDaemon project, it will fail for Blazor. 140 | We need to Publish the WebSite in order to build the web artifacts in `wwwroot`. 141 | 142 | ![](/img/docs/tutorials/webhost_2_publish.png) 143 | 144 | Just select `Folder` as target and configure the location. 145 | 146 | ![](/img/docs/tutorials/webhost_3_publish_dlg.png) 147 | 148 | 149 | After every publishing, the NetDaemon AddOn needs to be restarted. 150 | 151 | There's one final - but important! - step: you need to enable and map the port where the Kestrel is listening in the configuration section of the AddOn. 152 | ![](/img/docs/tutorials/webhost_4_port.png) 153 | 154 | ## Limitations 155 | The Kestrel server will not be available via your Nabu Casa cloud link as it only directs to your HA port on :8123. 156 | -------------------------------------------------------------------------------- /docs/user/whats_new.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: whats_new_v5 3 | title: What's new in V5 4 | --- 5 | ## Version 5 of NetDaemon 6 | 7 | We are exited to introduce you to version 5 of NetDaemon! 8 | 9 | For version 4 users it is a matter of using the latest version of the nuget packages and the v5/.NET 9 version of the NetDaemon 10 | Docker image or add-on. 11 | 12 | If you are still using version 3 it is really time to upgrade, we do not longer support that version, 13 | see the [upgrade path for version 3 users](user/app_model/moving_from_v3.md) 14 | 15 | 16 | ### .NET 9 and C# 13 17 | 18 | NetDaemon always have been releasing new version following the Microsoft release schedule 19 | of .NET to let our users to use the latest innovations and speed of modern .NET. 20 | 21 | 22 | :::info 23 | 24 | If you are using the source only deploy model you will now have to include the `NetDaemon.AppModel.SourceDeployedApps` nuget package in your C# project. 25 | 26 | ::: 27 | 28 | ### Removal of deprecated V2 references 29 | 30 | :::warning 31 | 32 | All references to old deprecated and unsupported version 2 of the runtime are removed from documentation and add-ons. Users that still use deprecated and unsupported software will have to fork NetDaemon repos from earlier commits. 33 | 34 | ::: 35 | -------------------------------------------------------------------------------- /docusaurus.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | title: 'NetDaemon', 3 | tagline: 'C# Automation platform for Home Assistant', 4 | url: 'https://netdaemon.xyz', 5 | baseUrl: '', 6 | favicon: 'img/favicon.png', 7 | organizationName: 'net-daemon', // Usually your GitHub org/user name. 8 | projectName: 'docs', // Usually your repo name. 9 | trailingSlash: true, 10 | markdown: { 11 | mermaid: true, 12 | }, 13 | i18n: { 14 | defaultLocale: 'en', 15 | locales: ['en'], 16 | localeConfigs: { 17 | en: { 18 | htmlLang: 'en-US', 19 | }, 20 | }, 21 | }, 22 | themes: ['@docusaurus/theme-mermaid'], 23 | themeConfig: { 24 | algolia: { 25 | // The application ID provided by Algolia 26 | appId: 'BD12ZZ1ACW', 27 | 28 | // Public API key: it is safe to commit it 29 | apiKey: 'cbc42a382bc223bf28b6947b86d874ab', 30 | 31 | indexName: 'netdaemon', 32 | 33 | // Optional: see doc section below 34 | contextualSearch: false, 35 | 36 | // Optional: Specify domains where the navigation should occur through window.location instead on history.push. Useful when our Algolia config crawls multiple documentation sites and we want to navigate with window.location.href to them. 37 | //externalUrlRegex: 'external\\.com|domain\\.com', 38 | 39 | // Optional: Replace parts of the item URLs from Algolia. Useful when using the same search index for multiple deployments using a different baseUrl. You can use regexp or string in the `from` param. For example: localhost:3000 vs myCompany.com/docs 40 | //replaceSearchResultPathname: { 41 | // from: '/docs/', // or as RegExp: /\/docs\// 42 | // to: '/', 43 | //}, 44 | 45 | // Optional: Algolia search parameters 46 | //searchParameters: {}, 47 | 48 | // Optional: path for search page that enabled by default (`false` to disable it) 49 | searchPagePath: 'search', 50 | 51 | //... other Algolia params 52 | }, 53 | prism: { 54 | additionalLanguages: ['csharp', 'powershell'], 55 | }, 56 | navbar: { 57 | title: 'NetDaemon', 58 | logo: { 59 | alt: 'NetDaemon Logo', 60 | src: 'img/favicon.png', 61 | }, 62 | items: [ 63 | { 64 | to: '/docs/user', 65 | activeBasePath: 'docs/user', 66 | label: 'Docs', 67 | position: 'left', 68 | }, 69 | { 70 | to: 'docs/developer', 71 | activeBasePath: 'docs', 72 | label: 'Developer', 73 | position: 'left', 74 | }, 75 | // { to: 'blog', label: 'Blog', position: 'left' }, 76 | { 77 | href: 'https://github.com/net-daemon/docs', 78 | label: 'GitHub', 79 | position: 'right', 80 | }, 81 | ], 82 | }, 83 | footer: { 84 | style: 'dark', 85 | links: [ 86 | { 87 | title: 'Docs', 88 | items: [ 89 | { 90 | label: 'Getting started', 91 | to: '/docs/user/started/get_started', 92 | }, 93 | // { 94 | // label: 'Second Doc', 95 | // to: 'docs/doc2', 96 | // }, 97 | ], 98 | }, 99 | { 100 | title: 'Community', 101 | items: [ 102 | { 103 | label: 'Discord', 104 | href: 'https://discord.gg/K3xwfcX', 105 | }, 106 | { 107 | label: 'Sponsor & support NetDeamon', 108 | to: 'docs/community/sponsor', 109 | }, 110 | ], 111 | }, 112 | { 113 | title: 'Github', 114 | items: [ 115 | { 116 | label: 'Issues', 117 | href: 'https://github.com/net-daemon/netdaemon/issues', 118 | }, 119 | { 120 | label: 'NetDaemon', 121 | href: 'https://github.com/net-daemon/netdaemon', 122 | }, 123 | ], 124 | }, 125 | { 126 | title: 'Attribution', 127 | items: [ 128 | // { 129 | // label: 'Blog', 130 | // to: 'blog', 131 | // }, 132 | { 133 | label: 'HACS - ludeeus', 134 | href: 'https://github.com/ludeeus', 135 | }, 136 | { 137 | label: 'Home Assistant devs', 138 | href: 'https://www.home-assistant.io/developers/credits/#contributors', 139 | }, 140 | ], 141 | }, 142 | ], 143 | copyright: `This site is powered by Netlify`, 144 | 145 | // image: 'https://www.netlify.com/img/global/badges/netlify-color-accent.svg', 146 | //image: 'img/favicon.png', 147 | }, 148 | }, 149 | presets: [ 150 | [ 151 | '@docusaurus/preset-classic', 152 | { 153 | docs: { 154 | sidebarPath: require.resolve('./sidebars.js'), 155 | editUrl: 156 | 'https://github.com/net-daemon/docs/tree/master', 157 | }, 158 | theme: { 159 | customCss: require.resolve('./src/css/custom.css'), 160 | }, 161 | sitemap: {}, 162 | }, 163 | ], 164 | ], 165 | plugins: [ 166 | ], 167 | }; 168 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docs", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "start": "docusaurus start --host 0.0.0.0", 7 | "build": "docusaurus build", 8 | "swizzle": "docusaurus swizzle", 9 | "deploy": "docusaurus deploy", 10 | "serve": "docusaurus serve", 11 | "cleanup": "npm prune; rm -R node_modules || echo ''; rm -R .docusaurus || echo ''; rm -R build || echo ''" 12 | }, 13 | "engines": { 14 | "node": ">=18.0.0" 15 | }, 16 | "dependencies": { 17 | "@docusaurus/core": "^3.6.0", 18 | "@docusaurus/preset-classic": "^3.6.0", 19 | "@docusaurus/theme-mermaid": "^3.6.0", 20 | "@easyops-cn/docusaurus-search-local": "^0.48.5", 21 | "classnames": "^2.5.1", 22 | "react": "^19.0.0", 23 | "react-dom": "^19.0.0" 24 | }, 25 | "devDependencies": { 26 | "yarn": "^1.22.22", 27 | "yarn-upgrade-all": "^0.7.4" 28 | }, 29 | "browserslist": { 30 | "production": [ 31 | ">0.2%", 32 | "not dead", 33 | "not op_mini all" 34 | ], 35 | "development": [ 36 | "last 1 chrome version", 37 | "last 1 firefox version", 38 | "last 1 safari version" 39 | ] 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /sidebars.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | someSidebar: { 3 | Documentation: [ 4 | 'user/index', 5 | 'user/user_support_policy', 6 | 'user/whats_new_v5', 7 | { 8 | type: 'category', 9 | label: 'Getting started', 10 | items: [ 11 | 'user/started/get_started', 12 | 'user/started/installation', 13 | 'user/started/integration', 14 | 'user/started/example', 15 | ] 16 | }, 17 | { 18 | type: 'category', 19 | label: 'AppModel', 20 | items: [ 21 | 'user/app_model/app_model', 22 | 'user/app_model/app_model_instancing', 23 | 'user/app_model/app_model_advanced_config', 24 | 'user/app_model/app_model_custom_logging', 25 | 'user/app_model/app_model_custom_config', 26 | 'user/app_model/app_model_moving_from_v3', 27 | ], 28 | }, 29 | { 30 | type: 'category', 31 | label: 'HassModelApi', 32 | items: [ 33 | 'user/hass_model/hass_model', 34 | 'user/hass_model/hass_model_codegen', 35 | 'user/hass_model/hass_model_generated_entities', 36 | 'user/hass_model/hass_model_generated_service', 37 | 'user/hass_model/hass_model_working_with_entities', 38 | 'user/hass_model/hass_model_subscribe_to_triggers', 39 | 'user/hass_model/hass_model_events', 40 | 'user/hass_model/hass_model_notifications', 41 | 'user/hass_model/hass_model_integration_servicecallback', 42 | 'user/hass_model/hass_model_registry', 43 | 'user/hass_model/hass_model_service_with_returnvalue', 44 | ] 45 | }, 46 | { 47 | type: 'category', 48 | label: 'Advanced', 49 | items: [ 50 | 'user/advanced/call_ha_api', 51 | 'user/advanced/async_features', 52 | 'user/advanced/dev_workflow3', 53 | 'user/advanced/advanced_client', 54 | 'user/advanced/application_lifecycle', 55 | ] 56 | }, 57 | { 58 | type: 'category', 59 | label: 'Extensions API', 60 | items: [ 61 | 'user/extensions/extensions_scheduling', 62 | 'user/extensions/extensions_tts', 63 | 'user/extensions/extensions_mqttentitymanager', 64 | ] 65 | }, 66 | { 67 | type:'category', 68 | label: 'Tutorials', 69 | items: [ 70 | 'user/tutorials/publish_script', 71 | 'user/tutorials/webhost', 72 | ] 73 | }, 74 | { 75 | type: 'category', 76 | label: 'Troubleshooting', 77 | items: [ 78 | 'user/troubleshooting/trouble_call_service', 79 | 'user/troubleshooting/unauthorized' 80 | ] 81 | }, 82 | ], 83 | }, 84 | "developer documentation": { 85 | "NetDaemon development": [ 86 | "developer/developer", 87 | "developer/architecture", 88 | "developer/vscode", 89 | "developer/vs", 90 | "developer/unit_test" 91 | ] 92 | }, 93 | }; 94 | -------------------------------------------------------------------------------- /src/css/custom.css: -------------------------------------------------------------------------------- 1 | /* stylelint-disable docusaurus/copyright-header */ 2 | /** 3 | * Any CSS included here will be global. The classic template 4 | * bundles Infima by default. Infima is a CSS framework designed to 5 | * work well for content-centric websites. 6 | */ 7 | 8 | /* You can override the default Infima variables here. */ 9 | :root { 10 | --ifm-color-primary: #3578e5; 11 | --ifm-color-primary-dark: #1d68e1; 12 | --ifm-color-primary-darker: #1b62d4; 13 | --ifm-color-primary-darkest: #1751af; 14 | --ifm-color-primary-light: #4e89e8; 15 | --ifm-color-primary-lighter: #5a91ea; 16 | --ifm-color-primary-lightest: #80aaef; 17 | /* --ifm-color-primary: #25c2a0; 18 | --ifm-color-primary-dark: rgb(33, 175, 144); 19 | --ifm-color-primary-darker: rgb(31, 165, 136); 20 | --ifm-color-primary-darkest: rgb(26, 136, 112); 21 | --ifm-color-primary-light: rgb(70, 203, 174); 22 | --ifm-color-primary-lighter: rgb(102, 212, 189); 23 | --ifm-color-primary-lightest: rgb(146, 224, 208); 24 | --ifm-code-font-size: 95%; */ 25 | 26 | --ifm-color-secondary: #1bd4d4; 27 | --ifm-color-info: #70bdd7; 28 | --ifm-color-warning: #c18802; 29 | --ifm-color-danger: #d78a70; 30 | --ifm-color-gray-900: #292929; 31 | --ifm-color-success: #70d7a7; 32 | --ifm-alert-border-radius: 4px; 33 | } 34 | 35 | /* .admonition-caution h5 { 36 | color: #f6ffb6; 37 | } */ 38 | 39 | /* .admonition-caution { 40 | background-color: rgb(102, 212, 189); 41 | border-left: 8px solid #267e22; 42 | } 43 | 44 | .admonition-caution h5 { 45 | color: #167e22; 46 | } 47 | 48 | .admonition-caution .admonition-icon svg { 49 | stroke: #0202e2; 50 | fill: #36e622; 51 | } */ 52 | 53 | -------------------------------------------------------------------------------- /src/pages/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import CodeBlock from '@theme/CodeBlock'; 3 | import classnames from 'classnames'; 4 | import Layout from '@theme/Layout'; 5 | import Link from '@docusaurus/Link'; 6 | import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; 7 | import useBaseUrl from '@docusaurus/useBaseUrl'; 8 | import styles from './styles.module.css'; 9 | 10 | const features = [ 11 | { 12 | title: <>For the C# developer, 13 | imageUrl: 'img/C_Sharp_logo.svg', 14 | description: ( 15 | <> 16 | With NetDaemon you can write your home automations in C# for Home Assistant using modern .NET design. NetDaemon is open source and will always be free! 17 | 18 | ), 19 | }, 20 | { 21 | title: <>Integrated with Home Assistant, 22 | imageUrl: 'img/hass.png', 23 | description: ( 24 | <> 25 | Integrated with Home Assistant using websockets for maximum performance. Has advanced scheduling and a easy to use API. 26 | 27 | ), 28 | }, 29 | { 30 | title: <>Code generation, 31 | Link: 'https://www.youtube.com/watch?v=OCej2TVdKQo', 32 | codeBlock: `entities.BinarySensor.OfficeMotion 33 | .StateChanges() 34 | .Where(e => e.New.IsOn()) 35 | .Subscribe(_ => 36 | entities.Light.Office.TurnOn());`, 37 | description: ( 38 | <> 39 | 40 | All your entities and services can be generated for full intellisense experience. 41 |
Watch our introduction video! 42 | 43 | ), 44 | }, 45 | 46 | ]; 47 | 48 | function Feature({ imageUrl, title, description, codeBlock }) { 49 | const imgUrl = useBaseUrl(imageUrl); 50 | return ( 51 |
52 | {imgUrl && !codeBlock &&( 53 |
54 | {title} 55 |
56 | )} 57 | {codeBlock &&( 58 | 59 | {codeBlock} 60 | 61 | )} 62 |

{title}

63 |

{description}

64 |
65 | ); 66 | } 67 | 68 | function Home() { 69 | const context = useDocusaurusContext(); 70 | const { siteConfig = {} } = context; 71 | return ( 72 | 75 |
76 |
77 |

{siteConfig.title}

78 |

{siteConfig.tagline}

79 |
80 | 86 | Get Started 87 | 88 |
89 |
90 |
91 |
92 | {features && features.length && ( 93 |
94 |
95 |
96 | {features.map((props, idx) => ( 97 | 98 | ))} 99 |
100 |
101 |
102 | )} 103 |
104 |
105 | ); 106 | } 107 | 108 | export default Home; 109 | -------------------------------------------------------------------------------- /src/pages/styles.module.css: -------------------------------------------------------------------------------- 1 | /* stylelint-disable docusaurus/copyright-header */ 2 | /** 3 | * CSS files with the .module.css suffix will be treated as CSS modules 4 | * and scoped locally. 5 | */ 6 | 7 | .heroBanner { 8 | padding: 4rem 0; 9 | text-align: center; 10 | position: relative; 11 | overflow: hidden; 12 | } 13 | 14 | @media screen and (max-width: 966px) { 15 | .heroBanner { 16 | padding: 2rem; 17 | } 18 | } 19 | 20 | .buttons { 21 | display: flex; 22 | align-items: center; 23 | justify-content: center; 24 | } 25 | 26 | .features { 27 | display: flex; 28 | align-items: center; 29 | padding: 2rem 0; 30 | width: 100%; 31 | } 32 | 33 | .featureImage { 34 | height: 150px; 35 | width: 150px; 36 | } 37 | 38 | -------------------------------------------------------------------------------- /static/_redirects: -------------------------------------------------------------------------------- 1 | https://netdaemon.netlify.com/* https://netdaemon.xyz/:splat 301! -------------------------------------------------------------------------------- /static/img/C_Sharp_logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /static/img/codegen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/net-daemon/docs/08c5bff872a9cb20cdc2ccb7d9ffcb2f80a71731/static/img/codegen.png -------------------------------------------------------------------------------- /static/img/docs/dev/overall_architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/net-daemon/docs/08c5bff872a9cb20cdc2ccb7d9ffcb2f80a71731/static/img/docs/dev/overall_architecture.png -------------------------------------------------------------------------------- /static/img/docs/installation/folder_structure_netdaemon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/net-daemon/docs/08c5bff872a9cb20cdc2ccb7d9ffcb2f80a71731/static/img/docs/installation/folder_structure_netdaemon.png -------------------------------------------------------------------------------- /static/img/docs/installation/folderstructure_v3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/net-daemon/docs/08c5bff872a9cb20cdc2ccb7d9ffcb2f80a71731/static/img/docs/installation/folderstructure_v3.png -------------------------------------------------------------------------------- /static/img/docs/installation/netdaemon_add_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/net-daemon/docs/08c5bff872a9cb20cdc2ccb7d9ffcb2f80a71731/static/img/docs/installation/netdaemon_add_on.png -------------------------------------------------------------------------------- /static/img/docs/started/daemon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/net-daemon/docs/08c5bff872a9cb20cdc2ccb7d9ffcb2f80a71731/static/img/docs/started/daemon.png -------------------------------------------------------------------------------- /static/img/docs/started/daemon3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/net-daemon/docs/08c5bff872a9cb20cdc2ccb7d9ffcb2f80a71731/static/img/docs/started/daemon3.png -------------------------------------------------------------------------------- /static/img/docs/started/daemon_addon_config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/net-daemon/docs/08c5bff872a9cb20cdc2ccb7d9ffcb2f80a71731/static/img/docs/started/daemon_addon_config.png -------------------------------------------------------------------------------- /static/img/docs/started/netdaemonfolder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/net-daemon/docs/08c5bff872a9cb20cdc2ccb7d9ffcb2f80a71731/static/img/docs/started/netdaemonfolder.png -------------------------------------------------------------------------------- /static/img/docs/started/newrepo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/net-daemon/docs/08c5bff872a9cb20cdc2ccb7d9ffcb2f80a71731/static/img/docs/started/newrepo.png -------------------------------------------------------------------------------- /static/img/docs/started/rootdir.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/net-daemon/docs/08c5bff872a9cb20cdc2ccb7d9ffcb2f80a71731/static/img/docs/started/rootdir.png -------------------------------------------------------------------------------- /static/img/docs/started/vs_publish_config.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/net-daemon/docs/08c5bff872a9cb20cdc2ccb7d9ffcb2f80a71731/static/img/docs/started/vs_publish_config.jpg -------------------------------------------------------------------------------- /static/img/docs/trouble/listen_events.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/net-daemon/docs/08c5bff872a9cb20cdc2ccb7d9ffcb2f80a71731/static/img/docs/trouble/listen_events.png -------------------------------------------------------------------------------- /static/img/docs/tutorials/webhost_1_output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/net-daemon/docs/08c5bff872a9cb20cdc2ccb7d9ffcb2f80a71731/static/img/docs/tutorials/webhost_1_output.png -------------------------------------------------------------------------------- /static/img/docs/tutorials/webhost_2_publish.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/net-daemon/docs/08c5bff872a9cb20cdc2ccb7d9ffcb2f80a71731/static/img/docs/tutorials/webhost_2_publish.png -------------------------------------------------------------------------------- /static/img/docs/tutorials/webhost_3_publish_dlg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/net-daemon/docs/08c5bff872a9cb20cdc2ccb7d9ffcb2f80a71731/static/img/docs/tutorials/webhost_3_publish_dlg.png -------------------------------------------------------------------------------- /static/img/docs/tutorials/webhost_4_port.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/net-daemon/docs/08c5bff872a9cb20cdc2ccb7d9ffcb2f80a71731/static/img/docs/tutorials/webhost_4_port.png -------------------------------------------------------------------------------- /static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/net-daemon/docs/08c5bff872a9cb20cdc2ccb7d9ffcb2f80a71731/static/img/favicon.ico -------------------------------------------------------------------------------- /static/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/net-daemon/docs/08c5bff872a9cb20cdc2ccb7d9ffcb2f80a71731/static/img/favicon.png -------------------------------------------------------------------------------- /static/img/hacs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/net-daemon/docs/08c5bff872a9cb20cdc2ccb7d9ffcb2f80a71731/static/img/hacs.png -------------------------------------------------------------------------------- /static/img/hacs.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/net-daemon/docs/08c5bff872a9cb20cdc2ccb7d9ffcb2f80a71731/static/img/hacs.psd -------------------------------------------------------------------------------- /static/img/hacs.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | 10 | 11 | 13 | 14 | 18 | 20 | 21 | 22 | 23 | 25 | 28 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /static/img/hass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/net-daemon/docs/08c5bff872a9cb20cdc2ccb7d9ffcb2f80a71731/static/img/hass.png -------------------------------------------------------------------------------- /static/img/hass.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/net-daemon/docs/08c5bff872a9cb20cdc2ccb7d9ffcb2f80a71731/static/img/hass.psd -------------------------------------------------------------------------------- /static/img/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/img/undraw_docusaurus_tree.svg: -------------------------------------------------------------------------------- 1 | docu_tree --------------------------------------------------------------------------------