<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>dotnet.work</title>
        <link>https://dotnet.work/</link>
        <description>Recent content on dotnet.work</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>en-US</language>
        <managingEditor>dotnet@oles.cloud (Ole Albers)</managingEditor>
        <webMaster>dotnet@oles.cloud (Ole Albers)</webMaster>
        <copyright>Ole Albers</copyright>
        <lastBuildDate>Wed, 04 Feb 2026 18:17:00 +0100</lastBuildDate><atom:link href="https://dotnet.work/index.xml" rel="self" type="application/rss+xml" /><item>
            <title>Use Claude AI in Home Assistant Studio</title>
            <link>https://dotnet.work/blog/use-claude-ai-in-home-assistant-studio/</link>
            <pubDate>Wed, 04 Feb 2026 18:17:00 +0100</pubDate><author>dotnet@oles.cloud (Ole Albers)</author>
            <guid>https://dotnet.work/blog/use-claude-ai-in-home-assistant-studio/</guid>
            <description>&lt;img src=&#34;https://dotnet.work/blog/use-claude-ai-in-home-assistant-studio/2026-02-04-10_37_55-Home-Assistant-Mozilla-Firefox_hu_9daa82fb4ffbfdf5.png&#34; alt=&#34;Featured image of post Use Claude AI in Home Assistant Studio&#34; /&gt;&lt;p&gt;Sometimes you have to configure stuff in Home Assistant the &amp;ldquo;painful&amp;rdquo; way: Working with Yaml-Files instead of using the UI. Therefore Studio Code Server is the first choice for most of us. But Yaml isn&amp;rsquo;t fun. Yaml in hass.io especially isn&amp;rsquo;t fun.&lt;/p&gt;&#xA;&lt;p&gt;But there is ai for the rescue. AI isn&amp;rsquo;t always the solution, but working with ugly configuration formats is exactly where it is good at. So let&amp;rsquo;s add claude to the Studio Code Server&lt;/p&gt;&#xA;&lt;h2 id=&#34;studio-code-server&#34;&gt;&lt;a href=&#34;#studio-code-server&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Studio Code Server&#xA;&lt;/h2&gt;&lt;p&gt;I assume you already have installed the Studio Code Server. If you haven&amp;rsquo;t: It is a simple Add-On. Just follow the official instructions:&#xA;&lt;a class=&#34;link&#34; href=&#34;https://github.com/hassio-addons/addon-vscode/blob/main/vscode/DOCS.md&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;https://github.com/hassio-addons/addon-vscode/blob/main/vscode/DOCS.md&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;Studio Code Server will then be installed as a docker container. (Don&amp;rsquo;t worry. No need to know anything about docker. This works in the background)&lt;/p&gt;&#xA;&lt;h2 id=&#34;preparing&#34;&gt;&lt;a href=&#34;#preparing&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Preparing&#xA;&lt;/h2&gt;&lt;p&gt;Claude needs NodeJs which isn&amp;rsquo;t in the studio code server docker. But don&amp;rsquo;t worry, this is easy to solve. You &lt;em&gt;could&lt;/em&gt; connect to the docker and install the required packages using app-get. But while this works this has two disadvantages. First it is a bit more complicated if you are not at home with Linux Systems and most important: Any update of the Addon would kill NodeJs and you would have to do this all over again.&lt;/p&gt;&#xA;&lt;p&gt;Thankfully the Add-On devs thought of an easy way to do that for you. Just go to &lt;em&gt;Settings - Add-Ons - Studio Code Server&lt;/em&gt;. There go to &lt;em&gt;Configuration&lt;/em&gt; and in the &amp;ldquo;packages&amp;rdquo; enter &amp;ldquo;npm&amp;rdquo; and then &amp;ldquo;nodejs&amp;rdquo;. Do not enter both at the time. Each one of them by enter. After that there should be two bubbles on top:&lt;/p&gt;&#xA;&lt;p&gt;&lt;img alt=&#34;Setup Page&#34; class=&#34;gallery-image&#34; data-flex-basis=&#34;339px&#34; data-flex-grow=&#34;141&#34; data-title-escaped=&#34;Setup Page&#34; height=&#34;298&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/use-claude-ai-in-home-assistant-studio/2026-02-04-10_37_55-Home-Assistant-Mozilla-Firefox_hu_9daa82fb4ffbfdf5.png&#34; title=&#34;Setup Page&#34; width=&#34;421&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;If you prefer editing in yaml, here is your content:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;packages:&#xA;  - nodejs&#xA;  - npm&#xA;init_commands: []&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Save and restart the addon. Don&amp;rsquo;t be alarmed if this takes a LONG time. There will have big packages to be downloaded and installed into that container. This will take minutes even on a fast machine and especially if using a Raspberry Pi.&lt;/p&gt;&#xA;&lt;h2 id=&#34;adding-claude&#34;&gt;&lt;a href=&#34;#adding-claude&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Adding Claude&#xA;&lt;/h2&gt;&lt;p&gt;&lt;img alt=&#34;Install Addon&#34; class=&#34;gallery-image&#34; data-flex-basis=&#34;880px&#34; data-flex-grow=&#34;366&#34; data-title-escaped=&#34;Install Addon&#34; height=&#34;260&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/use-claude-ai-in-home-assistant-studio/2026-02-04-10_42_17-Studio-Code-Server-Home-Assistant-Mozilla-Firefox_hu_8fee66ef6366d762.png&#34; srcset=&#34;https://dotnet.work/blog/use-claude-ai-in-home-assistant-studio/2026-02-04-10_42_17-Studio-Code-Server-Home-Assistant-Mozilla-Firefox_hu_9257303b5683b41e.png 800w, https://dotnet.work/blog/use-claude-ai-in-home-assistant-studio/2026-02-04-10_42_17-Studio-Code-Server-Home-Assistant-Mozilla-Firefox_hu_8fee66ef6366d762.png 954w&#34; title=&#34;Install Addon&#34; width=&#34;954&#34;&gt;&#xA;Now you can switch to the code server. Select &lt;em&gt;Extensions&lt;/em&gt; on the left side, enter &amp;ldquo;Claude&amp;rdquo; into the search and install &amp;ldquo;Claude Code for VS Code&amp;rdquo;. After that you should have that typical &amp;ldquo;Claude Star&amp;rdquo; on the top right corner. You will be greeted with the authentication method. Select the one that matches your account. (Most likely Claude.ai Subscription)&lt;/p&gt;&#xA;&lt;p&gt;&lt;img alt=&#34;Account selection from Claude&#34; class=&#34;gallery-image&#34; data-flex-basis=&#34;152px&#34; data-flex-grow=&#34;63&#34; data-title-escaped=&#34;Account Selection from Claude&#34; height=&#34;878&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/use-claude-ai-in-home-assistant-studio/2026-02-04-10_46_17-Studio-Code-Server-Home-Assistant-Mozilla-Firefox_hu_71d79e2b7d9c581c.png&#34; title=&#34;Account Selection from Claude&#34; width=&#34;559&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;If after using the Login clicking &amp;ldquo;Open&amp;rdquo; does not work because you get redirected to a local url use the url instead that is shown in the textbox:&lt;/p&gt;&#xA;&lt;p&gt;&lt;img alt=&#34;Continue in Browser &#34; class=&#34;gallery-image&#34; data-flex-basis=&#34;353px&#34; data-flex-grow=&#34;147&#34; data-title-escaped=&#34;Continue in Browser&#34; height=&#34;365&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/use-claude-ai-in-home-assistant-studio/2026-02-04-10_52_54-Studio-Code-Server-Home-Assistant-Mozilla-Firefox_hu_fe7c633de7eebace.png&#34; title=&#34;Continue in Browser&#34; width=&#34;538&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;Just copy paste that url manually and paste the returning code into Studio Code.&lt;/p&gt;&#xA;&lt;h2 id=&#34;using-claude&#34;&gt;&lt;a href=&#34;#using-claude&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Using Claude&#xA;&lt;/h2&gt;&lt;p&gt;Now you can open Claude using the star and let him do the work&lt;/p&gt;&#xA;&lt;p&gt;&lt;img alt=&#34;Screenshot of Claude analyzing the configuration.yaml&#34; class=&#34;gallery-image&#34; data-flex-basis=&#34;281px&#34; data-flex-grow=&#34;117&#34; data-title-escaped=&#34;Claude analyzing the configuration.yaml&#34; height=&#34;839&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/use-claude-ai-in-home-assistant-studio/2026-02-04-10_56_46-Studio-Code-Server-Home-Assistant-Mozilla-Firefox_hu_5fc2475700b80717.png&#34; srcset=&#34;https://dotnet.work/blog/use-claude-ai-in-home-assistant-studio/2026-02-04-10_56_46-Studio-Code-Server-Home-Assistant-Mozilla-Firefox_hu_fc58830bc1938829.png 800w, https://dotnet.work/blog/use-claude-ai-in-home-assistant-studio/2026-02-04-10_56_46-Studio-Code-Server-Home-Assistant-Mozilla-Firefox_hu_5fc2475700b80717.png 983w&#34; title=&#34;Claude analyzing the configuration.yaml&#34; width=&#34;983&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;That is it. Now you can use claude in Home Assistant Config Files&lt;/p&gt;</description>
        </item><item>
            <title>Vibe Coding: An experiment</title>
            <link>https://dotnet.work/blog/vibe-coding-an-experiment/</link>
            <pubDate>Sat, 18 Oct 2025 07:48:00 +0200</pubDate><author>dotnet@oles.cloud (Ole Albers)</author>
            <guid>https://dotnet.work/blog/vibe-coding-an-experiment/</guid>
            <description>&lt;img src=&#34;https://dotnet.work/blog/vibe-coding-an-experiment/Screenshot-from-2025-10-18-15-54-54_hu_2d4d42bb98978604.png&#34; alt=&#34;Featured image of post Vibe Coding: An experiment&#34; /&gt;&lt;p&gt;Before I begin to even start writing about this topic I assume it makes sense to mention that this article has &lt;em&gt;not&lt;/em&gt; been created using AI. Not a single word. (with one exception: The german version has been translated automatically using Claude, like all entries on my blog)  I also tried to write this for a wider audience than my usual articles. As this isn&amp;rsquo;t a specific problem solving for a weird use case in c# I tried to keep the tech gibberish down and especially do not expect you to have C# knowledge. Any coding experience helps, but isn&amp;rsquo;t required. On the other hand if you are a skilled dev, don&amp;rsquo;t be annoyed if I explain things trivial in your opinion; Just ignore the explanations and feel smart :)&lt;/p&gt;&#xA;&lt;h2 id=&#34;ruleset&#34;&gt;&lt;a href=&#34;#ruleset&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Ruleset&#xA;&lt;/h2&gt;&lt;p&gt;I simply wanted to test how good vibe coding (Letting the AI create your whole application) is today. Does it achieve what I want? Is the code quality good? Would it help someone who has no knowledge at all? So I set up a ruleset:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Make sure that what I want to achieve hasn&amp;rsquo;t be done before (so the AI can&amp;rsquo;t just copy/paste a complete application)&lt;/li&gt;&#xA;&lt;li&gt;DO NOT CODE: Really. I try to not change a single character in a source file even if a mistake is obvious&lt;/li&gt;&#xA;&lt;li&gt;Try not to help: Of course if something is wrong, I will tell the AI. But let it find out how to solve this, first only helping if it got really stuck&lt;/li&gt;&#xA;&lt;li&gt;Application should not be too ease (no &amp;ldquo;Hello World&amp;rdquo;)&lt;/li&gt;&#xA;&lt;li&gt;Application should not be too complecated either (no &amp;ldquo;Please create a better Microsoft Teams application&amp;rdquo;)&lt;/li&gt;&#xA;&lt;li&gt;No external tools needed; So no Unity or Photoshop requirements, etc. I should not need to leave the IDE (Rider)&lt;/li&gt;&#xA;&lt;li&gt;No Security related stuff: No Password hashing, not storing important secrets, simply because I trust AI enough to do this lightly. This might be worth another experiment on its own&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;environment-and-job-description&#34;&gt;&lt;a href=&#34;#environment-and-job-description&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Environment and Job description&#xA;&lt;/h2&gt;&lt;p&gt;I used Rider with Claude AI integration using Sonnet 4. Whenever it asked for a permission I granted it. I started by giving a simple task and iterate over time. The task was:&lt;/p&gt;&#xA;&#xA;    &lt;blockquote&gt;&#xA;        &lt;p&gt;Create a Razor Application that offers a SCRUM Poker functionality with a twist: It should have Joker Mechanics like in Balatro. The PO should be able to create a new game. Then an admin view and a player view should be created. Each of them by generating a random code like &lt;code&gt;play?code=dbddhkp&lt;/code&gt; and &lt;code&gt;admin?code=ukule2 &lt;/code&gt;. No database. Just store everything in memory&lt;/p&gt;&#xA;&#xA;    &lt;/blockquote&gt;&#xA;&lt;p&gt;I was sure this hasn&amp;rsquo;t been done before because it absolutely makes no sense. SCRUM poker is a tool to determine the &amp;ldquo;cost&amp;rdquo; (or complexity) of programming tasks. Each developer decides how complex the task at hand is usually using values from 1 to 21 (as always, there are a lot of variants with other numbers) and at the end these values gets compared and discussed. The main reason is that the number itself isn&amp;rsquo;t communicated openly at the beginning so if a senior dev tells his number other devs might just say &amp;ldquo;yepp. About the same&amp;rdquo;. The &amp;ldquo;poker&amp;rdquo; element makes sure everyone says the number he or she really thinks of and afterwards any discrepancies gets discussed.&lt;/p&gt;&#xA;&lt;p&gt;Balatro on the other hand is a fun Poker game. There are Jokers that change the value of the cards. E.G a &amp;ldquo;every heart gets multiplied by 4&amp;rdquo;. Obviously this would destroy any useful results that the SCRUM poker produced. So a nonsens project, a quite complex one nonetheless.&lt;/p&gt;&#xA;&lt;p&gt;I added additional features lateron like a multi-language feature but did want to give the AI the chance to iterate&lt;/p&gt;&#xA;&lt;h2 id=&#34;did-it-work&#34;&gt;&lt;a href=&#34;#did-it-work&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Did it work?&#xA;&lt;/h2&gt;&lt;p&gt;The short answer is: Yes. I did not write a single line of code and the application worked.&lt;/p&gt;&#xA;&lt;p&gt;But the long answer is: Developers wont loose their job very soon. As I will show in the learnings it requires a skilled developer to lead into the right direction when problems need to be solved. In addition some decision lead to bad code. The detailed pitfalls and also positive surprises can be found in Learnings.&lt;/p&gt;&#xA;&lt;p&gt;(And also I published the code on GitHub so you can see for yourself)&lt;/p&gt;&#xA;&lt;p&gt;The biggest benefit: I am a backend developer. My programs (usually) work, but I am not really good in UI or UX. That benefit the AI was able to solve. The application wasn&amp;rsquo;t the most beautiful thing ever to be seen in a browser window; Still: It looked way better than anything I would have created&lt;/p&gt;&#xA;&lt;p&gt;&lt;img alt=&#34;Player view: There is a welcome text, a few playing cards are displayed. The names from other players can be seen and the selected points (21) appear&#34; class=&#34;gallery-image&#34; data-flex-basis=&#34;333px&#34; data-flex-grow=&#34;139&#34; data-title-escaped=&#34;Player Screen in Play phase&#34; height=&#34;1938&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/vibe-coding-an-experiment/Screenshot-from-2025-10-18-15-54-54_hu_2d4d42bb98978604.png&#34; srcset=&#34;https://dotnet.work/blog/vibe-coding-an-experiment/Screenshot-from-2025-10-18-15-54-54_hu_b00de43bf113578e.png 800w, https://dotnet.work/blog/vibe-coding-an-experiment/Screenshot-from-2025-10-18-15-54-54_hu_b32281aa6e560614.png 1600w, https://dotnet.work/blog/vibe-coding-an-experiment/Screenshot-from-2025-10-18-15-54-54_hu_a4ee2b7cc259de5f.png 2400w, https://dotnet.work/blog/vibe-coding-an-experiment/Screenshot-from-2025-10-18-15-54-54_hu_2d4d42bb98978604.png 2694w&#34; title=&#34;Player Screen in Play phase&#34; width=&#34;2694&#34;&gt;&#xA;&lt;img alt=&#34;Player view: The game has ended. A joker appeared (Wrathful Joker). The scores from all players are shown&#34; class=&#34;gallery-image&#34; data-flex-basis=&#34;351px&#34; data-flex-grow=&#34;146&#34; data-title-escaped=&#34;Player screen at the end of the round&#34; height=&#34;1826&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/vibe-coding-an-experiment/Screenshot-from-2025-10-18-15-55-32_hu_a9d1e97d7f2f848d.png&#34; srcset=&#34;https://dotnet.work/blog/vibe-coding-an-experiment/Screenshot-from-2025-10-18-15-55-32_hu_18a2f7bfb5070fc.png 800w, https://dotnet.work/blog/vibe-coding-an-experiment/Screenshot-from-2025-10-18-15-55-32_hu_a79f2395f1c4e6d9.png 1600w, https://dotnet.work/blog/vibe-coding-an-experiment/Screenshot-from-2025-10-18-15-55-32_hu_a7a14fcd3f54a4c1.png 2400w, https://dotnet.work/blog/vibe-coding-an-experiment/Screenshot-from-2025-10-18-15-55-32_hu_a9d1e97d7f2f848d.png 2676w&#34; title=&#34;Player screen at the end of the round&#34; width=&#34;2676&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;img alt=&#34;Admin View: The selected cards of each player and the jokers are displayed&#34; class=&#34;gallery-image&#34; data-flex-basis=&#34;363px&#34; data-flex-grow=&#34;151&#34; data-title-escaped=&#34;Admin or PO- View of the same game&#34; height=&#34;1780&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/vibe-coding-an-experiment/Screenshot-from-2025-10-18-15-55-19_hu_c302ad1604baa332.png&#34; srcset=&#34;https://dotnet.work/blog/vibe-coding-an-experiment/Screenshot-from-2025-10-18-15-55-19_hu_7a02ef72974c6cb8.png 800w, https://dotnet.work/blog/vibe-coding-an-experiment/Screenshot-from-2025-10-18-15-55-19_hu_c568faca31145739.png 1600w, https://dotnet.work/blog/vibe-coding-an-experiment/Screenshot-from-2025-10-18-15-55-19_hu_e4b64a640fa7eaf8.png 2400w, https://dotnet.work/blog/vibe-coding-an-experiment/Screenshot-from-2025-10-18-15-55-19_hu_c302ad1604baa332.png 2698w&#34; title=&#34;Admin or PO- View of the same game&#34; width=&#34;2698&#34;&gt;&lt;/p&gt;&#xA;&lt;h3 id=&#34;sources-and-demonstration&#34;&gt;&lt;a href=&#34;#sources-and-demonstration&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Sources and demonstration&#xA;&lt;/h3&gt;&lt;p&gt;You can find the sources on the &lt;a class=&#34;link&#34; href=&#34;https://github.com/Guacam-Ole/FiboNuts&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;FiboNuts Repository on Github&lt;/a&gt;.&#xA;A working example can be found at &lt;a class=&#34;link&#34; href=&#34;http://poker.oles.cloud&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;Poker.Oles.Cloud&lt;/a&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;learnings&#34;&gt;&lt;a href=&#34;#learnings&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Learnings&#xA;&lt;/h2&gt;&lt;h3 id=&#34;code-quality&#34;&gt;&lt;a href=&#34;#code-quality&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Code quality&#xA;&lt;/h3&gt;&lt;p&gt;Code quality started good, but got worse, completely wrong assumptions leading to bad code. Forcing Claude to revert these things helped putting the code back in good shape. TBH I expected way worse&lt;/p&gt;&#xA;&lt;h3 id=&#34;help-needed&#34;&gt;&lt;a href=&#34;#help-needed&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Help needed&#xA;&lt;/h3&gt;&lt;p&gt;Quite at the beginning of the project I ran into a problem that could not be solved by the AI itself: There are URLs generated for the players. But when I enter those in a different browser (or even tab) the code does not get recognized. I tried to tell Claude in about 10 Iterations that it does not work. The AI did try different approaches but never came near a solution. After I told him that there is a problem in the Dependency Injection implementation and the class holding the state should not be added Scoped but added as a Singleton it was able to continue. But without my help this would be the end of the project.&lt;/p&gt;&#xA;&lt;p&gt;It was surprising to see that such quite simple cause could not be recognized while much more complex issues got resolved.&lt;/p&gt;&#xA;&lt;p&gt;Also at one point the communication between the server and client did not work and it seems like Claude simply could not believe when I said that. I had to offer assistance telling him to create logs that I can then paste back so Claude can analyze the issue.&#xA;&lt;img alt=&#34;Screenshot from Ai console showing the changes to be made&#34; class=&#34;gallery-image&#34; data-flex-basis=&#34;491px&#34; data-flex-grow=&#34;204&#34; data-title-escaped=&#34;Claude showing what he is doing right now&#34; height=&#34;1212&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/vibe-coding-an-experiment/Screenshot-from-2025-10-18-11-17-36_hu_af51d10f5fa9eb8.png&#34; srcset=&#34;https://dotnet.work/blog/vibe-coding-an-experiment/Screenshot-from-2025-10-18-11-17-36_hu_fe072cf662e55d17.png 800w, https://dotnet.work/blog/vibe-coding-an-experiment/Screenshot-from-2025-10-18-11-17-36_hu_6b966d8790182657.png 1600w, https://dotnet.work/blog/vibe-coding-an-experiment/Screenshot-from-2025-10-18-11-17-36_hu_def643116816e947.png 2400w, https://dotnet.work/blog/vibe-coding-an-experiment/Screenshot-from-2025-10-18-11-17-36_hu_af51d10f5fa9eb8.png 2484w&#34; title=&#34;Claude showing what he is doing right now&#34; width=&#34;2484&#34;&gt;&lt;/p&gt;&#xA;&lt;h3 id=&#34;adding-random-stuff&#34;&gt;&lt;a href=&#34;#adding-random-stuff&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Adding random stuff&#xA;&lt;/h3&gt;&lt;p&gt;Claude also decided at one point to add https. (It was http before) when I was asking to add a multi language feature. I assume he learned implementing that feature from a site where https was used. The https-part completly broke the application because ports had been assigned multiple times in the config.&lt;/p&gt;&#xA;&lt;p&gt;Also until the very end there always was an ugly &amp;ldquo;About&amp;rdquo; link on the upper part of the page linking to &lt;code&gt;https://learn.microsoft.com/en-us/aspnet/core/?view=aspnetcore-9.0&lt;/code&gt;. I assume this is part of any &amp;ldquo;how to create a Blazor Application&amp;rdquo; Tutorial and so Claude thought this is required&lt;/p&gt;&#xA;&lt;h3 id=&#34;wrong-assumptions&#34;&gt;&lt;a href=&#34;#wrong-assumptions&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Wrong assumptions&#xA;&lt;/h3&gt;&lt;p&gt;At one point I asked the AI to add a multi language feature using best practices. While it did that correctly it forgot to add a crucial Package from NuGet. The Build errors have been quite obvious stating &amp;lsquo;Are you missing a reference&amp;rsquo; but Claude did not seem to understand that. Instead it assumed that the code generated was too complex for the compiler (it absolutely wasn&amp;rsquo;t: Just a few lines adding language-strings to an array) and simply removed its code over and over again until the program compiled again but also had no language code left.&#xA;Again I had to help and tell him: &amp;ldquo;Hey. That wasn&amp;rsquo;t the issue. You forgot some NuGet Packages&amp;rdquo;. After that the issue was resolved.&lt;/p&gt;&#xA;&lt;p&gt;At some other point Claude managed to break the application completely. Right at the start it simply would not load anymore after the progress bar was finished. Claude did an assumption no dev would do: It was sure the cause was the logging. Too many log entries causing the application to slow down. So any log in any exception handling or anywhere else got removed. Not solving the issue, obviously but creating a way worse variant than before.&lt;/p&gt;&#xA;&lt;p&gt;As a rule of thumb: Whenever the Bots writes something like &amp;ldquo;this is a known issue&amp;rdquo; it probably found some very weird edge case that has nothing to do with a problem that should be simple. In my case multi language not working caused really weird actions: Creating dlls for each language, trying to use reflection to fix some (non-existing) issue of the localizer when at the end the text in the razor page was simply static. After I told that there should be variables or method calls instead of fixed strings Claude was able to revert all the nonsense changes, but without that help this was unsolvable.&lt;/p&gt;&#xA;&lt;h3 id=&#34;what-is-considered-easy&#34;&gt;&lt;a href=&#34;#what-is-considered-easy&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;What is considered easy?&#xA;&lt;/h3&gt;&lt;p&gt;Tasks that I see as quite complicated have been solved sometimes without any issues while others, that shouldn&amp;rsquo;t be too hard seemed to be unsolvable. For example I wanted a &amp;lsquo;/metrics&amp;rsquo; endpoint to be used with Prometheus (A tool to collect metrics and display them in a Dashboard). While it looked right, it was a html page that rendered the content. Claude did never manage to follow the simple task: &amp;ldquo;please output plain text. No html tags.&amp;rdquo; Even when providing the correct Mimetype it still did not understand correctly. An additional Server application was created where the clients sent metrics, suddenly a IIS Express configuration appeared in the launchsettings and other things that made the program way, way worse but did not even move an inch into the right direction.&lt;/p&gt;&#xA;&lt;p&gt;After that, Claude was so confused that not even rolling back worked. The AI wasn&amp;rsquo;t able to find its files anymore and even tried to find it in regions of my computer where it isn&amp;rsquo;t supposed to look:&#xA;&lt;img alt=&#34;Chat with ai to revert things. The AI then tries to find the files it just created but fails&#34; class=&#34;gallery-image&#34; data-flex-basis=&#34;456px&#34; data-flex-grow=&#34;190&#34; data-title-escaped=&#34;Claude getting completely lost&#34; height=&#34;1072&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/vibe-coding-an-experiment/Screenshot-from-2025-10-18-16-27-41_hu_70421582f6657a29.png&#34; srcset=&#34;https://dotnet.work/blog/vibe-coding-an-experiment/Screenshot-from-2025-10-18-16-27-41_hu_2e64447467a4da8c.png 800w, https://dotnet.work/blog/vibe-coding-an-experiment/Screenshot-from-2025-10-18-16-27-41_hu_80ada115db753ec7.png 1600w, https://dotnet.work/blog/vibe-coding-an-experiment/Screenshot-from-2025-10-18-16-27-41_hu_70421582f6657a29.png 2040w&#34; title=&#34;Claude getting completely lost&#34; width=&#34;2040&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;img alt=&#34;Screenshot Claude trying to access a folder above the project&#34; class=&#34;gallery-image&#34; data-flex-basis=&#34;7154px&#34; data-flex-grow=&#34;2981&#34; data-title-escaped=&#34;Claude walking at forbidden paths&#34; height=&#34;106&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/vibe-coding-an-experiment/Screenshot-from-2025-10-18-16-29-49_hu_f78bebe5125b148e.png&#34; srcset=&#34;https://dotnet.work/blog/vibe-coding-an-experiment/Screenshot-from-2025-10-18-16-29-49_hu_9d985e0e2bf1d3e6.png 800w, https://dotnet.work/blog/vibe-coding-an-experiment/Screenshot-from-2025-10-18-16-29-49_hu_a0bca926da421ec5.png 1600w, https://dotnet.work/blog/vibe-coding-an-experiment/Screenshot-from-2025-10-18-16-29-49_hu_a6e84a62f9e3e0d0.png 2400w, https://dotnet.work/blog/vibe-coding-an-experiment/Screenshot-from-2025-10-18-16-29-49_hu_f78bebe5125b148e.png 3160w&#34; title=&#34;Claude walking at forbidden paths&#34; width=&#34;3160&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;Needless to say that these operation have been costly:&#xA;&lt;img alt=&#34;Message that the daily limit has been reached&#34; class=&#34;gallery-image&#34; data-flex-basis=&#34;2749px&#34; data-flex-grow=&#34;1145&#34; data-title-escaped=&#34;Let&amp;amp;rsquo;s wait for a while&#34; height=&#34;110&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/vibe-coding-an-experiment/Screenshot-from-2025-10-18-16-30-51_hu_e37dd92af394fe4.png&#34; srcset=&#34;https://dotnet.work/blog/vibe-coding-an-experiment/Screenshot-from-2025-10-18-16-30-51_hu_9dbd0ac5f264e3f4.png 800w, https://dotnet.work/blog/vibe-coding-an-experiment/Screenshot-from-2025-10-18-16-30-51_hu_e37dd92af394fe4.png 1260w&#34; title=&#34;Let&amp;rsquo;s wait for a while&#34; width=&#34;1260&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;After a few retries I had to surrender. Claude was never able to create a simple output in text/plain. Even if I gave example snippets. It always rendered HTML that looked like Text but still had html tags.&lt;/p&gt;&#xA;&lt;h3 id=&#34;interesting-tactics&#34;&gt;&lt;a href=&#34;#interesting-tactics&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Interesting tactics&#xA;&lt;/h3&gt;&lt;p&gt;After breaking the application when adding https claude did some interesting things. It did not simply analyze the code but was sure it wasn&amp;rsquo;t its fault :) Instead it assumed there must be some other application causing the issue and so started &lt;code&gt;nmap&lt;/code&gt; to analyze what other services on my machine might be to blame. It did not expect it to &amp;ldquo;break out&amp;rdquo; of my IDE. A reminder to not just allow everything but have a close look on all actions. I would not be surprised if it had decided to simply kill that other process if it really had been in external issue. (But the implementation explicitly asks for your permission whenever it does something it hasn&amp;rsquo;t before)&lt;/p&gt;&#xA;&lt;h3 id=&#34;conclusionopinion&#34;&gt;&lt;a href=&#34;#conclusionopinion&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Conclusion/Opinion&#xA;&lt;/h3&gt;&lt;p&gt;Useful, but needs to be monitored closely. And you must be aware that the code isn&amp;rsquo;t really &amp;ldquo;good&amp;rdquo;, just working. Just a minor issue in this application but when customer data is processed and stored bad design decisions can lead to big problems. There are already a lot of vibe coded applications, used by millions that are just really badly written and leak data all the time. Just one example of many: &lt;a class=&#34;link&#34; href=&#34;https://cybernews.com/security/ai-girlfriend-app-leak-exposes-400k-users/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;https://cybernews.com/security/ai-girlfriend-app-leak-exposes-400k-users/&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;No developer wants to be responsible for a catastrophe like this even before thinking of GDPR fines.&lt;/p&gt;&#xA;&lt;p&gt;Also: Even more like in human developing: Use Source Control. Claude can create git commits without any problems. This helps rolling back to previous state if the AI created a mess after an error. But I would suggest to only use a local repository and push manually to be able to make sure no secrets end up in the code. Also don&amp;rsquo;t just check the current code but any commit if there was some secret at any time.&lt;/p&gt;&#xA;&lt;p&gt;Nonetheless we also can&amp;rsquo;t simply ignore AI when creating programs. Even if the AI bubble at some day bursts the tools will remain. LLMs will still be there and be just any other tool on the Cloud or maybe even on local machines. No matter if someone makes big cash selling AI tools or not. So in my opinion a senior dev has to decide where it works and where it does not. Also because of the nature of AI : I will get better, but there will always be the same kind of errors you can&amp;rsquo;t get rid of. Hallucinations are a thing and always will be.&lt;/p&gt;&#xA;&lt;p&gt;I also think the next generation(s) will be the ones that benefit the most but also suffer the most because of AI. Benefit because it never was so easy to start. Need something done in your OS? Ask the AI to write a python script. Having trouble about Dependy Injection? The AI will provide a document with good examples. You could find that without AI, but that is just simpler and faster. On the negative side: Your problem gets solved, but maybe not in the best way. And this isn&amp;rsquo;t just an academic argument: You have to &lt;em&gt;understand&lt;/em&gt; why we do not store passwords but use hashes instead to understand the underlying risk.&lt;/p&gt;&#xA;&lt;p&gt;And in addition from now on not much more content will be created. You get every information you need through AI. But when no new information is created on the internet in a few years AI will mostly scrape content it created itself leading to a perfect endless confirmation bias loop.&lt;/p&gt;&#xA;&lt;p&gt;And on a personal note: On the one hand: AI is a great tool, on the other hand it nearly already destroyed any use of searching the web. Have you ever tried to find information on &amp;ldquo;Is it ok if my dog eats &lt;em&gt;stuff&lt;/em&gt;&amp;rdquo;? The first two search pages mostly contain AI generated content. And these range from &amp;ldquo;No problem. &lt;em&gt;stuff&lt;/em&gt; is even very healthy&amp;rdquo; to &amp;ldquo;Never ever! One bite and your dog is dead!&amp;rdquo;. I got around this by using DuckDuckGo and the &amp;ldquo;Block all results from this site&amp;rdquo; - feature for a while but am at a point where sites with random domains get created faster than I cen add them to the list.&lt;/p&gt;&#xA;&lt;p&gt;Maybe I should use AI to keep these lists up to date&amp;hellip;. 😀&lt;/p&gt;&#xA;</description>
        </item><item>
            <title>Grafana - Notifications for Mastodon</title>
            <link>https://dotnet.work/blog/grafana-notifications-for-mastodon/</link>
            <pubDate>Tue, 14 Oct 2025 12:28:00 +0200</pubDate><author>dotnet@oles.cloud (Ole Albers)</author>
            <guid>https://dotnet.work/blog/grafana-notifications-for-mastodon/</guid>
            <description>&lt;img src=&#34;https://dotnet.work/blog/grafana-notifications-for-mastodon/Screenshot-from-2025-10-14-12-21-45_hu_6d1258580808eff7.png&#34; alt=&#34;Featured image of post Grafana - Notifications for Mastodon&#34; /&gt;&lt;p&gt;Grafana is a very useful tool for monitoring your own programs, discovering silly bugs, or simply looking at beautiful graphs :)&lt;/p&gt;&#xA;&lt;p&gt;Another feature is &amp;ldquo;Alerts&amp;rdquo;. These can be configured arbitrarily, whether high CPU load, too many errors in the log, or similar should trigger an alarm.&lt;/p&gt;&#xA;&lt;p&gt;This post won&amp;rsquo;t be about the alerts themselves, but rather about sending already configured alerts to Mastodon. These are called &lt;em&gt;Contact Points&lt;/em&gt; in Grafana and initially don&amp;rsquo;t offer Mastodon integration. However, this can be worked around with a few simple steps.&lt;/p&gt;&#xA;&lt;h2 id=&#34;mastodon&#34;&gt;&lt;a href=&#34;#mastodon&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Mastodon&#xA;&lt;/h2&gt;&lt;p&gt;First, we need some preparation, because of course not just any random application can post to Mastodon under our name (thankfully).&lt;/p&gt;&#xA;&lt;p&gt;Therefore, we log into the instance of our choice, select &lt;em&gt;Settings&lt;/em&gt; and then &lt;em&gt;Development&lt;/em&gt;. This should look something like this:&lt;/p&gt;&#xA;&lt;p&gt;&lt;img alt=&#34;Screenshot Applications&#34; class=&#34;gallery-image&#34; data-flex-basis=&#34;521px&#34; data-flex-grow=&#34;217&#34; data-title-escaped=&#34;Screenshot Applications&#34; height=&#34;922&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/grafana-notifications-for-mastodon/Screenshot-from-2025-10-14-12-21-45_hu_6d1258580808eff7.png&#34; srcset=&#34;https://dotnet.work/blog/grafana-notifications-for-mastodon/Screenshot-from-2025-10-14-12-21-45_hu_9918453b50955166.png 800w, https://dotnet.work/blog/grafana-notifications-for-mastodon/Screenshot-from-2025-10-14-12-21-45_hu_8244dccb771d446b.png 1600w, https://dotnet.work/blog/grafana-notifications-for-mastodon/Screenshot-from-2025-10-14-12-21-45_hu_6d1258580808eff7.png 2002w&#34; title=&#34;Screenshot Applications&#34; width=&#34;2002&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;(If the list is empty, it means you haven&amp;rsquo;t created any applications yet. If it&amp;rsquo;s not empty, you don&amp;rsquo;t need this part of the tutorial at all, because you&amp;rsquo;ve already done this before 😀)&lt;/p&gt;&#xA;&lt;p&gt;Click on &lt;em&gt;New Application&lt;/em&gt; here and choose a name. Leave the other two text fields as they are:&lt;/p&gt;&#xA;&lt;p&gt;&lt;img alt=&#34;Application Detail View&#34; class=&#34;gallery-image&#34; data-flex-basis=&#34;480px&#34; data-flex-grow=&#34;200&#34; data-title-escaped=&#34;Application Detail View&#34; height=&#34;1024&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/grafana-notifications-for-mastodon/Screenshot-from-2025-10-14-12-29-08_hu_8eca63e246ece1fd.png&#34; srcset=&#34;https://dotnet.work/blog/grafana-notifications-for-mastodon/Screenshot-from-2025-10-14-12-29-08_hu_c566f4335b88bed5.png 800w, https://dotnet.work/blog/grafana-notifications-for-mastodon/Screenshot-from-2025-10-14-12-29-08_hu_2067d1ad11c6660d.png 1600w, https://dotnet.work/blog/grafana-notifications-for-mastodon/Screenshot-from-2025-10-14-12-29-08_hu_8eca63e246ece1fd.png 2052w&#34; title=&#34;Application Detail View&#34; width=&#34;2052&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;For permissions, you only need &lt;em&gt;&amp;ldquo;write:statuses&amp;rdquo;&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;You should now see a page showing the secrets. You only need the access token (bottom value) from this.&lt;/p&gt;&#xA;&lt;h2 id=&#34;grafana&#34;&gt;&lt;a href=&#34;#grafana&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Grafana&#xA;&lt;/h2&gt;&lt;p&gt;Now we can switch to Grafana. There&amp;rsquo;s a menu item called &lt;em&gt;Contact points&lt;/em&gt; under &lt;em&gt;Alerting&lt;/em&gt;. At least Email should already appear in the list. We now add a new destination with &lt;em&gt;&amp;quot;+ Create contact point&amp;quot;&lt;/em&gt;:&lt;/p&gt;&#xA;&lt;p&gt;&lt;img alt=&#34;Screenshot Contact Point&#34; class=&#34;gallery-image&#34; data-flex-basis=&#34;535px&#34; data-flex-grow=&#34;223&#34; data-title-escaped=&#34;Screenshot Contact Point&#34; height=&#34;1112&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/grafana-notifications-for-mastodon/Screenshot-from-2025-10-14-12-35-32_hu_b50144b7f683b82.png&#34; srcset=&#34;https://dotnet.work/blog/grafana-notifications-for-mastodon/Screenshot-from-2025-10-14-12-35-32_hu_34840b37b23bf79e.png 800w, https://dotnet.work/blog/grafana-notifications-for-mastodon/Screenshot-from-2025-10-14-12-35-32_hu_6ceedc02f82f0213.png 1600w, https://dotnet.work/blog/grafana-notifications-for-mastodon/Screenshot-from-2025-10-14-12-35-32_hu_5ea25a905a5f1942.png 2400w, https://dotnet.work/blog/grafana-notifications-for-mastodon/Screenshot-from-2025-10-14-12-35-32_hu_b50144b7f683b82.png 2480w&#34; title=&#34;Screenshot Contact Point&#34; width=&#34;2480&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;The name is of course freely selectable, for integration choose &lt;em&gt;Webhook&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;For the URL, enter the following:&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;https://chaos.social/api/v1/statuses?access_token=&amp;lt;Secret&amp;gt;&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;Instead of &lt;em&gt;&lt;Secret&gt;&lt;/em&gt;, enter the access token from Mastodon. If you now click on &lt;em&gt;Test&lt;/em&gt; (and leave the default values as they are), a new entry will already be created in Mastodon under your name:&#xA;&lt;img alt=&#34;Screenshot Test Notification&#34; class=&#34;gallery-image&#34; data-flex-basis=&#34;450px&#34; data-flex-grow=&#34;187&#34; data-title-escaped=&#34;Screenshot Test Notification&#34; height=&#34;352&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/grafana-notifications-for-mastodon/Screenshot-from-2025-10-14-12-39-18_hu_a2c4001f64561a4c.png&#34; title=&#34;Screenshot Test Notification&#34; width=&#34;660&#34;&gt;&lt;/p&gt;&#xA;&lt;h3 id=&#34;getting-down-to-business&#34;&gt;&lt;a href=&#34;#getting-down-to-business&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Getting Down to Business&#xA;&lt;/h3&gt;&lt;p&gt;That&amp;rsquo;s nice for starters, but we obviously can&amp;rsquo;t do much with just &amp;ldquo;firing&amp;rdquo;. We want to see the actual error. Also, we probably don&amp;rsquo;t want to inform the whole world. It makes more sense to simply mention ourselves and set the visibility to &amp;ldquo;direct&amp;rdquo;.&lt;/p&gt;&#xA;&lt;p&gt;To do this, we open &lt;em&gt;Optional Webhook Settings&lt;/em&gt;, set &lt;em&gt;HTTP Method&lt;/em&gt; to &lt;em&gt;POST&lt;/em&gt; and click the &amp;ldquo;Edit Payload Template&amp;rdquo; button next to &lt;em&gt;Custom Payload&lt;/em&gt;. A new window pops up and after we&amp;rsquo;ve selected &lt;em&gt;Enter custom payload template&lt;/em&gt; there, we can enter the following content:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;{&#xA;  &amp;#34;status&amp;#34;: &amp;#34;Alert: {{ .CommonLabels.alertname }}\nStatus: {{ .Status }}\n{{ range .Alerts }}{{ .Annotations.summary }}{{ end }}&amp;#34;&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now the alert is already a bit more meaningful:&#xA;&lt;img alt=&#34;Screenshot Alert&#34; class=&#34;gallery-image&#34; data-flex-basis=&#34;353px&#34; data-flex-grow=&#34;147&#34; data-title-escaped=&#34;Screenshot Alert&#34; height=&#34;448&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/grafana-notifications-for-mastodon/Screenshot-from-2025-10-14-12-44-36_hu_d60f4511809a5bfc.png&#34; title=&#34;Screenshot Alert&#34; width=&#34;660&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;Still missing the private mode. This can be set via &lt;em&gt;Visibility&lt;/em&gt;. Additionally, it&amp;rsquo;s best to mention yourself. (I&amp;rsquo;ve modified my name slightly here so you don&amp;rsquo;t make a copy-paste mistake :) )&lt;/p&gt;&#xA;&lt;p&gt;While we&amp;rsquo;re at it, I&amp;rsquo;ve also used the &lt;em&gt;spoiler-text&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;{&#xA;  &amp;#34;status&amp;#34;: &amp;#34;@guacamoleeeee {{ .CommonAnnotations.summary }}&amp;#34;,&#xA;  &amp;#34;visibility&amp;#34;: &amp;#34;direct&amp;#34;,&#xA;  &amp;#34;sensitive&amp;#34;: false,&#xA;  &amp;#34;spoiler_text&amp;#34;: &amp;#34;🚨 {{ .CommonLabels.alertname }} {{ .Status }}&amp;#34;,&#xA;  &amp;#34;language&amp;#34;: &amp;#34;en&amp;#34;&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The result is then as follows:&#xA;&lt;img alt=&#34;Screenshot Alert&#34; class=&#34;gallery-image&#34; data-flex-basis=&#34;254px&#34; data-flex-grow=&#34;106&#34; data-title-escaped=&#34;Screenshot Alert&#34; height=&#34;648&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/grafana-notifications-for-mastodon/Screenshot-from-2025-10-14-12-52-52_hu_6b769ac31c113ea0.png&#34; title=&#34;Screenshot Alert&#34; width=&#34;688&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;A complete list of Mastodon properties can be found here:&#xA;&lt;a class=&#34;link&#34; href=&#34;https://docs.joinmastodon.org/methods/statuses/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;https://docs.joinmastodon.org/methods/statuses/&lt;/a&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;have-fun&#34;&gt;&lt;a href=&#34;#have-fun&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Have Fun&#xA;&lt;/h2&gt;&lt;p&gt;Wishing you many alerts might be a bit strange, but I hope I could help with this.&lt;/p&gt;</description>
        </item><item>
            <title>Led Progressbar for 3d Printing</title>
            <link>https://dotnet.work/blog/led-progressbar-for-3d-printing/</link>
            <pubDate>Tue, 06 May 2025 14:02:00 +0200</pubDate><author>dotnet@oles.cloud (Ole Albers)</author>
            <guid>https://dotnet.work/blog/led-progressbar-for-3d-printing/</guid>
            <description>&lt;p&gt;3D printing can be fun, but can also be very annoying if something bad happens. So I wanted to have an easy indicator if something goes wrong without having to check the print every few minutes&lt;/p&gt;&#xA;&lt;h2 id=&#34;wled-to-the-rescue&#34;&gt;&lt;a href=&#34;#wled-to-the-rescue&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;WLED to the rescue&#xA;&lt;/h2&gt;&lt;p&gt;My 3D - Printer is in a small room. I usually do not have to enter that room until I want to remove the print. So I decided to add an RGB-LED-Strip that shows how the print is running. I decided to use WLED using a cheap ESP8266MOD. Installing the WLED-Software is easy. It can even be done using a simple browser if you are on Windows or Mac: &lt;a class=&#34;link&#34; href=&#34;https://install.wled.me/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;https://install.wled.me/&lt;/a&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;home-assistant&#34;&gt;&lt;a href=&#34;#home-assistant&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Home Assistant&#xA;&lt;/h2&gt;&lt;p&gt;After that I needed to write some automations. The WLED is detected automatically on Home Assistant, for my Creality 3d Printer I used the &amp;ldquo;Moonraker&amp;rdquo; Integration available via HACS: &lt;a class=&#34;link&#34; href=&#34;https://moonraker-home-assistant.readthedocs.io/en/stable/index.html&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;https://moonraker-home-assistant.readthedocs.io/en/stable/index.html&lt;/a&gt;&lt;/p&gt;&#xA;&lt;h3 id=&#34;error-display&#34;&gt;&lt;a href=&#34;#error-display&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Error display&#xA;&lt;/h3&gt;&lt;p&gt;The printer reports its Print state through the &amp;ldquo;sensor.k2plus_b4f8_current_print_state&amp;rdquo; (yours can have a different prefix). The following values are possible:&#xA;&lt;code&gt;standby, printing, paused, complete, cancelled, error&lt;/code&gt;&#xA;But errors are never reported as an &amp;ldquo;error&amp;rdquo; but always as &amp;ldquo;paused&amp;rdquo;. There might be more critical ones that are reported as error but I never had those. So all we have to do is to react on the &amp;ldquo;paused&amp;rdquo; - state and set the WLED accordingly:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;alias: 3DP Error&#xA;description: &amp;#34;&amp;#34;&#xA;triggers:&#xA;  - entity_id:&#xA;      - sensor.k2plus_b4f8_current_print_state&#xA;    trigger: state&#xA;    to: paused&#xA;conditions: []&#xA;actions:&#xA;  - target:&#xA;      entity_id: light.wled_2&#xA;    action: light.turn_on&#xA;    data:&#xA;      effect: Breathe&#xA;      rgb_color:&#xA;        - 204&#xA;        - 0&#xA;        - 0&#xA;mode: single&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Again: Your Entities might have different names&lt;/p&gt;&#xA;&lt;h3 id=&#34;progress-display&#34;&gt;&lt;a href=&#34;#progress-display&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Progress display&#xA;&lt;/h3&gt;&lt;p&gt;To be able to display the progress we need the &lt;code&gt;progress&lt;/code&gt;-Entity and in addition make sure that the printer really is printing:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;alias: 3DP Progress&#xA;description: &amp;gt;-&#xA;  Updates WLED2 fill level to match sensor.k2plus_b4f8_progress value with&#xA;  purple color&#xA;triggers:&#xA;  - entity_id: sensor.k2plus_b4f8_progress&#xA;    trigger: state&#xA;conditions:&#xA;  - condition: state&#xA;    entity_id: sensor.k2plus_b4f8_current_print_state&#xA;    state: printing&#xA;actions:&#xA;  - target:&#xA;      entity_id: light.wled_2&#xA;    data:&#xA;      rgb_color:&#xA;        - 128&#xA;        - 0&#xA;        - 128&#xA;      effect: Percent&#xA;    action: light.turn_on&#xA;  - target:&#xA;      entity_id: number.wled_intensitat_2&#xA;    data:&#xA;      value: &amp;gt;&#xA;        {% set progress_value = states(&amp;#39;sensor.k2plus_b4f8_progress&amp;#39;) | float(0)&#xA;        %} {% if progress_value &amp;gt; 100 %}&#xA;          100&#xA;        {% elif progress_value &amp;lt; 0 %}&#xA;          0&#xA;        {% else %}&#xA;          {{ progress_value | round(1) }}&#xA;        {% endif %}&#xA;    action: number.set_value&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;display-countdown&#34;&gt;&lt;a href=&#34;#display-countdown&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Display Countdown&#xA;&lt;/h3&gt;&lt;p&gt;Last but not least: Because the 3d printer consumes quite an amount of power even on standby I want to shutdown power automatically when he finished printing. Shutting down itself is quite simple:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;alias: 3DP Strom automatisch aus&#xA;description: &amp;#34;&amp;#34;&#xA;triggers:&#xA;  - trigger: state&#xA;    entity_id:&#xA;      - sensor.k2plus_b4f8_current_print_state&#xA;    to: complete&#xA;    for:&#xA;      hours: 0&#xA;      minutes: 15&#xA;      seconds: 0&#xA;conditions:&#xA;  - condition: numeric_state&#xA;    entity_id: sensor.k2plus_b4f8_hotend_fan&#xA;    below: 10&#xA;  - condition: numeric_state&#xA;    entity_id: sensor.k2plus_b4f8_extruder_temperature&#xA;    below: 50&#xA;actions:&#xA;  - type: turn_off&#xA;    device_id: 710995d98fe3640e134ae275e94dc491&#xA;    entity_id: 7cd829033182474ac8f91a7c11539dfa&#xA;    domain: switch&#xA;mode: single&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I wait 15 minutes and just to be sure I make sure the temperature of the extruder has gone below 50 degrees&lt;/p&gt;&#xA;&lt;p&gt;Finally show the countdown for that 15 minutes:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;alias: 3DP CountDown for Shutdown&#xA;description: &amp;#34;&amp;#34;&#xA;triggers:&#xA;  - trigger: state&#xA;    entity_id:&#xA;      - sensor.k2plus_b4f8_current_print_state&#xA;    to: complete&#xA;    for:&#xA;      hours: 0&#xA;      minutes: 0&#xA;      seconds: 10&#xA;conditions: []&#xA;actions:&#xA;  - target:&#xA;      entity_id: light.wled_2&#xA;    data:&#xA;      rgb_color:&#xA;        - 0&#xA;        - 255&#xA;        - 0&#xA;      effect: Percent&#xA;    action: light.turn_on&#xA;  - target:&#xA;      entity_id: number.wled_intensitat_2&#xA;    data:&#xA;      value: 100&#xA;    action: number.set_value&#xA;  - repeat:&#xA;      count: 60&#xA;      sequence:&#xA;        - delay:&#xA;            hours: 0&#xA;            minutes: 0&#xA;            seconds: 15&#xA;            milliseconds: 0&#xA;        - target:&#xA;            entity_id: number.wled_intensitat_2&#xA;          data:&#xA;            value: &amp;gt;&#xA;              {% set calculated_value = 100 - ((repeat.index + 1) * (100/60)) %}&#xA;              {% if calculated_value &amp;lt; 0 %}&#xA;                0&#xA;              {% else %}&#xA;                {{ calculated_value | round(1) }}&#xA;              {% endif %}&#xA;          action: number.set_value&#xA;mode: single&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That&amp;rsquo;s it. These are my automations for my 3D printer status&lt;/p&gt;&#xA;</description>
        </item><item>
            <title>Make Videos work with DaVinci Resolve on Linux Mint</title>
            <link>https://dotnet.work/blog/davinci-resolve-on-linux-mint/</link>
            <pubDate>Sat, 25 Jan 2025 12:00:00 +0100</pubDate><author>dotnet@oles.cloud (Ole Albers)</author>
            <guid>https://dotnet.work/blog/davinci-resolve-on-linux-mint/</guid>
            <description>&lt;p&gt;Installing DaVinci Resolve on Linux Mint takes a few steps. But at the end it simply works after you have done those.&lt;/p&gt;&#xA;&lt;p&gt;I tested those on Linux Mint but should work on most Ubuntu - based Distros.&lt;/p&gt;&#xA;&lt;p&gt;&lt;em&gt;I mostly followed the instructions from &lt;a class=&#34;link&#34; href=&#34;https://www.virtualcuriosities.com/articles/1784/how-to-install-davinci-resolve-in-linux-mint&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;virtual curiosities&lt;/a&gt; and just added a few of my own additions&lt;/em&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;downloading-davinci-resolve&#34;&gt;&lt;a href=&#34;#downloading-davinci-resolve&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Downloading DaVinci Resolve&#xA;&lt;/h2&gt;&lt;p&gt;The first step is to download DaVinci resolve &lt;a class=&#34;link&#34; href=&#34;https://www.blackmagicdesign.com/products/davinciresolve&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;from the official website&lt;/a&gt;. You can chose between the free version and the paid version. I really think the paid version is worth the price but the free version nearly allows anything you will need. And of course for testing it should be the first way to go.&lt;/p&gt;&#xA;&lt;p&gt;After you have downloaded the zip, extract it and open the console on that folder.&#xA;Make sure the &lt;code&gt;.run&lt;/code&gt;- file can be executed:&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;chmod +x Davinci_Resolve_[version]_Linux.run&lt;/code&gt;&#xA;(replace version with the version you downloaded. There should be only one .run - file)&#xA;now run the installer:&#xA;&lt;code&gt;./Davinci_Resolve_[version]_Linux.run&lt;/code&gt;&#xA;You will get error messages about missing dependencies. Don&amp;rsquo;t worry. This is to be expected.  Use &lt;code&gt;apt install&lt;/code&gt; to install all the missing dependencies that are thrown at you&#xA;e.g.&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;sudo apt install libasound2 libglib2.0-0&lt;/code&gt;&#xA;There will be way more dependencies missing. After you have done that restart the installer. Now the next bunch of missing dependencies are shown. Repeat that process until no new dependencies are shown. And yes: This will take up to ten times or so. This does not mean you are in an endless loop. At some point this will stop.&lt;/p&gt;&#xA;&lt;p&gt;You will most likely find a missing dependency for &lt;code&gt;libasound2&lt;/code&gt; which cannot be installed on Mint. Use &lt;code&gt;libasound2t64&lt;/code&gt; instead.&lt;/p&gt;&#xA;&lt;p&gt;Once the installer only shows missing dependencies you already installed you can disable the dependencycheck:&#xA;&lt;code&gt;export SKIP_PACKAGE_CHECK=1&lt;/code&gt;&#xA;and rerun the installer&lt;/p&gt;&#xA;&lt;p&gt;Now finally the Wizard should show up and DaVinci gets installed. It will take a while until this is finished.&lt;/p&gt;&#xA;&lt;p&gt;After the installation you should create a shellscript to run DaVinci (the icon on the desktop created does not work from start):&#xA;&lt;code&gt;env LD_PRELOAD=/lib/x86_64-linux-gnu/libglib-2.0.so.0:/lib/x86_64-linux-gnu/libgio-2.0.so.0:/lib/x86_64-linux-gnu/libgdk_pixbuf-2.0.so.0:/lib/x86_64-linux-gnu/libgmodule-2.0.so.0 /opt/resolve/bin/resolve %u&lt;/code&gt;&#xA;call the file &lt;code&gt;davinci.sh&lt;/code&gt; and make sure it can be run:&#xA;&lt;code&gt;chmod +x ./davinci.sh&lt;/code&gt;&#xA;Now you should be able to start DaVinci with &lt;code&gt;./davinci.sh&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;Once you are in DaVinci Resolve you will notice that the first Videos you try to add as media will not display. And the few that do most likely will not have audio. The reason is that on Linux DaVinci is quite picky what Video formats to accept.&lt;/p&gt;&#xA;&lt;p&gt;Use &lt;a class=&#34;link&#34; href=&#34;https://github.com/Guacam-Ole/Piero&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;Piero&lt;/a&gt; for the rescue.&lt;/p&gt;&#xA;&lt;div class=&#34;video-wrapper&#34;&gt;&#xA;    &lt;iframe loading=&#34;lazy&#34; &#xA;            src=&#34;https://www.youtube-nocookie.com/embed/2WifdGUhIsU&#34; &#xA;            allowfullscreen &#xA;            title=&#34;YouTube Video&#34;&#xA;    &gt;&#xA;    &lt;/iframe&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&lt;p&gt;This tool automatically created Videos that can be read by Resolve and also creates smaller Proxy files. You will need to install dotnet 8 runtime for this to work&lt;/p&gt;&#xA;&lt;p&gt;And that`s it. You should be able to work with Resolve on Linux, now.&lt;/p&gt;&#xA;</description>
        </item><item>
            <title>Automatic Blogpost translation with Claude.ai</title>
            <link>https://dotnet.work/blog/automatic-blogpost-translation-with-claude-ai/</link>
            <pubDate>Thu, 16 Jan 2025 12:00:00 +0100</pubDate><author>dotnet@oles.cloud (Ole Albers)</author>
            <guid>https://dotnet.work/blog/automatic-blogpost-translation-with-claude-ai/</guid>
            <description>&lt;p&gt;To be completely honest: I do not really like automatic translations. Especially if you are not born in an english-speaking country, translations have been a real pain from the beginning.&#xA;MSDN was one of those contenders that did really, really bad translations, years before AI was something to talk about.&lt;/p&gt;&#xA;&lt;p&gt;Now with AI the translations get better, but there are still bad ones and too many contents get translated. YouTube is nearly not usable anymore from germany if you are interested in original content.&lt;/p&gt;&#xA;&lt;h2 id=&#34;what-way-to-go&#34;&gt;&lt;a href=&#34;#what-way-to-go&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;What way to go?&#xA;&lt;/h2&gt;&lt;p&gt;Having said that I still think a translation can be something really useful. First of all it has to be good. And second requirement is that users have a choice.&lt;/p&gt;&#xA;&lt;p&gt;So I decided to add translations to my blogpost. I stopped blogging in two languages in 2019, so all german entries have been very old. Time to check if there are good solutions out there.&lt;/p&gt;&#xA;&lt;p&gt;Because I translated a book I already had some comparisons. At least from german to english currently my favorite tool was Claude.io. I did not really like the quality of DeepL and ChatGPT had good results but was very slow. In addition: Claude really seems to like MarkDown. Which is helpful in this case.&lt;/p&gt;&#xA;&lt;p&gt;This time I wanted to use the API to automatically translate my Grav-Blogitems. Bad news is: You need a paid account for that. Good news is: It does not really matter how many money you use to fill your wallet and it is quite cheap.&lt;/p&gt;&#xA;&lt;p&gt;My 50KB of data made up about 20 Cents. Of course your milage may vary. This is not sponsored, BTW.&lt;/p&gt;&#xA;&lt;h2 id=&#34;preparations&#34;&gt;&lt;a href=&#34;#preparations&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Preparations&#xA;&lt;/h2&gt;&lt;p&gt;After creating an account, you can use the NuGet-Package &lt;a class=&#34;link&#34; href=&#34;https://www.nuget.org/packages/Anthropic.SDK&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;Anthrophic.SDK&lt;/a&gt;. You simply create a new Client with your secret and can start with some messages. If you ever worked with OpenAi-API or Llama creating the message is nearly the same:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;  var client = new AnthropicClient(new APIAuthentication(_apiSecret));&#xA;var messages = new List&amp;lt;Message&amp;gt;()&#xA;{&#xA;   new Message(RoleType.User, &amp;#34;Bitte übersetze das folgende Markdown nach Deutsch.&amp;#34;);&#xA;   &#xA;),&#xA;new Message(RoleType.User, markdown)&#xA;};&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In case you are curious the message simply tells to translate a markdown into german&lt;/p&gt;&#xA;&lt;p&gt;Then you add the parameters. Temperature is - as known for AI-APIS how &amp;ldquo;creative&amp;rdquo; the AI is allowed to get. &lt;code&gt;MaxTokens&lt;/code&gt; is a required value and can&amp;rsquo;t be too high:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;        var pars = new MessageParameters()&#xA;        {&#xA;            Messages = messages,&#xA;            MaxTokens = 1024,&#xA;            Model = AnthropicModels.Claude35Sonnet,&#xA;            Stream = false,&#xA;            Temperature = 1m,&#xA;        };&#xA;`Getting the response now is easy:`&#xA;var response = await client.Messages.GetClaudeMessageAsync(pars);&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;What I don&amp;rsquo;t like: If anything is wrong with your account you do not really receive a useful error code but simply &lt;code&gt;null&lt;/code&gt; I had this problem before putting some money into the account.&lt;/p&gt;&#xA;&lt;p&gt;The response is now in &lt;code&gt;response.Message&lt;/code&gt;. The result is great, but long texts just stop after a while. &lt;code&gt;Response.StopReason&lt;/code&gt; tells us why:&lt;/p&gt;&#xA;&#xA;    &lt;blockquote&gt;&#xA;        &lt;p&gt;max_tokens&lt;/p&gt;&#xA;&#xA;    &lt;/blockquote&gt;&#xA;&lt;p&gt;The obvious solution would be to increase the allowed tokens but as I said MaxTokens has a limited range. So you have to catch that error and go into a loop. Fun thing is: Claude &amp;ldquo;forgets&amp;rdquo; what he said before. So you have to tell him and ask to continue afterwards:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt; while (response.StopReason == &amp;#34;max_tokens&amp;#34;)&#xA; {&#xA;   pars.Messages.Add(new Message(RoleType.Assistant, response.Message));&#xA;   pars.Messages.Add(new Message(RoleType.User, &amp;#34;bitte fahre fort&amp;#34;));&#xA;   response = await client.Messages.GetClaudeMessageAsync(pars);&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That`s it. Not really that complicated after all.&lt;/p&gt;&#xA;&lt;h2 id=&#34;ai-doing-ai-things&#34;&gt;&lt;a href=&#34;#ai-doing-ai-things&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;AI doing AI things&#xA;&lt;/h2&gt;&lt;p&gt;While the result is quite good I had a few issues. Being a coder it is a bit of a learning curve that errors cannot be fixed through coding but only by messages. The first was that the translation is quite formal. An additional message changed that:&#xA;&lt;code&gt;pars.Messages.Add(new Message(RoleType.User, &amp;quot;Enthält der Text eine persönliche Anrede, verwende &#39;Du&#39; und nicht &#39;Sie&#39;. &amp;quot;);&lt;/code&gt;&#xA;(I will not be able to translate this command. It is a really german thing :) )&lt;/p&gt;&#xA;&lt;p&gt;Another issue is that Claude likes to explain things. So we have to tell him to stop doing that:&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;pars.Messages.Add(new Message(RoleType.User, &amp;quot;Antworte ausschließlich mit dem Dokument, ergänze keine Erklärungen. &amp;quot;);&lt;/code&gt;&#xA;(Which translates into &amp;lsquo;Only reply with the document, do not explain anything&amp;rsquo;)&lt;/p&gt;&#xA;&lt;p&gt;Surprisingly I had to add no additional message to not translate code. If you translate &lt;code&gt;apt-get install&lt;/code&gt; into german &lt;code&gt;apt-get installiere&lt;/code&gt; for example that would not work. I saw this happen on other sites but Claude did not need any additional hints.&lt;/p&gt;&#xA;&lt;p&gt;On the other hand. Claude sometimes decided that my code wasn&amp;rsquo;t interesting enough:&lt;/p&gt;&#xA;&#xA;    &lt;blockquote&gt;&#xA;        &lt;p&gt;[Rest des Dokuments enthält die gleichen Code-Blöcke und technischen Anweisungen wie im Original, nur der erklärende Text wurde ins Deutsche übersetzt]&lt;/p&gt;&#xA;&#xA;    &lt;/blockquote&gt;&#xA;&lt;p&gt;Which roughly translates into &amp;ldquo;the rest of the document contains the same code as the original. Did not translate that&amp;rdquo;. Did not expect that, but that was fixable as well:&#xA;&lt;code&gt;pars.Messages.Add(new Message(RoleType.User, &amp;quot;Fasse keine Inhalte zusammen, sondern gebe alles aus. Ziel soll sein dass man das deutschsprachige Dokument genauso alleine versteht wie das englischsprachige &amp;quot;):&lt;/code&gt;&#xA;which roughly translates into &amp;ldquo;Do not summarize contents. The goal should be that the german document can stand on its own&amp;rdquo;&lt;/p&gt;&#xA;&lt;p&gt;Now the result is exactly as needed. For completionists sake here is the complete code:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;private async Task&amp;lt;string&amp;gt; TranslateSingleFile(string source)&#xA;{&#xA;   var translated=string.Empty;&#xA;   var markdown = File.ReadAllText(source);&#xA;   var client = new AnthropicClient(new APIAuthentication(_apiSecret));&#xA;   var messages = new List&amp;lt;Message&amp;gt;()&#xA;   {&#xA;      new Message(RoleType.User, &amp;#34;Bitte übersetze das folgende Markdown nach Deutsch.&amp;#34;&#xA;      +&amp;#34; Antworte ausschließlich mit dem Dokument, ergänze keine Erklärungen. &amp;#34;&#xA;      + &amp;#34;Enthält der Text eine persönliche Anrede, verwende &amp;#39;Du&amp;#39; und nicht &amp;#39;Sie&amp;#39;. &amp;#34;&#xA;      +&amp;#34;Fasse keine Inhalte zusammen, sondern gebe alles aus. Ziel soll sein dass man das deutschsprachige Dokument genauso alleine versteht wie das englischsprachige &amp;#34;            &#xA;       ),&#xA;       new Message(RoleType.User, markdown)&#xA;   };&#xA;&#xA;   var pars = new MessageParameters()&#xA;   {&#xA;      Messages = messages,&#xA;      MaxTokens = 1024,&#xA;      Model = AnthropicModels.Claude35Sonnet,&#xA;      Stream = false,&#xA;      Temperature = 1m,&#xA;   };&#xA;&#xA;   var response = await client.Messages.GetClaudeMessageAsync(pars);&#xA;   if (response?.Message == null) throw new Exception(&amp;#34;No valid response from Claude&amp;#34;);&#xA;        &#xA;   translated += response.Message.ToString();&#xA;   while (response.StopReason == &amp;#34;max_tokens&amp;#34;)&#xA;   {      &#xA;      pars.Messages.Add(new Message(RoleType.Assistant, response.Message));&#xA;      pars.Messages.Add(new Message(RoleType.User, &amp;#34;bitte fahre fort&amp;#34;));&#xA;      response = await client.Messages.GetClaudeMessageAsync(pars);     &#xA;      translated += response.Message.ToString();&#xA;   }&#xA;&#xA;   if (response.StopReason != &amp;#34;end_turn&amp;#34;) throw new Exception($&amp;#34;Translation failed. Stop reason:{response.StopReason}&amp;#34;);&#xA;   return translated;&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;</description>
        </item><item>
            <title>Setting single Panels on NanoLeaf using Home Assistant</title>
            <link>https://dotnet.work/blog/nanoleaf/</link>
            <pubDate>Sat, 20 Jan 2024 12:00:00 +0100</pubDate><author>dotnet@oles.cloud (Ole Albers)</author>
            <guid>https://dotnet.work/blog/nanoleaf/</guid>
            <description>&lt;p&gt;I went deep into Home Assistant automation currently and - as everybody knows who ever went this road - more ideas came up. So my newest idea was to use the Hex-Panels from Nanoleaf to display some information. For example if the washing machine is ready, I have an appointment, the Smartphone Battery gets low and so on.&lt;/p&gt;&#xA;&lt;!-- image missing: https://github.com/Guacam-Ole/dotnetwork/assets/4692497/eaa1c25f-8b92-420c-b71d-0eee8d6758ab (NanoLeaf with different Colors on the wall) --&gt;&#xA;&lt;p&gt;This entry describes how I managed to do that.&lt;/p&gt;&#xA;&lt;p&gt;&lt;em&gt;I collected all this information from different sources. THe most important was from sygys who posted the Home Assistant configuration: &lt;a class=&#34;link&#34; href=&#34;https://community.home-assistant.io/t/nanoleaf-per-tile-light-control-build-in-ha/442720&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;https://community.home-assistant.io/t/nanoleaf-per-tile-light-control-build-in-ha/442720&lt;/a&gt;&#xA;By the API documentation alone I would not have been able to achieve that.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-hardware&#34;&gt;&lt;a href=&#34;#the-hardware&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;The hardware&#xA;&lt;/h2&gt;&lt;p&gt;I mostly used Nanoleaf Elements in the wooden design. These cannot display any colors but just lighten up. I like the descent appearance of those. Do not want to create an all-rgb-gaming-experience in my living room. I also added three dark black hexagon tiles to it. Those where meant to display the status if needed.&#xA;BTW: The nanoleaf app does not offer any color scenes in this setup because the wooden panels are the &amp;ldquo;main&amp;rdquo; panels. But that does not matter for this scenario.&lt;/p&gt;&#xA;&lt;h2 id=&#34;integrating-in-home-assistant&#34;&gt;&lt;a href=&#34;#integrating-in-home-assistant&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Integrating in Home Assistant&#xA;&lt;/h2&gt;&lt;p&gt;For the main features I used the standard integration for Home Assistant. That allows detecing swipe controls (another advantage of the wooden panels because you don&amp;rsquo;t see fingerprints there) and start mostly anything what the app allows. Which does NOT include adressing single panels&lt;/p&gt;&#xA;&lt;h2 id=&#34;using-the-api&#34;&gt;&lt;a href=&#34;#using-the-api&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Using the API&#xA;&lt;/h2&gt;&lt;p&gt;Currently this can only be done by a REST-Api. Initially you need any tool that can send POST - Commands. You can use Curl if you like to work on the shell, or UI-Tools like Postman.&lt;/p&gt;&#xA;&lt;h3 id=&#34;retrieving-a-secret&#34;&gt;&lt;a href=&#34;#retrieving-a-secret&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Retrieving a secret&#xA;&lt;/h3&gt;&lt;p&gt;The first manual step is to obtain a secret. This is done exactly like when installing the Home Assistant integration. You just pair the Panels by pressing the power button for a few seconds until the LEDs start blnking/moving. Don&amp;rsquo;t worry, this will NOT remove any previous pairings.&#xA;Now while the pairing is active call the following URL:&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;POST http://192.168.178.65:16021/api/v1/new&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;(replace the IP by the Nanoleaf IP. Should be visible in your Router for example)&lt;/p&gt;&#xA;&lt;p&gt;This will return just a simple value which contains your secret token. This token will remain valid even if you unpower the Nanoleaf. Make sure you store this secret somewhere, you will need it.&lt;/p&gt;&#xA;&lt;h3 id=&#34;retrieve-the-configuration&#34;&gt;&lt;a href=&#34;#retrieve-the-configuration&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Retrieve the Configuration&#xA;&lt;/h3&gt;&lt;p&gt;Now it is time to test your secret and retrieve the configuration from your Nanoleaf panels. You even can do this in your browser, or still in Postman/Curl:&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;http://192.168.178.65:16021/api/v1/thisisthemagicsecretreturnedbefore/&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;The most interesting part is the layout/positionData-Part in that response:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;      &amp;#34;layout&amp;#34;: {&#xA;            &amp;#34;numPanels&amp;#34;: 46,&#xA;            &amp;#34;sideLength&amp;#34;: 0,&#xA;            &amp;#34;positionData&amp;#34;: [&#xA;                {&#xA;                    &amp;#34;panelId&amp;#34;: 8172,&#xA;                    &amp;#34;x&amp;#34;: 301,&#xA;                    &amp;#34;y&amp;#34;: 747,&#xA;                    &amp;#34;o&amp;#34;: 360,&#xA;                    &amp;#34;shapeType&amp;#34;: 15&#xA;                },&#xA;                {&#xA;                    &amp;#34;panelId&amp;#34;: 36653,&#xA;                    &amp;#34;x&amp;#34;: 276,&#xA;                    &amp;#34;y&amp;#34;: 703,&#xA;                    &amp;#34;o&amp;#34;: 300,&#xA;                    &amp;#34;shapeType&amp;#34;: 15&#xA;                },&#xA;[..]&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I honestly was a bit surprised, because I do NOT have 46 panels. Far from it. The simple truth is: THe wooden Panels have 6 LEDs. each of them has their own ID. So having 7 wooden panels that makes 42 Ids, additional 3 Ids for the three black panels and the control buttons is an additional panel (with id 0)&lt;/p&gt;&#xA;&lt;p&gt;If you do not need to adress all Panels but just a single one, a good hint is to just rotate one of the panels, make that call again and see which panel has a new orientation (&amp;ldquo;o&amp;rdquo; in the json)&lt;/p&gt;&#xA;&lt;p&gt;Now you have to write down all PanelIds. At least those you want to modifiy. Because I mostly want to use the black panels I split the following part in two, but you can also work with one (or even more).&lt;/p&gt;&#xA;&lt;h3 id=&#34;configure-rest-in-home-assistant&#34;&gt;&lt;a href=&#34;#configure-rest-in-home-assistant&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Configure Rest in Home Assistant&#xA;&lt;/h3&gt;&lt;p&gt;Go to your beloved Home Assistant Configuration (configuration.yaml) and add the following entries:&#xA;{% raw %}&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;rest_command:&#xA;  nanoleaf_wood:&#xA;    url: http://192.168.178.65:16021/api/v1/thisisthemagicsecretreturnedbefore/effects&#xA;    method: PUT&#xA;    payload: &amp;gt;&#xA;      { &amp;#34;write&amp;#34; : {&amp;#34;command&amp;#34;: &amp;#34;display&amp;#34;, &amp;#34;animType&amp;#34;: &amp;#34;static&amp;#34;, &amp;#34;animData&amp;#34;:&#xA;      {%- set all = [ 8172,36653,  32365,61100,6889,35368,57498,28763,33051,4570,9112,45913,61802,25003,37099,42,14434,43171,34741,29680,58161,4721,33456,45298,4960,8482,45539,16547,53346,9255,5361,58801,30064,33077,4596,57524,54769,50577,21840,42000,13521,1683] %}&#xA;      {%- set ns = namespace(panels=[panels | count | string]) %}&#xA;      {%- for panel in panels %}&#xA;      {%- set ns.panels = ns.panels + [ &amp;#39;{} 1 {} {} {} 0 20&amp;#39;.format(all[panel.number-1], panel.r, panel.g, panel.b) ] %}&#xA;      {%- endfor %}&#xA;      &amp;#34;{{ ns.panels | join(&amp;#39; &amp;#39;) }}&amp;#34;,&#xA;      &amp;#34;loop&amp;#34;: false, &amp;#34;palette&amp;#34;: [], &amp;#34;colorType&amp;#34;: &amp;#34;HSB&amp;#34;}&#xA;      }&#xA;    content_type: &amp;#34;application/json&amp;#34;&#xA;  nanoleaf_black:&#xA;    url: http://192.168.178.65:16021/api/v1/thisisthemagicsecretreturnedbefore/effects&#xA;    method: PUT&#xA;    payload: &amp;gt;&#xA;      { &amp;#34;write&amp;#34; : {&amp;#34;command&amp;#34;: &amp;#34;display&amp;#34;, &amp;#34;animType&amp;#34;: &amp;#34;static&amp;#34;, &amp;#34;animData&amp;#34;:&#xA;      {%- set all = [ 52732, 44680, 17782 ] %}      &#xA;      {%- set ns = namespace(panels=[panels | count | string]) %}&#xA;      {%- for panel in panels %}&#xA;      {%- set ns.panels = ns.panels + [ &amp;#39;{} 1 {} {} {} 0 20&amp;#39;.format(all[panel.number-1], panel.r, panel.g, panel.b) ] %}&#xA;      {%- endfor %}&#xA;      &amp;#34;{{ ns.panels | join(&amp;#39; &amp;#39;) }}&amp;#34;,&#xA;      &amp;#34;loop&amp;#34;: false, &amp;#34;palette&amp;#34;: [], &amp;#34;colorType&amp;#34;: &amp;#34;HSB&amp;#34;}&#xA;      }&#xA;    content_type: &amp;#34;application/json&amp;#34;&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;{% endraw %}&#xA;Replace the IP with your Nenoleaf - IP and the secret with your secret (before &amp;ldquo;/effects&amp;rdquo;). It is up to you if you want one or more different calls. The numbers in the &amp;ldquo;set all&amp;rdquo; - part have to be replaced by the &lt;code&gt;panelId&lt;/code&gt;s you retrieved earlier.&lt;/p&gt;&#xA;&lt;p&gt;What comes next is of course testing your config and then restarting Home Assistant.&lt;/p&gt;&#xA;&lt;h3 id=&#34;automation-examples&#34;&gt;&lt;a href=&#34;#automation-examples&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Automation Examples&#xA;&lt;/h3&gt;&lt;p&gt;Now you can use this in any automation. The number is the number in the array, starting at 1(!) Example for the simple ones:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;alias: Nano_BlackTest&#xA;description: &amp;#34;&amp;#34;&#xA;trigger: []&#xA;condition: []&#xA;action:&#xA;  - service: rest_command.nanoleaf_black&#xA;    data:&#xA;      panels:&#xA;        - number: 1&#xA;          r: 255&#xA;          g: 0&#xA;          b: 0&#xA;        - number: 2&#xA;          r: 0&#xA;          g: 255&#xA;          b: 0&#xA;        - number: 3&#xA;          r: 0&#xA;          g: 0&#xA;          b: 255&#xA;mode: single&#xA;`and an example to light up the three wooden panels (remember. Each panel has 6 LEDs):`&#xA;alias: Nano_WoodTest&#xA;description: &amp;#34;&amp;#34;&#xA;trigger: []&#xA;condition: []&#xA;action:&#xA;  - service: rest_command.nanoleaf_wood&#xA;    data:&#xA;      panels:&#xA;        - number: 31&#xA;          r: 255&#xA;          g: 255&#xA;          b: 255&#xA;        - number: 32&#xA;          r: 255&#xA;          g: 255&#xA;          b: 255&#xA;        - number: 33&#xA;          r: 255&#xA;          g: 255&#xA;          b: 255&#xA;        - number: 34&#xA;          r: 255&#xA;          g: 255&#xA;          b: 255&#xA;        - number: 35&#xA;          r: 255&#xA;          g: 255&#xA;          b: 255&#xA;        - number: 36&#xA;          r: 255&#xA;          g: 255&#xA;          b: 255&#xA;        - number: 19&#xA;          r: 255&#xA;          g: 255&#xA;          b: 255&#xA;        - number: 20&#xA;          r: 255&#xA;          g: 255&#xA;          b: 255&#xA;        - number: 21&#xA;          r: 255&#xA;          g: 255&#xA;          b: 255&#xA;        - number: 22&#xA;          r: 255&#xA;          g: 255&#xA;          b: 255&#xA;        - number: 23&#xA;          r: 255&#xA;          g: 255&#xA;          b: 255&#xA;        - number: 24&#xA;          r: 255&#xA;          g: 255&#xA;          b: 255&#xA;        - number: 1&#xA;          r: 255&#xA;          g: 255&#xA;          b: 255&#xA;        - number: 2&#xA;          r: 255&#xA;          g: 255&#xA;          b: 255&#xA;        - number: 3&#xA;          r: 255&#xA;          g: 255&#xA;          b: 255&#xA;        - number: 4&#xA;          r: 255&#xA;          g: 255&#xA;          b: 255&#xA;        - number: 5&#xA;          r: 255&#xA;          g: 255&#xA;          b: 255&#xA;        - number: 6&#xA;          r: 255&#xA;          g: 255&#xA;          b: 255&#xA;mode: single&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That&amp;rsquo;s it. Have fun with your panels-&lt;/p&gt;&#xA;</description>
        </item><item>
            <title>How to track a squirrel</title>
            <link>https://dotnet.work/blog/squirrel/</link>
            <pubDate>Sun, 17 Dec 2023 12:00:00 +0100</pubDate><author>dotnet@oles.cloud (Ole Albers)</author>
            <guid>https://dotnet.work/blog/squirrel/</guid>
            <description>&lt;img src=&#34;https://dotnet.work/blog/squirrel/feeder_hu_5e0b693f30391cc3.png&#34; alt=&#34;Featured image of post How to track a squirrel&#34; /&gt;&lt;p&gt;[Disclaimer: This is not meant as a tutorial, more as some chitchat. You can find the source on github nonetheless, but I assume this is a VERY specific use case noone else uses]&lt;/p&gt;&#xA;&lt;p&gt;Let&amp;rsquo;s start by telling you that this whole project ist utter nonsense. Noone really &lt;em&gt;needs&lt;/em&gt; this feature. I decided to create this nonetheless.&lt;/p&gt;&#xA;&lt;h3 id=&#34;the-goal&#34;&gt;&lt;a href=&#34;#the-goal&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;The Goal&#xA;&lt;/h3&gt;&lt;p&gt;I own a little feeding box for squirrels of the neighbourhood. And it works as designed: Squirrels get here, steal their food and run away.&lt;/p&gt;&#xA;&lt;p&gt;I can see them through my glass door and it just looks amazing, especially in the typical &amp;ldquo;Hamburger Wetter&amp;rdquo; (Hamburg weather, means: raining horizontally). Sometimes I take a picture or even a video, but through the door that just does not look very great.&lt;/p&gt;&#xA;&lt;p&gt;So I decided to add a surveillance cam to the mix, and while I am on it: Automatically publish whenever a squirrel appears&lt;/p&gt;&#xA;&lt;h4 id=&#34;hardware&#34;&gt;&lt;a href=&#34;#hardware&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Hardware&#xA;&lt;/h4&gt;&lt;p&gt;I use a dlink - outdoor webcam with WiFi. Not really worth looking up the model number here, just the cheapest WiFi-Cam I could find which is compatible with Synology Surveillance Station (but nearly all are)&lt;/p&gt;&#xA;&lt;p&gt;In addition the Synology NAS with installed surveillance station. I own a DS920+, but any should do&lt;/p&gt;&#xA;&lt;h4 id=&#34;privacy&#34;&gt;&lt;a href=&#34;#privacy&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Privacy&#xA;&lt;/h4&gt;&lt;p&gt;I do not want to track any persons. Not me, not others. This is not meant to be surveillance, it is meant to film squirrels. TBH I did not ask the squirrels how they think about privacy and GDPR, but I assume they are fine with it. After all they accepted all Cookies until now, and not only the required ones.&lt;/p&gt;&#xA;&lt;p&gt;To make sure only squirrels (or other animals) get caught I wanted to do a few conditions:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Only track movement directly in front of the feeder.&#xA;&lt;ul&gt;&#xA;&lt;li&gt;So I can make sure not Humans or Traffic in the background trigger the motion sensor&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;Put the Camera in &amp;ldquo;Squirrel&amp;rdquo;-height.&#xA;&lt;ul&gt;&#xA;&lt;li&gt;So even if someone (me or my guests) manages to trigger the alarm no faces can be seen&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;Have a simple to use approval workflow&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Just in case. If someone ends up lying there drunk I do not want to publish a video of that person anywhere&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h5 id=&#34;only-track-movement-directly-in-front-of-the-feeder&#34;&gt;&lt;a href=&#34;#only-track-movement-directly-in-front-of-the-feeder&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Only Track movement directly in front of the feeder&#xA;&lt;/h5&gt;&lt;p&gt;That is one of the reasons why I use the Surveillance Station of Synology. The Camera itself has movement detection and (according to the documentation) even a fancy AI &amp;ldquo;this is a person&amp;rdquo; algortihm. I do not want the last for obvious reasons and especially can not decide which area of the image should be used for detection. In addition this seems to require a cloud feature.&#xA;I do not want my camera feed in the cloud from anyone. This should never leave my house without my consent. The surveillance station can select very small pieces of the image to be selected as alert regions. I put it directly in front of the feeder where no background movement can be detected. This way I can also adjust the sensibility of the detection to quite a high value because no noise becaues of branches moving in the wind etc. is to be expected.&lt;/p&gt;&#xA;&lt;h5 id=&#34;put-the-camera-in-squirrel-height&#34;&gt;&lt;a href=&#34;#put-the-camera-in-squirrel-height&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Put the camera in squirrel-height&#xA;&lt;/h5&gt;&lt;p&gt;That was easy as there are some flowerboxes directly before the feeder where I mounted the cam&#xA;&lt;img alt=&#34;Photo from garden. A squirrelfeeder in the back, the camera on front&#34; class=&#34;gallery-image&#34; data-flex-basis=&#34;187px&#34; data-flex-grow=&#34;78&#34; height=&#34;701&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/squirrel/feeder_hu_5e0b693f30391cc3.png&#34; width=&#34;548&#34;&gt;&lt;/p&gt;&#xA;&lt;h5 id=&#34;have-a-simple-approval-workflow&#34;&gt;&lt;a href=&#34;#have-a-simple-approval-workflow&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Have a simple approval workflow&#xA;&lt;/h5&gt;&lt;p&gt;Because I mostly am at Mastodon at time of this writing (these sentences sometimes age very bad. A few months ago that would have been the artist formerly known as Twitter, which itself was just a replacement for Google+ which, well, you know the sad story). Let&amp;rsquo;s just hope that was the last Social Media switch for me.&lt;/p&gt;&#xA;&lt;p&gt;One of the advantages of Mastodon is that it has a very nice, simple API that allows sending toots, getting Notifications and so on. So my idea for the workflow is to notify me (and only me) that a new movement has been detected. Attach an image of that movement and if I approve this (e.g. by adding a &amp;ldquo;fav&amp;rdquo; to that toot) the bot knows that this detection was a valid Squirrel video and posts it. So AI without the &amp;ldquo;A&amp;rdquo;. (some might argue even without the &amp;ldquo;I&amp;rdquo;&amp;hellip;)&lt;/p&gt;&#xA;&lt;h3 id=&#34;synology&#34;&gt;&lt;a href=&#34;#synology&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Synology&#xA;&lt;/h3&gt;&lt;p&gt;Adding the Webcam is quite simple. It nearly is found automatically in Surveillance station. Finding out how to connect was a bit harder. Username is always &amp;ldquo;admin&amp;rdquo; and the required password is a PIN. Printed onto the Power Plug, not the device&amp;hellip;&#xA;After that the App of the camera does not like the Camera anymore, which is bonus for  me as I do not want images to be uploaded to dlink, anyways.&lt;/p&gt;&#xA;&lt;p&gt;The detection area can be set in the Camera options. &lt;img alt=&#34;Screenshot from “Motion Detection Area” within Surveillance Station&#34; class=&#34;gallery-image&#34; data-flex-basis=&#34;474px&#34; data-flex-grow=&#34;197&#34; height=&#34;631&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/squirrel/motiondetection_hu_2689ea674daa870d.png&#34; srcset=&#34;https://dotnet.work/blog/squirrel/motiondetection_hu_29f7050f1aceaac3.png 800w, https://dotnet.work/blog/squirrel/motiondetection_hu_2689ea674daa870d.png 1247w&#34; width=&#34;1247&#34;&gt; Fiddling with the thresholds takes a while, especially as I do not have a trained squirrel to assist me on that. I then changed the time plan to always motion detection (so disabling the constant recording) and updated the minimum record time to five minutes. A good comprimise between getting everything and not creating videos that are too big for mastodon/youtube/wherever I wanted to upload this, which I had not decided at this point&lt;/p&gt;&#xA;&lt;p&gt;Synology has multiple ways to notify about movements: Email, SMS, Push notifications and Webhooks. Obviously the last one was the way to go as it does not spam me in any way and I have to program stuff anyways.&lt;/p&gt;&#xA;&lt;h3 id=&#34;writing-a-webhook&#34;&gt;&lt;a href=&#34;#writing-a-webhook&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Writing a Webhook&#xA;&lt;/h3&gt;&lt;p&gt;&lt;img alt=&#34;Alt text&#34; class=&#34;gallery-image&#34; data-flex-basis=&#34;330px&#34; data-flex-grow=&#34;137&#34; height=&#34;590&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/squirrel/synowebhook_hu_51827eb84ea23d97.png&#34; srcset=&#34;https://dotnet.work/blog/squirrel/synowebhook_hu_156b73cfdf29fd8.png 800w, https://dotnet.work/blog/squirrel/synowebhook_hu_51827eb84ea23d97.png 813w&#34; width=&#34;813&#34;&gt;&#xA;A webhook is just an HTTP-call essentially. Synology allows POST and PUT in Surveillance Station. So I created a simple Web Api in Visual Studio and was ready to debug the incoming data.&lt;/p&gt;&#xA;&lt;h4 id=&#34;firewalls-firewalls-everywhere&#34;&gt;&lt;a href=&#34;#firewalls-firewalls-everywhere&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Firewalls, Firewalls everywhere!&#xA;&lt;/h4&gt;&lt;p&gt;Synology allows you to send a test-notification which just resulted in an error. WHAT exactly went wrong is nowhere to be seen. Not in surveillance station and not in the logs from the synology. So I had to fly blind at this point.&lt;/p&gt;&#xA;&lt;p&gt;I knew it worked when using my local machine, either via localhost or the ipadress from my development machine.&lt;/p&gt;&#xA;&lt;p&gt;So the next step was to connect to my synology via SSH and try to access my API throuh a simple webcall via GET:&#xA;&lt;code&gt;wget https://192.168.178.23:6789/ping&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;This simple call already did not work. So must have been a firewall issue. I checked everything in my Synology configuration FOR A VERY LONG TIME and did find nothing. Finally I put my attention to Windows, disabled the Defender Firewall and it went through. Because I do not want to have the firewall open I added an exception through the &amp;ldquo;allow an app through windows firewall&amp;rdquo; and finally it did work with the firewall still enabled.&#xA;&lt;img alt=&#34;Windows Firewall Rules&#34; class=&#34;gallery-image&#34; data-flex-basis=&#34;334px&#34; data-flex-grow=&#34;139&#34; height=&#34;444&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/squirrel/firewall_hu_72f0a67ccecaa8b.png&#34; width=&#34;618&#34;&gt;&lt;/p&gt;&#xA;&lt;h4 id=&#34;receiving-data-from-the-webhook&#34;&gt;&lt;a href=&#34;#receiving-data-from-the-webhook&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Receiving data from the webhook&#xA;&lt;/h4&gt;&lt;p&gt;The Webhook arrived as expected, has some informations in it and can even provide an url to a preview image. That image URL is a generated &amp;ldquo;public&amp;rdquo; (within my LAN) Address I am able to receive and download:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MapPost&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;/squirrel&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;context&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;requestBody&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Request&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ReadFromJsonAsync&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;SynologyBody&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;();&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;requestBody&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(!&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;IsNullOrWhiteSpace&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;requestBody&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Capture&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;previewImage&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;downloader&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DownloadFile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;requestBody&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Capture&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;                                    &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;Console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;WriteLine&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;received event without image. Will ignore it&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;WriteAsync&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;event received&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;SynologyBody&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Subject&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Desc&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Capture&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;(Synology does not really care about the response but I found it useful when testing locally through Postman)&lt;/p&gt;&#xA;&lt;p&gt;At this point I do not have a video yet which is to be expected, as &amp;ldquo;normal&amp;rdquo; users would expect to receive the warning &lt;em&gt;immediately&lt;/em&gt; and not five minutes &lt;em&gt;after&lt;/em&gt; someone stole their nuts.&lt;/p&gt;&#xA;&lt;h4 id=&#34;send-private-notification-to-mastodon&#34;&gt;&lt;a href=&#34;#send-private-notification-to-mastodon&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Send private notification to Mastodon&#xA;&lt;/h4&gt;&lt;p&gt;After an event have been received the Image is downloaded and a message is sent to Mastodon using MastoNet:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MastodonClient&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Login&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MastodonClient&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_instance&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;_accessToken&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Task&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;?&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SendPreview&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Stream&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;previewFile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;DateTime&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;notificationDate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;client&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Login&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;text&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;$&amp;#34;{_approver} ALARM! Possible Squirrel detection! ALARM! #preview DATE:\n{notificationDate}\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;media&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;client&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;UploadMedia&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;previewFile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;squirrel.png&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;possibly a squirrel&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;client&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;PublishStatus&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;text&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Visibility&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Direct&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mediaIds&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;media&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Id&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;});&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;IsNullOrWhiteSpace&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;Console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;WriteLine&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Could not send preview&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;Console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;WriteLine&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;$&amp;#34;Published to Mastodon with id {status.Id}&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This creates the following toot:&#xA;&lt;img alt=&#34;Private Notification in Mastodon with previewimage&#34; class=&#34;gallery-image&#34; data-flex-basis=&#34;202px&#34; data-flex-grow=&#34;84&#34; height=&#34;468&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/squirrel/notasquirrel_hu_d5d3b1454f2618bf.png&#34; width=&#34;395&#34;&gt;&lt;/p&gt;&#xA;&lt;h4 id=&#34;waiting-for-approval&#34;&gt;&lt;a href=&#34;#waiting-for-approval&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Waiting for approval&#xA;&lt;/h4&gt;&lt;p&gt;This toot has only been sent to me privately (mastodon has no encryption so always keep that in mind about the &amp;ldquo;privacy&amp;rdquo; - part). I can then decide that this is a valid Squirrel detection by setting the &amp;ldquo;fav&amp;rdquo; star of that toot.&lt;/p&gt;&#xA;&lt;h4 id=&#34;keeping-the-state&#34;&gt;&lt;a href=&#34;#keeping-the-state&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Keeping the state&#xA;&lt;/h4&gt;&lt;p&gt;To be able to understand which previewtoot belongs to what webhook call I need to store the state. I uses LitDB for this. LiteDB is a &lt;strong&gt;very&lt;/strong&gt; simple document database which needs no installation at all. It completely only lives within the NuGet-Package and the data is stored in a Bson-Format (Json, but compressed).&#xA;It is a bit like simply serializing my contents and store it into a Json file. Just performing much better.&lt;/p&gt;&#xA;&lt;p&gt;That is why the complete &amp;ldquo;database&amp;rdquo;-class is as simple as this:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;DataBase&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;_databaseName&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;@&amp;#34;Filename=squirrel.db;connection=shared&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;T&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;GetContents&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;using&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;db&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;LiteDatabase&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_databaseName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;GetCollection&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;().&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;FindById&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;BsonValue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;RemoveContents&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;using&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;db&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;LiteDatabase&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_databaseName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;GetCollection&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;().&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Delete&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;BsonValue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;UpsertContents&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;contents&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;using&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;db&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;LiteDatabase&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_databaseName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;GetCollection&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;().&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Upsert&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;contents&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SquirrelData&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;FindByFilename&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;filename&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;using&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;db&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;LiteDatabase&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_databaseName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;GetCollection&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;SquirrelData&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;().&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;FindOne&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;q&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;q&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ExpectedMovieFile&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;filename&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now the data inside the Database contains the following stuff:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;SquirrelData&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;na&#34;&gt;        [BsonId]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Id&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// First MastodonID&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string?&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;PublicId&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;DateTime&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;EventReceived&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;DateTime&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SentForApproval&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;DateTime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SentPublic&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;DateTime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Faved&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string?&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ExpectedMovieFile&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The Id simply is the Id from the message just posted to mastodon. This object mostly contains Mastodon-Information and Information about the WebHook event.&lt;/p&gt;&#xA;&lt;p&gt;As mentioned above the WebHook has no information about the Video that is created (or most likely: Is still being created when the WebHook is sent). So we simply have to find it.&#xA;The rules are quite simple: Within the directory with surveillance videos it is simply the newest file we can find.&lt;/p&gt;&#xA;&lt;p&gt;One problem remains: The preview image was within the WebHook and did not need authentication do be downloaded. The surveillance directory does need authentication. So I created a user for that specific purpouse allowed it to access the folder and needed to do some SMB-Stuff for authentication.&lt;/p&gt;&#xA;&lt;p&gt;I used the SMBLibrary for that. It isn&amp;rsquo;t as straight forward as navigating through a filesystem, but it does work fine. First I have to connect using my credentials:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; &lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Connect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;smbClient&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SMB1Client&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;isConnected&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;smbClient&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Connect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;IPAddress&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Parse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_server&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SMBTransportType&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DirectTCPTransport&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;isConnected&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;NTStatus&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;status&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;smbClient&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Login&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Empty&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;_username&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;_password&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;status&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;NTStatus&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;STATUS_SUCCESS&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;_smbClient&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;smbClient&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Exception&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Connecting failed. Check your credentials&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;After successfully connecting I can access the share and search for the most current mp4-file recursively:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;     &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;FindMostCurrentVideo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;store&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;_smbClient&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;TreeConnect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;surveillance&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;out&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;status&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;NTStatus&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;STATUS_SUCCESS&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Exception&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Connecting to Share failed&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mostCurrentFile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;GetMostCurrentFileRec&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;SMB1FileStore&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;store&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;_startPath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Combine&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mostCurrentFile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mostCurrentFile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Filename&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MostCurrentFile&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;GetMostCurrentFileRec&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;SMB1FileStore&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;store&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;folder&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;folder&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;\\&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mostCurrentFile&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MostCurrentFile&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Created&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;DateTime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MinValue&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;store&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;QueryDirectory&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;out&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;subdirectoryList&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;folder&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SMBLibrary&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;SMB1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;FindInformationLevel&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;SMB_FIND_FILE_DIRECTORY_INFO&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;NTStatus&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;STATUS_SUCCESS&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kt&#34;&gt;object&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;directoryHandle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;FileStatus&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fileStatus&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;status&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;store&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;CreateFile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;out&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;directoryHandle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;out&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fileStatus&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;folder&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;AccessMask&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;GENERIC_READ&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SMBLibrary&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;FileAttributes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Directory&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SMBLibrary&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;FileAttributes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Normal&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ShareAccess&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Read&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ShareAccess&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Write&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;CreateDisposition&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;FILE_OPEN&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;CreateOptions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;FILE_DIRECTORY_FILE&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;status&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;NTStatus&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;STATUS_SUCCESS&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;List&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;FindInformation&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fileList2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;status&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;store&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;QueryDirectory&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;out&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fileList2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;folder&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;*&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;FindInformationLevel&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;SMB_FIND_FILE_DIRECTORY_INFO&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dirInfo&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fileList2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Where&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;q&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;q&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;GetType&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;typeof&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;FindFileDirectoryInfo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)).&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Cast&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;FindFileDirectoryInfo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;();&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;subdirectories&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dirInfo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Where&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;q&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;q&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ExtFileAttributes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;HasFlag&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ExtendedFileAttributes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Directory&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;files&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dirInfo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Where&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;q&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;q&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ExtFileAttributes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;HasFlag&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ExtendedFileAttributes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Directory&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;// Look for subdirectories and drill down to them:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;foreach&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;directory&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;subdirectories&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;directory&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;FileName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Contains&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;.&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;continue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mostCurrentSubFile&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;GetMostCurrentFileRec&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;store&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Combine&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;folder&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;directory&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;FileName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mostCurrentSubFile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Created&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mostCurrentFile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Created&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mostCurrentFile&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mostCurrentSubFile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;c1&#34;&gt;// Look for .mp4 - Files in current directory and check if they are newer than the most current file known:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;k&#34;&gt;foreach&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;file&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;files&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Where&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;q&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;q&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;FileName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;EndsWith&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;.mp4&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)))&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;file&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;CreationTime&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mostCurrentFile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Created&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;n&#34;&gt;mostCurrentFile&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;MostCurrentFile&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                        &lt;span class=&#34;n&#34;&gt;Created&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;file&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;CreationTime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                        &lt;span class=&#34;n&#34;&gt;Filename&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;file&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;FileName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                        &lt;span class=&#34;n&#34;&gt;Path&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;folder&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                    &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            &lt;span class=&#34;n&#34;&gt;status&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;store&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;CloseFile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;directoryHandle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mostCurrentFile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;At this point I do not need the filecontents itself, but only the path so I can use it lateron.&lt;/p&gt;&#xA;&lt;p&gt;After that we have the whole WebHook complete:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Reveive the Contents (including preview image) from the webhook&lt;/li&gt;&#xA;&lt;li&gt;Determine which Video File matches&lt;/li&gt;&#xA;&lt;li&gt;CHeck if we already had this file (if within a video multiple webhooks are fired) and avoid duplicates&lt;/li&gt;&#xA;&lt;li&gt;Send a notification to Mastodon&lt;/li&gt;&#xA;&lt;li&gt;Store all known information into our database.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;So our Webhook from the beginning now looks like this:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MapPost&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;/squirrel&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;context&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;received&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;DateTime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Now&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;Console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;WriteLine&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Received squirrel Event&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;requestBody&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Request&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ReadFromJsonAsync&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;SynologyBody&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;();&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;requestBody&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(!&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;IsNullOrWhiteSpace&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;requestBody&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Capture&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;previewImage&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;downloader&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DownloadFile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;requestBody&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Capture&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;n&#34;&gt;downloader&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Connect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;videoFilename&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;downloader&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;FindMostCurrentVideo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;??&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Exception&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;No Video Found&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;existingItem&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;database&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;FindByFilename&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;videoFilename&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;existingItem&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;Console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;WriteLine&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;$&amp;#34;&amp;#39;{videoFilename}&amp;#39; already processed with MastodonId {existingItem.Id}&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;WriteAsync&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;duplicate event&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mastodonStatus&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mastodon&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;SendPreview&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;previewImage&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;received&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mastodonStatus&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;database&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;UpsertContents&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SquirrelData&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;EventReceived&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;received&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ExpectedMovieFile&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;videoFilename&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Id&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mastodonStatus&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SentForApproval&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mastodonStatus&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;CreatedAt&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Response&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;WriteAsync&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;event received&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;checking-for-notifications&#34;&gt;&lt;a href=&#34;#checking-for-notifications&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Checking for Notifications&#xA;&lt;/h3&gt;&lt;p&gt;As mentioned the &amp;ldquo;approval workflow&amp;rdquo; (a big word for this simple mechanism, I know) is to wait for a fav. If the fav is received it means I decided this is a valid Squirrel detection and the video can be published.&lt;/p&gt;&#xA;&lt;p&gt;Also do not wait longer than 7 days. If I did not approve in that time, it probably is not a good video.&lt;/p&gt;&#xA;&lt;p&gt;Again using MastoNet I can receive the Notifications for that user:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Task&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;List&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Notification&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;GetFavs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;favourites&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;List&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Notification&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;();&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;client&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Login&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;notifications&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;client&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;GetNotifications&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;foreach&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;notification&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;notifications&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;switch&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;notification&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;mention&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;client&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;PublishStatus&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;$&amp;#34;Sorry. I am just a bot and will not respond to this. Ask {_approver} if you want to know anything about what I do&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;replyStatusId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;notification&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;client&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DismissNotification&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;notification&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;break&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;favourite&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;favourites&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;notification&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;break&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;k&#34;&gt;default&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;Console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;WriteLine&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;$&amp;#34;discarding notification of type {notification.Type}&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;client&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;DismissNotification&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;notification&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;break&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;favourites&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The nofication.Status.Id now is the id we already stored in the Database and can use to find all known WebHook-Data of that event and send a new Toot. This time just with the Video file instead of the preview image.&lt;/p&gt;&#xA;&lt;h5 id=&#34;why-no-mastodon-webhooks&#34;&gt;&lt;a href=&#34;#why-no-mastodon-webhooks&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Why no Mastodon Webhooks?&#xA;&lt;/h5&gt;&lt;p&gt;The Mastodon API also offers Webhooks. So instead of periodially polling for new notifications this could be used to receive the favs. The main reason why I did not use this (apart from the fact that polling is simply fast enough) is the fact that my application is not accessible from the outside. To allow this I would need to make my NAS (where the application is hosted) accessible from the Internet. And I do not want that. I could add Firewall rules to only allow specific ports and so on, but no access is even a bit better than a limited access. Even if noone would get through, just bots &lt;em&gt;trying&lt;/em&gt; the flood my machine with requests is a headache I do not need.&lt;/p&gt;&#xA;&lt;h3 id=&#34;uploading-the-video&#34;&gt;&lt;a href=&#34;#uploading-the-video&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Uploading the Video&#xA;&lt;/h3&gt;&lt;p&gt;After trying to uplaad the Video directly to Mastodon the next problem came up: The file was too big. While it was way inside the limits the upload (to the mastodon.social instance) just responded with an error &amp;ldquo;something went wrong&amp;rdquo;. I suspect the Library to be the main issue as the Upload is not done async and so a timeout might be the cause.&lt;/p&gt;&#xA;&lt;p&gt;That is why I decided to upload the Video to YouTube instead. YouTube provides APIs to do that. The main issue is that this also requires an OAuth authentication. Luckily the documentation for this are quite straight-forward so a simple Test worked fine:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Task&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;UploadVideo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Stream&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fileStream&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;youtubeService&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;YouTubeService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;BaseClientService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Initializer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;n&#34;&gt;ApiKey&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;_apiKey&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;n&#34;&gt;ApplicationName&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;GetType&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ToString&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;UserCredential&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;credential&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;using&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;stream&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;FileStream&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;secrets.json&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;FileMode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Open&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;FileAccess&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Read&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;credential&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;GoogleWebAuthorizationBroker&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;AuthorizeAsync&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;GoogleClientSecrets&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Load&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stream&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Secrets&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;YouTubeService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Scope&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;YoutubeUpload&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;user&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;CancellationToken&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;None&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;youtubeService&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;YouTubeService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;BaseClientService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Initializer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;n&#34;&gt;HttpClientInitializer&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;credential&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;n&#34;&gt;ApplicationName&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Assembly&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;GetExecutingAssembly&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;GetName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Name&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;video&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Video&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;video&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Snippet&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;VideoSnippet&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;video&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Snippet&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Title&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;Squirrel detected&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;video&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Snippet&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Description&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;You should have bought a squirrel&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;video&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Snippet&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Tags&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;squirrel&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;video&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Snippet&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;CategoryId&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;22&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;video&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Status&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;VideoStatus&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;n&#34;&gt;MadeForKids&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      &lt;span class=&#34;n&#34;&gt;SelfDeclaredMadeForKids&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;video&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Status&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;PrivacyStatus&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;private&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;videosInsertRequest&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;youtubeService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Videos&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Insert&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;video&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;snippet,status&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fileStream&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;video/*&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;videosInsertRequest&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ProgressChanged&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;videosInsertRequest_ProgressChanged&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;videosInsertRequest&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ResponseReceived&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;videosInsertRequest_ResponseReceived&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;videosInsertRequest&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;UploadAsync&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;videosInsertRequest_ResponseReceived&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Video&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;Console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;WriteLine&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;$&amp;#34;done&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;videosInsertRequest_ProgressChanged&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;IUploadProgress&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;obj&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;n&#34;&gt;Console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;WriteLine&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;$&amp;#34;Bytes: {obj.BytesSent}| Status: {obj.Status}&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;(secrets.json contains a client_id and client_secret for oauth)&#xA;While uploading the &lt;code&gt;ProgressChanged&lt;/code&gt; event gets fired a few times. Obj.Status cycles from Starting, Uploading to Completed. Which is also the Status when &lt;code&gt;ResponseReceived&lt;/code&gt; is fired.&lt;/p&gt;&#xA;&lt;p&gt;Because I had to switch to YouTube I also changed the workflow a bit. Now I already upload the video when posting the preview toot as private on YouTube. So I can verify the complete contents before an approval. And YouTube can process the video while it is waiting for approval. At the end I just switch from &lt;code&gt;private&lt;/code&gt; to &lt;code&gt;unlisted&lt;/code&gt; if it is approved or delete the Video if no approval has been made for a period of time.&lt;/p&gt;&#xA;&lt;h4 id=&#34;another-pitfall&#34;&gt;&lt;a href=&#34;#another-pitfall&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Another Pitfall&#xA;&lt;/h4&gt;&lt;p&gt;YouTube does &lt;strong&gt;not&lt;/strong&gt; allow using service accounts when uploading Videos. This was crucial for my application because this should run on a server. While the code above works I hit a dead end here. So back to Mastodon:&lt;/p&gt;&#xA;&lt;h4 id=&#34;ffmpeg&#34;&gt;&lt;a href=&#34;#ffmpeg&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;FfMpeg&#xA;&lt;/h4&gt;&lt;p&gt;To reduce the filesize I went to FFMpeg. I can compress Videos and - if required - even resize them. Because I do not want to call something on the console, I needed a wrapper. I found FfmpegCore which does exactly what I want:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Task&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;ResizeVideo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;source&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;target&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;FFMpegArguments&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;FromFileInput&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;source&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;OutputToFile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;target&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;n&#34;&gt;options&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;options&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&#x9;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;WithVideoCodec&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;VideoCodec&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;LibX265&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&#x9;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;WithVideoFilters&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;filter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;filter&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&#x9;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Scale&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;videoSize&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;FFMpegCore&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Enums&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;VideoSize&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;FullHd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&#x9;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;WithFastStart&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ProcessAsynchronously&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This tiny little piece of code takes any videofile, compresses it as h265 (using default quality settings) and resized it to FullHD (1080 height) maintaining the correvt aspect ratio.&lt;/p&gt;&#xA;&lt;p&gt;That all works fine on my machine, but because I want to host this (like all my bots) on my Synology-NAS I had to dockerize the contents.  I was in luck this time, but this also turned out to be quite easy:&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;RUN apt-get -y update &amp;amp;&amp;amp; apt-get -y upgrade &amp;amp;&amp;amp; apt-get install -y --no-install-recommends ffmpeg&lt;/code&gt;&#xA;A single Line in the DockerFile and everything was working smoothly&lt;/p&gt;&#xA;&lt;h4 id=&#34;fixing-the-api&#34;&gt;&lt;a href=&#34;#fixing-the-api&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Fixing the Api&#xA;&lt;/h4&gt;&lt;p&gt;The reduced Filesize did not solve the problem that Mastodon did not want to upload my file. So I digged a bit deeper into the Mastodon API. The media upload used by MastoNet seemed to marked as depricated (v1) exactly for the reason why I did not work in my project: It was meant for small preview images, not for Video files.&lt;/p&gt;&#xA;&lt;p&gt;A V2-Version &lt;a class=&#34;link&#34; href=&#34;https://docs.joinmastodon.org/methods/media/#v2&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;is available&lt;/a&gt; but not implemented by MastoNet. So I forked the packages from Github and added a V2-Version:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Task&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Attachment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;UploadMediaAsync&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MediaDefinition&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;media&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string?&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;description&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;AttachmentFocusData&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;?&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;focus&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;n&#34;&gt;media&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ParamName&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;file&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;list&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;List&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;MediaDefinition&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;media&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;data&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Dictionary&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;();&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;description&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;focus&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;focus&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;$&amp;#34;{focus.X},{focus.Y}&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Post&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Attachment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;/api/v2/media&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;list&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This was confusingly easy as I simpy just replaced &amp;ldquo;v1&amp;rdquo; by &amp;ldquo;v2&amp;rdquo;. Still it worked. Problem was: The next step, usually taking the returned Id and add this to a toot failed, because Mastodon was not finished, yet. I could simply have waited a long enough time, but decided to add a poll to receive status updates. Every Media of a known I can be requested. There is no statusflag about it processing but some properties give a hint:&#xA;First there is the &lt;code&gt;previewUrl&lt;/code&gt; this is filled at the very start after uploading. Tbe &lt;code&gt;Url&lt;/code&gt;-Property, however &lt;em&gt;isn&amp;rsquo;t&lt;/em&gt;. This will always be Null until everything is processed. So just requesting the Media every 10 seconds until the Url is filled did the trick;&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-csharp&#34; data-lang=&#34;csharp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Task&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Attachment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;WaitUntilMediaIsUploaded&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Attachment&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;attachment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;waitSeconds&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;maxWaitSeconds&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;300&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;totalTimeWasted&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;k&#34;&gt;while&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;attachment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Id&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;attachment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Url&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;attachment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;PreviewUrl&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;totalTimeWasted&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;maxWaitSeconds&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;n&#34;&gt;Thread&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Sleep&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;waitSeconds&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;1000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;n&#34;&gt;totalTimeWasted&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;waitSeconds&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&#x9;&lt;span class=&#34;n&#34;&gt;attachment&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;GetMedia&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;attachment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;attachment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Task&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Attachment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;GetMedia&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mediaId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;attachment&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Attachment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;/api/v1/media/&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mediaId&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;prev&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;attachment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;PreviewUrl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;kt&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;url&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;attachment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#x9;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;attachment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;finally&#34;&gt;&lt;a href=&#34;#finally&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Finally&#xA;&lt;/h2&gt;&lt;p&gt;So that was the last Pitfall. Now everything works:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;If a motion is detected my WebHook gets called&lt;/li&gt;&#xA;&lt;li&gt;The Webhook posts a previewimage as a direct toot (only be seen by me) to Mastodon including a Preview Image&lt;/li&gt;&#xA;&lt;li&gt;If &amp;ldquo;fav&amp;rdquo; the toot when this is a valid Squirrel detection&lt;/li&gt;&#xA;&lt;li&gt;The Bot checks for new Favs and finds mine&lt;/li&gt;&#xA;&lt;li&gt;The according Video is found in the LiteDB&lt;/li&gt;&#xA;&lt;li&gt;The Video gets downloaded via SMB-Stuff&lt;/li&gt;&#xA;&lt;li&gt;The Video gets Resized by FfMpeg&lt;/li&gt;&#xA;&lt;li&gt;The resized video gets uploaded to Mastodon&lt;/li&gt;&#xA;&lt;li&gt;A public toot containing the video is created&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;(Using standard Mastodon-Features, all toots will get deleted after two weeks reducing the space required)&lt;/p&gt;&#xA;&lt;h3 id=&#34;but-why&#34;&gt;&lt;a href=&#34;#but-why&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;But WHY?&#xA;&lt;/h3&gt;&lt;p&gt;I am &lt;strong&gt;very&lt;/strong&gt; aware that this was mostly a waste of time. A lot of time for a simple &amp;ldquo;Lets show when there is a squirrel&amp;rdquo;. Simone Giertz once did a great TED - Talk &lt;a class=&#34;link&#34; href=&#34;https://www.youtube.com/watch?v=c0bsKc4tiuY&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;Why you should make useless things&lt;/a&gt;. While she talks about robots, by fun thing to do was alway coding. No matter how useless the results are.&#xA;So: Yes. I totally agree with her. Creating stupid things just because it is fun, with noone asking why this takes so long and even be able to quit if something becomes boring is a nice diversion to the normal coding reality I have for paying the rent.&lt;/p&gt;&#xA;&lt;p&gt;I hope you also had some fun reading this; I had writing it :)&lt;/p&gt;&#xA;</description>
        </item><item>
            <title>Raspberry Pi 4: usB first!</title>
            <link>https://dotnet.work/blog/raspberry-pi-4-usb-first/</link>
            <pubDate>Wed, 27 May 2020 12:00:00 +0200</pubDate><author>dotnet@oles.cloud (Ole Albers)</author>
            <guid>https://dotnet.work/blog/raspberry-pi-4-usb-first/</guid>
            <description>&lt;p&gt;At the time of this writing the boot-from-usb has just been made available as a Beta feature for the Raspberry Pi 4.&lt;/p&gt;&#xA;&lt;p&gt;There are a few tutorials on how to activate that feature but I had an additional requirement: Boot from USB first, then boot from SD which requires a few additional steps.&lt;/p&gt;&#xA;&lt;p&gt;This document might still be useful if you just want the SD first approach. Just ignore the second last part in that case.&lt;/p&gt;&#xA;&lt;p&gt;But before we begin:&lt;/p&gt;&#xA;&lt;p&gt;Use at your own risk. We are writing data to the EEPROM here using beta contents and even manipulate configs for that. If something fails your pi might be bricked. Not even reinstall Linux will repair that.&lt;/p&gt;&#xA;&lt;p&gt;Additional warning: Don&amp;rsquo;t try this on a N00bs install.&lt;/p&gt;&#xA;&lt;p&gt;Still here? So let&amp;rsquo;s begin. (I will asume you use Raspbian Buster)&lt;/p&gt;&#xA;&lt;h3 id=&#34;updating-the-pi&#34;&gt;&lt;a href=&#34;#updating-the-pi&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Updating the Pi&#xA;&lt;/h3&gt;&lt;p&gt;Before we start we should always have the most current Linux version installed. So make sure your internet connection works and enter&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;sudo apt-get update&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;followed by&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;sudo apt-get upgrade&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;and in addition&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;sudo rpi-update&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;to update the firmware on the sdcard&lt;/p&gt;&#xA;&lt;h3 id=&#34;switching-to-beta&#34;&gt;&lt;a href=&#34;#switching-to-beta&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Switching to beta&#xA;&lt;/h3&gt;&lt;p&gt;now change rpi to switch to beta - state. (&lt;em&gt;If you are reading this from the future: Hi there! Cars still a thing? And maybe you don&amp;rsquo;t need to switch to beta anymore&lt;/em&gt;)&lt;/p&gt;&#xA;&lt;p&gt;do this by using your favourite editor like vi or nano to change the rpi-config:&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;sudo nano /etc/default/rpi-eeprom-update&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;change the release-status from &lt;code&gt;critical&lt;/code&gt; to &lt;code&gt;beta&lt;/code&gt; and save the file.&lt;/p&gt;&#xA;&lt;h3 id=&#34;flashing-the-eeprom&#34;&gt;&lt;a href=&#34;#flashing-the-eeprom&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Flashing the EEPROM&#xA;&lt;/h3&gt;&lt;p&gt;Now check the most current version on your pi:&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;ls /lib/firmware/raspberrypi/bootloader/beta/pieeprom*.bin&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;At time of this writing the most current version there is 2020-05-15. So flash the eeprom with that version:&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;sudo rpi-eeprom-update -d -f /lib/firmware/raspberrypi/bootloader/beta/pieeprom-2020-05-15.bin&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;You will be thrown back to the prompt a while after. Now reboot your pi and hope nothing went wrong:&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;sudo shutdown - r now&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;now check if the version has been installed by typing&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;sudo vcgencmd bootloader_version&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;the response should display the date corresponding to your file. So something like &lt;code&gt;May 15&lt;/code&gt;&lt;/p&gt;&#xA;&lt;h3 id=&#34;changing-the-boot-order&#34;&gt;&lt;a href=&#34;#changing-the-boot-order&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Changing the boot order&#xA;&lt;/h3&gt;&lt;p&gt;By default now the boot order will be SD first, USB second. So if you just want to throw away your SD and boot grom USB: Ignore this chapter. If you need a &amp;ldquo;hardware multiboot&amp;rdquo; (boot from USB if attached but still have a fallback SD in place if not) follow these instructions:&lt;/p&gt;&#xA;&lt;p&gt;First make sure I did not lie about the default order and display the current config:&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;sudo vcgencmd bootloader_config&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;this should display some information about the configuration. Most interesting for us is the last part called &lt;code&gt;BOOT_ORDER&lt;/code&gt;which will contain the entry&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;BOOT_ORDER=0xf41&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;where 4=USB and 1=SD. More details like network bo&lt;strong&gt;ot can be found at the bootloader documentation.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;While this might look like just how we want it you have to read this right to left. So it is SD (1) first and USB (4) second. But we are about to change that.&lt;/p&gt;&#xA;&lt;p&gt;First it is highly recommend to not fiddle with the official ROM files but make a copy of that. Let&amp;rsquo;s do that:&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;sudo cp /lib/firmware/respberrypi/bootloader/beta/pieeprom-2015-05-15.bin pieeprom.bin&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;Now create a config from that rom:&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;sudo rpi-eeprom-config pieeprom-usbfirst.bin &amp;gt;bootconf.txt&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;You now created a config file containing the config-output which you can edit by nano again:&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;sudo nano bootconf.txt&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;and change the last line to:&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;BOOT_ORDER=0xf14&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;At the last step we now apply our changed config und update the eeprom again:&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;  sudo rpi-eeprom-config --out pieeprom-usbfirst.bin --config bootconf.txt pieeprom.bin   sudo rpi-eeprom-update -d -f ./pieeprom-usbfirst.bin   sudo shutdown -r now  &lt;/code&gt;&lt;/p&gt;&#xA;&lt;h3 id=&#34;finalizing-the-usb-drive&#34;&gt;&lt;a href=&#34;#finalizing-the-usb-drive&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Finalizing the USB drive&#xA;&lt;/h3&gt;&lt;p&gt;Now our raspberry can boot from USB (and boot from there first) but the linux on our USB does not really know how to handle that. This part is simple:&lt;/p&gt;&#xA;&lt;p&gt;First you need to flash a Raspbian image onto your USB-drive (just like you would to with an SD-card) . Then attach and mount the usb drive and copy the most current firmware files from the /boot - directory of the sdcard to the Usb drive. Alternatively you can select those files from the &lt;a class=&#34;link&#34; href=&#34;https://github.com/raspberrypi/firmware/tree/master/boot&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;master-branch on github.&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;Have fun :)&lt;/p&gt;&#xA;</description>
        </item><item>
            <title>FlashAirDownloader - development: Now it&#39;s your turn</title>
            <link>https://dotnet.work/blog/flashairdownloader-development-now-its-your-turn/</link>
            <pubDate>Tue, 03 Jan 2017 12:00:00 +0100</pubDate><author>dotnet@oles.cloud (Ole Albers)</author>
            <guid>https://dotnet.work/blog/flashairdownloader-development-now-its-your-turn/</guid>
            <description>&lt;p&gt;Hi,. folks,&lt;/p&gt;&#xA;&lt;p&gt;I just programmed a small windows service to automatically FlashAir-pictures when I found out &lt;strong&gt;that I lost my FlashAir - card&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;So I am not able to test that program anymore. I decided for myself that I need something better than the unstable FlashAir-Cards and bought a camera with WiFi inside. (Just in case you are curious: It is a Sony HX400)&lt;/p&gt;&#xA;&lt;p&gt;I do &lt;strong&gt;not&lt;/strong&gt; publish this as a working download, because I am not able to test it. So there is a big chance that this even breaks the original program. &lt;strong&gt;But&lt;/strong&gt; FAD2 is open source, so I checked the code in into the &lt;a class=&#34;link&#34; href=&#34;https://github.com/OleAlbers/fad2&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;development-branch on GitHub&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;So if there is any C# - developer out there who is willing to invest one or two hours to finish that, feel free to do that.&lt;/p&gt;&#xA;&lt;p&gt; &lt;/p&gt;&#xA;&lt;p&gt;P.S: No, please don&amp;rsquo;t buy me a new FlashAir :)&lt;/p&gt;&#xA;</description>
        </item><item>
            <title>Two Apps at the same time: Deploy SharePoint Add-In to Cloud AND On-Premise</title>
            <link>https://dotnet.work/blog/two-apps-at-the-same-time/</link>
            <pubDate>Tue, 13 Dec 2016 12:00:00 +0100</pubDate><author>dotnet@oles.cloud (Ole Albers)</author>
            <guid>https://dotnet.work/blog/two-apps-at-the-same-time/</guid>
            <description>&lt;img src=&#34;https://dotnet.work/blog/two-apps-at-the-same-time/showBuildConfigurations-300x221_hu_6302578466ea388f.png&#34; alt=&#34;Featured image of post Two Apps at the same time: Deploy SharePoint Add-In to Cloud AND On-Premise&#34; /&gt;&lt;p&gt;What the fuzz is this about?&lt;/p&gt;&#xA;&lt;p&gt;I am not absolutely sure if I am the first one who did this, but it seems at least noone else wrote it down until now. :) We hat a customer who liked our Office365 Provider-Hosted App &amp;ldquo;Mydea&amp;rdquo;, but wanted us to install that on his SharePoint 2013 - Installation. In Germany a lot of companies do not trust the cloud. So we had three options:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Tell the customer this does not work&lt;/li&gt;&#xA;&lt;li&gt;Copy&amp;amp;paste the whole solution and change it so it works on OnPrem&lt;/li&gt;&#xA;&lt;li&gt;Do something else&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;The first option - of course - wasn&amp;rsquo;t valid for me. The second would have worked. But I did not want to end up in maintanance hell. Our product is about innovation management. And the most innovative about that is the product itself. We develop agile, adding new features (and sometimes bugs) continuously. We do not want to do that twice.&lt;/p&gt;&#xA;&lt;p&gt;So I ended up on the third option: Changing my solution so it will run in an Office365 - SharePoint &lt;strong&gt;and&lt;/strong&gt; on an On-Premise installation of SharePoint 2013 (or above).&lt;/p&gt;&#xA;&lt;p&gt;Common sense about that path seems to be: &lt;em&gt;That does not work&lt;/em&gt;. But it does. It just is a real bumby road to get there. This also means: You should know the basics. You should have created at least one &lt;abbr title=&#34;Microsoft renamed &#39;Apps&#39; to &#39;Add-Ins&#39; Whenever I write one of those it is just always the same&#34;&gt;Add-In&lt;/abbr&gt; for Office365 and one for SharePoint 2013 or above. I will not explain these steps here (which are complicated enough for local installation). I also cannot help you there in the comments or by mail.&lt;/p&gt;&#xA;&lt;p&gt;There are plenty of documents out there that explain what&amp;rsquo;s needed for this. You should read those, first.&lt;/p&gt;&#xA;&lt;p&gt;If you find this article useful and earn lots of money because of it: Feel free to buy me a beer :)&lt;/p&gt;&#xA;&lt;h3 id=&#34;analyzing-the-differences-what-has-to-be-done&#34;&gt;&lt;a href=&#34;#analyzing-the-differences-what-has-to-be-done&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Analyzing the differences: What has to be done?&#xA;&lt;/h3&gt;&lt;p&gt;I started with an Office365-App and added the possibility to run that App under an OnPrem-Installation, too. But you should be able to go the other way with little modifications.&lt;/p&gt;&#xA;&lt;p&gt;My first step was to create two dummy &amp;ldquo;Hello World&amp;quot;ish Applications. One for O365 and one for an OnPrem-Installation and look at the source, to &amp;ldquo;diff&amp;rdquo; it. These are the conclusions I came around:&lt;/p&gt;&#xA;&lt;p&gt;At most of us use NuGet-Packages, that was the starting point. There are different Packages: You need the following:&lt;/p&gt;&#xA;&lt;p&gt;OnPrem: - AppForSharePointWebToolKit&lt;/p&gt;&#xA;&lt;p&gt;Office 365: - AppForSharePointOnlineWebToolKit - Microsoft.SharePointOnline.CSOM&lt;/p&gt;&#xA;&lt;p&gt;These Packages result in different DLLs with the Prefix &lt;em&gt;&amp;ldquo;Microsoft.SharePoint.Client.*&amp;rdquo;&lt;/em&gt;, but different Versions. OnPrem: - Version 15.xxx Office 365: - Version 16.xxx&lt;/p&gt;&#xA;&lt;p&gt;The Sourcecode of the *.cs - files created by that NuGet-Packages (_SharePointContext.cs_ &amp;amp; _Tokenhelper.cs_) are exactly the same. So no need to change anything there.&lt;/p&gt;&#xA;&lt;p&gt;Finally you usually store your Office365-Apps on Azure, but don&amp;rsquo;t for On-Premise-Installations. This results in different Authentications: OnPrem: - Using Certificates - Using Windows Authentication&lt;/p&gt;&#xA;&lt;p&gt;Office 365: - Using ClientSecret - Using OAuth Authentication&lt;/p&gt;&#xA;&lt;p&gt;These Informations are stored inside the &lt;em&gt;Web.Config&lt;/em&gt;&lt;/p&gt;&#xA;&lt;h3 id=&#34;before-we-start&#34;&gt;&lt;a href=&#34;#before-we-start&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Before we start&#xA;&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Create a new Branch in your repository.&lt;/strong&gt; You never know what can go wrong :)&lt;/p&gt;&#xA;&lt;h3 id=&#34;the-web-project-mvc-most-likely&#34;&gt;&lt;a href=&#34;#the-web-project-mvc-most-likely&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;The Web-Project (MVC most likely)&#xA;&lt;/h3&gt;&lt;h5 id=&#34;creating-different-build-configurations&#34;&gt;&lt;a href=&#34;#creating-different-build-configurations&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Creating different Build configurations&#xA;&lt;/h5&gt;&lt;p&gt;The main purpose if this action was to have just one CodeBase for both versions. So new features or bugfixes have only to be made once. No extra code, no branch, no nothing.&lt;/p&gt;&#xA;&lt;p&gt;So I started by creating new Build Configurations. You can create them using &amp;ldquo;Build - Configuration Manager&amp;rdquo;: &lt;img alt=&#34;Show Build Configurations&#34; class=&#34;gallery-image&#34; data-flex-basis=&#34;325px&#34; data-flex-grow=&#34;135&#34; height=&#34;221&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/two-apps-at-the-same-time/showBuildConfigurations-300x221_hu_6302578466ea388f.png&#34; width=&#34;300&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt; &lt;/p&gt;&#xA;&lt;p&gt;Create two Configurations. One for Debug and one for Release. Make sure to copy the Settings from the existing &amp;ldquo;Debug&amp;rdquo;- and &amp;ldquo;Release&amp;rdquo;-Configuration &lt;img alt=&#34;New build configuration&#34; class=&#34;gallery-image&#34; data-flex-basis=&#34;331px&#34; data-flex-grow=&#34;138&#34; height=&#34;217&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/two-apps-at-the-same-time/newBuildConfiguration-300x217_hu_48cb9448667d9621.png&#34; width=&#34;300&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;At this point, only do this for your web project and - if you have - any backend-projects that are used by your webproject. We will add others later on, but this way you have the chance to validate every single step:&lt;/p&gt;&#xA;&lt;p&gt;&lt;img alt=&#34;Changed build configurations&#34; class=&#34;gallery-image&#34; data-flex-basis=&#34;328px&#34; data-flex-grow=&#34;136&#34; height=&#34;219&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/two-apps-at-the-same-time/buildConfigurationShow-300x219_hu_9fb22bccea96f250.png&#34; width=&#34;300&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;Now its time to create your config file. Find your web.config, click on it with the right mouse button and select &amp;ldquo;Add config transform&amp;rdquo;. After that you should have a Config file for each configuration&lt;/p&gt;&#xA;&lt;h5 id=&#34;nuget-references&#34;&gt;&lt;a href=&#34;#nuget-references&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Nuget-References&#xA;&lt;/h5&gt;&lt;p&gt;I used the PnP - Packages to develop our O365-Add-In. If you only use the &amp;ldquo;base&amp;rdquo; Packages that come with the Add-In-Solution-Template you might have different packages. Nonetheless you will always have a package &lt;em&gt;&amp;ldquo;AppForSharePointOnlineWebToolKit&amp;rdquo;&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Package-wise the main difference between the O365/OnPrem - road are the Azure- and OAuth-packages that are only on the 365-PnP. Also the Office365 Libraries use Version 16, While OnPrem use Version 15.&lt;/p&gt;&#xA;&lt;p&gt;To be able to keep your application updated no matter what configuration you use, create two dummy projects e.g. &lt;em&gt;dummy365&lt;/em&gt; &amp;amp; &lt;em&gt;dummyOnprem&lt;/em&gt;. It does not matter what kind of projects these are. The only purpose of these projects is that there are always the packages for both O365 and OnPrem in the &lt;em&gt;./packages&lt;/em&gt; - folder.&lt;/p&gt;&#xA;&lt;p&gt;Add the O365-pnp-NuGet-packages to your dummy365 and the 2013-pnp-nuget-package to your dummyOnPrem&lt;/p&gt;&#xA;&lt;p&gt;Now: Before you continue, **make a backup of you .csproj - file. It conains information that will be lost after the next step.&lt;/p&gt;&#xA;&lt;p&gt;Now add the &amp;ldquo;other&amp;rdquo; PnP-Package. If you already have the &lt;em&gt;SharePointPnPCoreOnline&lt;/em&gt;-Package installed. now install the &lt;em&gt;SharePointPnpCore2013&lt;/em&gt; NuGet-Package (and vice-versa)&lt;/p&gt;&#xA;&lt;h5 id=&#34;csproj-changes&#34;&gt;&lt;a href=&#34;#csproj-changes&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;csProj-Changes&#xA;&lt;/h5&gt;&lt;p&gt;You can now diff your csproj - files before and after. Or simply believe my words: There are now two differences. One of them is the &lt;em&gt;OfficeDevPnP.Core&lt;/em&gt; - package. The &lt;em&gt;HintPath&lt;/em&gt; from the O365 contains &lt;em&gt;SharePointPnPCoreOnline&lt;/em&gt;, the &lt;em&gt;HintPath&lt;/em&gt; from the 2013-Version contains &lt;em&gt;SharePointPnpCore2013&lt;/em&gt;. This does not seem to be an issue as both DLLs are identical. Nonetheless we will have a look at it. Just in case.&lt;/p&gt;&#xA;&lt;p&gt;The more interesting detail is the Reference to &lt;em&gt;&amp;ldquo;Microsoft.Online.SharePoint.Client.Tenant&amp;rdquo;&lt;/em&gt;. The Office365 - Version uses Version 16.x and the OnPrem - Version uses 15.x. This is absolutely something we have to care about.&lt;/p&gt;&#xA;&lt;p&gt;Luckily, the csProj allows some conditions. So create the following new lines:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;lt;ItemGroup Condition=&amp;#34;&amp;#39;$(Configuration)&amp;#39;==&amp;#39;Debug.OnPrem&amp;#39; Or &amp;#39;$(Configuration)&amp;#39;==&amp;#39;Release.OnPrem&amp;#39;&amp;#34;&amp;gt;&#xA;&amp;lt;Reference Include=&amp;#34;Microsoft.Online.SharePoint.Client.Tenant, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c, processorArchitecture=MSIL&amp;#34;&amp;gt;&#xA;&amp;lt;HintPath&amp;gt;..\\packages\\SharePointPnPCore2013.2.9.1611.0\\lib\\net45\\Microsoft.Online.SharePoint.Client.Tenant.dll&amp;lt;/HintPath&amp;gt;&#xA;&amp;lt;Private&amp;gt;True&amp;lt;/Private&amp;gt;&#xA;&amp;lt;/Reference&amp;gt;&#xA;&amp;lt;Reference Include=&amp;#34;OfficeDevPnP.Core, Version=2.9.1611.0, Culture=neutral, PublicKeyToken=3751622786b357c2, processorArchitecture=MSIL&amp;#34;&amp;gt;&#xA;&amp;lt;HintPath&amp;gt;..\\packages\\SharePointPnPCore2013.2.9.1611.0\\lib\\net45\\OfficeDevPnP.Core.dll&amp;lt;/HintPath&amp;gt;&#xA;&amp;lt;Private&amp;gt;True&amp;lt;/Private&amp;gt;&#xA;&amp;lt;/Reference&amp;gt;&#xA;&amp;lt;/ItemGroup&amp;gt;&#xA;&#xA;&amp;lt;ItemGroup Condition=&amp;#34;&amp;#39;$(Configuration)&amp;#39;==&amp;#39;Debug&amp;#39; Or &amp;#39;$(Configuration)&amp;#39;==&amp;#39;Release&amp;#39;&amp;#34;&amp;gt;&#xA;&amp;lt;Reference Include=&amp;#34;Microsoft.Online.SharePoint.Client.Tenant, Version=16.1.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c, processorArchitecture=MSIL&amp;#34;&amp;gt;&#xA;&amp;lt;HintPath&amp;gt;..\\packages\\Microsoft.SharePointOnline.CSOM.16.1.5813.1200\\lib\\net45\\Microsoft.Online.SharePoint.Client.Tenant.dll&amp;lt;/HintPath&amp;gt;&#xA;&amp;lt;Private&amp;gt;True&amp;lt;/Private&amp;gt;&#xA;&amp;lt;/Reference&amp;gt;&#xA;&#xA;&amp;lt;Reference Include=&amp;#34;OfficeDevPnP.Core, Version=2.9.1611.0, Culture=neutral, PublicKeyToken=3751622786b357c2, processorArchitecture=MSIL&amp;#34;&amp;gt;&#xA;&amp;lt;HintPath&amp;gt;..\\packages\\SharePointPnPCoreOnline.2.9.1611.0\\lib\\net45\\OfficeDevPnP.Core.dll&amp;lt;/HintPath&amp;gt;&#xA;&amp;lt;Private&amp;gt;True&amp;lt;/Private&amp;gt;&#xA;&amp;lt;/Reference&amp;gt;&#xA;&amp;lt;/ItemGroup&amp;gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Do not just copy/paste the content from here!&lt;/strong&gt; Use the lines from within &lt;strong&gt;your&lt;/strong&gt; csProj-File instead! Versions will change over time. And when you read these lines, my version used might already be outdated.&lt;/p&gt;&#xA;&lt;p&gt;As a last step, remove the original lines from within the main &lt;em&gt;ItemGroup&lt;/em&gt; containing these two libraries.&lt;/p&gt;&#xA;&lt;p&gt;Well done. You should now try to rebuild your solution on all your four Build configurations, where the OnPrem-Configurations should create a &lt;em&gt;Microsoft.Online.SharePoint.Client.Tenant.dll&lt;/em&gt; with Version 15 in your bin-directory and the Office365-Version should create a V16-dll. If you encounter a problem try to unload and reload the projects.&lt;/p&gt;&#xA;&lt;p&gt;Please be advised: I am not sure what happens if you update one of those two Nuget-Packages. You might have to modify your csproj-file again&lt;/p&gt;&#xA;&lt;h5 id=&#34;webconfig&#34;&gt;&lt;a href=&#34;#webconfig&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Web.Config&#xA;&lt;/h5&gt;&lt;p&gt;Now its time to modify your Web.Configs.&lt;/p&gt;&#xA;&lt;p&gt;Add the following lines just before the closing &lt;em&gt;&amp;quot;&lt;/configuration&gt;&amp;rdquo;&lt;/em&gt; section into your *.OnPrem.config - files:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;lt;runtime&amp;gt;&#xA;&amp;lt;assemblyBinding xmlns=&amp;#34;urn:schemas-microsoft-com:asm.v1&amp;#34;&amp;gt;&#xA;&amp;lt;dependentAssembly xdt:Transform=&amp;#34;Replace&amp;#34; xdt:Locator=&amp;#34;Condition(./\_defaultNamespace:assemblyIdentity/@name=&amp;#39;Microsoft.Online.SharePoint.Client.Tenant&amp;#39;)&amp;#34;&amp;gt;&#xA;&amp;lt;assemblyIdentity name=&amp;#34;Microsoft.Online.SharePoint.Client.Tenant&amp;#34; publicKeyToken=&amp;#34;71e9bce111e9429c&amp;#34; culture=&amp;#34;neutral&amp;#34; /&amp;gt;&#xA;&amp;lt;bindingRedirect oldVersion=&amp;#34;0.0.0.0-15.0.0.0&amp;#34; newVersion=&amp;#34;15.0.0.0&amp;#34; /&amp;gt;&#xA;&amp;lt;/dependentAssembly&amp;gt;&#xA;&amp;lt;/assemblyBinding&amp;gt;&#xA;&amp;lt;/runtime&amp;gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As before: If at time of reading, the most current version is not 15.0, but 15.1 or higher replace the version numbers instead.&lt;/p&gt;&#xA;&lt;p&gt;Now it is time to modify your &amp;ldquo;normal&amp;rdquo; Configs for Debug and Release&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;lt;runtime&amp;gt;&#xA;&amp;lt;assemblyBinding xmlns=&amp;#34;urn:schemas-microsoft-com:asm.v1&amp;#34;&amp;gt;&#xA;&amp;lt;dependentAssembly xdt:Transform=&amp;#34;Replace&amp;#34; xdt:Locator=&amp;#34;Condition(./\_defaultNamespace:assemblyIdentity/@name=&amp;#39;Microsoft.Online.SharePoint.Client.Tenant&amp;#39;)&amp;#34;&amp;gt;&#xA;&amp;lt;assemblyIdentity name=&amp;#34;Microsoft.Online.SharePoint.Client.Tenant&amp;#34; publicKeyToken=&amp;#34;71e9bce111e9429c&amp;#34; culture=&amp;#34;neutral&amp;#34; /&amp;gt;&#xA;&amp;lt;bindingRedirect oldVersion=&amp;#34;0.0.0.0-16.1.0.0&amp;#34; newVersion=&amp;#34;16.1.0.0&amp;#34; /&amp;gt;&#xA;&amp;lt;/dependentAssembly&amp;gt;&#xA;&amp;lt;/assemblyBinding&amp;gt;&#xA;&amp;lt;/runtime&amp;gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is not 100% necessary at this time as these values should already be in the main Web.Config, but this makes sure it still works if that changes. Again: Check the version number.&lt;/p&gt;&#xA;&lt;h3 id=&#34;authentication&#34;&gt;&lt;a href=&#34;#authentication&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Authentication&#xA;&lt;/h3&gt;&lt;p&gt;Your O365 - Config Files should still contain something like this:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;lt;appSettings&amp;gt;&#xA;&amp;lt;add key=&amp;#34;ClientValidationEnabled&amp;#34; value=&amp;#34;true&amp;#34; /&amp;gt;&#xA;&amp;lt;add key=&amp;#34;UnobtrusiveJavaScriptEnabled&amp;#34; value=&amp;#34;true&amp;#34; /&amp;gt;&#xA;&amp;lt;add key=&amp;#34;ClientId&amp;#34; value=&amp;#34;aaaaaaaa-beef-face-bbbb-cchcchcchcch&amp;#34; /&amp;gt;&#xA;&amp;lt;add key=&amp;#34;ClientSecret&amp;#34; value=&amp;#34;lsdschnabbeldiwuppsfkjashriuae=&amp;#34; /&amp;gt;&#xA;&amp;lt;/appSettings&amp;gt;&#xA;`now your OnPrem-configs looks a little bit different:`&#xA;&amp;lt;appSettings&amp;gt;&#xA;&amp;lt;add key=&amp;#34;ClientValidationEnabled&amp;#34; value=&amp;#34;true&amp;#34; /&amp;gt;&#xA;&amp;lt;add key=&amp;#34;UnobtrusiveJavaScriptEnabled&amp;#34; value=&amp;#34;true&amp;#34; /&amp;gt;&#xA;&amp;lt;add key=&amp;#34;ClientId&amp;#34; value=&amp;#34;aaaaaaaa-beed-face-bbbb-cchcchcchcch&amp;#34; /&amp;gt;&#xA;&amp;lt;add key=&amp;#34;ClientSigningCertificatePath&amp;#34; value=&amp;#34;C:\\Certs\\myCert.pfx&amp;#34; /&amp;gt;&#xA;&amp;lt;add key=&amp;#34;ClientSigningCertificatePassword&amp;#34; value=&amp;#34;p@ssw0rd&amp;#34; /&amp;gt;&#xA;&amp;lt;add key=&amp;#34;IssuerId&amp;#34; value=&amp;#34;bbbbbbbb-beef-face-bbbb-cchcchcchcch&amp;#34; /&amp;gt;&#xA;&amp;lt;/appSettings&amp;gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The main difference here is that you don&amp;rsquo;t just use a ClientId/ClientSecret - Combination for an OnPrem-Installation but have to use a Certificate. I will not describe here how to obtain it. It is just the &amp;ldquo;normal&amp;rdquo; OnPrem-procedure for provider hosted apps. Also be aware that the IssuerId is &lt;strong&gt;not&lt;/strong&gt; the ClientId.&lt;/p&gt;&#xA;&lt;p&gt;If you need help on this part: There are plenty of documents about this. For example on&lt;a class=&#34;link&#34; href=&#34;https://msdn.microsoft.com/en-us/library/office/fp179901.aspx&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;MSDN&lt;/a&gt;&lt;/p&gt;&#xA;&lt;h3 id=&#34;backend-webjobs-and-other-projects-that-talk-directly-to-sharepoint&#34;&gt;&lt;a href=&#34;#backend-webjobs-and-other-projects-that-talk-directly-to-sharepoint&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Backend, Webjobs and other projects that talk directly to SharePoint&#xA;&lt;/h3&gt;&lt;h5 id=&#34;appconfig&#34;&gt;&lt;a href=&#34;#appconfig&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;App.Config&#xA;&lt;/h5&gt;&lt;p&gt;If you have any other Projects than the Web Project and the AddIn, you might have to do some extra steps. For example if you use a backend-project, you have to modify that, too. Ignore this chapter if you don&amp;rsquo;t have any additional projects.&lt;/p&gt;&#xA;&lt;p&gt;But if you have any console application (like Azure WebJobs) you have to modify those configs, too. Sadly. Config-transform does not work out of the box with App.Config - files. But there is a way to change that.&lt;/p&gt;&#xA;&lt;p&gt;First: Create your Config Files manually. So select Add - New Item and select &amp;ldquo;&lt;em&gt;Application Config File&lt;/em&gt;&amp;rdquo;. Name it &lt;em&gt;App.OnPrem.config&lt;/em&gt;. Now open the csProj-File with Notepad. Look for an &lt;em&gt;ItemGroup&lt;/em&gt; containing the App.Config. There already should be an &lt;em&gt;App.Debug.config&lt;/em&gt; and &lt;em&gt;App.Release.config&lt;/em&gt; with a &amp;ldquo;&lt;em&gt;DependentUpon&lt;/em&gt;&amp;rdquo; - Attribute. Just copy that attribute to your newly created files. So that you end with something like this:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;lt;ItemGroup&amp;gt;&#xA;&amp;lt;None Include=&amp;#34;App.config&amp;#34; /&amp;gt;&#xA;&amp;lt;None Include=&amp;#34;App.Debug.config&amp;#34;&amp;gt;&#xA;&amp;lt;DependentUpon&amp;gt;App.config&amp;lt;/DependentUpon&amp;gt;&#xA;&amp;lt;/None&amp;gt;&#xA;&amp;lt;None Include=&amp;#34;App.Debug.OnPrem.config&amp;#34; &amp;gt;&#xA;&amp;lt;DependentUpon&amp;gt;App.config&amp;lt;/DependentUpon&amp;gt;&#xA;&amp;lt;/None&amp;gt;&#xA;&amp;lt;None Include=&amp;#34;App.Release.OnPrem.config&amp;#34; &amp;gt;&#xA;&amp;lt;DependentUpon&amp;gt;App.config&amp;lt;/DependentUpon&amp;gt;&#xA;&amp;lt;/None&amp;gt;&#xA;&amp;lt;None Include=&amp;#34;App.Release.config&amp;#34;&amp;gt;&#xA;&amp;lt;DependentUpon&amp;gt;App.config&amp;lt;/DependentUpon&amp;gt;&#xA;&amp;lt;/None&amp;gt;&#xA;&amp;lt;/ItemGroup&amp;gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;After that all your OnPrem-Configs should be displayed as children from App.Config in Visual Studio (2015). Now transform should work. You can check your csproj - File. If there is something like &lt;em&gt;&amp;lt;UsingTask TaskName=&amp;ldquo;TransformXml&amp;rdquo; ../&amp;gt;&lt;/em&gt; in it you are ready to go. If not, you are probably using an older Visual Studio version. I would not advise it, but you can go with it, but have to do some extra steps: &lt;a class=&#34;link&#34; href=&#34;http://harrewijnen.net/app-config-xdt-transforms/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;XDT Transforms for older VS Versions&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;Now its the same fun again with the config files as before. So add the same stuff into the App.*.config - files as you have done with the web.configs. In addtion add the transform Property to the configuration tag, so it looks like this: &lt;configuration xmlns:xdt=&#34;http://schemas.microsoft.com/XML-Document-Transform&#34;&gt;&lt;/p&gt;&#xA;&lt;h3 id=&#34;test-what-you-have-done-until-now&#34;&gt;&lt;a href=&#34;#test-what-you-have-done-until-now&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Test what you have done until now&#xA;&lt;/h3&gt;&lt;p&gt;Now you should build your Solution for all four configurations. Check if all DLLs where created correctly for the O365 and OnPrem-Configurations. Deploy your application still with the O365 - Settings. Everything should look like before.&lt;/p&gt;&#xA;&lt;h3 id=&#34;changes-on-the-web-project&#34;&gt;&lt;a href=&#34;#changes-on-the-web-project&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Changes on the Web Project&#xA;&lt;/h3&gt;&lt;p&gt;Now it is time to modify your main SharePoint-Project.&lt;/p&gt;&#xA;&lt;p&gt;First of all: Add your SharePoint-Project to the build configurations. The good thing is: This time you don&amp;rsquo;t have to care about the configs. There will most likely be an app.config in your SharePoint-project, but you can just ignore it, because there are no real references in this project.&lt;/p&gt;&#xA;&lt;h5 id=&#34;sharepoint---url&#34;&gt;&lt;a href=&#34;#sharepoint---url&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;SharePoint - URL&#xA;&lt;/h5&gt;&lt;p&gt;The (nearly) final step is to put the right SharePoint-URL to your AddIn-Project depending on your Configuration. The simple way is to just enter the right URL each time you start debugging. The more complex (but better) way is to go the same way as you did with the MVC (Server) part. To do this, enter your Build-Configuration Settings again and set &amp;ldquo;&lt;em&gt;Debug.OnPrem&lt;/em&gt;&amp;rdquo; and &amp;ldquo;&lt;em&gt;Release.OnPrem&lt;/em&gt;&amp;rdquo; for your AddIn-Project.&lt;/p&gt;&#xA;&lt;p&gt;The URL is stored in the user-settings instead of the project settings. But you can use the same magic to make a conditional path there. Open the file YourApp.csproj.user and add the condition to the &amp;ldquo;SharePointSiteUrl&amp;rdquo; - entry:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;lt;PropertyGroup&amp;gt;&#xA;&amp;lt;SharePointSiteUrl Condition=&amp;#34;&amp;#39;$(Configuration)&amp;#39;==&amp;#39;Debug.OnPrem&amp;#39; Or &amp;#39;$(Configuration)&amp;#39;==&amp;#39;Release.OnPrem&amp;#39;&amp;#34;&amp;gt;http://myDevMachine:10000/sites/Dev\_Ole/&amp;lt;/SharePointSiteUrl&amp;gt;&#xA;&amp;lt;SharePointSiteUrl Condition=&amp;#34;&amp;#39;$(Configuration)&amp;#39;==&amp;#39;Debug&amp;#39; Or &amp;#39;$(Configuration)&amp;#39;==&amp;#39;Release&amp;#39;&amp;#34;&amp;gt;https://my365AppName.sharepoint.com/sites/dev-ole/&amp;lt;/SharePointSiteUrl&amp;gt;&#xA;&amp;lt;/PropertyGroup&amp;gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;IF you want you can also simply delete the user-file and put these values into your main .csproj. Sadly this cannot be loaded &amp;ldquo;on the fly&amp;rdquo; by switching your Build configuration . You have to unload and reload your project for this setting to be re-read. This is no issue when deploying your solution using an automated build (like tfs online), but while debugging locally.&lt;/p&gt;&#xA;&lt;h5 id=&#34;target-office-version--sharepoint-url&#34;&gt;&lt;a href=&#34;#target-office-version--sharepoint-url&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Target office version &amp;amp; SharePoint-URL&#xA;&lt;/h5&gt;&lt;p&gt;The csproj - file contains an entry called &amp;ldquo;&lt;em&gt;TargetOfficeVersion&lt;/em&gt;&amp;rdquo; which should contain the value &amp;ldquo;16.1&amp;rdquo;. Just like before we need to modify this using conditional values.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;lt;TargetOfficeVersion Condition=&amp;#34;&amp;#39;$(Configuration)&amp;#39;==&amp;#39;Debug&amp;#39; Or &amp;#39;$(Configuration)&amp;#39;==&amp;#39;Release&amp;#39;&amp;#34;&amp;gt;16.1&amp;lt;/TargetOfficeVersion&amp;gt;&#xA;&amp;lt;TargetOfficeVersion Condition=&amp;#34;&amp;#39;$(Configuration)&amp;#39;==&amp;#39;Debug.OnPrem&amp;#39; Or &amp;#39;$(Configuration)&amp;#39;==&amp;#39;Release.OnPrem&amp;#39;&amp;#34;&amp;gt;15.0&amp;lt;/TargetOfficeVersion&amp;gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can validate this setting while selecting the properties of the project and display the &amp;ldquo;Sharepoint&amp;rdquo;-Tab. Under &amp;ldquo;Target Sharepoint version&amp;rdquo; it should say &amp;ldquo;SharePoint 2013&amp;rdquo; if 15.0 is used or &amp;ldquo;SharePoint Online&amp;rdquo; if 16.1 is used.&lt;/p&gt;&#xA;&lt;h5 id=&#34;authentication-1&#34;&gt;&lt;a href=&#34;#authentication-1&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Authentication&#xA;&lt;/h5&gt;&lt;p&gt;Office365 uses OAuth for Authentication. For SharePoint 2013 you have to use Windows Authentication instead. As I wrote before: There are a few documents out there explaining the Certificate-Part. But you also have to tell your project to use Windows Authentication. Again we use the same conditions like before, but this time use it for the IISExpressAuth:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;lt;IISExpressWindowsAuthentication Condition=&amp;#34;&amp;#39;$(Configuration)&amp;#39;==&amp;#39;Debug.OnPrem&amp;#39; Or &amp;#39;$(Configuration)&amp;#39;==&amp;#39;Release.OnPrem&amp;#39;&amp;#34;&amp;gt;enabled&amp;lt;/IISExpressWindowsAuthentication&amp;gt;&#xA;&amp;lt;IISExpressWindowsAuthentication Condition=&amp;#34;&amp;#39;$(Configuration)&amp;#39;==&amp;#39;Debug&amp;#39; Or &amp;#39;$(Configuration)&amp;#39;==&amp;#39;Release&amp;#39;&amp;#34;&amp;gt;disabled&amp;lt;/IISExpressWindowsAuthentication&amp;gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h5 id=&#34;appmanifest&#34;&gt;&lt;a href=&#34;#appmanifest&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;AppManifest&#xA;&lt;/h5&gt;&lt;p&gt;The Appmanifest does not allow any conditional entries. That&amp;rsquo;s why we have to create a copy of it and do the conditional stuff inside the csproj file. So first create a copy called AppManifest.OnPrem.xmlns&lt;/p&gt;&#xA;&lt;p&gt;In that file change the &amp;ldquo;SharePointMinVersion&amp;rdquo; from &lt;em&gt;16.0.0.0&lt;/em&gt; to &lt;em&gt;15.0.0.0&lt;/em&gt; in the &amp;ldquo;&lt;em&gt;App&lt;/em&gt;&amp;quot;-Section.&lt;/p&gt;&#xA;&lt;p&gt;Now it is finally time again to modify the csproj - File. You should be used to it by now :)&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;lt;ItemGroup Condition=&amp;#34;&amp;#39;$(Configuration)&amp;#39;==&amp;#39;Debug.OnPrem&amp;#39; Or &amp;#39;$(Configuration)&amp;#39;==&amp;#39;Release.OnPrem&amp;#39;&amp;#34;&amp;gt;&#xA;&amp;lt;AppManifestFile Include=&amp;#34;AppManifest.OnPrem.xml&amp;#34;&amp;gt;&#xA;&amp;lt;SubType&amp;gt;Designer&amp;lt;/SubType&amp;gt;&#xA;&amp;lt;/AppManifestFile&amp;gt;&#xA;&amp;lt;/ItemGroup&amp;gt;&#xA;&amp;lt;ItemGroup Condition=&amp;#34;&amp;#39;$(Configuration)&amp;#39;==&amp;#39;Debug&amp;#39; Or &amp;#39;$(Configuration)&amp;#39;==&amp;#39;Release&amp;#39;&amp;#34;&amp;gt;&#xA;&amp;lt;AppManifestFile Include=&amp;#34;AppManifest.xml&amp;#34;&amp;gt;&#xA;&amp;lt;SubType&amp;gt;Designer&amp;lt;/SubType&amp;gt;&#xA;&amp;lt;/AppManifestFile&amp;gt;&#xA;&amp;lt;/ItemGroup&amp;gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt; &lt;/p&gt;&#xA;&lt;h2 id=&#34;things-to-mention-on-v-150-sharepoint-2013&#34;&gt;&lt;a href=&#34;#things-to-mention-on-v-150-sharepoint-2013&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Things to mention on V 15.0 (SharePoint 2013)&#xA;&lt;/h2&gt;&lt;p&gt;While most of the code works on Version 15.0 as well on 16.1, there are some minor differences between these two. The following issues appeared on my project: - FieldUserValue has no Property &amp;ldquo;Email&amp;rdquo; - Microsoft.Client.SharePoint.Folder has no &amp;ldquo;Exists()&amp;rdquo; Both work on 16.1 but not on 15.0. So you have to write your own code to get around this issue.&lt;/p&gt;&#xA;&lt;h3 id=&#34;custom-compiler-symbols&#34;&gt;&lt;a href=&#34;#custom-compiler-symbols&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Custom Compiler Symbols&#xA;&lt;/h3&gt;&lt;p&gt;Because of the differences between V16 and V15 you might want to have custom code depending on your configuration. Just like the DEBUG - Symbol you can add your custom Symbol for the OnPrem-Configurations. Just select the properties of each project you need that symbol in, select the Build - tab and into &amp;ldquo;_Conditional compilation symbol_s&amp;rdquo; enter &amp;ldquo;&lt;em&gt;ONPREM&lt;/em&gt;&amp;rdquo; (or whatever you like):&lt;/p&gt;&#xA;&lt;p&gt;&lt;img alt=&#34;symbol&#34; class=&#34;gallery-image&#34; data-flex-basis=&#34;612px&#34; data-flex-grow=&#34;255&#34; height=&#34;278&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/two-apps-at-the-same-time/symbol_hu_350c26df9784dc31.png&#34; width=&#34;710&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;Afterwards you can use the following code:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#if OnPrem&#xA;var userMail=GetMailFromUser(fieldUserValue); &#xA;#else &#xA;var userMail=FieldUserValue.Email;&#xA;#endif&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;final-thoughts&#34;&gt;&lt;a href=&#34;#final-thoughts&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Final thoughts&#xA;&lt;/h2&gt;&lt;p&gt;You are done now. Just keep in mind: If you use any Azure-connections (e.g. for caching, insights, blob-storage) you will have to handle that, too if you need to get rid of any external connection. The custom Compiler Symbols should help you with that task&lt;/p&gt;&#xA;</description>
        </item><item>
            <title>Wayback Machine and 301 redirects</title>
            <link>https://dotnet.work/blog/wayback-machine-and-301-redirects/</link>
            <pubDate>Tue, 15 Nov 2016 12:00:00 +0100</pubDate><author>dotnet@oles.cloud (Ole Albers)</author>
            <guid>https://dotnet.work/blog/wayback-machine-and-301-redirects/</guid>
            <description>&lt;p&gt;As you might know: the wayback machine from archive.org saves snapshots of websites. It does this automatically or can be forced to do so. However. If you ever had a 301 redirect on your site you are very much doomed.&lt;/p&gt;&#xA;&lt;p&gt;Like browsers also the wayback-machine caches the redirect and does not continue to take snapshots from your sites. Even if you enter your url into the wayback-machine, you still will be redirected.&lt;/p&gt;&#xA;&lt;p&gt;You can however force it to ignore the cache by manually store your site again:&lt;/p&gt;&#xA;&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://web.archive.org/save/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;https://web.archive.org/save/&lt;/a&gt;[YOURSITE]&lt;/p&gt;&#xA;&lt;p&gt;for example:&lt;/p&gt;&#xA;&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://web.archive.org/save/http://blathering.de&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;https://web.archive.org/save/http://blathering.de&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;Et voílà: Everything works nice from now on.&lt;/p&gt;&#xA;&lt;p&gt; &lt;/p&gt;&#xA;&lt;p&gt;[Image is CC-BY-NC-ND © David Baldingerhttp://www.dbaldinger.com/opinion_cartoons/second_page/wayback.html]&lt;/p&gt;&#xA;</description>
        </item><item>
            <title>Getting rid of Proxy limitations using Fiddler</title>
            <link>https://dotnet.work/blog/getting-rid-of-proxy-limitations-using-fiddler/</link>
            <pubDate>Fri, 16 Sep 2016 12:00:00 +0200</pubDate><author>dotnet@oles.cloud (Ole Albers)</author>
            <guid>https://dotnet.work/blog/getting-rid-of-proxy-limitations-using-fiddler/</guid>
            <description>&lt;img src=&#34;https://dotnet.work/blog/getting-rid-of-proxy-limitations-using-fiddler/proxyIE_hu_916867fbbfb567d9.png&#34; alt=&#34;Featured image of post Getting rid of Proxy limitations using Fiddler&#34; /&gt;&lt;p&gt;If you work in a company that uses Proxy authentication (aka forces you to enter a password to access websites) and are a developer you know my pain: Some Most applications just don&amp;rsquo;t really like proxies. Visual Studio itself for example does not like proxy authentication at all and responses with a wide variety of error codes (407 for example).&lt;/p&gt;&#xA;&lt;p&gt;You can get rid of all these errors through different workarounds, but these are a real PITA.&lt;/p&gt;&#xA;&lt;p&gt;In addition the application you code itself needs Internet access, for example if you develop Office365 AddIns, you are very much screwed.&lt;/p&gt;&#xA;&lt;p&gt;After a long research I finally found a solution using &lt;a class=&#34;link&#34; href=&#34;http://www.telerik.com/fiddler&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;Fiddler&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Most developers already use Fiddler for debugging reasons so there isn&amp;rsquo;t even any software that needs to be installed.&lt;/p&gt;&#xA;&lt;p&gt;First of all:&lt;/p&gt;&#xA;&lt;p&gt;Start your computer. You should &lt;strong&gt;not&lt;/strong&gt; have been entered your Proxy credentials yet. Use Fiddler as proxy in your IE and enter your real Proxy in Fiddler:&lt;/p&gt;&#xA;&lt;p&gt;&lt;img alt=&#34;proxyie&#34; class=&#34;gallery-image&#34; data-flex-basis=&#34;272px&#34; data-flex-grow=&#34;113&#34; height=&#34;446&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/getting-rid-of-proxy-limitations-using-fiddler/proxyIE_hu_916867fbbfb567d9.png&#34; width=&#34;506&#34;&gt; Default Proxy-Settings to use Fiddler&lt;/p&gt;&#xA;&lt;p&gt;&lt;img alt=&#34;fiddlerproxy&#34; class=&#34;gallery-image&#34; data-flex-basis=&#34;410px&#34; data-flex-grow=&#34;171&#34; height=&#34;490&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/getting-rid-of-proxy-limitations-using-fiddler/fiddlerproxy_hu_e1a07ea8d09b2f21.png&#34; srcset=&#34;https://dotnet.work/blog/getting-rid-of-proxy-limitations-using-fiddler/fiddlerproxy_hu_f28009f203e428a2.png 800w, https://dotnet.work/blog/getting-rid-of-proxy-limitations-using-fiddler/fiddlerproxy_hu_e1a07ea8d09b2f21.png 838w&#34; width=&#34;838&#34;&gt; Proxy-Settings in Fiddler&lt;/p&gt;&#xA;&lt;p&gt; &lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Do not select &amp;ldquo;&lt;em&gt;System Proxy&amp;rdquo;&lt;/em&gt;.&lt;/strong&gt; Otherwise. Fiddler will enter itself here, because Fiddler is the System Proxy right now.&lt;/p&gt;&#xA;&lt;p&gt;Now open your favorite browser (except FireFox, as it uses its own proxy) and open any site that opens up the Proxy authentication window. Enter your credentials and load that site. In Fiddler you should now see (at least) one entry with response code 407 followed by a few entries with 200:&lt;/p&gt;&#xA;&lt;p&gt;&lt;img alt=&#34;fiddler1&#34; class=&#34;gallery-image&#34; data-flex-basis=&#34;582px&#34; data-flex-grow=&#34;242&#34; height=&#34;440&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/getting-rid-of-proxy-limitations-using-fiddler/fiddler1_hu_d3015f78c419550b.png&#34; srcset=&#34;https://dotnet.work/blog/getting-rid-of-proxy-limitations-using-fiddler/fiddler1_hu_2bd5f2b7748eace5.png 800w, https://dotnet.work/blog/getting-rid-of-proxy-limitations-using-fiddler/fiddler1_hu_d3015f78c419550b.png 1068w&#34; width=&#34;1068&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;Select the entry with StatusCode 200 and display the Request Headers. Under &amp;ldquo;Security there now is a line with &amp;ldquo;Proxy-Authorization: &amp;ldquo;&amp;hellip;&lt;/p&gt;&#xA;&lt;p&gt;&lt;img alt=&#34;fiddler2&#34; class=&#34;gallery-image&#34; data-flex-basis=&#34;453px&#34; data-flex-grow=&#34;189&#34; height=&#34;364&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/getting-rid-of-proxy-limitations-using-fiddler/fiddler2_hu_f319efbd5e5a1b6e.png&#34; width=&#34;688&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;Copy that line.&lt;/p&gt;&#xA;&lt;p&gt;Now Enter Ctrl-R in Fiddler to open the rules file. Look for&lt;/p&gt;&#xA;&lt;p&gt; static function OnBeforeRequest(oSession: Session) {&lt;/p&gt;&#xA;&lt;p&gt;in that file and add the following line directly at the top of the function:&lt;/p&gt;&#xA;&lt;p&gt;oSession.oRequest[&amp;ldquo;Proxy-Authorization&amp;rdquo;] = &amp;ldquo;Basic DBDDHKPadasdkahsdasd=&amp;rdquo;;&lt;/p&gt;&#xA;&lt;p&gt;where you replace the token behind &amp;ldquo;Basic&amp;rdquo; with the value you copied before.&lt;/p&gt;&#xA;&lt;p&gt;Now save CustomRules.js and from now on no application has to enter any more Proxy passwords (as long as you use Fiddler as System Proxy)&lt;/p&gt;&#xA;&lt;p&gt;If you are forced to change your Password, remove this line in the configuration file, and repeat the procedure from the beginning with the new password.&lt;/p&gt;&#xA;&lt;p&gt;That&amp;rsquo;s it, mostly. For more comfort I would recommend to use the &lt;a class=&#34;link&#34; href=&#34;http://proxyswitcher.net/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;proxy switcher&lt;/a&gt; if you have to change the proxy more often.&lt;/p&gt;&#xA;</description>
        </item><item>
            <title>Displaying Data from HomeMatic Actors on a WebInterface</title>
            <link>https://dotnet.work/blog/displaying-data-from-homematic-actors-on-a-webinterface/</link>
            <pubDate>Mon, 11 Jul 2016 12:00:00 +0200</pubDate><author>dotnet@oles.cloud (Ole Albers)</author>
            <guid>https://dotnet.work/blog/displaying-data-from-homematic-actors-on-a-webinterface/</guid>
            <description>&lt;p&gt;This tutorial assumes that you already installed and configured FHEM and know how to use AngularJS.&lt;/p&gt;&#xA;&lt;p&gt; &lt;/p&gt;&#xA;&lt;p&gt;As you might know I use my Raspberry PI to automate tasks at home. I already &lt;a class=&#34;link&#34; href=&#34;https://dotnet.work/blog/detecting-smartphone-via-bluetooth-on-linux-raspberry-pi-2/&#34; &gt;detect my SmartPhone&lt;/a&gt;, and my &lt;a class=&#34;link&#34; href=&#34;https://dotnet.work/blog/tracking-fitbit-presence-under-linux-raspberry-pi-2/&#34; &gt;FitBit&lt;/a&gt; and also &lt;a class=&#34;link&#34; href=&#34;https://dotnet.work/blog/automate-your-synology-surveillancestation-with-some-simple-linux-commands/&#34; &gt;control my Webcam&lt;/a&gt; and &lt;a class=&#34;link&#34; href=&#34;https://dotnet.work/blog/control-your-harmony-hub-with-a-raspberry-pi-linux/&#34; &gt;Harmony remote&lt;/a&gt; using a raspberry and a web interface.&lt;/p&gt;&#xA;&lt;p&gt;Now I also wanted to include actors from HomeMatic thermostats into my configuration and display it in a nice way.&lt;/p&gt;&#xA;&lt;p&gt;Hardware used:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;HomeMatic Wireless Configuration Adapter LAN (HM-CFG-LAN)&lt;/li&gt;&#xA;&lt;li&gt;HomeMatic Thermostate HM-CC-RT-DN&lt;/li&gt;&#xA;&lt;li&gt;Raspberry Pi V2.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Software used:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;HTML/CSS&lt;/li&gt;&#xA;&lt;li&gt;JavaScript&lt;/li&gt;&#xA;&lt;li&gt;AngularJS&lt;/li&gt;&#xA;&lt;li&gt;FHEM&lt;/li&gt;&#xA;&lt;li&gt;PHP (optional)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Before we begin make sure you have already installed FHEM and the hardware for it. I will not discuss that here as there are a lot of good tutorials for that. A good (german) tutorial can be found in the &lt;a class=&#34;link&#34; href=&#34;http://www.fhemwiki.de/wiki/HM-CFG-LAN_LAN_Konfigurations-Adapter&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;FHEM Wiki&lt;/a&gt;. (If you got any english documentation, feel free to post it in the comments.)&lt;/p&gt;&#xA;&lt;p&gt;I wrote a &amp;ldquo;general&amp;rdquo; PHP script to send commands and receive commands from FHEM. This can also be done without PHP and pure JavaScript-Code. I chose this solution because my configuration website is accessable through the internet, but my FHEM isn&amp;rsquo;t. So JS-Could (running on the client) would not work, but PHP (running on the server) does.&lt;/p&gt;&#xA;&lt;p&gt;The PHP-Script:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;lt;?php&#xA; $host=&amp;#34;http://192.168.178.36:8083/fhem?&amp;#34;;&#xA;&#xA; $sensor= $\_GET\[&amp;#39;sensor&amp;#39;\];&#xA; $param= $\_GET\[&amp;#39;param&amp;#39;\];&#xA; $action= $\_GET\[&amp;#39;action&amp;#39;\];&#xA;&#xA; if ($action==&amp;#34;get&amp;#34;) {&#xA; $get=&amp;#34;detail=SENSOR&amp;amp;dev.getSENSOR=SENSOR&amp;amp;cmd.getSENSOR=get&amp;amp;arg.getSENSOR=param&amp;amp;val.getSENSOR=PARAM&amp;amp;XHR=1&amp;amp;addLinks=1&amp;#34;;&#xA; } else {&#xA; $get=&amp;#34;cmd=set%20SENSOR%20desired-temp%20PARAM&amp;amp;XHR=1&amp;#34;;&#xA; }&#xA;&#xA; $replaced=str\_replace(&amp;#34;SENSOR&amp;#34;,$sensor,$get);&#xA; $replaced=str\_replace(&amp;#34;PARAM&amp;#34;,$param,$replaced);&#xA; $total=$host.$replaced;&#xA; &#xA; $ch = curl\_init();&#xA; curl\_setopt($ch, CURLOPT\_URL, $total);&#xA; curl\_setopt($ch, CURLOPT\_RETURNTRANSFER, 1);&#xA; $return = curl\_exec($ch);&#xA;&#xA; echo $return;&#xA;&#xA;?&amp;gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The only thing you need to modify here is the IP-Adress (or hostname) and maybe the port from the $host - variable. As you can see, this is a simple GET-Request which could easily used through JavaScript (or even just inside a href) also.&lt;/p&gt;&#xA;&lt;p&gt;You can test this script by simply calling it with some parameters:&lt;/p&gt;&#xA;&lt;p&gt;http://yourserver/fhem.php?action=get&amp;amp;sensor={sensorname}_Clima&amp;amp;param=measured-temp&lt;/p&gt;&#xA;&lt;p&gt;Replace &amp;ldquo;Sensorname&amp;rdquo; by the name of your sensor. You should receive the current temperature of the sensor as a result.&lt;/p&gt;&#xA;&lt;p&gt;Now it is time to display this data in a fancy way in html. Create any HTML - Page using AngularJS. To use these nice Gauges you need &lt;a class=&#34;link&#34; href=&#34;https://github.com/angular-google-chart/angular-google-chart&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;ng-Google-Chart&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;Now lets see the HTML-Part containing the AngularJS-Directives to display and set the thermostate values:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;lt;html&amp;gt;&amp;lt;body ng-app=&amp;#34;piApp&amp;#34;&amp;gt;&#xA;  &amp;lt;div class=&amp;#34;container&amp;#34; ng-Controller=&amp;#34;heatController&amp;#34; ng-init=&amp;#34;getStats()&amp;#34;&amp;gt;&#xA;        &amp;lt;div class=&amp;#34;row&amp;#34;&amp;gt;&#xA;            &amp;lt;div class=&amp;#34;box&amp;#34;&amp;gt;&#xA;                &amp;lt;div class=&amp;#34;col-lg-12&amp;#34;&amp;gt;&#xA;                    &amp;lt;hr&amp;gt;&#xA;                    &amp;lt;h2 class=&amp;#34;intro-text text-center&amp;#34;&amp;gt;Heizungs&#xA;                        &amp;lt;strong&amp;gt;Einstellungen&amp;lt;/strong&amp;gt;&#xA;                    &amp;lt;/h2&amp;gt;&#xA;                    &amp;lt;hr&amp;gt;&#xA;                &amp;lt;/div&amp;gt;&#xA;                &amp;lt;div class=&amp;#34;col-md-12&amp;#34;&amp;gt;&#xA;                    &amp;lt;div ng-repeat=&amp;#34;heater in heaterSettings.allHeaters&amp;#34; class=&amp;#34;heater&amp;#34;&amp;gt;&#xA;                        &amp;lt;div class=&amp;#34;battery&amp;#34;&amp;gt;&amp;lt;i class=&amp;#34;fa fa-4x fa-{{heater.battery.icon}} {{heater.battery.color}}&amp;#34;&amp;gt;&amp;lt;/i&amp;gt; &amp;lt;/div&amp;gt;&#xA;                        &amp;lt;div google-chart chart=&amp;#34;heater.gauge&amp;#34;  &amp;gt;&amp;lt;/div&amp;gt;&#xA;                        &amp;lt;div class=&amp;#34;caption&amp;#34;&amp;gt;{{heater.name}}&amp;lt;br/&amp;gt;&#xA;                            &amp;lt;select ng-model=&amp;#34;heater.ui.wish&amp;#34; ng-change=&amp;#34;setTemperature(heater.id,heater.ui.wish)&amp;#34;&amp;gt;&#xA;                                &amp;lt;option ng-repeat=&amp;#34;option in availableTemperatures()&amp;#34; value=&amp;#34;{{option}}&amp;#34;&amp;gt;{{option}}&amp;lt;/option&amp;gt;&#xA;                            &amp;lt;/select&amp;gt;&#xA;                        &amp;lt;/div&amp;gt;&#xA;                    &amp;lt;/div&amp;gt;&#xA;                &amp;lt;/div&amp;gt;&#xA;&#xA;        &amp;lt;/div&amp;gt;&#xA;    &amp;lt;/div&amp;gt;&#xA;&amp;lt;/div&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Some explanation:&lt;/p&gt;&#xA;&lt;p&gt;piApp is the AngularJS - App for the website, getStats() loads the current Sensors using FHEM.php mentioned above directly from the start. This html uses Bootstrap (for repsonsive design) and Font Awesome to display the Battery indicator. Both use CSS to achieve this. (The Gauge-Display works without these libraries)&lt;/p&gt;&#xA;&lt;p&gt;The Angular-Controller looks like this:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;angular.module(&amp;#39;piApp&amp;#39;, \[&amp;#39;googlechart&amp;#39;\])&#xA;    .controller(&amp;#39;heatController&amp;#39;, function($scope, $http) {&#xA;        $scope.url=&amp;#39;fhem.php?&amp;#39;;&#xA;        $scope.heaterSettings={&#xA;            allHeaters:\[&#xA;                {name:&amp;#39;Wohnzimmer&amp;#39;, id:&amp;#39;Wohnzimmer\_Clima&amp;#39;, ui:{},battery:{},gauge:{}},&#xA;                {name:&amp;#39;Schlafzimmer&amp;#39;,id:&amp;#39;Schlafzimmer\_Clima&amp;#39;,ui:{},battery:{},gauge:{}},&#xA;                {name:&amp;#39;Bad&amp;#39;,id:&amp;#39;Bad\_Clima&amp;#39;,ui:{},battery:{},gauge:{}}&#xA;            \]&#xA;        };&#xA;&#xA;        $scope.getParamUrl=function(sensor,param) {&#xA;            return &amp;#34;action=get&amp;amp;sensor=&amp;#34;+sensor+&amp;#34;&amp;amp;param=&amp;#34;+param;&#xA;        };&#xA;        $scope.setParamUrl=function(sensor,param) {&#xA;            return &amp;#34;action=set&amp;amp;sensor=&amp;#34;+sensor+&amp;#34;&amp;amp;param=&amp;#34;+param;&#xA;        };&#xA;&#xA;        $scope.getBattery=function(sensor, target) {&#xA;            $http.get($scope.url+$scope.getParamUrl(sensor,&amp;#39;batteryLevel&amp;#39;))&#xA;                .success(function(data) {&#xA;                    $scope.setBattery(target,data);&#xA;                });&#xA;        };&#xA;&#xA;        $scope.getStats=function() {&#xA;            $scope.heaterSettings.allHeaters.forEach(function (heater) {&#xA;                // gauge:&#xA;                heater.gauge={};&#xA;                heater.gauge.type=&amp;#34;Gauge&amp;#34;;&#xA;                heater.gauge.options = {&#xA;                    width: 250,&#xA;                    height: 250,&#xA;                    redFrom: 20,&#xA;                    redTo: 30,&#xA;                    greenFrom: 0,&#xA;                    greenTo: 10,&#xA;                    yellowFrom: 10,&#xA;                    yellowTo: 20,&#xA;                    minorTicks: 10,&#xA;                    majorTicks: \[&amp;#39;0&amp;#39;,&amp;#39;5&amp;#39;,&amp;#39;10&amp;#39;,&amp;#39;15&amp;#39;,&amp;#39;20&amp;#39;,&amp;#39;25&amp;#39;,&amp;#39;30&amp;#39;\],&#xA;                    max:30,&#xA;                    animation:{&#xA;                        startup: true,&#xA;                        duration: 1000,&#xA;                        easing: &amp;#39;inAndOut&amp;#39;&#xA;                    }&#xA;                };&#xA;&#xA;                // Gemessene Temperatur:&#xA;                $http.get($scope.url+$scope.getParamUrl(heater.id,&amp;#39;measured-temp&amp;#39;))&#xA;                    .success(function(data) {&#xA;                        $scope.setData(heater.gauge,data);&#xA;                    });&#xA;                $http.get($scope.url+$scope.getParamUrl(heater.id,&amp;#39;desired-temp&amp;#39;))&#xA;                    .success(function(data) {&#xA;                        $scope.setWish(heater.ui,data);&#xA;                    });&#xA;                $scope.getBattery(heater.name,heater);&#xA;            });&#xA;        };&#xA;&#xA;        $scope.setTemperature=function(sensor, temperature) {&#xA;            $http.get($scope.url+$scope.setParamUrl(sensor,temperature))&#xA;                .success(function(data) {&#xA;                    console.log(&amp;#34;Temperatur von &amp;#34;+sensor+&amp;#34; auf &amp;#34;+temperature+&amp;#34; Grad eingestellt&amp;#34;);&#xA;                    //$scope.getStats();&#xA;                })&#xA;                .error(function(data) {&#xA;                    alert(&amp;#34;Fehlgeschlagen&amp;#34;);&#xA;                }&#xA;            );&#xA;        };&#xA;&#xA;        $scope.availableTemperatures=function() {&#xA;            var available=\[\];&#xA;            for(var i=5;i&amp;lt;=30;i=i+0.5)  {&#xA;                var iasString= i.toString();&#xA;                if (Math.round(i)===i) {&#xA;                    iasString+=&amp;#39;.0&amp;#39;&#xA;                }&#xA;                available.push(iasString);&#xA;            }&#xA;            available.push(&amp;#34;on&amp;#34;);&#xA;            available.push(&amp;#34;off&amp;#34;);&#xA;            return available;&#xA;        };&#xA;&#xA;        $scope.setData=function(target,data) {&#xA;            var value=parseFloat(data.replace(/(\\r\\n|\\n|\\r)/gm,&amp;#34;&amp;#34;));&#xA;            target.data=  \[ \[&amp;#39;Label&amp;#39;, &amp;#39;Value&amp;#39;\], \[&amp;#39;&amp;#39;, value\]\];&#xA;        };&#xA;&#xA;        $scope.setWish=function(target,data) {&#xA;            var value=data.replace(/(\\r\\n|\\n|\\r)/gm,&amp;#34;&amp;#34;).trim();&#xA;            target.wish=value;&#xA;        };&#xA;     &#xA;        $scope.setBattery=function(target,value) {&#xA;            var value=parseFloat(value.replace(/(\\r\\n|\\n|\\r)/gm,&amp;#34;&amp;#34;).trim());&#xA;            var color=&amp;#39;green&amp;#39;;&#xA;            var icon=&amp;#39;battery-full&amp;#39;;&#xA;            if (value&amp;lt;2.1) {&#xA;                color=&amp;#34;red&amp;#34;;&#xA;                icon=&amp;#39;battery-empty&amp;#39;;&#xA;            } else if (value&amp;lt;2.3) {&#xA;                color=&amp;#34;red&amp;#34;;&#xA;                icon=&amp;#39;battery-quarter&amp;#39;;&#xA;            } else if (value&amp;lt;2.6) {&#xA;                color=&amp;#34;yellow&amp;#34;;&#xA;                icon=&amp;#39;battery-half&amp;#39;;&#xA;            }&#xA;            target.battery={&#xA;                value:value,&#xA;                color:color,&#xA;                icon:icon&#xA;            }&#xA;        };&#xA;&#xA;    })&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Explanation:&lt;/p&gt;&#xA;&lt;p&gt;&amp;ldquo;googlechart&amp;rdquo; in the module-configuration allows to paint those fancy gauges.&lt;/p&gt;&#xA;&lt;p&gt;in $scope.heaterSettings.allHeaters you have to modify the Names of your Sensors. Enter as many sensors as you want.&lt;/p&gt;&#xA;&lt;p&gt;All other functions communicate with fhem.php or display values.&lt;/p&gt;&#xA;</description>
        </item><item>
            <title>It&#39;s because they at Microsoft hate web developers</title>
            <link>https://dotnet.work/blog/its-because-they-at-microsoft-hate-web-developers/</link>
            <pubDate>Tue, 14 Jun 2016 12:00:00 +0200</pubDate><author>dotnet@oles.cloud (Ole Albers)</author>
            <guid>https://dotnet.work/blog/its-because-they-at-microsoft-hate-web-developers/</guid>
            <description>&lt;p&gt;That remark isn&amp;rsquo;t mine, it is from StackOverflow. :)&lt;/p&gt;&#xA;&lt;p&gt;I had an issue that my Office365-App did not work correctly on the machine of ONE user. Sadly though that user was the Microsoft validation crew. I was unable to reproduce the error and did not even see any clue in the Server logs. Everything seemed to work fine.&lt;/p&gt;&#xA;&lt;p&gt;Finally I managed to reproduce the error. It only happened under the following conditions: 1. Internet Explorer 11; IE - what else :) 2. Caching in IE has to be on &amp;ldquo;Automatic&amp;rdquo; 3. Developer Console MUST NOT be open 3.1. In fact: Even if you open it and close it afterwards, the error does not appear anymore 4. I had to retract/reinstall the app each time to check this behaviour which took about 10 minutes every time&lt;/p&gt;&#xA;&lt;p&gt;I had issues with console on older IEs before, but at least angular uses some magic, so that issue is resolved. Nearly all questions in the web combining IE and console repeat the solution that you may not use &amp;ldquo;console.log()&amp;rdquo;.&lt;/p&gt;&#xA;&lt;p&gt;But the real issue was: Caching. IE - in its wisdom - decided that the API-Call that returns the installation state should be cached. That cached value always responded that the app is not installed, even if it was. So the user kept receiving the &amp;ldquo;please install&amp;rdquo; - Page.&lt;/p&gt;&#xA;&lt;p&gt;Any other browser (Chrome, Firefox, &amp;hellip;) doesn&amp;rsquo;t cache these calls. And the IE doesn&amp;rsquo;t either when you open the Developer Console (what you do if you want to debug an application) which makes it really hard to track that error.&lt;/p&gt;&#xA;&lt;p&gt;So the solution is to make sure the call isn&amp;rsquo;t cached using No-Cache-Headers or just by adding a random value to the target address. And of course resist the urge to display a reminder to use another browser. That could be bad for the validation results :)&lt;/p&gt;&#xA;</description>
        </item><item>
            <title>FlashAirDownloader 2</title>
            <link>https://dotnet.work/blog/flashairdownloader-2/</link>
            <pubDate>Sun, 01 May 2016 12:00:00 +0200</pubDate><author>dotnet@oles.cloud (Ole Albers)</author>
            <guid>https://dotnet.work/blog/flashairdownloader-2/</guid>
            <description>&lt;img src=&#34;https://dotnet.work/blog/flashairdownloader-2/autocopy_hu_dd06e496e28f5a46.png&#34; alt=&#34;Featured image of post FlashAirDownloader 2&#34; /&gt;&lt;blockquote class=&#34;alert alert-warning&#34;&gt;&#xA;        &lt;div class=&#34;alert-header&#34;&gt;&#xA;            &lt;span class=&#34;alert-icon&#34;&gt;⚠️&lt;/span&gt;&#xA;            &lt;span class=&#34;alert-title&#34;&gt;Warning&lt;/span&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div class=&#34;alert-body&#34;&gt;&#xA;            &lt;p&gt;&lt;strong&gt;Development stopped!&lt;/strong&gt; &lt;a class=&#34;link&#34; href=&#34;https://dotnet.work/blog/flashairdownloader-development-now-its-your-turn/&#34; &gt;Please read this&lt;/a&gt; before downloading FAD&lt;/p&gt;&#xA;        &lt;/div&gt;&#xA;    &lt;/blockquote&gt;&#xA;&lt;blockquote class=&#34;alert alert-note&#34;&gt;&#xA;        &lt;div class=&#34;alert-header&#34;&gt;&#xA;            &lt;span class=&#34;alert-icon&#34;&gt;📝&lt;/span&gt;&#xA;            &lt;span class=&#34;alert-title&#34;&gt;Note&lt;/span&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div class=&#34;alert-body&#34;&gt;&#xA;            &lt;p&gt;Keep your Version updated! Always click onto the &amp;ldquo;Check for Updates&amp;rdquo; in the Program after installation&lt;/p&gt;&#xA;        &lt;/div&gt;&#xA;    &lt;/blockquote&gt;&#xA;&lt;p&gt;The FlashAirDownloader 2 is out there (Because of my Laziness I will just call it FAD from now on)&lt;/p&gt;&#xA;&lt;p&gt;A lot of people used the FlashAirDownloader which was meant as a personal tool and quick hack to access my Toshiba FlashAir Card. As more and more people asked me for features (and some for the code) I decided to make it open source so more folks can help improve the tool.&lt;/p&gt;&#xA;&lt;p&gt;Sadly so: The code was a mess. As I told you: Just a quick hack. It would be to embarrassing to make this public :D So I decided to rewrite it from scratch: This time making it right regarding code quality and UI design. Nonetheless this program still uses Framework 4.0, cause I know there are Windows XP users out there who have no other possibilities to access the FlashAir.&lt;/p&gt;&#xA;&lt;p&gt;But enough of talk. Here is the new FAD:&lt;/p&gt;&#xA;&lt;p&gt;&lt;img class=&#34;gallery-image&#34; data-flex-basis=&#34;436px&#34; data-flex-grow=&#34;181&#34; height=&#34;620&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/flashairdownloader-2/autocopy_hu_dd06e496e28f5a46.png&#34; srcset=&#34;https://dotnet.work/blog/flashairdownloader-2/autocopy_hu_c1dcdd4024d23a07.png 800w, https://dotnet.work/blog/flashairdownloader-2/autocopy_hu_dd06e496e28f5a46.png 1127w&#34; width=&#34;1127&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;You can &lt;a class=&#34;link&#34; href=&#34;https://github.com/OleAlbers/fad2&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;download the source directly from Github&lt;/a&gt;. Feel free to make the code better. Or download the binary here:&lt;/p&gt;&#xA;&lt;p&gt;&lt;em&gt;(The download from the old site is gone, please build from source.)&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;You will get an annoying amount of warnings from your browser and/or operating system. This is because the file isn&amp;rsquo;t signed. Valid signing requires a valid certificate which does not come for free. (and requires some amount of work) Feel free to build from source in GitHub if you have any doubts.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;first-start&#34;&gt;&lt;a href=&#34;#first-start&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;First start&#xA;&lt;/h2&gt;&lt;p&gt;After Installation and starting FAD you should see something like this:&lt;/p&gt;&#xA;&lt;p&gt;&lt;img class=&#34;gallery-image&#34; data-flex-basis=&#34;377px&#34; data-flex-grow=&#34;157&#34; height=&#34;730&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/flashairdownloader-2/searching-card_hu_253f747665c1ef66.png&#34; srcset=&#34;https://dotnet.work/blog/flashairdownloader-2/searching-card_hu_216bdb2a59a5aeb4.png 800w, https://dotnet.work/blog/flashairdownloader-2/searching-card_hu_253f747665c1ef66.png 1147w&#34; width=&#34;1147&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;If your FlashAir - card is already configured it should be found and the display should change to this:&lt;/p&gt;&#xA;&lt;p&gt;&lt;img class=&#34;gallery-image&#34; data-flex-basis=&#34;375px&#34; data-flex-grow=&#34;156&#34; height=&#34;738&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/flashairdownloader-2/foundcard_hu_161662514a5c1345.png&#34; srcset=&#34;https://dotnet.work/blog/flashairdownloader-2/foundcard_hu_fded45859a3bddeb.png 800w, https://dotnet.work/blog/flashairdownloader-2/foundcard_hu_161662514a5c1345.png 1154w&#34; width=&#34;1154&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;For now: Keep calm and do not click on any of the blue squares. Use &amp;ldquo;File/Settings&amp;rdquo; instead.&lt;/p&gt;&#xA;&lt;h2 id=&#34;setup&#34;&gt;&lt;a href=&#34;#setup&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Setup&#xA;&lt;/h2&gt;&lt;p&gt;&lt;img class=&#34;gallery-image&#34; data-flex-basis=&#34;443px&#34; data-flex-grow=&#34;184&#34; height=&#34;745&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/flashairdownloader-2/settings1_hu_56098891be23d5a3.png&#34; srcset=&#34;https://dotnet.work/blog/flashairdownloader-2/settings1_hu_d53401fe7a55266a.png 800w, https://dotnet.work/blog/flashairdownloader-2/settings1_hu_56098891be23d5a3.png 1376w&#34; width=&#34;1376&#34;&gt;&lt;/p&gt;&#xA;&lt;h3 id=&#34;program-settings&#34;&gt;&lt;a href=&#34;#program-settings&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Program Settings&#xA;&lt;/h3&gt;&lt;p&gt;Check if your settings are ok. Here is a small description from the settings:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;FlashAir Url This is the Url from your FlashAir. You can try if you can access your FlashAir by entering that address into your Browser&lt;/li&gt;&#xA;&lt;li&gt;Local Path Here the Images will be saved when using the Auto-Mode&lt;/li&gt;&#xA;&lt;li&gt;Existing Files (Auto mode) Decide what to do if the file already exists.&lt;/li&gt;&#xA;&lt;li&gt;Delete files after downloading (Auto mode) If checked, the files will be deleted after a successful download from the FlashAir&lt;/li&gt;&#xA;&lt;li&gt;Flashair Imagepath Where your Images are located (may contain subfolders)&lt;/li&gt;&#xA;&lt;li&gt;Filetypes to copy (Auto mode) Decide if you only want image files or other files, too&lt;/li&gt;&#xA;&lt;li&gt;Load Thumbnails If enabled Thumbnails from Images on the FlashAir will be collected and shown. Might take a while. Thumbnails for local files are always displayed regardless of this setting.&lt;/li&gt;&#xA;&lt;li&gt;Automatic folder Creation (Auto Mode) * Multiple Cards Use your Card ID or AppID to create subfolders if you use more than one card * Create By Date Decide in what format the directories should be named * Custom You can als create your own custom path. {0} stands for the CardId, {1} for the AppId and {2} for the current date. Examples about the date-format &lt;a class=&#34;link&#34; href=&#34;http://www.csharp-examples.net/string-format-datetime/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;can be found here&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;Styling Decide if (and what) images should be displayed on the start page and the settings page of FAD.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Don&amp;rsquo;t forget to click onto [Save] under Card Settings in the lower left area of the screen.&lt;/p&gt;&#xA;&lt;h3 id=&#34;card-settings&#34;&gt;&lt;a href=&#34;#card-settings&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Card Settings&#xA;&lt;/h3&gt;&lt;p&gt;Now after the application is set up it is time to check your FlashAir-Card. Just put your SD-Card into the computer and click onto [Load] in the upper left corner. After a few seconds three new tabs should appear: &amp;ldquo;Vendor&amp;rdquo;, &amp;ldquo;WLANSD&amp;rdquo; and &amp;ldquo;Disable Functions&amp;rdquo;&lt;/p&gt;&#xA;&lt;p&gt;&lt;img class=&#34;gallery-image&#34; data-flex-basis=&#34;444px&#34; data-flex-grow=&#34;185&#34; height=&#34;739&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/flashairdownloader-2/settings2_hu_1f9588edf82daffb.png&#34; srcset=&#34;https://dotnet.work/blog/flashairdownloader-2/settings2_hu_fa17fd284fd9eec8.png 800w, https://dotnet.work/blog/flashairdownloader-2/settings2_hu_1f9588edf82daffb.png 1370w&#34; width=&#34;1370&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;Click onto &amp;ldquo;Vendor&amp;rdquo; to setup the most important settings. I will only describe the most critical values. A complete list is available &lt;a class=&#34;link&#34; href=&#34;https://flashair-developers.com/en/documents/api/config/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;at the Toshiba FlashAir Website.&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;First make sure the Connection Timeout is correct. It must be at least 1 Minute. You can also set it to &amp;ldquo;0&amp;rdquo; to disable timeout completely. &amp;ldquo;App-Info&amp;rdquo; and &amp;ldquo;Card ID&amp;rdquo; can be modified if you want to use those values in the automatic folder creation.&lt;/p&gt;&#xA;&lt;p&gt;If your Network settings already work, don&amp;rsquo;t touch them here. Otherwise &lt;a class=&#34;link&#34; href=&#34;https://www.toshiba.co.jp/p-media/wwsite/flashair.htm&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;follow the instructions on the FlashAir website.&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;Now make sure the &amp;ldquo;Upload Directory&amp;rdquo; fits the &amp;ldquo;Flashair Imagepath&amp;rdquo; in the &amp;ldquo;Application Settings&amp;rdquo;.&lt;/p&gt;&#xA;&lt;p&gt;The last setting to check is &amp;ldquo;Upload Enabled&amp;rdquo;. This is required if you want to be able to save files onto the Flashair or want to delete files after downloading.&lt;/p&gt;&#xA;&lt;p&gt;Now all you have to do is to click [Save] in the upper Left region and FAD is ready to go.&lt;/p&gt;&#xA;&lt;p&gt;For the first run I would advice just keeping your card in your card-reader. It works exactly there like in your camera, but you can modify your settings more easy this way.&lt;/p&gt;&#xA;&lt;h3 id=&#34;auto---mode&#34;&gt;&lt;a href=&#34;#auto---mode&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Auto - Mode&#xA;&lt;/h3&gt;&lt;p&gt;&lt;img class=&#34;gallery-image&#34; data-flex-basis=&#34;375px&#34; data-flex-grow=&#34;156&#34; height=&#34;738&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/flashairdownloader-2/foundcard_hu_161662514a5c1345.png&#34; srcset=&#34;https://dotnet.work/blog/flashairdownloader-2/foundcard_hu_fded45859a3bddeb.png 800w, https://dotnet.work/blog/flashairdownloader-2/foundcard_hu_161662514a5c1345.png 1154w&#34; width=&#34;1154&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;After FAD found your card click onto &amp;ldquo;Auto Mode&amp;rdquo; to start automatic downloading of your data:&lt;/p&gt;&#xA;&lt;p&gt;At first the files are loaded recursively from your card. In the next step (if activated) the thumbnails gets downloaded. Then the files are being downloaded and deleted (if selected).&lt;/p&gt;&#xA;&lt;p&gt;&lt;img class=&#34;gallery-image&#34; data-flex-basis=&#34;436px&#34; data-flex-grow=&#34;181&#34; height=&#34;620&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/flashairdownloader-2/autocopy_hu_dd06e496e28f5a46.png&#34; srcset=&#34;https://dotnet.work/blog/flashairdownloader-2/autocopy_hu_c1dcdd4024d23a07.png 800w, https://dotnet.work/blog/flashairdownloader-2/autocopy_hu_dd06e496e28f5a46.png 1127w&#34; width=&#34;1127&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;If the thumbnails cannot be retrieved red squares are displayed instead.&lt;/p&gt;&#xA;&lt;h3 id=&#34;file-explorer&#34;&gt;&lt;a href=&#34;#file-explorer&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;File Explorer&#xA;&lt;/h3&gt;&lt;p&gt;The file explorer does not download automatically images from the Flashair. Instead you can browse through your card and copy files manually. Just mark them by clicking and select the [&amp;laquo;] or [&amp;raquo;] - Button to copy them#&lt;/p&gt;&#xA;&lt;p&gt;&lt;img class=&#34;gallery-image&#34; data-flex-basis=&#34;438px&#34; data-flex-grow=&#34;182&#34; height=&#34;617&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/flashairdownloader-2/markedright_hu_4048b87f5c92571d.png&#34; srcset=&#34;https://dotnet.work/blog/flashairdownloader-2/markedright_hu_30bd2edf0ffe4317.png 800w, https://dotnet.work/blog/flashairdownloader-2/markedright_hu_4048b87f5c92571d.png 1127w&#34; width=&#34;1127&#34;&gt;&lt;/p&gt;&#xA;&lt;h3 id=&#34;known-issues&#34;&gt;&lt;a href=&#34;#known-issues&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Known issues&#xA;&lt;/h3&gt;&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.youtube.com/watch?v=PtXtIivRRKQ&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;https://www.youtube.com/watch?v=PtXtIivRRKQ&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt; &lt;/p&gt;&#xA;&lt;p&gt;The FlashAir is sometimes a bit unstable (at least mine). There are a lot of reasons why something goes wrong. That&amp;rsquo;s why FAD tries up to five times to download or upload data. (Just Thumbnails are only tried once). There are many reasons why a download does not work. There can be a timeout, the Card can &amp;ldquo;crash&amp;rdquo; (it is a webserver running on it) or the camera just goes into standby. So often the best solution is to try again and or restart the camera and re-enter the FlashAir.&lt;/p&gt;&#xA;&lt;h3 id=&#34;any-more-questions&#34;&gt;&lt;a href=&#34;#any-more-questions&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Any more questions?&#xA;&lt;/h3&gt;&lt;p&gt;Just look at the documentation on GitHub or open an issue there if you encounter any problems.&lt;/p&gt;&#xA;&lt;p&gt;Love it?&lt;/p&gt;&#xA;&lt;p&gt;Feel free to send some money to my Panama-Account&lt;/p&gt;&#xA;</description>
        </item><item>
            <title>Detecting Smartphone via Bluetooth on Linux (Raspberry Pi 2)</title>
            <link>https://dotnet.work/blog/detecting-smartphone-via-bluetooth-on-linux-raspberry-pi-2/</link>
            <pubDate>Sat, 27 Feb 2016 12:00:00 +0100</pubDate><author>dotnet@oles.cloud (Ole Albers)</author>
            <guid>https://dotnet.work/blog/detecting-smartphone-via-bluetooth-on-linux-raspberry-pi-2/</guid>
            <description>&lt;p&gt;After &lt;a class=&#34;link&#34; href=&#34;https://dotnet.work/blog/automate-your-synology-surveillancestation-with-some-simple-linux-commands/&#34; &gt;detecting my phone using WiFi&lt;/a&gt; (which does only work under Marshmallow while the phone is loading or the display is enabled) and &lt;a class=&#34;link&#34; href=&#34;https://dotnet.work/blog/tracking-fitbit-presence-under-linux-raspberry-pi-2/&#34; &gt;detecting the fitbit device&lt;/a&gt; I came back to my original task to detect my phone using Bluetooth.&lt;/p&gt;&#xA;&lt;p&gt; &lt;/p&gt;&#xA;&lt;p&gt;While there are many documents out there that describe this on different Linux and even raspberry variants none of those really worked for me. The raspberry 2 (Debian Jessie) seem to be a bit different there.&lt;/p&gt;&#xA;&lt;p&gt;First of all you need a Bluetooth USB dongle. I buyed the Logilink BT00015 (available at &lt;a class=&#34;link&#34; href=&#34;http://www.amazon.com/gp/product/B0096Y2HFW/ref=as_li_tl?ie=UTF8&amp;amp;camp=1638&amp;amp;creative=19454&amp;amp;creativeASIN=B0096Y2HFW&amp;amp;linkCode=as2&amp;amp;tag=derstammti-21&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;amazon.com&lt;/a&gt;, &lt;a class=&#34;link&#34; href=&#34;http://www.amazon.de/gp/product/B0096Y2HFW/ref=as_li_tl?ie=UTF8&amp;amp;camp=1638&amp;amp;creative=19454&amp;amp;creativeASIN=B0096Y2HFW&amp;amp;linkCode=as2&amp;amp;tag=derstammti-21&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;amazon.de&lt;/a&gt; or elsewhere). I would expect that others work, too. But as always: On Linux, drivers can be tricky and it&amp;rsquo;s always good to know working hardware.&lt;/p&gt;&#xA;&lt;p&gt;Before installing drivers it is always a good idea to update everything:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;sudo apt-get update&#xA;sudo apt-get upgrade&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then install the libraries needed for bluetooth&lt;/p&gt;&#xA;&lt;p&gt;sudo apt-get install &amp;ndash;no-install-recommends bluetooth&lt;/p&gt;&#xA;&lt;p&gt;I received an error that some dependencies could not be resolved. I fixed that by entering the following commands:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;sudo dpkg --configure -a&#xA;sudo apt-get install -f&#xA;`Now insert the USB dongle and restart the raspberry`&#xA;sudo shutdown -r now&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To check if the USB-dongle is detected,  enter the following command:&lt;/p&gt;&#xA;&lt;p&gt;lsusb&lt;/p&gt;&#xA;&lt;p&gt;This lists all USB devices. There should be one offering Bluetooth - services&lt;/p&gt;&#xA;&lt;p&gt;Now lets check if the Bluetooth itself works:&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;/etc/init.d/bluetooth status&lt;/code&gt;&#xA;The response should now contain the word &amp;ldquo;Running&amp;rdquo;:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;pi@raspberrypi:~ $ /etc/init.d/bluetooth status&#xA;● bluetooth.service - Bluetooth service&#xA; Loaded: loaded (/lib/systemd/system/bluetooth.service; enabled)&#xA; Active: active (running) since Sat 2016-02-27 19:31:45 UTC; 17min ago&#xA; Docs: man:bluetoothd(8)&#xA; Main PID: 7841 (bluetoothd)&#xA; Status: &amp;#34;Running&amp;#34;&#xA; CGroup: /system.slice/bluetooth.service&#xA; └─7841 /usr/lib/bluetooth/bluetoothd&#xA;`Now it&amp;#39;s time to check for devices. Do nothing with your phone right now and enter:`&#xA;hcitool scan&#xA;`This should display all Bluetooth-devices in Range. As you haven&amp;#39;t done anything with your phone yet it should not be displayed there. The output should be something like this:`&#xA;Scanning ...&#xA; 7C:2F:BE:EF:FA:CE Ole&#xA; 00:04:BE:EF:FA:CE SHIELD&#xA; B8:86:BE:EF:FA:CE Fette Glotze&#xA; 88:53:BE:EF:FA:CE TOMATO-PC&#xA; 48:44:BE:EF:FA:CE TVBluetooth&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt; &lt;/p&gt;&#xA;&lt;p&gt;(I modified the MAC addresses. I am not absolutely sure if anyone could do evil things using the MACs, but I prefer not to find that out :) )&lt;/p&gt;&#xA;&lt;p&gt;Now put your phone into discovery mode. On Android Marshmallow you just have to enter your Bluetooth-settings for this. Then re-scan your devices. There should be one more, now:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;hcitool scan&#xA;&#xA;7C:2F:BE:EF:FA:CE Ole&#xA;00:04:BE:EF:FA:CE SHIELD&#xA;B8:86:BE:EF:FA:CE Fette Glotze&#xA;88:53:BE:EF:FA:CE TOMATO-PC&#xA;48:44:BE:EF:FA:CE TVBluetooth&#xA;&#xA;F8:95:C7:H0:0H:0H G4&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now just note your Mac-Address. We will &lt;strong&gt;not&lt;/strong&gt; pair our device. That takes a few steps and additional packages and additional problems. Pairing is not needed once we know the mac-address.&lt;/p&gt;&#xA;&lt;p&gt;Now we cam just check the presence by using the info command. Simply leave your Bluetooth-settings on the phone and enter&#xA;&lt;code&gt;sudo hcitool info F8:95:C7:H0:0H:0H&lt;/code&gt;&#xA;Event though your phone isn&amp;rsquo;t in discovery-mode anymore you will get some status information like the device name:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Requesting information ...&#xA; BD Address: F8:95:C7:H0:0H:0H&#xA; Device Name: G4&#xA; LMP Version: 4.1 (0x7) LMP Subversion: 0x6109&#xA; Manufacturer: Broadcom Corporation (15)&#xA;`if you turn off Bluetooth on your phone and re-enter that command you will receive a different response:`&#xA;Requesting information ...&#xA;Can&amp;#39;t create connection: Input/output error&#xA;`So that&amp;#39;s it. Just use the good old grep in your bash-script to check whether the phone is there or not:`&#xA;if hcitool info F8:95:C7:H0:0H:0H | grep -q &amp;#39;Device Name&amp;#39;; then&#xA; // Do whatever you like if you found it&#xA;fi&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Again: Have fun :)&lt;/p&gt;&#xA;</description>
        </item><item>
            <title>Tracking fitbit presence under Linux (Raspberry Pi 2)</title>
            <link>https://dotnet.work/blog/tracking-fitbit-presence-under-linux-raspberry-pi-2/</link>
            <pubDate>Sat, 27 Feb 2016 12:00:00 +0100</pubDate><author>dotnet@oles.cloud (Ole Albers)</author>
            <guid>https://dotnet.work/blog/tracking-fitbit-presence-under-linux-raspberry-pi-2/</guid>
            <description>&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://dotnet.work/blog/automate-your-synology-surveillancestation-with-some-simple-linux-commands/&#34; &gt;As I wrote before&lt;/a&gt; I track the presence of my smartphone using WiFi. Since Android Marshmellow this doesn&amp;rsquo;t work anymore, because when switching to idle mode, the WiFi is disconnected. The easiest way to get around this issue would be to use Bluetooth instead. And in fact: I already decided to go that way. But then my company gave me a nice present: A &amp;ldquo;Fitbit Charge HR&amp;rdquo;. While my company assured, that the HR is not for Human Resource and does not try to track if I am programming enough I decided to give this a try.&lt;/p&gt;&#xA;&lt;p&gt;Being a nerd I am not that much into that &amp;ldquo;fitness&amp;rdquo;-area. But I like the device as a small watch and the vibration alarm is nice too. I will surely sync this with my Appointments lateron. Getting some information about how many steps it takes to get the beer out of the cellar or the chips out of the kitchen is a nice benefit. Not really useful. But at least nerdy :)&lt;/p&gt;&#xA;&lt;p&gt;So I plan to wear this device for a while. So it should be a good idea to use it for tracking my presence. Maybe in combination with a phone-detection as a fall-back. First I tried to use a standard USB-dongle to detect it. But as this device works on Bluetooth Low Energy (BLE) it cannot be detected with normal Bluetooth-Dongles.  So&#xA;&lt;code&gt;hcitool scan&lt;/code&gt;&#xA;or even&#xA;&lt;code&gt;hcitool lescan&lt;/code&gt;&#xA;did not receive any results. Luckily the Fitbit ships with an USB-dongle. And even better, there is a package called &amp;ldquo;&lt;a class=&#34;link&#34; href=&#34;https://bitbucket.org/benallard/galileo&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;galileo&lt;/a&gt;&amp;rdquo; which purpose is to sync the fitbit. Installation is easy:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;sudo apt-get install python-usb python-requests&#xA;sudo pip install galileo&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now install the dongle and restart the raspberry.&lt;/p&gt;&#xA;&lt;p&gt;If you enter the following command&#xA;&lt;code&gt;sudo galileo -v&lt;/code&gt;&#xA;you should see something like&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;2016-02-27 16:28:21,875:INFO: 1 trackers discovered&lt;/code&gt;&#xA;All you have to do now to check if the fitbit is in range automaticly is some bash-magic:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;if galileo -v | grep -q &amp;#39;1 trackers&amp;#39;; then&#xA;  // Do whatever you like if you found it&#xA;fi&#xA;&lt;/code&gt;&lt;/pre&gt;</description>
        </item><item>
            <title>Control your Harmony Hub with a Raspberry Pi (Linux)</title>
            <link>https://dotnet.work/blog/control-your-harmony-hub-with-a-raspberry-pi-linux/</link>
            <pubDate>Fri, 15 Jan 2016 12:00:00 +0100</pubDate><author>dotnet@oles.cloud (Ole Albers)</author>
            <guid>https://dotnet.work/blog/control-your-harmony-hub-with-a-raspberry-pi-linux/</guid>
            <description>&lt;img src=&#34;https://dotnet.work/blog/control-your-harmony-hub-with-a-raspberry-pi-linux/hh1_hu_8bb48e2bca1c02c6.png&#34; alt=&#34;Featured image of post Control your Harmony Hub with a Raspberry Pi (Linux)&#34; /&gt;&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://dotnet.work/blog/automate-your-synology-surveillancestation-with-some-simple-linux-commands/&#34; &gt;So my surveillance cam now only records when I am not at home&lt;/a&gt;. But what if I forget to turn off the TV? That should happen automaticly, too! But fear not: There is a solution - at least if you own a harmony hub. With the help of the &amp;ldquo;HarmonyHubControl&amp;rdquo; - tool (written in c++) this is done quite easily.&lt;/p&gt;&#xA;&lt;p&gt;First of all you need to put the source onto the Raspberry. Don&amp;rsquo;t be afraid. No rocket science here. All needed tools should be already on your Linux distribution. The easiest way would be to clone the source via git:&#xA;&lt;code&gt;git clone git://git.code.sf.net/p/harmonyhubcontrol/code harmonyhubcontrol-code&lt;/code&gt;&#xA;If you prefer you may - of course - also use the long way and download those files in a zip file.&lt;/p&gt;&#xA;&lt;p&gt;Now after you have the source code on your Pi you have to compile it. Done with ease. Just a single&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;make&lt;/code&gt;&#xA;and you created the file &amp;ldquo;HarmonyHubControl&amp;rdquo; in that directory. Now all you need to know is the IP-address from your hub. You usually can find it in your WiFi-Router under the name &amp;ldquo;HarmonyHub&amp;rdquo;&lt;/p&gt;&#xA;&lt;p&gt;Let&amp;rsquo;s try if the connection works:&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;./HarmonyHubControl \[Login\] \[Password\] \[Ip\]&lt;/code&gt;&#xA;&lt;strong&gt;Warning!&lt;/strong&gt; Double-check your password. If you enter a wrong one, you can still stert the activities, but on the next login in your &amp;ldquo;MyHarmony&amp;rdquo;-application you will be thrown out for a while. Don&amp;rsquo;t ask why this works this way. Better ask Logitech instead.&lt;/p&gt;&#xA;&lt;p&gt;The Login is your email-address. If username and password are right you should see the following result: &lt;img alt=&#34;hh1&#34; class=&#34;gallery-image&#34; data-flex-basis=&#34;1720px&#34; data-flex-grow=&#34;716&#34; height=&#34;48&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/control-your-harmony-hub-with-a-raspberry-pi-linux/hh1_hu_8bb48e2bca1c02c6.png&#34; width=&#34;344&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;So let&amp;rsquo;s list all activities:&#xA;&lt;code&gt;./HarmonyHubControl \[Login\] \[Password\] \[Ip\] list\_activies&lt;/code&gt;&#xA;&lt;img alt=&#34;HH2&#34; class=&#34;gallery-image&#34; data-flex-basis=&#34;388px&#34; data-flex-grow=&#34;161&#34; height=&#34;213&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/control-your-harmony-hub-with-a-raspberry-pi-linux/HH2_hu_a6c3acb4df58670a.png&#34; width=&#34;345&#34;&gt; As you can see there is an activity with the id &amp;ldquo;-1&amp;rdquo;. This activity shuts down all devices. For testing let&amp;rsquo;s just start an activity:&#xA;&lt;code&gt;./HarmonyHubControl \[Login\] \[Password\] \[Ip\] start\_activity 15231836&lt;/code&gt;&#xA;does start the FireTV in my case,&#xA;&lt;code&gt;./HarmonyHubControl \[Login\] \[Password\] \[Ip\] start\_activity -1&lt;/code&gt;&#xA;does shut em all down.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img alt=&#34;hh3&#34; class=&#34;gallery-image&#34; data-flex-basis=&#34;3015px&#34; data-flex-grow=&#34;1256&#34; height=&#34;85&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/control-your-harmony-hub-with-a-raspberry-pi-linux/hh3-1_hu_a3fe2e1f9d30a0e1.png&#34; srcset=&#34;https://dotnet.work/blog/control-your-harmony-hub-with-a-raspberry-pi-linux/hh3-1_hu_c0131141837dabc1.png 800w, https://dotnet.work/blog/control-your-harmony-hub-with-a-raspberry-pi-linux/hh3-1_hu_a3fe2e1f9d30a0e1.png 1068w&#34; width=&#34;1068&#34;&gt; If you want to, do some fancy stuff with that possibilities. Use your stereo as an alarm clock? Have fun! (your neighbours, too, btw) :)&lt;/p&gt;&#xA;</description>
        </item><item>
            <title>Switching the LED of a surveillance cam on and off using Linux</title>
            <link>https://dotnet.work/blog/switching-the-led-of-a-surveillance-cam-on-and-off-using-linux/</link>
            <pubDate>Fri, 15 Jan 2016 12:00:00 +0100</pubDate><author>dotnet@oles.cloud (Ole Albers)</author>
            <guid>https://dotnet.work/blog/switching-the-led-of-a-surveillance-cam-on-and-off-using-linux/</guid>
            <description>&lt;img src=&#34;https://dotnet.work/blog/switching-the-led-of-a-surveillance-cam-on-and-off-using-linux/konsole_hu_e820623820643dd.png&#34; alt=&#34;Featured image of post Switching the LED of a surveillance cam on and off using Linux&#34; /&gt;&lt;p&gt;I already wrote a script on my Raspberry &lt;a class=&#34;link&#34; href=&#34;https://dotnet.work/blog/automate-your-synology-surveillancestation-with-some-simple-linux-commands/&#34; &gt;that starts and stops the surveillance mode&lt;/a&gt; of my camera using the &amp;ldquo;Surveillance Station&amp;rdquo; on the Synology DiskStation. Now I wanted to extend that tool and light the LEDs of the cam when it is recording and turning them off if it isn&amp;rsquo;t.&lt;/p&gt;&#xA;&lt;p&gt;This has two reasons: First: I do not want to use the cam for &amp;ldquo;secret recordings&amp;rdquo;, damaging the private spheres of my visitors. Second: I can easily see if the cam is working as expected. Now my cam has the option to activate the LEDs or deactivate from a web interface, but it is all-or-nothing. It does not depend if it is recording or not. Which isn&amp;rsquo;t a surprise as the camera does not trigger the recordings itself but this is done by the Surveillance Station.&lt;/p&gt;&#xA;&lt;p&gt;I own (and use) the &amp;ldquo;Robocam 4&amp;rdquo;, sold by 7Links. But this hardware is quite common and used under different names, too.&lt;/p&gt;&#xA;&lt;p&gt;Finding out the right commands is quite straight-forward: Open the webinterface and listen what happens there using the developer console in Chrome:&lt;/p&gt;&#xA;&lt;p&gt;&lt;img alt=&#34;konsole&#34; class=&#34;gallery-image&#34; data-flex-basis=&#34;660px&#34; data-flex-grow=&#34;275&#34; height=&#34;296&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/switching-the-led-of-a-surveillance-cam-on-and-off-using-linux/konsole_hu_e820623820643dd.png&#34; srcset=&#34;https://dotnet.work/blog/switching-the-led-of-a-surveillance-cam-on-and-off-using-linux/konsole_hu_c4fbcf0a1e4c0823.png 800w, https://dotnet.work/blog/switching-the-led-of-a-surveillance-cam-on-and-off-using-linux/konsole_hu_e820623820643dd.png 814w&#34; width=&#34;814&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;The &amp;ldquo;Request URL&amp;rdquo; and &amp;ldquo;Form Data&amp;rdquo; are the most interesting informations here. Before entering that stuff into the Linux-shell let&amp;rsquo;s test this with a more &amp;ldquo;comfortable&amp;rdquo; interface. The chrome extension &amp;ldquo;&lt;a class=&#34;link&#34; href=&#34;https://chrome.google.com/webstore/detail/postman/fhbjgbiflinjbdggehcddcbncdddomop&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;Postman&lt;/a&gt;&amp;rdquo; offers a nice way to play around with POST-Requests. The setting should be &amp;ldquo;x-www-form-urlencoded&amp;rdquo; here:&lt;/p&gt;&#xA;&lt;p&gt;&lt;img alt=&#34;postman&#34; class=&#34;gallery-image&#34; data-flex-basis=&#34;573px&#34; data-flex-grow=&#34;238&#34; height=&#34;232&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/switching-the-led-of-a-surveillance-cam-on-and-off-using-linux/postman_hu_7ecfe6389cbb2caa.png&#34; width=&#34;554&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt; &lt;/p&gt;&#xA;&lt;p&gt;If everything works as desired it is time to put that command to Linux using shell commands. Especially easy if the come does not only accept POST-requests, but GET-requests, too.&lt;/p&gt;&#xA;&lt;p&gt;http://192.168.0.88/admin/system.cgi?led=2&lt;/p&gt;&#xA;&lt;p&gt;worked like a charm in my case.&lt;/p&gt;&#xA;&lt;p&gt;The only thing that&amp;rsquo;s left is the authentifivcation. If you receive a classic login-screen like from a .htaccess password-protection it can be easily added to the url. So&#xA;&lt;code&gt;wget http://username:password@192.168.0.88/admin/system.cgi?led=2&lt;/code&gt;&#xA;turns on the LEDs&#xA;&lt;code&gt;wget http://username:password@192.168.0.88/admin/system.cgi?led=0&lt;/code&gt;&#xA;turns them off&lt;/p&gt;&#xA;&lt;p&gt;(0=off, 1=on, 2=blinking)&lt;/p&gt;&#xA;&lt;p&gt; &lt;/p&gt;&#xA;&lt;p&gt;Just add these lines to your existing script and the LEDs should be work just like needed :)&lt;/p&gt;&#xA;</description>
        </item><item>
            <title>Automate your Synology Surveillancestation with some simple Linux - commands</title>
            <link>https://dotnet.work/blog/automate-your-synology-surveillancestation-with-some-simple-linux-commands/</link>
            <pubDate>Thu, 07 Jan 2016 12:00:00 +0100</pubDate><author>dotnet@oles.cloud (Ole Albers)</author>
            <guid>https://dotnet.work/blog/automate-your-synology-surveillancestation-with-some-simple-linux-commands/</guid>
            <description>&lt;img src=&#34;https://dotnet.work/blog/automate-your-synology-surveillancestation-with-some-simple-linux-commands/reserveip_hu_ec0476895da7cc0d.png&#34; alt=&#34;Featured image of post Automate your Synology Surveillancestation with some simple Linux - commands&#34; /&gt;&lt;p&gt;I own a Synology diskstation which has the &amp;ldquo;surveillance station&amp;rdquo; on board which records content from any LAN (or WiFi)-enabled webcam and sends an alarm when someone is moving at my home. It is possible to enter time schedules when the camera should be enabled and when it shouldn&amp;rsquo;t. But that does not really work the way I wanted. When I come home earlier or even have a day off I have to disable the camera manually.&lt;/p&gt;&#xA;&lt;p&gt;So I decided that I want to use my smartphone to trigger the surveillance mode. Like most of us I always carry the phone with me. So it should be easy: If the phone is at home, I am, too. So the camera should be off. The same way when I am not at home.&lt;/p&gt;&#xA;&lt;p&gt;The solution for this was surprisingly simple: A small bash-script first checks if the phone is in the network. The Surveillance station cam gets enabled or disabled according to this one condition.&lt;/p&gt;&#xA;&lt;p&gt;But first things first. First of all, you should go to your routers admin interface and make sure, your phone will always have the same IP in your local WiFi. This works different on any reouter. On TP-Link it is called &amp;ldquo;reserve Ip&amp;rdquo;:&lt;img alt=&#34;Reserve IP TPLink&#34; class=&#34;gallery-image&#34; data-flex-basis=&#34;316px&#34; data-flex-grow=&#34;131&#34; height=&#34;420&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/automate-your-synology-surveillancestation-with-some-simple-linux-commands/reserveip_hu_ec0476895da7cc0d.png&#34; width=&#34;553&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt; &lt;/p&gt;&#xA;&lt;p&gt; &lt;/p&gt;&#xA;&lt;p&gt;Now all you need is a Linux machine. I took a fresh Raspberry for this goal. Any simple distribution should be fine. We won&amp;rsquo;t need any special libraries.&lt;/p&gt;&#xA;&lt;p&gt;After you connected to your Raspberry (or other Linux) create the following shell script via vi (or any other tool you prefer):&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;user=&amp;#39;pi&amp;#39;&#xA;pass=&amp;#39;circle&amp;#39;&#xA;server=&amp;#39;sauger:5000&amp;#39;&#xA;phone=&amp;#39;192.168.0.7&amp;#39;&#xA;&#xA;dt=$(date &amp;#39;+%d/%m/%Y %H:%M:%S&amp;#39;);&#xA;cd $(dirname $0)&#xA;touch lgstate.txt&#xA;read oldValue &amp;lt; lgstate.txt&#xA;if ping -c 3 ${phone} | grep &amp;#39;bytes from&amp;#39;; then&#xA;  if \[ $oldValue != &amp;#39;isda&amp;#39; \]; then&#xA;    echo \[${dt}\] &amp;#39;stop recording&amp;#39;&#xA;    echo &amp;#39;isda&amp;#39; &amp;gt;lgstate.txt&#xA;    #login&#xA;    wget -q --keep-session-cookies --save-cookies cookies.txt -O- &amp;#34;http://${server}/webapi/auth.cgi?api=SYNO.API.Auth&amp;amp;method=Login&amp;amp;version=3&amp;amp;account=${user}&amp;amp;passwd=${pass}&amp;#34;&#xA;    #disable&#xA;    wget -q --load-cookies cookies.txt -O- &amp;#34;http://${server}/webapi/entry.cgi?api=SYNO.SurveillanceStation.Camera&amp;amp;method=Disable&amp;amp;version=3&amp;amp;cameraIds=1&amp;#34; &amp;gt;dev&amp;gt;null&#xA;    #logout&#xA;    wget -q --load-cookies cookies.txt -O- &amp;#34;http://${server}/webapi/auth.cgi?api=SYNO.API.Auth&amp;amp;method=Logout&amp;amp;version=3&amp;#34; &amp;gt;dev&amp;gt;null&#xA;  else&#xA;    echo \[${dt}\] &amp;#39;still there...&amp;#39;&#xA;  fi&#xA;else&#xA;  if \[ $oldValue != &amp;#39;iswech&amp;#39; \]; then&#xA;    echo \[${dt}\] &amp;#39;start recording&amp;#39;&#xA;    echo &amp;#39;iswech&amp;#39; &amp;gt;lgstate.txt&#xA;    #login&#xA;    wget -q --keep-session-cookies --save-cookies cookies.txt -O- &amp;#34;http://${server}/webapi/auth.cgi?api=SYNO.API.Auth&amp;amp;method=Login&amp;amp;version=3&amp;amp;account=${user}&amp;amp;passwd=${pass}&amp;#34;&#xA;    #enable&#xA;    wget -q --load-cookies cookies.txt -O- &amp;#34;http://${server}/webapi/entry.cgi?api=SYNO.SurveillanceStation.Camera&amp;amp;method=Enable&amp;amp;version=3&amp;amp;cameraIds=1&amp;#34; &amp;gt;dev&amp;gt;null&#xA;    #logout&#xA;    wget -q --load-cookies cookies.txt -O- &amp;#34;http://${server}/webapi/auth.cgi?api=SYNO.API.Auth&amp;amp;method=Logout&amp;amp;version=3&amp;#34; &amp;gt;dev&amp;gt;null&#xA;  else&#xA;    echo \[${dt}\] &amp;#39;still noone at home&amp;#39;&#xA;  fi&#xA;fi&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Change &amp;ldquo;user&amp;rdquo; and &amp;ldquo;pass&amp;rdquo; in the beginning of the script with your user credentials. You can create a new user for this or use the &amp;ldquo;admin&amp;rdquo; - user. The user has to have admin rights sadly so don&amp;rsquo;t use this script on any machine that can be accessed by anyone but you.&lt;/p&gt;&#xA;&lt;p&gt;&amp;ldquo;server&amp;rdquo; is your server address enter the hostname (or ip) and port there&lt;/p&gt;&#xA;&lt;p&gt;&amp;ldquo;phone&amp;rdquo; is your phone&amp;rsquo;s IP-address&lt;/p&gt;&#xA;&lt;p&gt;So what does this script do?&lt;/p&gt;&#xA;&lt;p&gt;First it tries to ping your smartphone. If this is successful, it checks if this is the first time that works. If it isn&amp;rsquo;t the first time, nothing has to be done. But if the phone wasn&amp;rsquo;t accessable before it logs onto the synology and disables the camera using the web api.&lt;/p&gt;&#xA;&lt;p&gt;The same way it enables the camera if the phone cannot be contacted anymore.&lt;/p&gt;&#xA;&lt;p&gt;The script does only minimum outputs. If you run into problems, just remove the &amp;ldquo;&amp;gt;dev&amp;gt;null&amp;rdquo;  part on any line to get more information.&lt;/p&gt;&#xA;&lt;p&gt;Before you can start the script you should create an empty file where the state is saved by entereing&#xA;&lt;code&gt;touch lgstate.txt&lt;/code&gt;&#xA;You can run that script by entering &amp;ldquo;./pingPhone.sh&amp;rdquo; on the command line. You can watch the changes live in the Surveillance Station -View of the Synology. Changes are displayed instantly. You do not need to refresh the page.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img alt=&#34;cam1&#34; class=&#34;gallery-image&#34; data-flex-basis=&#34;426px&#34; data-flex-grow=&#34;177&#34; height=&#34;539&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/automate-your-synology-surveillancestation-with-some-simple-linux-commands/cam1_hu_51d66631dbf7787a.png&#34; srcset=&#34;https://dotnet.work/blog/automate-your-synology-surveillancestation-with-some-simple-linux-commands/cam1_hu_c4b0e092f271b69f.png 800w, https://dotnet.work/blog/automate-your-synology-surveillancestation-with-some-simple-linux-commands/cam1_hu_51d66631dbf7787a.png 957w&#34; width=&#34;957&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;If you have more than one camera change the value of &amp;ldquo;cameraIds&amp;rdquo; to another value that is needed. You also can select multiple cameras by using commas: &amp;ldquo;&amp;amp;cameraIds=1,4,5&amp;rdquo;&lt;/p&gt;&#xA;&lt;p&gt;Finally if you want to run this script automaticly you can create a simple cronjob for it&lt;/p&gt;&#xA;&lt;p&gt;open&#xA;&lt;code&gt;crontab -e&lt;/code&gt;&#xA;and enter the following line:&#xA;&lt;code&gt;\*/15 \* \* \* \* /home/pi/pingPhone.sh &amp;gt;&amp;gt;/home/pi/pingPhone.log&lt;/code&gt;&#xA;(of course the path may differ on your system)&lt;/p&gt;&#xA;&lt;p&gt;Now the presence will be checked every 15 minutes and the cameras will enabled or disabled accordingly. Now all you have to do is to modify your time schedule in the surveillance station that the camera is always in detection mode.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img alt=&#34;cam2&#34; class=&#34;gallery-image&#34; data-flex-basis=&#34;443px&#34; data-flex-grow=&#34;184&#34; height=&#34;518&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/automate-your-synology-surveillancestation-with-some-simple-linux-commands/cam2_hu_873d5afd6c8b24c9.png&#34; srcset=&#34;https://dotnet.work/blog/automate-your-synology-surveillancestation-with-some-simple-linux-commands/cam2_hu_15b29fbd01379668.png 800w, https://dotnet.work/blog/automate-your-synology-surveillancestation-with-some-simple-linux-commands/cam2_hu_873d5afd6c8b24c9.png 957w&#34; width=&#34;957&#34;&gt;&lt;/p&gt;&#xA;</description>
        </item><item>
            <title>SCRUM and Git: Optimized Gitflow // with a lot of fancy colors :)</title>
            <link>https://dotnet.work/blog/scrum-and-git-optimized-gitflow-with-a-lot-of-fancy-colors/</link>
            <pubDate>Wed, 04 Mar 2015 12:00:00 +0100</pubDate><author>dotnet@oles.cloud (Ole Albers)</author>
            <guid>https://dotnet.work/blog/scrum-and-git-optimized-gitflow-with-a-lot-of-fancy-colors/</guid>
            <description>&lt;img src=&#34;https://dotnet.work/blog/scrum-and-git-optimized-gitflow-with-a-lot-of-fancy-colors/gitflow1_hu_ef8620f4237a401d.png&#34; alt=&#34;Featured image of post SCRUM and Git: Optimized Gitflow // with a lot of fancy colors :)&#34; /&gt;&lt;p&gt;For a lot of teams, GIT is the first choice for maintaining the code. So it is for mine. But this post isn&amp;rsquo;t limited to GIT and should work with SVN or TFS with little modifications, too.&lt;/p&gt;&#xA;&lt;h2 id=&#34;why-is-the-git-flow-important-when-using-scrum&#34;&gt;&lt;a href=&#34;#why-is-the-git-flow-important-when-using-scrum&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Why is the Git flow important when using SCRUM?&#xA;&lt;/h2&gt;&lt;p&gt;Aren&amp;rsquo;t those just two worlds, anyway?&lt;/p&gt;&#xA;&lt;p&gt;Well. Kind of. But while SCRUM is quite flexible, scrums paradigm still has some very basic contents when it comes to manage projects:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;You and your team work in sprints.&lt;/li&gt;&#xA;&lt;li&gt;Inside these sprints there are user-stories you want to implement.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;We decided to map sprints and user stories to our git flow.&lt;/p&gt;&#xA;&lt;h2 id=&#34;classic-git-flow&#34;&gt;&lt;a href=&#34;#classic-git-flow&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;Classic git flow:&#xA;&lt;/h2&gt;&lt;p&gt;You usually have the following structure in a git flow: (More fancy description and image at &lt;a class=&#34;link&#34; href=&#34;https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;Atlassian&lt;/a&gt;)&lt;/p&gt;&#xA;&lt;p&gt;&lt;img alt=&#34;classic git flow&#34; class=&#34;gallery-image&#34; data-flex-basis=&#34;492px&#34; data-flex-grow=&#34;205&#34; height=&#34;288&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/scrum-and-git-optimized-gitflow-with-a-lot-of-fancy-colors/gitflow1_hu_ef8620f4237a401d.png&#34; width=&#34;591&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;You always got your master branch. From that branch you create single branches for each Hotfix. Your daily work is done in feature branches that start at the development branch. When a feature is finished, you merge it into your development branch. For a release, the development branch (which contains the added features) is the root of a new Release-branch. That release is tested and maybe modified until it fits the needs of your customer. When you are happy with that release you merge it back into the development branch as well as the master branch.&lt;/p&gt;&#xA;&lt;h2 id=&#34;scrumified-git-flow&#34;&gt;&lt;a href=&#34;#scrumified-git-flow&#34; class=&#34;header-anchor&#34;&gt;&lt;/a&gt;&amp;ldquo;Scrumified&amp;rdquo; Git Flow:&#xA;&lt;/h2&gt;&lt;p&gt;&lt;img alt=&#34;scrum git flow&#34; class=&#34;gallery-image&#34; data-flex-basis=&#34;365px&#34; data-flex-grow=&#34;152&#34; height=&#34;388&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://dotnet.work/blog/scrum-and-git-optimized-gitflow-with-a-lot-of-fancy-colors/gitflow2_hu_22260977631a1eb5.png&#34; width=&#34;591&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;When trying to put SCRUM into the flow you keep your basic structure but add &lt;strong&gt;two additional layers&lt;/strong&gt; to it. The single features are not created directly from the development branch. Instead at first for each sprint a new branch is created from the development branch. From the sprint there are branches created from each user story. After you&amp;rsquo;ve done that, the features are created from the corresponding stories. Merging back goes the same way. You merge your features back into the user story and the user story back into the sprint before merging the sprint back into your development branch.&lt;/p&gt;&#xA;&lt;p&gt;This is a bit more work (and might look a little bit chaotic on these pictures at first) but it offers the advantages you have from &amp;ldquo;normal&amp;rdquo; git flow also to your sprint. Like before you only merge features that are finished. But now you also only merge user stories that are complete. And as a small bonus you can always analyze which user story was started and finished in which sprint. A Win-Win-situation like those unlikable marketing guys would call it. (If they were able to understand a single word I just wrote here) :)&lt;/p&gt;&#xA;</description>
        </item><item>
            <title>IMAP-HTML-Autoresponder with PHP</title>
            <link>https://dotnet.work/blog/imap-html-php-autoresponder-autoreply/</link>
            <pubDate>Fri, 13 Feb 2015 12:00:00 +0100</pubDate><author>dotnet@oles.cloud (Ole Albers)</author>
            <guid>https://dotnet.work/blog/imap-html-php-autoresponder-autoreply/</guid>
            <description>&lt;p&gt;Everyone knows spam. Everyone &lt;strong&gt;hates&lt;/strong&gt; spam. And everyone who has his email-address written on the net (for example in the NIC-records for a domain) gets &lt;strong&gt;a lot of&lt;/strong&gt; spam.&lt;/p&gt;&#xA;&lt;p&gt;I am one of those guys who gets a lot of spam. Of course there are anti-spam solutions out there, but the best would be to not even receive spam. And spam filters need a lot of work and / or do not filter 100% perfect. That is why I decided to use a public email that has to go through a &amp;ldquo;gatekeeper&amp;rdquo;. A PHP-script that tells everyone that this is not my real mail address, but that there is another one where I could be contacted.&lt;/p&gt;&#xA;&lt;p&gt;That email-address is not displayed in plain text but as an image to get rid of all those bots. SOME bots however should be able to get through: For example my hoster who needs to validate my email periodically. So these messages just are being forwarded.&lt;/p&gt;&#xA;&lt;p&gt;I wanted to be as simple as possible. That&amp;rsquo;s why this script does not need much more than plain php. Only a replacement for phps &amp;ldquo;mail&amp;rdquo; was needed.&lt;/p&gt;&#xA;&lt;p&gt;First step: Download swiftmailer. Just put the contents of the &amp;ldquo;lib&amp;rdquo; - directory onto your web space. That&amp;rsquo;s it. Ready to go.&lt;/p&gt;&#xA;&lt;p&gt;Simple include the following line to access swiftmailer:&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;require\_once &#39;./swift/lib/swift\_required.php&#39;;&lt;/code&gt;&#xA;To access your mail you need to enter the IMAP-Settings. POP would work, too, but IMAP would be much more useful because we do not want to download the email-body of the messages processed:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;// IMAP-Settings:&#xA; $hostname = &amp;#34;{imap.hostname.com:993/imap/ssl}INBOX&amp;#34;;&#xA; $username = &amp;#34;username&amp;#34;;&#xA; $password = &amp;#34;password&amp;#34;;&#xA;`Now enter your email-addresses. $targetMail is your real (&amp;#34;hidden&amp;#34;) email-address where valid emails should be forwarded to. $thisMail is the public address that receives the spam.`&#xA;$targetMail=&amp;#34;private@mydomain.com&amp;#34;; &#xA;$thisMail=&amp;#34;webmaster@mydomain.com&amp;#34;;&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Next you need to create an image with your favourite picture editing tool (GiMP, Paint.NET, PaintShopPro, etc.) This image should only contain your private email. Take a font you like. You might also use a &amp;ldquo;Captcha&amp;rdquo;-font. I - personally - hate that&amp;hellip; :)&lt;/p&gt;&#xA;&lt;p&gt;Upload that image to your web server and enter the url of that image. PNG would be a good choice.&lt;/p&gt;&#xA;&lt;p&gt;$image=&amp;ldquo;&lt;a class=&#34;link&#34; href=&#34;http://www.example.com/images/mail.png%22;&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;http://www.example.com/images/mail.png&#34;;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;Next, enter the text the sender should receive. By default everyone receives $templateHtml. For those who do not have the capabilities to display html (or don&amp;rsquo;t want to) a simple text-mail ($templatePlain) will be displayed. Users with a html-browser will see your email-image directly in the mail. Plain-text mail-clienst will display a link to the image instead.&lt;/p&gt;&#xA;&lt;p&gt;$templateForward defines the mail content YOU will see if a mail is forwarded to you from a valid sender&lt;/p&gt;&#xA;&lt;p&gt;So it is time to be creative :)&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$templateHtml=&amp;#34;&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;Thanks for your mail. Please note that this mail is no more. It has ceased to be! It is expired and gone to meet its maker! This is an Ex-Mail!&amp;lt;br/&amp;gt;But don&amp;#39;t be afraid, there is a replacement for that. Just send your mail to: &amp;lt;br/&amp;gt;&amp;lt;img src=\\&amp;#34;\_\_EMBED\_\_\\&amp;#34;/&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;If you cannot see that image: Click onto the following link to see my: &amp;lt;a href=\\&amp;#34;\_\_URL\_\_\\&amp;#34;&amp;gt;new mail-address&amp;lt;/a&amp;gt;&amp;lt;br/&amp;gt;And no: This mail will not be forwarded. No one will see this until you forward it to my new mail-address. Thanks!&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;#34;;&#xA;&#xA;$templatePlain=&amp;#34;Thanks for your mail. Please note that this mail is no more. It has ceased to be! It is expired and gone to meet its maker! This is an Ex-Mail!\\r\\nBut don&amp;#39;t be afraid, there is a replacement for that. Just click onto the following link to see my: new mail-address: \_\_URL\_\_&amp;#34;;&#xA;&#xA;$templateForward=&amp;#34;new message redirected&amp;#34;;&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As you can see there are two special tags in those templates. &amp;ldquo;__EMBED__&amp;rdquo; will be replaced with the embedded image (html-template only) and &amp;ldquo;__URL__&amp;rdquo; will contain a link to that image.&lt;/p&gt;&#xA;&lt;p&gt;The last variable you can define is the white-list. Everything entered there is interpreted as a valid sender. Enter multiple values seperated by commas.&#xA;&lt;code&gt;$whitelist = &amp;quot;uberspace,denic.de,domainbox.net,romrobot.com,inwx.de,millerntor.hamburg,dotnet.work&amp;quot;;&lt;/code&gt;&#xA;that&amp;rsquo;s all for configuration. Now just some explanation what the script does:&lt;/p&gt;&#xA;&lt;p&gt;First of all the script checks your IMAP-Server for unread messages. It collects the header of each of those messages and compares the sender with the white-list. If the sender is recognized, the mail body is downloaded and sent as an attachment to the mail address you defined as $targetMail.&lt;/p&gt;&#xA;&lt;p&gt;If the sender is not in the white-list, an automatic reply is generated using your html-template. It contains the image you downloaded as an attachment and embedded image of the mail. The last step is to mark the mail as read so it won&amp;rsquo;t be processed again.&lt;/p&gt;&#xA;&lt;p&gt;In a perfect world&amp;hellip;&lt;/p&gt;&#xA;&lt;p&gt;Everything works now. But you can (and will) get problems sending the mail to the sender if the sender&amp;rsquo;s address does not comply with RFC 2822. While most spammers DO comply, mom DOESN&amp;rsquo;T&amp;hellip; :) That&amp;rsquo;s because there is a mail client from a tiny little software company in Redmond, where they do not know much about standards. But this is no big problem at all. Just open &lt;em&gt;lib\classes\swift\Mime\Headers\MailboxHeader.php&lt;/em&gt; from swiftmailer and put &lt;em&gt;/* */&lt;/em&gt; around the throw-part of the last function:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;private function \_assertValidAddress($address)&#xA;{&#xA;   if (!preg\_match(&amp;#39;/^&amp;#39;.$this-&amp;gt;getGrammar()-&amp;gt;getDefinition(&amp;#39;addr-spec&amp;#39;).&amp;#39;$/D&amp;#39;, $address)) {&#xA;     /\* throw new Swift\_RfcComplianceException(&amp;#39;Address in mailbox given \[&amp;#39;.$address.&amp;#39;\] does not comply with RFC 2822, 3.6.2.&amp;#39;);\*/&#xA;   }&#xA;}&#xA;`So finally here is the complete script.`&#xA;&amp;lt;?php&#xA;   require\_once &amp;#39;./swift/lib/swift\_required.php&amp;#39;;// IMAP-Settings:&#xA;   $hostname = &amp;#34;{imap.example.com:993/imap/ssl}INBOX&amp;#34;;&#xA;   $username = &amp;#34;username&amp;#34;;&#xA;   $password = &amp;#34;password&amp;#34;; // Script-Settings:&#xA;   $targetMail=&amp;#34;private@example.com&amp;#34;; // &amp;#34;hidden&amp;#34; target mail-address&#xA;   $thisMail=&amp;#34;webmaster@example.com&amp;#34;; // &amp;#34;official&amp;#34; mail-address (which receives the spam) &#xA;   $image=&amp;#34;http://www.dotnet.work/reply/mail.png&amp;#34;; // Image to be shown&#xA;   $templateHtml=&amp;#34;&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;Thanks for your mail. Please note that this mail is no more. It has ceased to be! It is expired and gone to meet its maker! This is an Ex-Mail!&amp;lt;br/&amp;gt;But don&amp;#39;t be afraid, there is a replacement for that. Just send your mail to: &amp;lt;br/&amp;gt;&amp;lt;img src=\\&amp;#34;\_\_EMBED\_\_\\&amp;#34;/&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;If you cannot see that image: Click onto the following link to see my: &amp;lt;a href=\\&amp;#34;\_\_URL\_\_\\&amp;#34;&amp;gt;new mail-address&amp;lt;/a&amp;gt;&amp;lt;br/&amp;gt;And no: This mail will not be forwarded. No one will see this until you forward it to my new mail-address. Thanks!&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;#34;;&#xA;   $templatePlain=&amp;#34;Thanks for your mail. Please note that this mail is no more. It has ceased to be! It is expired and gone to meet its maker! This is an Ex-Mail!\\r\\nBut don&amp;#39;t be afraid, there is a replacement for that. Just click onto the following link to see my: new mail-address: \_\_URL\_\_&amp;#34;;&#xA;   $templateForward=&amp;#34;new message redirected&amp;#34;;&#xA;&#xA;   $whitelist = &amp;#34;uberspace,denic.de,domainbox.net,romrobot.com,inwx.de,millerntor.hamburg,dotnet.work&amp;#34;;&#xA; &#xA;   $allowedSenders=explode(&amp;#34;,&amp;#34;,$whitelist);&#xA;   $inbox = imap\_open($hostname,$username,$password) or die(&amp;#34;Sorry, connection failed&amp;#34;);&#xA;   $emails = imap\_search($inbox,&amp;#34;UNSEEN&amp;#34;);&#xA;   $count=0; &#xA;   echo &amp;#34;&amp;lt;pre&amp;gt;&amp;#34;; &#xA;   if($emails) {&#xA;     $transport = Swift\_MailTransport::newInstance();&#xA;     foreach($emails as $email) {&#xA;        $count++;&#xA;        $header = imap\_fetch\_overview($inbox,$email,0); &#xA;        $subject=$header\[0\]-&amp;gt;subject;&#xA;        $sender=$header\[0\]-&amp;gt;from;&#xA;&#xA;        $messageHeader =  imap\_fetchheader($inbox, 1)&#xA;        if (strpos($messageHeader,&amp;#34;X-RESPONDERDATE&amp;#34;)!==false) {&#xA;          continue; // no wild bouncing, please&#xA;        }&#xA;&#xA;        if (strpos($sender,$targetMail!==false || strpos($sender,$thisMail!==false) {&#xA;           // We want no loops... :)&#xA;           continue;&#xA;        }&#xA;        echo &amp;#34;retrieving &amp;#34;.$subject.&amp;#34; from &amp;#34;.$sender.&amp;#34;\\r\\n&amp;#34;;&#xA;        // Whitelist: &#xA;        $isWhiteListed=false;&#xA;      &#xA;        foreach($allowedSenders as $allowedSender) {&#xA;           if (stripos($sender,$allowedSender)!==false) {&#xA;              $isWhiteListed=true;&#xA;              break;&#xA;           }&#xA;        }&#xA; &#xA;        if ($isWhiteListed) {&#xA;           $email\_body = imap\_body($inbox, $email);&#xA;           $mailer = Swift\_Mailer::newInstance($transport);&#xA;           $message = Swift\_Message::newInstance()&#xA;              -&amp;gt;setSubject(&amp;#34;Fw:&amp;#34;.$subject)&#xA;              -&amp;gt;setFrom($sender)&#xA;              -&amp;gt;setTo($targetMail)&#xA;              -&amp;gt;setBody($templateForward)&#xA;           ;&#xA; &#xA;           $attachment = Swift\_Attachment::newInstance($email\_body, &amp;#39;origin.eml&amp;#39;, &amp;#39;message/rfc822&amp;#39;); &#xA;           $message-&amp;gt;attach($attachment);&#xA; &#xA;           $result = $mailer-&amp;gt;send($message);&#xA;           echo &amp;#34;whitelist redirect\\r\\n&amp;#34;;&#xA;        } else { &#xA;           $mailer = Swift\_Mailer::newInstance($transport);&#xA;           $message = Swift\_Message::newInstance();&#xA;           $swiftheaders = $message-&amp;gt;getHeaders();&#xA;           $swiftheaders-&amp;gt;addTextHeader(&amp;#39;X-RESPONDERDATE&amp;#39;, date(&amp;#39;Y-m-d H:i:s&amp;#39;));&#xA;           $cid = $message-&amp;gt;embed(Swift\_Image::fromPath($image));&#xA;           $html=str\_replace(&amp;#34;\_\_URL\_\_&amp;#34;,$image,$templateHtml);&#xA;           $html=str\_replace(&amp;#34;\_\_EMBED\_\_&amp;#34;,$cid,$html);&#xA;           $plain=str\_replace(&amp;#34;\_\_URL\_\_&amp;#34;,$image,$templatePlain);&#xA;           $message&#xA;              -&amp;gt;setSubject(&amp;#34;Re:&amp;#34;.$subject)&#xA;              -&amp;gt;setFrom($thisMail)&#xA;              -&amp;gt;setTo($sender) &#xA;              -&amp;gt;setBody($html, &amp;#39;text/html&amp;#39;)&#xA;              -&amp;gt;addPart($plain, &amp;#39;text/plain&amp;#39;)&#xA;           ;&#xA;           $result = $mailer-&amp;gt;send($message);&#xA;           echo &amp;#34;answered\\r\\n&amp;#34;;&#xA;        } &#xA;        imap\_setflag\_full($inbox, imap\_uid($inbox,$email), &amp;#34;\\\\Seen \\\\Flagged&amp;#34;, ST\_UID);&#xA;     } &#xA;  }&#xA;  imap\_close($inbox);&#xA;  echo &amp;#34;done redirecting &amp;#34;.$count.&amp;#34; mails\\r\\n&amp;#34;;&#xA;  echo &amp;#34;&amp;lt;/pre&amp;gt;&amp;#34;;&#xA;?&amp;gt;&#xA;`Just save this program as &amp;#34;reply.php&amp;#34;, send test-mails and open &amp;#34;reply.php&amp;#34; in your browser to test the settings. When everything works fine you can simply add a cronjob so this script is run automaticly. The following example runs the script every 5 minutes:`&#xA;\*/5 \* \* \* \* php /var/www/reply.php&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt; &lt;/p&gt;&#xA;&lt;p&gt;Just use the comments if you need any help&lt;/p&gt;&#xA;</description>
        </item><item>
            <title>FlashAir Downloader for Windows</title>
            <link>https://dotnet.work/blog/flashair-downloader-for-windows/</link>
            <pubDate>Thu, 25 Sep 2014 12:00:00 +0200</pubDate><author>dotnet@oles.cloud (Ole Albers)</author>
            <guid>https://dotnet.work/blog/flashair-downloader-for-windows/</guid>
            <description>&lt;blockquote class=&#34;alert alert-warning&#34;&gt;&#xA;        &lt;div class=&#34;alert-header&#34;&gt;&#xA;            &lt;span class=&#34;alert-icon&#34;&gt;⚠️&lt;/span&gt;&#xA;            &lt;span class=&#34;alert-title&#34;&gt;Warning&lt;/span&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div class=&#34;alert-body&#34;&gt;&#xA;            &lt;p&gt;&lt;strong&gt;Development stopped!&lt;/strong&gt; &lt;a class=&#34;link&#34; href=&#34;https://dotnet.work/blog/flashairdownloader-development-now-its-your-turn/&#34; &gt;Please read this&lt;/a&gt; before downloading FAD&lt;/p&gt;&#xA;        &lt;/div&gt;&#xA;    &lt;/blockquote&gt;&#xA;&lt;blockquote class=&#34;alert alert-note&#34;&gt;&#xA;        &lt;div class=&#34;alert-header&#34;&gt;&#xA;            &lt;span class=&#34;alert-icon&#34;&gt;📝&lt;/span&gt;&#xA;            &lt;span class=&#34;alert-title&#34;&gt;Note&lt;/span&gt;&#xA;        &lt;/div&gt;&#xA;        &lt;div class=&#34;alert-body&#34;&gt;&#xA;            &lt;p&gt;**New Version available!**This version is obsolete. I would strongly suggest to use the FAD2 instead. You can download it here: &lt;a class=&#34;link&#34; href=&#34;http://dotnet.work/fad2&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;http://dotnet.work/fad2&lt;/a&gt;.&lt;/p&gt;&#xA;        &lt;/div&gt;&#xA;    &lt;/blockquote&gt;&#xA;&lt;p&gt;Toshibas FlashAir is an SD-Card with built-in WiFi. It allows you to download Pictures from your DigiCam through WiFi. Sadly there is no working Windows-Application.&lt;/p&gt;&#xA;&lt;p&gt;The FlashAir-Downloader solves that issue. It allowes Bulk-Downloading of your images. Your pictures can be saved in folders created automaticly containing date-information.&lt;/p&gt;&#xA;&lt;p&gt;Installation:&lt;/p&gt;&#xA;&lt;p&gt;Unpack the Zip and start the Setup.Exe&lt;/p&gt;&#xA;&lt;p&gt;Start the Application and go to Settings:&lt;/p&gt;&#xA;&lt;!-- image missing: images/flash1_qn247z.png (flash1) --&gt;&#xA;&lt;p&gt;Adjust the Settings if needed. Especially for “local Path”.&lt;/p&gt;&#xA;&lt;p&gt;To use the Delete-Option you have to adjust the configuration of the SD-Card. Insert the SD-Card into your computer and open the file “CONFIG” file in the hidden “SD_WLAN”-folder with a text editor. Add the line&#xA;&lt;code&gt;UPLOAD=1&lt;/code&gt;&#xA;to the CONFIG-File.&lt;/p&gt;&#xA;&lt;p&gt;Thats it. Close that window, turn on your camera and click onto “CONNECT”. If the connection works, you should see the folders and images of your SD-Card. Enter a folder or preview an image by double-clicking onto the icon. Mark the images you want to download and start downloading by clicking onto “Download”.&lt;/p&gt;&#xA;&lt;p&gt;I give no guarantee about anything. This application shouldn’t do anything bad. But if your Camera explodes: Don’t blame me.This tool is and always will be free. But I don’t mind donations, of course :)&lt;/p&gt;&#xA;&lt;p&gt;— Update V 1.1 —&lt;/p&gt;&#xA;&lt;p&gt;I just updated the FlashAirDownloader to Version 1.1.&lt;/p&gt;&#xA;&lt;p&gt;The following has changed:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Bugfix: If the file date is quite old (&amp;lt;2000) it could not be downloaded. This has been fixed. [*Yeah! Year 2000 bug in 2014. Well done, Toshiba *🙄  😁  ]&lt;/li&gt;&#xA;&lt;li&gt;New Feature: You can now change the contents of the CONFIG – File directly. Just insert the SD-Card into your computer and Select “Card Properties &lt;a class=&#34;link&#34; href=&#34;http://res.cloudinary.com/dyzmmxp1s/image/upload/v1422648202/fadConfig_nvxiz4.png&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;&lt;!-- image missing: images/fadConfig_nvxiz4.png (fadConfig) --&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;New Feature: If you use more than just One FlashAir-Card you can now automatically create subfolders for each card. You can use the internal “CID” ID for that or the Application ID which may contain any custom name. &lt;!-- image missing: images/fadconfig2_arr8bm.png (fadconfig2) --&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;(Tested with Windows 7 and Windows 8.1)&lt;/p&gt;&#xA;&lt;p&gt;A new Version is available: &lt;a class=&#34;link&#34; href=&#34;http://dotnet.work/fad2&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;http://dotnet.work/fad2&lt;/a&gt;&lt;/p&gt;&#xA;</description>
        </item><item>
            <title>Emptying SharePoint-List the (quite) fast way</title>
            <link>https://dotnet.work/blog/emptying-sharepoint-list-the-quite-fast-way/</link>
            <pubDate>Fri, 10 Aug 2012 12:00:00 +0200</pubDate><author>dotnet@oles.cloud (Ole Albers)</author>
            <guid>https://dotnet.work/blog/emptying-sharepoint-list-the-quite-fast-way/</guid>
            <description>&lt;p&gt;I already offered a solution to &lt;a class=&#34;link&#34; href=&#34;http://www.stammtischphilosoph.com/2012/08/emptying-sharepoint-lists-the-ultra-fast-way/&#34;  title=&#34;Emptying SharePoint lists the ultra fast way&#34;&#xA;     target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;empty a SharePoint list at light speed&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;But this solution hast two big disadvantages:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;You cannot empty a list that has more entries thatn the allowed list view threshold (default is 5000)&lt;/li&gt;&#xA;&lt;li&gt;All Lookups will be lost&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;This solution has non of those drawbacks. It is much slower than that solution but also much faster than “normal” deletion of SPListItems or using a Caml-Query to do that.&lt;/p&gt;&#xA;&lt;p&gt;Just be asure that this is not thread-safe. So make sure noone is writing into that list in the same time or that lines might not be deleted.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;private int rest;&#xA;public static void TruncateList(Guid listId, int limit)&#xA;{&#xA;SPSecurity.RunWithElevatedPrivileges(delegate()&#xA;{&#xA;using (SPSite site = new SPSite(SPContext.Current.Site.ID))&#xA;{&#xA;using (SPWeb web = SPContext.Current.Web)&#xA;{&#xA;try&#xA;{&#xA;\_rest = limit;&#xA;web.AllowUnsafeUpdates = true;&#xA;SPList listCopy = web.Lists\[listId\];&#xA;&#xA;ContentIterator iterator = new ContentIterator();&#xA;iterator.ProcessListItems(listCopy, ProcessItem, ProcessError);&#xA;&#xA;web.Update();&#xA;web.AllowUnsafeUpdates = false;&#xA;}&#xA;catch (Exception ex)&#xA;{&#xA;if (!ex.Message.Contains(&amp;#34;Limit reached&amp;#34;))&#xA;{&#xA;Debug.WriteLine(ex);&#xA;}&#xA;&#xA;}&#xA;}&#xA;}&#xA;}&#xA;);&#xA;}&#xA;&#xA;public static bool ProcessError(SPListItem item, Exception ex)&#xA;{&#xA;if (!ex.Message.Contains(&amp;#34;Limit reached&amp;#34;))&#xA;{&#xA;Debug.WriteLine(ex);&#xA;}&#xA;&#xA;return true;&#xA;}&#xA;&#xA;public static void ProcessItem(SPListItem item)&#xA;{&#xA;if (rest&amp;lt;=0)&#xA;{&#xA;Debug.WriteLine(&amp;#34;Limit reached&amp;#34;);&#xA;throw new Exception(&amp;#34;Limit reached&amp;#34;);&#xA;return;&#xA;}&#xA;item.Delete();&#xA;\_rest–;&#xA;&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;</description>
        </item><item>
            <title>Emptying SharePoint lists the ultra fast way</title>
            <link>https://dotnet.work/blog/emptying-sharepoint-lists-the-ultra-fast-way/</link>
            <pubDate>Thu, 09 Aug 2012 12:00:00 +0200</pubDate><author>dotnet@oles.cloud (Ole Albers)</author>
            <guid>https://dotnet.work/blog/emptying-sharepoint-lists-the-ultra-fast-way/</guid>
            <description>&lt;p&gt;SharePoint is damn slow when deleting lines from lists. Each entry can take up to some seconds which isn’t nice when you have to delete thousands of lines.&lt;/p&gt;&#xA;&lt;p&gt;Also no Trunc-Methode is available.&lt;/p&gt;&#xA;&lt;p&gt;Other solutions found on the net creating xml-files to bulk delete aren’t very fast either.&lt;/p&gt;&#xA;&lt;p&gt;That’s why I decided to write my own fast truncate-method for SharePoint-Lists.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;All content of that list will be deleted. Use at your own risk!&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;em&gt;What it does&lt;/em&gt;:&lt;/p&gt;&#xA;&lt;p&gt;First a template of that list is being created. After that the list is deleted completely. Than a new list using that template is being created and at the end that template is being deleted.&lt;/p&gt;&#xA;&lt;p&gt;Works damn fast (just a few seconds even on big lists). IF anything goes wrong you can use the template to create a list manually.&lt;/p&gt;&#xA;&lt;p&gt; &lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;public void TruncateList(Guid listID)&#xA;{&#xA;SPSecurity.RunWithElevatedPrivileges(delegate()&#xA;{&#xA;using (SPSite site = new SPSite(SPContext.Current.Site.ID))&#xA;{&#xA;using (SPWeb web = SPContext.Current.Web)&#xA;{&#xA;try&#xA;{&#xA;web.AllowUnsafeUpdates = true;&#xA;// alte Werte merken&#xA;SPList listCopy = web.Lists\[listID\];&#xA;string title = listCopy.Title;&#xA;string description = listCopy.Description;&#xA;&#xA;string backupName = string.Format(&amp;#34;BACKUP{0}{1:d} {1:HH}{1:mm}&amp;#34;, title,DateTime.Now);&#xA;&#xA;// save template&#xA;listCopy.SaveAsTemplate(backupName, backupName, string.Empty, false);&#xA;SPListTemplate template = web.Site.GetCustomListTemplates(web)\[backupName\];&#xA;&#xA;// delete list&#xA;listCopy.Delete();&#xA;web.Lists.Add(title, description, template);&#xA;&#xA;//  remove template&#xA;SPList gallery = web.Lists\[&amp;#34;List Template Gallery&amp;#34;\];&#xA;foreach (SPListItem item in gallery.Items.Cast().Where(item =&amp;gt; item.Title == backupName))&#xA;{&#xA;item.Delete();&#xA;break;&#xA;}&#xA;&#xA;web.Update();&#xA;web.AllowUnsafeUpdates = false;&#xA;}&#xA;catch (Exception ex)&#xA;{&#xA;\_log.Error(ex);&#xA;throw;&#xA;}&#xA;}&#xA;}&#xA;}&#xA;);&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;</description>
        </item></channel>
</rss>
