<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Bogdan Bujdea</title>
    <description>The latest articles on DEV Community by Bogdan Bujdea (@thewindev).</description>
    <link>https://dev.to/thewindev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F41225%2F85505353-aee8-4888-8a11-f09269e09858.png</url>
      <title>DEV Community: Bogdan Bujdea</title>
      <link>https://dev.to/thewindev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/thewindev"/>
    <language>en</language>
    <item>
      <title>Communicating with your bot from Microsoft Teams</title>
      <dc:creator>Bogdan Bujdea</dc:creator>
      <pubDate>Sun, 19 Sep 2021 18:54:37 +0000</pubDate>
      <link>https://dev.to/thewindev/communicating-with-your-bot-from-microsoft-teams-63h</link>
      <guid>https://dev.to/thewindev/communicating-with-your-bot-from-microsoft-teams-63h</guid>
      <description>&lt;p&gt;In the previous article I showed you how you can create your own bot in C# and talk to it by using the Bot Emulator. Now, I'm going to show you how to communicate with it from Microsoft Teams. There are two approaches, one for production purposes and one that allows you to debug it locally from Visual Studio. We'll go with the second approach, and in the next post I'll show you how to deploy it in Azure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Content
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
Create an Azure Bot &lt;/li&gt;
&lt;li&gt;
Connect the bot to the MS Teams channel &lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  1. Create an Azure Bot
&lt;/h3&gt;

&lt;p&gt;Prerequisites:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Azure subscription&lt;/li&gt;
&lt;li&gt;MS Teams account&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ngrok.com/"&gt;ngrok&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You'll first need to login into the Azure Portal and create a new Azure Bot.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ETNitS7O--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1632043394287/ZXm6_fu1E.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ETNitS7O--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1632043394287/ZXm6_fu1E.png" alt="Azure bot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Choose your region or the pricing plan you need, it doesn't have to be the same as in the screenshot below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uN05TAXm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1632043474785/PA2nIbl3b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uN05TAXm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1632043474785/PA2nIbl3b.png" alt="Create bot in Azure"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's it for the first step, our bot is created but we still have to add it to MS Teams&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Connect the bot to the MS Teams channel
&lt;/h3&gt;

&lt;p&gt;Connecting the bot to MS Teams is not that complicated. We first need to add a Teams channel by going to the "Channels" option and clicking on the Teams icon where it says "Add a featured channel":&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hsmVUoy0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1632069561385/-odLyWFoh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hsmVUoy0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1632069561385/-odLyWFoh.png" alt="Add Teams channel"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now you'll have to click on Save and agree to the terms and conditions:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ue_6-WbP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1632069595349/tlxVRZYff.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ue_6-WbP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1632069595349/tlxVRZYff.png" alt="Save channel"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the next step we need to head over to the "Configuration" tab.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lhOAh33m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1632077099918/ACQ6k0dnD.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lhOAh33m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1632077099918/ACQ6k0dnD.png" alt="Configuration tab"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here we can configure the messaging endpoint where it can send the messages. Usually, this should be another app deployed in Azure, but in order to make things simple we're going to connect the bot to our local machine by using ngrok.&lt;/p&gt;

&lt;p&gt;With ngrok you can expose a port from your machine to the internet, and that's what we're going to do by using this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ngrok http 3978 --host-header=localhost:3978

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you remember from the past article, our web app uses the port 3978 for communication, so we need to expose this port to the internet. The command above will generate the following output:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tG_z_wEK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1632072621808/4S1OaFOIm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tG_z_wEK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1632072621808/4S1OaFOIm.png" alt="ngrok"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We need to grab the https endpoint and use it in the bot configuration like this, by appending /api/messages to it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--n2NbHWzP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1632070461233/mvh9enKjE.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--n2NbHWzP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1632070461233/mvh9enKjE.png" alt="Bot configuration"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now you can go back to channels and click on the Teams icon. This should redirect you to a conversation with the bot inside MS Teams (web or desktop, depends on your preferences), and you can now try sending a message. The only thing left to do is to run the project from Visual Studio that we created on the previous post.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xA1e4STx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1632070564174/vu3ia-sQa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xA1e4STx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1632070564174/vu3ia-sQa.png" alt="401 Unauthorized bot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, the bot isn't replying back and if we look at the ngrok console we see a 401 status code returned by our bot. This happens because we need an app id and secret in order to connect to our Azure Bot. Follow the steps below to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;create a new Secret&lt;/li&gt;
&lt;li&gt;get the Microsoft App Id of the bot&lt;/li&gt;
&lt;li&gt;paste them inside appsettings.json&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--G5hYLIso--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1632072872738/Lr4NDpPS1.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--G5hYLIso--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1632072872738/Lr4NDpPS1.gif" alt="add bot id secret.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you do this you need to run the bot again in Visual Studio and send a message from Teams, now everything should work ok and the bot will echo back whatever you send it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GmbsFiV2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1632076646670/aCX-tc8ge.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GmbsFiV2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1632076646670/aCX-tc8ge.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The advantage of this approach is that you can debug every message you send by setting breakpoints in Visual Studio, but each time you start ngrok you have to change the URL in Azure as well. In the next post we'll deploy our bot to Azure, as you should do when you send it to production.&lt;/p&gt;

&lt;p&gt;If you don't understand something or you're stuck somewhere then please let me know in the comments and I'll try to cover this in a YouTube video.&lt;/p&gt;

&lt;p&gt;PS: You can follow me on &lt;a href="https://twitter.com/thewindev"&gt;Twitter&lt;/a&gt;, so you can get notified instantly when I post something new.&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://thewindev.net/communicating-with-your-bot-from-microsoft-teams"&gt;Communicating with your bot from Microsoft Teams&lt;/a&gt; appeared first on &lt;a href="https://thewindev.net"&gt;thewindev.net&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>c</category>
      <category>dotnet</category>
      <category>teams</category>
      <category>azure</category>
    </item>
    <item>
      <title>Creating a bot with the Bot Framework SDK</title>
      <dc:creator>Bogdan Bujdea</dc:creator>
      <pubDate>Sat, 18 Sep 2021 15:51:11 +0000</pubDate>
      <link>https://dev.to/thewindev/creating-a-bot-with-the-bot-framework-sdk-23bl</link>
      <guid>https://dev.to/thewindev/creating-a-bot-with-the-bot-framework-sdk-23bl</guid>
      <description>&lt;p&gt;In the past few months I worked on creating a Microsoft Teams bot using the Bot Framework SDK so I thing I have some useful knowledge to share about this topic. In this series of articles I want to show you how you can create your own Teams bot starting from scratch, and then we'll do more complex scenarios like SSO authentication with Microsoft Graph/Microsoft Dynamics 365, using adaptive cards and pushing them to their limits, etc.&lt;/p&gt;

&lt;p&gt;In this article I'll show you how you can create your bot from scratch in C# and talk to it from the Bot Emulator. In the next article we'll connect to it from Teams as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Content
&lt;/h2&gt;

&lt;p&gt;🔹1. Creating a bot using C#&lt;/p&gt;

&lt;p&gt;🔹2. Connect to the bot using the Bot Emulator&lt;/p&gt;




&lt;h3&gt;
  
  
  🔹 1. Creating a bot using C
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dotnet.microsoft.com/download/dotnet/thank-you/sdk-3.1.413-windows-x64-installer"&gt;.NET Core 3.1&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/microsoft/BotFramework-Emulator/blob/master/README.md"&gt;Bot Framework Emulator&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://marketplace.visualstudio.com/items?itemName=BotBuilder.botbuilderv4"&gt;Bot Framework Visual Studio Templates&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you installed everything, the first step is to open Visual Studio and create a new "Echo Bot" project like in the screenshot below (make sure you're using the 3.1 version).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pcRSp6OY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1631977663780/hOh6MaMeI.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pcRSp6OY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1631977663780/hOh6MaMeI.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This template will automatically install the &lt;a href="https://www.nuget.org/packages/Microsoft.Bot.Builder.Integration.AspNet.Core/4.14.0/"&gt;Microsoft.Bot.Builder.Integration.AspNet.Core&lt;/a&gt; Nuget package and create a basic bot that will reply with any text you send it. After this, pick a name for your bot and click on Create.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FF-o4RJ_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1631977850997/eNMwEL9wM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FF-o4RJ_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1631977850997/eNMwEL9wM.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Connect to the bot using the Bot Emulator
&lt;/h3&gt;

&lt;p&gt;Once the solution is ready, you just have to run your bot (hit F5) and then we'll connect using the Bot Framework Emulator. If everything went fine, you should see this page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Z4qYfEYk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1631977966197/7_7jMIsAS.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Z4qYfEYk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1631977966197/7_7jMIsAS.png" alt="image.png"&gt;&lt;/a&gt;From this page we'll need the bot URL, in my case it's &lt;a href="http://localhost:3978/api/messages"&gt;http://localhost:3978/api/messages&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I will take this URL and put it in the Bot Framework Emulator, and then hit Connect.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ae7DqKCB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1631978029192/ASXoNIuns.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ae7DqKCB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1631978029192/ASXoNIuns.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we can send the first message:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WD7pdt80--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1631978100157/krofz5y6h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WD7pdt80--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1631978100157/krofz5y6h.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, everything I type is sent back to me by the bot.&lt;/p&gt;

&lt;h3&gt;
  
  
  How it works
&lt;/h3&gt;

&lt;p&gt;Now, I want to explain you how everything works, so let's head back to Visual Studio and look at the solution.&lt;/p&gt;

&lt;p&gt;The "magic" starts in the file "BotController.cs". This is a basic ASP.NET controller with only one endpoint, POST/GET on /api/messages. In this endpoint you can see that there's a simple call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;await Adapter.ProcessAsync(Request, Response, Bot);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once this line is executed, the method "OnMessageActivityAsync" from EchoBot.cs is invoked. But how exactly does it do this? Well, if you look in the constructor, you can see that the BotController receives two parameters, an IBotFrameworkHttpAdapter and an IBot.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public BotController(IBotFrameworkHttpAdapter adapter, IBot bot)
{
       Adapter = adapter;
       Bot = bot;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you now look in Startup.cs, we do the following service registrations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;services.AddSingleton&amp;lt;IBotFrameworkHttpAdapter, AdapterWithErrorHandler&amp;gt;();
services.AddTransient&amp;lt;IBot, EchoBot&amp;gt;();

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So every GET/POST on /api/messages will be processed by our EchoBot and the OnMessageActivityAsync is just one of the ways it can interpret it.&lt;/p&gt;

&lt;p&gt;Here, you can see that we do two things:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;protected override async Task OnMessageActivityAsync(ITurnContext&amp;lt;IMessageActivity&amp;gt; turnContext, CancellationToken cancellationToken)
{
      var replyText = $"Echo: {turnContext.Activity.Text}";
      await turnContext.SendActivityAsync(MessageFactory.Text(replyText, replyText), cancellationToken);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, we take the text send by the user, this can be found in &lt;strong&gt;turnContext.Activity.Text&lt;/strong&gt;. Then, we send this text back using the method &lt;strong&gt;SendActivityAsync()&lt;/strong&gt; from turnContext.&lt;/p&gt;

&lt;p&gt;There's also a call to &lt;strong&gt;MessageFactory.Text()&lt;/strong&gt; here. This is because we are actually sending an &lt;strong&gt;Activity&lt;/strong&gt; , but an activity can be of multiple times and we just send text, hence the call to MessageFactory.Text. In a future blog post from this series we'll send other types of activities as well.&lt;/p&gt;

&lt;p&gt;That's it for this article, I don't want to make it too long. In the next one we'll connect to the bot from Microsoft Teams, so if you want to learn more you can follow me on &lt;a href="https://twitter.com/thewindev"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://thewindev.net/series/bot-framework"&gt;Creating a bot with the Bot Framework SDK&lt;/a&gt; appeared first on &lt;a href="https://thewindev.net"&gt;thewindev.net&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>botframework</category>
      <category>teams</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>Migrating a Blazor WebAssembly app to .NET 5</title>
      <dc:creator>Bogdan Bujdea</dc:creator>
      <pubDate>Thu, 12 Nov 2020 00:24:24 +0000</pubDate>
      <link>https://dev.to/thewindev/migrating-a-blazor-webassembly-app-to-net-5-414g</link>
      <guid>https://dev.to/thewindev/migrating-a-blazor-webassembly-app-to-net-5-414g</guid>
      <description>&lt;p&gt;I have a small side project made with Blazor WebAssembly and although it's great for my needs, the debugging experience is very poor. Lucky for us, this was greatly improved in .NET 5 and I wanted to try it out immediately. The migration was quite easy but I did encounter some issues, so here are the steps you have to do to to upgrade your app and then we'll go over the issues:&lt;/p&gt;

&lt;h2&gt;
  
  
  Migration
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Install  &lt;a href="https://devblogs.microsoft.com/visualstudio/visual-studio-2019-v16-8/" rel="noopener noreferrer"&gt;Visual Studio 16.8&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;Change the first line in your Blazor WebAssembly project from
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Microsoft.NET.Sdk.Web
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Microsoft.NET.Sdk.BlazorWebAssembly
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Change target framework to .NET 5
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;TargetFramework&amp;gt;netstandard2.1&amp;lt;/TargetFramework&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;becomes&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;TargetFramework&amp;gt;net5.0&amp;lt;/TargetFramework&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Update other packages to &lt;code&gt;5.0.0&lt;/code&gt; (&lt;code&gt;Microsoft.AspNetCore.*&lt;/code&gt;, &lt;code&gt;System.Net.Http.Json&lt;/code&gt;, &lt;code&gt;Microsoft.EntityFrameworkCore&lt;/code&gt;, etc.)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In the end, this is how my diff looks like for a fresh project (&lt;a href="https://cdn.hashnode.com/res/hashnode/image/upload/v1605139050446/v8GMkgpP9.png" rel="noopener noreferrer"&gt;click to open in a new tab&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1605139050446%2Fv8GMkgpP9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1605139050446%2Fv8GMkgpP9.png" alt="Migration"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Errors
&lt;/h2&gt;

&lt;p&gt;If you have any errors, there are some things you can try:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Remove &lt;code&gt;Microsoft.AspNetCore.Components.WebAssembly.Build&lt;/code&gt; from your project&lt;/li&gt;
&lt;li&gt;Remove the properties
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;RuntimeIdentifier&amp;gt;browser-wasm&amp;lt;/RuntimeIdentifier&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;UseBlazorWebAssembly&amp;gt;true&amp;lt;/UseBlazorWebAssembly&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;If the build still fails, do the usual routine in Visual Studio:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Clean solution&lt;/li&gt;
&lt;li&gt;Close Visual Studio and delete the bin/obj folders&lt;/li&gt;
&lt;li&gt;Delete the .vs folder&lt;/li&gt;
&lt;li&gt;Open Visual Studio and build the solution again&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Azure App Service
&lt;/h2&gt;

&lt;p&gt;If you're using Azure App Service, then you can deploy your Blazor app immediately because .NET 5 is already supported. However, if you just deploy your Blazor app after upgrading to .NET 5 then you'll encounter this error:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The specified version of Microsoft.NetCore.App or Microsoft.AspNetCore.App was not found.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To fix it, you just have to go to the App Service Configuration page and change the Stack from &lt;code&gt;.NET Core&lt;/code&gt; to &lt;code&gt;.NET&lt;/code&gt; and the .NET Framework version to &lt;code&gt;.NET 5&lt;/code&gt;. It should look like this in the end:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1605143271515%2FF7-_KTLhH.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1605143271515%2FF7-_KTLhH.png" alt="Azure App Service"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's it! Now you can take advantage of all the goodies that Blazor has to offer with .NET 5&lt;/p&gt;

&lt;p&gt;PS: I tried the debugging experience and indeed, it's way better now :)&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://thewindev.net/migrating-a-blazor-webassembly-app-to-net-5" rel="noopener noreferrer"&gt;Migrating a Blazor WebAssembly app to .NET 5&lt;/a&gt; appeared first on &lt;a href="https://thewindev.net" rel="noopener noreferrer"&gt;thewindev.net&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>dotnet5</category>
      <category>dotnet</category>
      <category>blazor</category>
      <category>webassembly</category>
    </item>
    <item>
      <title>Overview of my smart home: The bedroom</title>
      <dc:creator>Bogdan Bujdea</dc:creator>
      <pubDate>Fri, 30 Oct 2020 10:57:11 +0000</pubDate>
      <link>https://dev.to/thewindev/overview-of-my-smart-home-the-bedroom-6c7</link>
      <guid>https://dev.to/thewindev/overview-of-my-smart-home-the-bedroom-6c7</guid>
      <description>&lt;p&gt;I haven't finished automating my house and I don't think I ever will. For me, this is a hobby, so there will always be some new gadget or I'll have a new idea that could make my life easier.&lt;/p&gt;

&lt;p&gt;One of the joys of implementing all of this is sharing it with the world, and a few months ago I wrote the first article in this series where I talked a bit about my home. Now, I want to go into greater detail and talk about everything I did in the past year with Home Assistant. I kept thinking how to categorize this in multiple blog posts because one would be too much, and I finally decided to go room by room.&lt;/p&gt;

&lt;p&gt;In each post you'll see what automations I implemented, what devices I purchased and how much I paid for them. I know that pricing matters when you're starting to create a smart home, so I want to be transparent and also mention which purchases I regret and which I don't. The detailed costs are at the end of the article.&lt;/p&gt;

&lt;p&gt;Now, let's start with the bedroom.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Remotes and switches &lt;/li&gt;
&lt;li&gt; Lights &lt;/li&gt;
&lt;li&gt; Curtains &lt;/li&gt;
&lt;li&gt; TV &lt;/li&gt;
&lt;li&gt; Baby monitor &lt;/li&gt;
&lt;li&gt; Sensors &lt;/li&gt;
&lt;li&gt; Climate &lt;/li&gt;
&lt;li&gt; Voice assistants &lt;/li&gt;
&lt;li&gt; Cost &lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Remotes and switches
&lt;/h2&gt;

&lt;p&gt;I still think that physical switches are useful because they are a lot faster than voice commands or opening the Home Assistant app. In the bedroom I have 2 smart switches which are &lt;a href="https://xiaomi-mi.com/sockets-and-sensors/xiaomi-mi-wireless-switch/"&gt;Xiaomi Zigbee&lt;/a&gt; buttons, and 1 &lt;a href="https://www.philips-hue.com/en-us/p/hue-dimmer-switch/046677473372"&gt;Philips Hue remote&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I have one Xiaomi button right on top of my old light switch:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--K9lDi7rQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603991050483/RylysNN4E.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--K9lDi7rQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603991050483/RylysNN4E.png" alt="Door switch"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On top of my bed, I have the other Xiaomi button and the Philips Hue remote. You can't see them from this photo because they are out of sight, but I wanted to show you that I placed them above my head, so me or my wife just have to reach up to control them. &lt;/p&gt;

&lt;p&gt;Keep in mind that a plain physical switch might not be as fancy as voice control or an app, but it is a lot faster and you'll use it more often if it is placed in the right place.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--outvHlnC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603991770524/98-4aTh9f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--outvHlnC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603991770524/98-4aTh9f.png" alt="Bed switches"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Xiaomi buttons allow you to map actions for 6 events, these are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"click": toggle lights&lt;/li&gt;
&lt;li&gt;"double click": toggle TV&lt;/li&gt;
&lt;li&gt;"triple click": call Roborock to clean this room&lt;/li&gt;
&lt;li&gt;"quadruple click": toggle only the light strip under the bed&lt;/li&gt;
&lt;li&gt;"long press": toggle the curtain&lt;/li&gt;
&lt;li&gt;"multiple click": I haven't used this one&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can see these configured in Node-Red here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--C5dkp5Ya--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603992203632/0stv6HBju.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--C5dkp5Ya--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603992203632/0stv6HBju.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Both buttons have the same set of commands I described above because in some cases I want to trigger them from the bed and in other cases I want to trigger them when I enter/exit the room.&lt;/p&gt;

&lt;p&gt;The Hue remote has 4 buttons that I use to control my lights, on/off and +/- for brightness. The remote is paired directly with the lights so even if Home Assistant is down this means I can still control my lights. This means that the light responds instantly as well, so if you have Zigbee lights and switches, try to pair them directly. The Xiaomi buttons can't be paired with other devices, so each time I press one of them they first have to wake up, communicate with the Conbee II stick, then with Home Assistant, and then I can process the command, so in the end it's always 1-2 seconds slow.&lt;/p&gt;

&lt;h4&gt;
  
  
  Future plans
&lt;/h4&gt;

&lt;p&gt;I still have the "dumb" light switch near the door and I plan to use Shelly 1L when it will be available to make it smart. The problem with my initial light switches is that they don't have the neutral wire which would keep the device powered when the light switch is off.  Shelly 1L should solve this, so I plan to buy a few and test them in the following months.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lights
&lt;/h2&gt;

&lt;p&gt;In this room I have a Philips Hue light bulb on the ceiling, and a Philips Hue light strip under my bed (you can see it in the photo above). They can be controlled like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Echo Dot and Google Home Mini by voice&lt;/li&gt;
&lt;li&gt;Hue remote&lt;/li&gt;
&lt;li&gt;Xiaomi Zigbee buttons&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I use motion sensors to trigger lights in other rooms, but not in this one. Our kids sleep with us in the bedroom and turning on the lights in this room is something we're very careful with, because we don't want to wake them up by accident. People with kids will understand :)&lt;/p&gt;

&lt;h3&gt;
  
  
  Curtains
&lt;/h3&gt;

&lt;p&gt;This is one of the best ideas I've had because our bedroom window points to the east, so each morning the sun light fills the entire room and wakes me up every time. Using Node-Red, I have the following automation in place:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;each morning, half an hour before sunrise, the curtain will close, keeping the room dark&lt;/li&gt;
&lt;li&gt;at 8AM when I have to wake up, the curtain will open waking me up for work&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The curtain can be controlled by voice with Alexa and Google Assistant, but I can also do a long press on the two Xiaomi buttons to toggle it opened or closed. &lt;/p&gt;

&lt;h2&gt;
  
  
  TV
&lt;/h2&gt;

&lt;p&gt;In the bedroom we have our old TV that we bought ~5 years ago, which is not smart. Our son chewed the remote control ~4 years ago and broke it, so ever since we use a Chromecast to control it. We usually use voice to turn it on or off (Google Home Mini), and I bought a BroadLink remote that can send the most important commands like volume, on/off, change source, etc.&lt;br&gt;
We use the Hue Remote that I mentioned earlier to control the TV volume with the brightness buttons. So, if I press the "+" button for brightness, then an event will be triggered in Node-Red and I use this to call the BroadLink remote with the command to increase the volume.&lt;/p&gt;

&lt;h3&gt;
  
  
  Baby monitor
&lt;/h3&gt;

&lt;p&gt;When my daughter was born this year, I used this opportunity to create a baby monitor using some parts that I had lying around: a Pi Zero with motionEye installed, a Hue motion sensor and an old Logitech web cam. After a while I bought a Pi camera because it has night vision.&lt;/p&gt;

&lt;p&gt;For easier control, I created a card in Home Assistant for the baby monitor. It includes the camera feed and a few buttons.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dO6IIlZx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603701937308/nkrFs1u3W.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dO6IIlZx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603701937308/nkrFs1u3W.png" alt="Baby monitor card"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I blurred the camera feed, but on the bottom line you can see a few buttons. The one from the center is the motion sensor. The color will turn white when motion is detected, and when I click on it, I can see the history of the sensor.&lt;br&gt;
On the right there are 3 more buttons. The first one enables notifications for the motion sensor. The second one is for the light strip and the 3rd one is for the ceiling light, so we can toggle them with one click.&lt;/p&gt;

&lt;p&gt;We can turn on notifications by voice as well, I just have to say "Alexa, turn on notifications for bedroom motion", and each time our daughter will move, Home Assistant will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Send push notifications to our phones&lt;/li&gt;
&lt;li&gt;Flicker the lights in the other rooms of the house each time motion is detected&lt;/li&gt;
&lt;li&gt;Alexa will whisper in every room besides the bedroom that "motion was detected in the baby crib"&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In this way I can watch a movie with my wife in the living room without being worried that our daughter is awake and we don't hear her crying. Then we can open the camera feed in Home Assistant and see if she's really awake or she just turned on the other side.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sensors
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;I have a motion sensor that is mainly used for the baby crib. This is a &lt;a href="https://www.philips-hue.com/en-us/p/hue-motion-sensor/046677473389"&gt;Philips Hue sensor&lt;/a&gt; so it also has light and temperature sensors.&lt;/li&gt;
&lt;li&gt;for CO2 monitoring I use a  &lt;a href="https://www.netatmo.com/en-eu/weather/weatherstation"&gt;Netatmo weather station&lt;/a&gt;. This one also has sound and temperature, but I don't use the sound sensor because it only updates every 10 minutes so it's not very useful. It also has an outdoor module which I only use for the outside temperature. Overall, this was a bad investment, I only use the CO2 sensor mostly so I could've bought something cheaper.&lt;/li&gt;
&lt;li&gt;I have a  &lt;a href="https://www.amazon.co.uk/Xiaomi-Window-Sensor-Smart-Ecosystem/dp/B07VGHDL7M/ref=sr_1_2?dchild=1&amp;amp;keywords=xiaomi+door+window&amp;amp;qid=1603994006&amp;amp;sr=8-2"&gt;window sensor from Xiaomi&lt;/a&gt;  which is useful in two situations. First, Alexa warns me when I leave home and I forget the window opened, and the bedroom AC will turn off if the window is opened for more than 1 minute.&lt;/li&gt;
&lt;li&gt;the door has a Xiaomi sensor as well but I don't use it at all, now I realize I should find another use for it&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Future plans
&lt;/h4&gt;

&lt;p&gt;I'd like to have a better way of detecting how many people are in the bedroom and who they are, so I can create more precise automations. I started to play with Azure Cognitive services so I can use the feed from my camera to detect and identify faces. It's still a work in progress but it has potential.&lt;/p&gt;

&lt;h2&gt;
  
  
  Climate
&lt;/h2&gt;

&lt;p&gt;I have a multi split unit from Gree, and I managed to find a HACS component for it. This allows me to control the AC from Home Assistant, Alexa but I also have some automations that control it by themselves when the "auto climate" switch is on in Home Assistant.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;if we're not home and the temperature is over 25C, it will turn on the AC, and below 23C it will turn it back off&lt;/li&gt;
&lt;li&gt;during the night it will maintain the temperature between 23.5C and 24.5C by turning the AC on and setting the fan speed to low so it won't wake us up&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Future plans
&lt;/h4&gt;

&lt;p&gt;I have underfloor heating in my home but it's not smart. I only have some thermostats on the wall in some of my rooms that control the heating for those rooms independently. This week I should receive a bunch of Shelly 1 devices which I hope will help me make the heating smart. Either I'll succeed, or I'll break my heating just as winter is around the corner, so wish me luck!&lt;/p&gt;

&lt;h2&gt;
  
  
  Voice assistants
&lt;/h2&gt;

&lt;p&gt;We have an Echo Dot which is mostly used by our son to play music. We use the Google Home Mini because it can directly control the Chromecast. Both devices can control most of the devices we have in our home, but we mostly use Alexa.&lt;/p&gt;

&lt;h4&gt;
  
  
  Future plans
&lt;/h4&gt;

&lt;p&gt;I want to make Alexa ask me if she should trigger any actions. For example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"It's been 2 days since you vacuumed the bedroom, would you like me to call Roborock now?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;or&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The temperature is over 25 degrees, would you like me to turn on the AC?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This can be done with  &lt;a href="https://github.com/keatontaylor/alexa-actions/wiki"&gt;Alexa Actions&lt;/a&gt;, I just have to find time for it :)&lt;/p&gt;

&lt;p&gt;In the next year however I'd like to give up Google and Alexa, and instead use &lt;a href="https://www.home-assistant.io/blog/2019/11/20/privacy-focused-voice-assistant/"&gt;Almond &amp;amp; Ada&lt;/a&gt;. The reasons are obvious, I want better control and I want to own my data, so it's about privacy as well.&lt;/p&gt;

&lt;h4&gt;
  
  
  Future plans for the bedroom
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;I plan to use Azure Cognitive Services to detect which persons are in the bedroom and I can use the baby monitor for this. I'm still playing with this but it looks promising!&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;I have a twin adjustable bed which can be controlled with two remote controls  &lt;a href="https://cdn.hashnode.com/res/hashnode/image/upload/v1603991770524/98-4aTh9f.png"&gt;on each side&lt;/a&gt;. I plan to integrate them somehow in Home Assistant, so when I say "Hey Google, play X on Netflix" then each side will go up enough to be able to see the TV, something like this:&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kBPBlA5U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603998152297/hz08RipGS.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kBPBlA5U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603998152297/hz08RipGS.png" alt="Adjustable bed"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The curtain will close, lights will be dimmed, and I can just eat my snacks and watch the movie :D&lt;/p&gt;

&lt;h2&gt;
  
  
  Cost
&lt;/h2&gt;

&lt;p&gt;Overall, the bedroom was one of the most expensive rooms, with a total of ~868 $. After all this time I think the top 3 devices/automations that make my life easier in this room are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The baby monitor&lt;/li&gt;
&lt;li&gt;The curtain&lt;/li&gt;
&lt;li&gt;Alexa&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0ykqCk6b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603993628683/sKDq-793Y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0ykqCk6b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603993628683/sKDq-793Y.png" alt="Cost"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the next article I'll talk about my office, where you'll see how Home Assistant is also a Work Assistant.&lt;/p&gt;

</description>
      <category>homeassistant</category>
      <category>smarthome</category>
    </item>
    <item>
      <title>Creating subflows in Node-Red</title>
      <dc:creator>Bogdan Bujdea</dc:creator>
      <pubDate>Thu, 22 Oct 2020 12:26:23 +0000</pubDate>
      <link>https://dev.to/thewindev/creating-subflows-in-node-red-4ce5</link>
      <guid>https://dev.to/thewindev/creating-subflows-in-node-red-4ce5</guid>
      <description>&lt;p&gt;In my opinion, one of the most useful features that Node-Red has to offer is the ability to create subflows. According to the  &lt;a href="https://nodered.org/docs/user-guide/editor/workspace/subflows"&gt;official documentation&lt;/a&gt; , subflows are:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;a collection of nodes that are collapsed into a single node in the workspace.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I won't bore you with the documentation, instead I'll give you a simple example that you can try without having to install anything besides Node-Red and Home Assistant. The goal is for you to be able to follow this article and create your own subflow and understand where and why it's useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scenario: Sending notifications to your phone from Node-Red
&lt;/h2&gt;

&lt;p&gt;Let's say that you want to send notifications from Node-Red to your phone using the "notify" service. The easiest way to do this would be to use the "call service" node like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4KkL55aF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603306217592/bWhPGhbVR.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4KkL55aF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603306217592/bWhPGhbVR.png" alt="call service node"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, if you want to send a notification, you can use this node like this wherever you want:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JKNDo6tX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603306312853/xxNps8i3F.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JKNDo6tX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603306312853/xxNps8i3F.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here, I duplicated my node and I just changed the message. In one node I send the message "The office door is opened", and in the other one I send the message "The office door is closed". &lt;/p&gt;

&lt;p&gt;For this scenario, this is enough, you can just duplicate the node and change the message, but things are not always that simple. &lt;/p&gt;

&lt;p&gt;For example, let's say that you don't want to send notifications between 10 PM and 8 AM. An easy way to implement this requirement would be like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kR1v5QaO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603306591477/eHbwWUPB_.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kR1v5QaO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603306591477/eHbwWUPB_.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I used the "time range" node to check if the current time is between 10 PM and 8 AM, and if it's not, then I'll allow the notification to be sent.&lt;/p&gt;

&lt;p&gt;Then one day you're in a meeting and your phone starts ringing from a notification, so you realize that it's not enough to turn it off during the night, you need a setting that could be turned on/off during the day. In this case we can use an input_boolean in our Home Assistant dashboard which will block all notifications from being sent when it's turned off. To implement this, we have to add another node, and because we're sending two notifications, it's two nodes that have to be added. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kKHYhyoC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603306914703/DfheBRVWk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kKHYhyoC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603306914703/DfheBRVWk.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is starting to look ugly, and imagine if you had to do this for many other places as well, it's a nightmare. If you're a programmer, then principles like "DRY"(Don't Repeat Yourself) are starting to pop into your head, and indeed, this is just like "code" duplication. Once you have a new idea or you find an issue, you'll have to go everywhere you have a notification and either add another node or apply the same fix. But how do we fix this?&lt;/p&gt;

&lt;h2&gt;
  
  
  Subflows
&lt;/h2&gt;

&lt;p&gt;I'm going to create a subflow with these nodes so I can reuse them in other places. Here's how you can do that:&lt;/p&gt;

&lt;p&gt;First, select the 3 nodes from the first notification:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wukbQUOE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603307202078/5csXL4nZo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wukbQUOE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603307202078/5csXL4nZo.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now open the menu from the right, and choose "Subflows" -&amp;gt; "Selection to subflow".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0GRf8DDT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603307257806/tCMUgqYE7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0GRf8DDT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603307257806/tCMUgqYE7.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You should now see something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lf0qvAbN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603307304596/ezBc-sFa3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lf0qvAbN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603307304596/ezBc-sFa3.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This means that a new subflow was created with the name "Subflow 1", and every time you want to use it you can drag it from the "subflows" sections in the left part. &lt;/p&gt;

&lt;p&gt;However, we're not done yet. Indeed, you can reuse it everywhere but now you just have a subflow that will send a notification with the message "The office door is opened" between 8 AM and 10 PM. If we really want to make this right then we'll have to add "parameters" so that we can specify what message and title to be sent.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using environment variables
&lt;/h3&gt;

&lt;p&gt;Let's open our subflow by double clicking it, and then we'll click on "Edit subflow template". You'll be taken to the subflow template where you can see the 3 nodes we had earlier:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JdRu42l5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603307502926/2Ad_aiSBc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JdRu42l5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603307502926/2Ad_aiSBc.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now click on "edit properties", and then click on the "add" button:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aYujlm1s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603307626521/UnAt0yvdc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aYujlm1s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603307626521/UnAt0yvdc.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You just added an environment variable. You can set a name for it that we'll use to reference it later, and you can also add a default value. In my example, I added a variable called "message" with the label "Message" and the default value "Hello".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Qo20Qe9s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603307774177/pkfmZ1jr3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Qo20Qe9s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603307774177/pkfmZ1jr3.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you add these two, just click on Done and get back to our flow. Once you're there, double click on the subflow node again.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FYikTBWD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603307835997/oYY07EBiu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FYikTBWD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603307835997/oYY07EBiu.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You should see the variables like in the image above. The "message" variable has the label "Message", and the default value "Hello". You can change this one with the message "The office door is opened", and set whatever title you want. We'll now delete the 3 nodes from the other branch of the sensor and put our subflow instead.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SaZ5B_5J--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603307991738/0lrlhSQAN.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SaZ5B_5J--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603307991738/0lrlhSQAN.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now there's one more thing we have to do in order to use the values we set in the variables. Inside our subflow we have the notification node, but this node still uses the message "The office door is opened", so instead of using a hardcoded message we'll tell it to use whatever value we have in our "message" variable, and we'll apply the same for the "title".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--a84P1nYr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603308146938/j8GDQFHY5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--a84P1nYr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603308146938/j8GDQFHY5.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For retrieving the variable values we used  &lt;a href="https://thewindev.net/communication-in-node-red-jsonata"&gt;JSONata&lt;/a&gt; , and this is how the JSON looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{ "message": $env('message'), "title": $env('title') }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now you can deploy and test the flow. You should receive a different message according to what you used in the "message" and "title" variable, something similar to the image below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IE20uXH_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603310695556/4KSp5gw5A.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IE20uXH_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603310695556/4KSp5gw5A.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also import this JSON in Node-Red if you're having issues:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;Congratulations, you just created your first subflow! The advantages are pretty obvious. Each time you want to send a notification, you won't have to duplicate the initial 3 nodes that we had, instead you'll just use this subflow. And if you need to modify something in the future(like the schedule 10 PM - 8 AM), you have to do it only in the subflow template, not in every usage of the subflow.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's next?
&lt;/h3&gt;

&lt;p&gt;It's up to you. Now you can create your own nodes as subflows that you can reuse as you see fit. The one I'm most proud is my Alexa subflow.&lt;/p&gt;

&lt;p&gt;These are the environment variables it supports:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xLVHGTmW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603310823900/AElV3kibp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xLVHGTmW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603310823900/AElV3kibp.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, it's not only strings, I also have a numeric control for the volume, a dropdown where I can pick a sound instead of a message, and checkboxes that represent all my Echo Dot devices. Here's how it looks inside the subflow:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SofdlJPq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603310908378/6E9zSo56S.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SofdlJPq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603310908378/6E9zSo56S.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I know it's a bit messy, so I'll try to explain the subflow from the start:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The entry point will call a function that transforms the data received in a model that can be understood by the Alexa Media Player component, basically it's a JSON with certain properties(message, list of devices to play it on, volume, etc).&lt;/li&gt;
&lt;li&gt;Next, I'll check if the JSON is valid or not&lt;/li&gt;
&lt;li&gt;Then I'll check if an input_boolean called "Alexa notifications" is on. If it's not on, then I'll stop. I actually have a card for Alexa where I can turn settings on or off&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--N7LbjsOO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603311801236/6OFbaCWeY.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--N7LbjsOO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603311801236/6OFbaCWeY.png" alt="image.png"&gt;&lt;/a&gt;&lt;br&gt;
The sleep mode triggers automatically during the night when there's no motion for 5 minutes straight. The "whisper" mode will convert all my messages to whisper instead of loud voice, I usually set this on when my daughter is asleep. And my fridge is not smart, so I used a contact sensor and Alexa to notify me if I left it open more than 1 minute. Let's continue with our flow :)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the notifications are allowed, then I'll check if the sleep mode is on, otherwise I won't continue.&lt;/li&gt;
&lt;li&gt;I'll get the volume for each echo dot and see if any of them needs to be changed. I could do this directly but setting the volume takes a few seconds, so in most cases it's not necessary and I'll save time by not calling the set volume function.&lt;/li&gt;
&lt;li&gt;I check if I should play a sound or a message. We can't have both, so that's why I have two different nodes for messages and sounds.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And that's it. I hope you enjoyed this article and you learned enough about subflows so that you can create your own. The next article from the series will be about functions, we'll see how we can access Home Assistant entities and set environment variables with javascript. Until then, you can subscribe to this blog with the form on top and follow me on  &lt;a href="https://twitter.com/thewindev"&gt;Twitter&lt;/a&gt; !&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://thewindev.net/creating-subflows-in-node-red"&gt;Creating subflows in Node-Red&lt;/a&gt; appeared first on &lt;a href="https://thewindev.net"&gt;thewindev.net&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>nodered</category>
      <category>homeassistant</category>
      <category>smarthome</category>
    </item>
    <item>
      <title>Integrating the Hashnode API with my smart home</title>
      <dc:creator>Bogdan Bujdea</dc:creator>
      <pubDate>Wed, 21 Oct 2020 09:06:47 +0000</pubDate>
      <link>https://dev.to/thewindev/integrating-the-hashnode-api-with-my-smart-home-3i4p</link>
      <guid>https://dev.to/thewindev/integrating-the-hashnode-api-with-my-smart-home-3i4p</guid>
      <description>&lt;p&gt;Someone asked what cool things you can do with the Hashnode API, so I'll show you the first thing I did right after I migrated to Hashnode.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/GZdfawS65f0"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;I'm trying to make a habit out of posting one article every day, so I created a recurrent task in Todoist for this. However, I like to automate things, so instead of manually checking that task every day, how about I do this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;each time I post an article, the recurrent task completes itself(it will appear again tomorrow)&lt;/li&gt;
&lt;li&gt;Alexa will congratulate me for posting the article in the Echo Dot from my office&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;How can I do all of this? With Home Assistant of course, because that's the place where I integrate my smart home with most of my online tools. I'm going to use Node-Red for this, so I have to find a way to refresh the latest article and trigger an event when it changes. Here are the steps to make this happen:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
I'll make a request to the Hashnode API every 30 minutes and save the id of the last posted article in a Home Assistant variable&lt;/li&gt;
&lt;li&gt;When the variable changes an event will be triggered to which I'll subscribe using Node-Red. Once the event fires, I'll complete the Todoist task and I'll ask Alexa to congratulate me for the new article&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Making GraphQL requests from Node-Red
&lt;/h2&gt;

&lt;p&gt;In order to make requests to the Hashnode API we need to use GraphQL. Fortunately for us,  &lt;a href="https://flows.nodered.org/node/node-red-contrib-graphql"&gt;Node-Red has a node for this called "graphql"&lt;/a&gt;,  so make sure you have it installed before going further.&lt;br&gt;
Next, we're going to add the node to our flow and configure it to make a request towards Hashnode. I'll be using this GraphQL query to retrieve the ids of my latest articles, so even if I update a title I will know it's the same article because I'm storing the id, which won't change.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;And here's how the node is configured in the UI.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--huwhVjEY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603213698765/J2HRIGad_.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--huwhVjEY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603213698765/J2HRIGad_.png" alt="Add GraphQL node"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, there's a dropdown where you can define the server. Click on the edit button and set the address of the Hashnode API, which is  &lt;a href="https://api.hashnode.com"&gt;https://api.hashnode.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--b6SWmW9Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603213792193/LpJIXZwSU.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--b6SWmW9Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603213792193/LpJIXZwSU.png" alt="Add hashnode server"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now let's run this and see what we get. I'll add an inject node before the GraphQL request and a debug node at the end so we can see what response we have from Hashnode.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--80jT-J9z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603213986795/SsqW-cfI5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--80jT-J9z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603213986795/SsqW-cfI5.png" alt="Hashnode response"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, I have the latest articles returned with their title and id because these are the only fields I requested. For my scenario I need the first one because that is the latest article. Now I am going to use a HACS component called  &lt;a href="https://github.com/snarky-snark/home-assistant-variables"&gt;"home-assistant-variables"&lt;/a&gt;. Go ahead and install this, because we're going to save the id in a variable.&lt;/p&gt;

&lt;p&gt;Once you have it installed, you're going to have to define a variable in your configuration.&lt;/p&gt;

&lt;p&gt;This is how I defined my variable:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Now you'll have to restart Home Assistant before going further. Go on... I'll wait!&lt;/p&gt;

&lt;p&gt;We'll use Node-Red to set its value with the response we get from Hashnode. To do this, grab a "call service" node from Node-Red and configure it like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--szzyiMb5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603214530367/_YZMoYDZN.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--szzyiMb5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603214530367/_YZMoYDZN.png" alt="Set variable"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select JSONata on the data field, and add this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yK1aD0nj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603214567119/sbSIZMlg6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yK1aD0nj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603214567119/sbSIZMlg6.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now you can deploy and run the flow. Once we get a response, the variable should have the value of the latest article.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3PlphBa9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603214638643/_a3UjH9zY.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3PlphBa9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603214638643/_a3UjH9zY.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Congratulations, if you got here it means the first part is ready.&lt;/p&gt;

&lt;h2&gt;
  
  
  Alexa integration
&lt;/h2&gt;

&lt;p&gt;Now that we have our variable updated every time a new article is posted, this means we can listen to state changes using Node-Red. Grab a "events: state" node and configure it like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NLKCwf4q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603214839052/E2NAVhwDU.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NLKCwf4q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603214839052/E2NAVhwDU.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next part is a bit tricky, especially if you don't have Alexa configured in Home Assistant. I won't cover this here because it would make this blog post too long and the documentation from the  &lt;a href="https://github.com/custom-components/alexa_media_player"&gt;Alexa Media Player&lt;/a&gt;  component is good enough to get you started.&lt;/p&gt;

&lt;p&gt;In order to make things easier for myself, I created a subflow for Alexa which handles a lot of stuff, from deciding what Echo Dot device to use, to setting the volume or canceling notifications if it knows we're asleep. I use this subflow like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iWOod2Ax--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603215014948/jWC0eny52C.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iWOod2Ax--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603215014948/jWC0eny52C.png" alt="Configuring Alexa"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, I can set the message, which device to play this on, what volume to use, if it should whisper or broadcast, etc.&lt;br&gt;
I even have sounds so I could ring an alarm if an article is featured for example... or if someone breaks into the house :)&lt;/p&gt;

&lt;p&gt;That's it for the Alexa configuration. At the end of the article I'll show you a live demo of this! Now let's see how we can update a Todoist task from Node-Red!&lt;/p&gt;
&lt;h2&gt;
  
  
  Working with the Todoist API from Node-Red
&lt;/h2&gt;

&lt;p&gt;Todoist has a REST API that we can use to update a task. Basically, we'll do a POST request with our access token on a specific endpoint, and we'll need the id of the task we want to update.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--S9-CyOdo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603215267737/_gyzZRodk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--S9-CyOdo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603215267737/_gyzZRodk.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The easiest way to get the ID of the task is from the URL if you use the web app. Once we have this, we'll use it in Node-Red and make a request to Todoist.&lt;/p&gt;

&lt;p&gt;First we need a function to define the payload for Todoist. In here we'll encode a JSON that has the task id, the Todoist token for authentication and a random UID. The easiest thing to do would be to copy this and replace the task id and the token.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GDDHMzN6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603215656578/GM4sPpT8N.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GDDHMzN6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603215656578/GM4sPpT8N.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we'll add an HTTP request node where we configure the  &lt;a href="https://api.todoist.com/sync/v8/sync"&gt;Todoist API URL&lt;/a&gt;. The method and the body of the request are taken from the payload sent from the function. Here's how it should look like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--h4RHbSE3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603215760329/9pFa4RtDP.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--h4RHbSE3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603215760329/9pFa4RtDP.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, I'll post this article and record what happens with Alexa and the Todoist task. If everything goes well, I'll check every 5 seconds and my event should fire when the article is posted. Wish me luck :)&lt;/p&gt;

&lt;p&gt;EDIT: It worked :)&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/GZdfawS65f0"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://thewindev.net/integrating-the-hashnode-api-with-my-smart-home"&gt;Integrating the Hashnode API with my smart home&lt;/a&gt; appeared first on &lt;a href="https://thewindev.net"&gt;thewindev.net&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>smarthome</category>
      <category>alexa</category>
      <category>homeassistant</category>
      <category>todoist</category>
    </item>
    <item>
      <title>Background processing in .NET Core: Azure Functions</title>
      <dc:creator>Bogdan Bujdea</dc:creator>
      <pubDate>Mon, 19 Oct 2020 23:58:42 +0000</pubDate>
      <link>https://dev.to/thewindev/background-processing-in-net-core-azure-functions-3io9</link>
      <guid>https://dev.to/thewindev/background-processing-in-net-core-azure-functions-3io9</guid>
      <description>&lt;p&gt;In this article I'll talk about my experience with Azure Functions. Instead of showing you how to do a  &lt;a href="https://docs.microsoft.com/en-us/azure/azure-functions/functions-create-your-first-function-visual-studio"&gt;"hello world in Azure Functions"&lt;/a&gt; , I'm going to talk about some scenarios where I used them and what I learned from this. So in this article I'll tell you how I created a small project one night that gained hundreds of thousands of users by next morning. We'll see if Azure Functions lived up to the challenge and if they are as cheap as they are advertised.&lt;/p&gt;


&lt;blockquote class="ltag__twitter-tweet"&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--k77uldNK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/1088218418950463489/keMT19rX_normal.jpg" alt="Bogdan Bujdea profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        Bogdan Bujdea
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        &lt;a class="comment-mentioned-user" href="https://dev.to/thewindev"&gt;@thewindev&lt;/a&gt;

      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P4t6ys1m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      Took 30 minutes to write and deploy an &lt;a href="https://twitter.com/hashtag/AzureFunctions"&gt;#AzureFunctions&lt;/a&gt; project that processes partial results for Romanian elections...now let's see how it will hold up with the flow of people eager to see the results &lt;a href="https://twitter.com/hashtag/europarlamentare2019"&gt;#europarlamentare2019&lt;/a&gt; &lt;a href="https://t.co/InFTDchGXx"&gt;bit.ly/rezultateprovi…&lt;/a&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      22:39 PM - 26 May 2019
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1132778411552907264" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-reply-action.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1132778411552907264" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-retweet-action.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      9
      &lt;a href="https://twitter.com/intent/like?tweet_id=1132778411552907264" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-like-action.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
      51
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


&lt;p&gt;I'll break this down in the 3 parts, so it's easier for you to navigate:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The story&lt;/strong&gt; - I'll talk a bit about the project so we'll understand what it does and why I did some things in a certain way&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Azure Functions&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Conclusion&lt;/strong&gt; - we'll see what I learned and what would I change&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The story
&lt;/h2&gt;

&lt;p&gt;It was May 26th, 2019, and in Romania it was election day for the European Parliament. After 10 PM the results started to appear on the official government website, but they were stored in csv files. The process was like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;each polling station sends their results to the central election committee&lt;/li&gt;
&lt;li&gt;they add this as a line in a CSV file&lt;/li&gt;
&lt;li&gt;people could download this CSV file and see the latest results&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means that if you wanted to see the stats for the counted votes up to a certain time, you would have to download the CSV and sum up the columns for each candidate. Then you had to do this each time a polling station sent in their votes so you would have a live update of the results. Of course, I did this once, I did it again after 10 minutes... but then I said to myself "why the hell am I manually downloading a file when this could be done in a few lines of code?". So I started working on this.&lt;/p&gt;

&lt;p&gt;First, there was a console app. Each time I run it, the CSV is downloaded, the results are aggregated and displayed in descending order. Great, now I have this tool for myself, but there are 18 million voters out there who would be interested in the results as well as I am. So how could I share this with them in the easiest way possible?&lt;/p&gt;

&lt;h2&gt;
  
  
  Azure Functions
&lt;/h2&gt;

&lt;p&gt;In one of the talks I've given two years ago, I said that Azure Functions are great for prototyping. You could just create a function with the piece of code that you need and deploy it in Azure, so this is what I did. I took the code from my console app and put it in a function. Then, I just formatted the results as HTML and returned them from each HTTP call. Here's how it worked:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DYSoDme0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603146293353/CLYTMQog5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DYSoDme0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603146293353/CLYTMQog5.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is actually &lt;a href="https://github.com/thewindev/election-results-parser"&gt;the latest version&lt;/a&gt;. When I first deployed this I had only the HTTP function that would download the CSV file for every request. This is not a good idea when you have this many users, so I created another function that would download the file every 5 minutes and store the results in Azure Table Storage. Then, for each request I got from the users I would take the latest results from the table and sent them back as HTML.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;I know, the code is ugly, the UI was ugly as well, but it got the job done and this is what mattered.&lt;/p&gt;

&lt;p&gt;As soon as the function was live and functional, I posted the link on Facebook. &lt;br&gt;
The word got out and soon I was having users hitting the endpoint and seeing the results. After a while I got from a few users to a few thousand users per second, and this continued for about a day.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;PS: In case you're curious and you really want to see that page, the function is still up at  &lt;a href="https://bit.ly/rezultateprovizorii"&gt;this address&lt;/a&gt;, and this is what &lt;a href="https://rezultatevot.ro/elections/95/turnout"&gt;the project evolved into&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  What went well
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Easy to publish
&lt;/h4&gt;

&lt;p&gt;I liked how easy it was to deploy my code from a console app in an Azure Function. I just created the function, modified the code so it will return an HTML file with the results, and then I used the Visual Studio option for publishing a project. From there you're a few clicks away from seeing your function live.&lt;/p&gt;
&lt;h4&gt;
  
  
  Auto-scaling
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1HFv-qX3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603144657023/vZQJ0jCfP.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1HFv-qX3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1603144657023/vZQJ0jCfP.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The function scaled pretty fast when people started to use it and I enjoyed the fact that I didn't had to do anything special to make this work. This was just the built-in behavior and I just sat there and watched as it created more servers or killed them according to the number of users.&lt;/p&gt;
&lt;h4&gt;
  
  
  Free
&lt;/h4&gt;

&lt;p&gt;The function didn't charge me anything because I was under 4 million executions, and back then this was the limit. &lt;/p&gt;
&lt;h3&gt;
  
  
  What went bad
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Slow deployment
&lt;/h4&gt;

&lt;p&gt;A few hours after users started pouring in, I did a deploy to change the name of a candidate. The deploy is usually very fast but now I had tens of thousands of users hitting F5 in their browsers at the same time, so Azure couldn't create new servers that fast. It took ~10-15 minutes until the function managed to deploy enough servers (over 100 from what I remember) and I stopped seeing errors.&lt;/p&gt;
&lt;h4&gt;
  
  
  Not really free
&lt;/h4&gt;

&lt;p&gt;The function was free, but logging and storage are not. I enabled Application Insights to see more stats about the function and in a few days the cost was about ~40 $ for insights and ~10 $ for Azure Table Storage. So yes, the functions are cheap, even free, but additional services will incur costs and you have to pay attention if you need them. Microsoft is not actually selling Azure Functions, they're selling additional services that go along with them, remember this :)&lt;/p&gt;
&lt;h3&gt;
  
  
  What would I change?
&lt;/h3&gt;

&lt;p&gt;Well, I would be more careful on what services I decide to use with my functions. Application Insights provided a lot of metrics but it was too much for my needs, so if I had to cut costs, I would start with this. Also, I kept every snapshot of the results(meaning a csv file) in Azure Table Storage. It would've been easier to keep only the latest version, that would incur less costs.&lt;/p&gt;

&lt;p&gt;Overall, I think this was a perfect scenario for using Azure Functions. I needed something &lt;strong&gt;fast, easy to deploy, and cheap&lt;/strong&gt;. Azure Functions checked every item on the list and I would use this again if I had to. &lt;/p&gt;

&lt;p&gt;This doesn't mean you have to use Azure Functions everywhere you can, but only everywhere you think it's a match. Sometimes developers have the tendency to use every new technology as a hammer and every requirement as a nail. Don't be like that! There are alternatives to Azure Functions like scheduling libraries( &lt;a href="https://thewindev.net/background-processing-in-net-core-hangfire"&gt;I just wrote about Hangfire&lt;/a&gt; , Quartz.NET), or even &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-3.1&amp;amp;tabs=visual-studio"&gt;hosted services from .NET Core&lt;/a&gt;. The last two I'll cover in my next articles from this series, so if you want to get notified just subscribe to my newsfeed or &lt;a href="https://twitter.com/thewindev"&gt;follow me on Twitter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;PS: If you want to see something cool done with Azure Functions, I would recommend  the presentation below made by &lt;a href="https://twitter.com/amolenk"&gt;Sander Molenkamp&lt;/a&gt;  at  &lt;a href="https://dotnetdays.ro"&gt;dotnetdays&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/n_o0np2UhIo"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://thewindev.net/background-processing-in-net-core-azure-functions"&gt;Background processing in .NET Core: Azure Functions&lt;/a&gt; appeared first on &lt;a href="https://thewindev.net"&gt;thewindev.net&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>functions</category>
      <category>dotnet</category>
      <category>serverless</category>
      <category>microsoftazure</category>
    </item>
    <item>
      <title>Background processing in .NET Core: Hangfire</title>
      <dc:creator>Bogdan Bujdea</dc:creator>
      <pubDate>Mon, 12 Oct 2020 20:11:11 +0000</pubDate>
      <link>https://dev.to/thewindev/background-processing-in-net-core-hangfire-3ilk</link>
      <guid>https://dev.to/thewindev/background-processing-in-net-core-hangfire-3ilk</guid>
      <description>&lt;p&gt;In the past few weeks I’ve been using Quartz.NET and Azure Functions for some projects, so given the fact that I also used Hangfire in the past, I thought I’d make a short comparison between these 3 based on my experience. In the next 3 articles you’ll find a brief description of what they do, basic setup, pros and cons, and a conclusion. The idea is to give you a few “war stories” that really put these to the test, not just a “hello world” type of article.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://www.hangfire.io/"&gt;Hangfire&lt;/a&gt;
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;An easy way to perform background processing in .NET and .NET Core applications. No Windows Service or separate process required.&lt;br&gt;&lt;br&gt;
Backed by persistent storage. Open and free for commercial use.&lt;/p&gt;

&lt;p&gt;&lt;cite&gt;&lt;a href="https://www.hangfire.io/"&gt;https://www.hangfire.io/&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I used Hangfire almost 2 years ago for a small .NET Core project. The task was very simple, I had a .NET Core API distributed on multiple machines, and every 2 minutes I had to execute a background job. That job would sync some items in a database, so it was easier for me if it wasn’t executed concurrently on every machine. This was easily done with Hangfire, but you’ll soon see it came at a cost.&lt;/p&gt;

&lt;h4&gt;
  
  
  Setup
&lt;/h4&gt;

&lt;p&gt;Hangfire could store the jobs in memory, but it could also use a database. In my project I already used MySQL so I configured Hangfire to use the same database. Another cool feature that Hangfire has is the built-in dashboard that they have. You just need a few lines of code in your Startup.cs file to enable it and also add authorization if you want. Instead of making queries in the database you have this nice dashboard, so I really liked this feature.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zLnycVkC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.hangfire.io/img/ui/retries.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zLnycVkC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.hangfire.io/img/ui/retries.png" alt="Configuration for Hangfire"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Once you do this, you just need to configure your job. For this you’ll need a method to pass along to Hangfire and a schedule where you define when or how often that method should be executed. Hangfire is really flexible here, allowing you to create recurring jobs, fire-and-forget jobs, continuations, batches, etc. My app used recurring jobs.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
Configuring a job to be executed with Hangfire




&lt;p&gt;In the piece of code above you can see that I first remove that job if it exists, and then I AddOrUpdate my job using a unique ID. I do this because sometimes the job would get stuck and the easiest way to fix it would be to push a new ID. In this way the old job with the old ID would get discarded, and I would have a new one registered for execution.&lt;/p&gt;

&lt;h4&gt;
  
  
  Issues
&lt;/h4&gt;

&lt;p&gt;Why would the job get stuck? I blame this on the way it synchronized jobs. In order to make sure that a job would not run concurrently, it would create a database lock. I’m not a DBA, but we had one in our team, and he was constantly pissed about this. I don’t know many details but all the databases that we had were on the same Galera cluster, and Hangfire was causing significant performance issues for all of our apps, not just my API. If you don’t believe me, here’s a screenshot of a dashboard that our DBA sent me after removing Hangfire, you can tell he wasn’t insane:&lt;/p&gt;


&lt;blockquote class="ltag__twitter-tweet"&gt;
      &lt;div class="ltag__twitter-tweet__media"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pwyYh5Rf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/media/Dy0oJGEXgAEULGv.jpg" alt="unknown tweet media content"&gt;
      &lt;/div&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--k77uldNK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/1088218418950463489/keMT19rX_normal.jpg" alt="Bogdan Bujdea profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        Bogdan Bujdea
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        &lt;a class="comment-mentioned-user" href="https://dev.to/thewindev"&gt;@thewindev&lt;/a&gt;

      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P4t6ys1m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      today I removed hangfire from a &lt;a href="https://twitter.com/hashtag/dotnet"&gt;#dotnet&lt;/a&gt; core app...and I made some people really happy 
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      18:23 PM - 07 Feb 2019
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1093576069540704256" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-reply-action.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1093576069540704256" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-retweet-action.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      0
      &lt;a href="https://twitter.com/intent/like?tweet_id=1093576069540704256" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-like-action.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
      1
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


&lt;p&gt;I honestly tried to search for a fix, and I found many people &lt;a href="https://stackoverflow.com/questions/56321781/hangfire-causing-locks-in-sql-server"&gt;in the same situation&lt;/a&gt;. Eventually I landed on the official Hangfire forum where someone asked the same question and their reply was to switch to Azure Functions or something like this. This is when I decided to stop looking for a solution and remove Hangfire from our app. I would’ve tried a Lambda function (we were already on AWS), but the DevOps team preferred a Kubernetes cron job. I just had to create a new .NET Core console app which was triggered by the cron job that was executing my piece of code. The result was much better in terms of performance and although it wasn’t perfect, we kept this solution.&lt;/p&gt;

&lt;p&gt;As I said in the beginning of this post, I used Hangfire ~2 years ago, so maybe they fixed this or they’re still suggesting going serverless. Either way, I enjoyed how easy it was to setup and considering that I was just learning .NET Core on a project with a strict deadline, I really appreciated this!&lt;/p&gt;

&lt;p&gt;Tomorrow I’ll talk about Quartz.NET, so make sure to follow me on  &lt;a href="https://twitter.com/thewindev"&gt;Twitter&lt;/a&gt; where I’ll announce when the next post is published.&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://thewindev.net/background-processing-in-net-core-hangfire/"&gt;Background processing in .NET Core: Hangfire&lt;/a&gt; appeared first on &lt;a href="https://thewindev.net"&gt;thewindev.net&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnetcore</category>
      <category>hangfire</category>
      <category>dotnet</category>
    </item>
  </channel>
</rss>
