<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet href="/rss.xsl" type="text/xsl"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Blake Niemyjski</title><description>Software architect writing about open source, observability, agentic development, and home automation.</description><link>https://blakeniemyjski.com/</link><item><title>Agentic Driven Development (ADD): AGENTS.md, Skills, and the Full Workflow</title><link>https://blakeniemyjski.com/blog/agentic-driven-development/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/agentic-driven-development/</guid><description>How I use AGENTS.md files, agent skills, multi-agent reviews, and GitHub CLI to give AI agents real codebase context.</description><pubDate>Wed, 04 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I&apos;ve been spending a lot of time over the past year integrating AI agents into my development workflow across several projects. It started with &lt;a href=&quot;https://docs.github.com/copilot/customizing-copilot/adding-custom-instructions-for-github-copilot&quot;&gt;&lt;code&gt;.github/copilot-instructions.md&lt;/code&gt;&lt;/a&gt;, which was GitHub Copilot&apos;s first attempt at letting you give the AI project context. That was a good start, but it was a single monolithic file tied to one tool. Then &lt;a href=&quot;https://agents.md/&quot;&gt;&lt;code&gt;AGENTS.md&lt;/code&gt;&lt;/a&gt; came along as an agent-agnostic standard, and more recently agent skills have taken things even further with domain-specific instruction files that get loaded on demand. The progression from &quot;one big instruction file for one tool&quot; to &quot;a curated set of skills that any agent can use&quot; has been really interesting to watch and build on.&lt;/p&gt;
&lt;p&gt;I wanted to share what I&apos;ve learned across that journey, what&apos;s working, and where I think this needs to go.&lt;/p&gt;
&lt;h2&gt;What Are AGENTS.md and Agent Skills?&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://agents.md/&quot;&gt;&lt;code&gt;AGENTS.md&lt;/code&gt;&lt;/a&gt; is an &lt;a href=&quot;https://github.com/agentsmd/agents.md&quot;&gt;open format&lt;/a&gt; for giving coding agents project-specific context. It emerged from collaboration across the AI coding ecosystem and is now stewarded by the Agentic AI Foundation under the Linux Foundation. It&apos;s a markdown file you place at the root of your repository that tells agents how to build, test, and navigate the codebase, what conventions to follow, what architectural decisions matter, and what pitfalls to avoid. Think of it as onboarding documentation, but for AI.&lt;/p&gt;
&lt;p&gt;It&apos;s already been adopted by over 60,000 open source projects, and the list of tools that support it keeps growing: &lt;a href=&quot;https://code.visualstudio.com/docs/copilot/customization/custom-instructions&quot;&gt;VS Code&lt;/a&gt; (v1.105+), &lt;a href=&quot;https://docs.github.com/copilot/customizing-copilot/adding-custom-instructions-for-github-copilot&quot;&gt;GitHub Copilot&lt;/a&gt;, &lt;a href=&quot;https://www.cursor.com/&quot;&gt;Cursor&lt;/a&gt;, &lt;a href=&quot;https://windsurf.com/&quot;&gt;Windsurf&lt;/a&gt;, &lt;a href=&quot;https://github.com/google-gemini/gemini-cli&quot;&gt;Gemini CLI&lt;/a&gt;, &lt;a href=&quot;https://zed.dev/docs/ai/external-agents&quot;&gt;Zed&lt;/a&gt;, &lt;a href=&quot;https://www.warp.dev/&quot;&gt;Warp&lt;/a&gt;, &lt;a href=&quot;https://aider.chat/&quot;&gt;Aider&lt;/a&gt;, and &lt;a href=&quot;https://roocode.com/&quot;&gt;RooCode&lt;/a&gt;. Claude Code is one notable exception here. It uses &lt;code&gt;CLAUDE.md&lt;/code&gt; instead of &lt;code&gt;AGENTS.md&lt;/code&gt;, which I&apos;ll come back to later.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/vercel-labs/skills&quot;&gt;Agent skills&lt;/a&gt; take this further. They&apos;re focused instruction files (&lt;code&gt;SKILL.md&lt;/code&gt;) organized into directories like &lt;code&gt;.agents/skills/&lt;/code&gt;, &lt;code&gt;.github/skills/&lt;/code&gt;, or &lt;code&gt;.claude/skills/&lt;/code&gt; depending on the tool, and they give the AI deep domain knowledge about specific areas of your project. When the agent is working on frontend code, it loads the frontend skill. When it&apos;s writing tests, it loads the testing skill.&lt;/p&gt;
&lt;p&gt;What I really like about this is that a lot of this structure works across multiple agents. Support and invocation behavior still vary by tool, but you can increasingly write these instructions once and benefit across more than one agent, which is a big step away from vendor lock-in.&lt;/p&gt;
&lt;p&gt;One thing to know: skills don&apos;t always get invoked when you&apos;d expect. Sometimes the agent will just announce that a skill exists instead of actually using it, or it&apos;ll ignore a relevant skill entirely. In VS Code, I have had better results with the experimental &lt;a href=&quot;https://code.visualstudio.com/docs/copilot/customization/agent-skills&quot;&gt;&lt;code&gt;chat.experimental.useSkillAdherencePrompt&lt;/code&gt;&lt;/a&gt; setting. It uses a stronger adherence prompt that pushes the model to invoke relevant skills instead of just mentioning them. I&apos;ve found this makes a noticeable difference, but since it&apos;s experimental, I&apos;d still treat it as something to test in your own workflow rather than assume across every setup.&lt;/p&gt;
&lt;h3&gt;Skip the Hierarchy, Keep It Flat&lt;/h3&gt;
&lt;p&gt;Some tools support hierarchical &lt;code&gt;AGENTS.md&lt;/code&gt; files: parent directories, subdirectories, nested overrides. VS Code even has a &lt;a href=&quot;https://code.visualstudio.com/docs/copilot/customization/custom-instructions&quot;&gt;&lt;code&gt;chat.useNestedAgentsMdFiles&lt;/code&gt;&lt;/a&gt; setting for it. In theory this lets you share common conventions at the top and specialize deeper in the tree. In practice, I&apos;ve had bad luck with it. Agents don&apos;t always pick up the right files in the right order, rules from different levels can conflict in confusing ways, and debugging which instructions actually got loaded is a pain.&lt;/p&gt;
&lt;p&gt;My recommendation: &lt;strong&gt;use a single &lt;code&gt;AGENTS.md&lt;/code&gt; at the repo root and put everything else in skills.&lt;/strong&gt; The root file stays concise: build commands, project structure, high-level conventions, and a table pointing to your skills. Domain-specific knowledge goes in skill files that get loaded on demand. This is simpler to reason about, easier to maintain, and more predictable across different agents.&lt;/p&gt;
&lt;h2&gt;How We Use This in Practice&lt;/h2&gt;
&lt;h3&gt;Foundatio&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/FoundatioFx/Foundatio&quot;&gt;Foundatio&lt;/a&gt; is an open source library providing pluggable building blocks for distributed .NET applications (caching, queues, locks, messaging, jobs, file storage). The &lt;a href=&quot;https://github.com/FoundatioFx/Foundatio/blob/main/AGENTS.md&quot;&gt;&lt;code&gt;AGENTS.md&lt;/code&gt;&lt;/a&gt; file covers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Repository overview&lt;/strong&gt;, what each abstraction does and the design principles (interface-first, testable, swappable implementations)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Quick start commands&lt;/strong&gt;, &lt;code&gt;dotnet build&lt;/code&gt;, &lt;code&gt;dotnet test&lt;/code&gt;, &lt;code&gt;dotnet format&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Project structure&lt;/strong&gt;, mapping out &lt;code&gt;src/&lt;/code&gt;, &lt;code&gt;tests/&lt;/code&gt;, &lt;code&gt;samples/&lt;/code&gt;, &lt;code&gt;benchmarks/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Coding standards&lt;/strong&gt;, from style rules to architecture patterns like async suffixes, cancellation token conventions, and when to use &lt;code&gt;ValueTask&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Common patterns&lt;/strong&gt;, extension methods, logging, exception types, single responsibility guidance&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We also use the &lt;code&gt;AGENTS.md&lt;/code&gt; to keep the documentation up to date. When the agent makes changes to Foundatio, the instructions tell it to update the relevant docs as part of the same task. This means the README and API docs stay in sync with the code without us having to remember to do it manually.&lt;/p&gt;
&lt;p&gt;The key insight here is the &quot;Craftsmanship Mindset&quot; preamble: &lt;em&gt;&quot;Every line of code should be intentional, readable, and maintainable. Write code you&apos;d be proud to have reviewed by senior engineers.&quot;&lt;/em&gt; Setting the tone matters. The agent will mirror the standards you set.&lt;/p&gt;
&lt;p&gt;This has been a really important pattern in my own prompting: the opening of your instructions sets the tone for everything that follows. If you lead with &quot;write clean, production-ready code,&quot; the model approaches the task differently than if you lead with a list of file paths. I wouldn&apos;t treat this as a universal law for every model and harness, but in practice I&apos;ve found that putting your standards right at the top of &lt;code&gt;AGENTS.md&lt;/code&gt; noticeably improves the quality bar.&lt;/p&gt;
&lt;p&gt;Some official model documentation suggests this matters less than it used to. I&apos;ve seen it firsthand though, and in my workflow it makes a massive difference when working with system prompts. If you want to go deeper on prompting techniques, I&apos;d recommend reading the &lt;a href=&quot;https://docs.anthropic.com/en/docs/build-with-claude/prompt-engineering/overview&quot;&gt;Anthropic prompting guide&lt;/a&gt;, the &lt;a href=&quot;https://platform.openai.com/docs/guides/prompting&quot;&gt;OpenAI prompt engineering guide&lt;/a&gt;, the &lt;a href=&quot;https://developers.openai.com/cookbook/examples/gpt-5/codex_prompting_guide/&quot;&gt;Codex prompting guide&lt;/a&gt;, and the &lt;a href=&quot;https://ai.google.dev/gemini-api/docs/prompting-strategies&quot;&gt;Google Gemini prompting strategies&lt;/a&gt;. Each provider has slightly different recommendations, but they all agree that clarity and structure matter.&lt;/p&gt;
&lt;p&gt;It&apos;s also worth noting that some models and harnesses actually work better with less in the &lt;code&gt;AGENTS.md&lt;/code&gt;. Codex is a good example of this, where a leaner, more focused context file can outperform a comprehensive one. The &lt;a href=&quot;https://arxiv.org/abs/2602.11988&quot;&gt;arxiv study on evaluating AGENTS.md&lt;/a&gt; found exactly this: unnecessary requirements made tasks harder for agents, not easier. So know your tools and test what works. More isn&apos;t always better.&lt;/p&gt;
&lt;h3&gt;Exceptionless&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/exceptionless/Exceptionless&quot;&gt;Exceptionless&lt;/a&gt; is a real-time error monitoring platform (ASP.NET Core 10 + Svelte 5) and this is where we went much deeper with skills. The &lt;a href=&quot;https://github.com/exceptionless/Exceptionless/blob/main/AGENTS.md&quot;&gt;&lt;code&gt;AGENTS.md&lt;/code&gt;&lt;/a&gt; is relatively concise, pointing agents to load specific skills from &lt;code&gt;.agents/skills/&amp;lt;name&amp;gt;/SKILL.md&lt;/code&gt; based on the domain they&apos;re working in:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Domain&lt;/th&gt;
&lt;th&gt;Skills&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Backend&lt;/td&gt;
&lt;td&gt;dotnet-conventions, backend-architecture, dotnet-cli, backend-testing, foundatio&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Frontend&lt;/td&gt;
&lt;td&gt;svelte-components, tanstack-form, tanstack-query, shadcn-svelte, typescript-conventions, frontend-architecture, storybook, accessibility, frontend-design&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Testing&lt;/td&gt;
&lt;td&gt;frontend-testing, e2e-testing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-cutting&lt;/td&gt;
&lt;td&gt;security-principles, releasenotes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Billing&lt;/td&gt;
&lt;td&gt;stripe-best-practices, upgrade-stripe&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agents&lt;/td&gt;
&lt;td&gt;agent-browser, dogfood&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;That&apos;s 22 skills total, including 6 third-party ones (browser automation, dogfood, frontend design, release notes, Stripe) that we manage separately. When an agent is working on a Svelte component, it knows to use shadcn-svelte patterns, TanStack Query for data fetching, and our specific component conventions. When it&apos;s working on the backend, it understands our repository patterns, Foundatio abstractions, and how we use FluentValidation.&lt;/p&gt;
&lt;p&gt;Third-party skills are managed with the &lt;a href=&quot;https://github.com/vercel-labs/skills&quot;&gt;Vercel Skills CLI&lt;/a&gt;. You add them with &lt;code&gt;npx skills add&lt;/code&gt; and keep them up to date with &lt;code&gt;npx skills update&lt;/code&gt;, which uses a lock file to track versions. It&apos;s really clean. Companies like Stripe and Anthropic publish official skills for their products, so instead of writing your own Stripe integration guide, you just pull theirs. There&apos;s also &lt;a href=&quot;https://skills.sh&quot;&gt;skills.sh&lt;/a&gt;, a directory for discovering community and official skills across platforms.&lt;/p&gt;
&lt;p&gt;Vercel has done a really great job with the &lt;a href=&quot;https://github.com/vercel-labs/skills&quot;&gt;npx skills CLI&lt;/a&gt;. It&apos;s the best tool out there right now for managing third-party skills. That said, it feels a bit odd running &lt;code&gt;npx&lt;/code&gt; and pulling in Node.js as a dependency for a .NET-only project that doesn&apos;t otherwise have a &lt;code&gt;package.json&lt;/code&gt;. You end up with a Node toolchain just to manage your agent skill files, which is a weird ask for a team that&apos;s all-in on dotnet. I&apos;d really love to see a &lt;code&gt;dotnet skills&lt;/code&gt; tool or even a &lt;code&gt;dotnet tool install skills&lt;/code&gt; that does the same thing natively. If anyone&apos;s looking for an open source project to start, there&apos;s your idea!&lt;/p&gt;
&lt;h3&gt;Security Risks of Third-Party Skills&lt;/h3&gt;
&lt;p&gt;One thing I want to be really clear about: third-party skills are code that directly influences what your AI agent does. They&apos;re instructions that get injected into the agent&apos;s context, and a malicious skill could tell the agent to do things you really don&apos;t want, like exfiltrating secrets, introducing backdoors, or modifying files outside the scope of the task. This is a real supply chain risk, similar to pulling in an untrusted npm package or NuGet dependency.&lt;/p&gt;
&lt;p&gt;Here&apos;s how I handle it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Always commit skills to your repo.&lt;/strong&gt; Don&apos;t pull them at build time or on the fly. They should be checked into source control so you have a clear record of what&apos;s in your project.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Review skill updates like code changes.&lt;/strong&gt; When you run &lt;code&gt;npx skills add&lt;/code&gt; to update a third-party skill, treat the diff the same way you&apos;d treat a dependency update. Read what changed. Make sure nothing sketchy got introduced. Don&apos;t just blindly accept updates.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Run independent security scans.&lt;/strong&gt; Tools like CodeQL and Copilot code review can catch some of this, but you should also be scanning your skill files as part of your security pipeline. If a skill file suddenly tells the agent to ignore security conventions or access environment variables, that&apos;s a red flag. &lt;a href=&quot;https://skills.sh&quot;&gt;skills.sh&lt;/a&gt; does a nice job here by having three different vendors scan each skill in their registry. That&apos;s a good start, but you still need to review every single change yourself. At the end of the day, skills are instruction-level supply chain risk. Treat them with the same seriousness you would any other dependency that can influence execution.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Prefer official skills from known sources.&lt;/strong&gt; Skills published by Stripe, Anthropic, Vercel, and Microsoft carry a lot more trust than random community skills. That doesn&apos;t mean community skills are bad, but you should be more careful vetting them.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is still a pretty new ecosystem, so the tooling around skill verification and trust is still evolving. But the principles are the same as any other supply chain security: know what you&apos;re pulling in, review it before you trust it, and keep a record of changes.&lt;/p&gt;
&lt;h3&gt;This Blog&lt;/h3&gt;
&lt;p&gt;Even this Astro blog has skills. There&apos;s a &lt;code&gt;project-structure&lt;/code&gt; skill documenting the site layout, a &lt;code&gt;brand-guidelines&lt;/code&gt; skill for visual identity and voice, and an &lt;code&gt;seo-schema-markup&lt;/code&gt; skill for JSON-LD structured data. When I ask an agent to write a blog post, it knows the frontmatter schema, the content directory structure, and the tech stack (Astro 5, Tailwind CSS v4, Pagefind).&lt;/p&gt;
&lt;h2&gt;Self-Learning: The Promise and the Trap&lt;/h2&gt;
&lt;p&gt;Exceptionless has a &quot;Continuous Improvement&quot; section in its &lt;code&gt;AGENTS.md&lt;/code&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Each time you complete a task or learn important information about the project, you must update the &lt;code&gt;AGENTS.md&lt;/code&gt;, &lt;code&gt;README.md&lt;/code&gt;, or relevant skill files.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is the self-learning pattern. The agent documents what it discovers as it works. In theory, the knowledge base grows organically over time.&lt;/p&gt;
&lt;p&gt;In practice, this needs guardrails. Without them, you end up with bloated instruction files full of edge cases, one-off fixes, and stale information. It becomes like a FAQ that nobody maintains, every entry technically true but not really helpful because the useful stuff gets buried.&lt;/p&gt;
&lt;p&gt;What I think works better is treating skills more like curated knowledge than an append-only log. A few principles:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Be selective about what gets documented.&lt;/strong&gt; Not every discovery is worth persisting. A quirky workaround for a dependency bug isn&apos;t a &quot;skill&quot;, it&apos;s a footnote that&apos;ll be irrelevant after the next update.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;New features should trigger pruning.&lt;/strong&gt; When you add a new capability or refactor a system, that&apos;s your cue to review the relevant skill files. Remove rules that reference old patterns. Update conventions that have evolved. Skills should reflect the codebase as it is, not as it was.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Guard against tech debt in your instructions.&lt;/strong&gt; Ironically, the files meant to prevent tech debt can become tech debt themselves. If a skill file has contradictory rules, outdated examples, or references to deleted code, the agent gets confused and produces worse output. Maintain your instruction files with the same discipline you maintain your code.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Think FAQ, not encyclopedia.&lt;/strong&gt; Document the patterns and decisions that come up repeatedly. If you&apos;ve explained the same architectural choice three times in code reviews, that&apos;s a skill entry. If it&apos;s a one-off edge case, let it go.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There&apos;s actually academic research that backs up the &quot;less is more&quot; approach. A recent study, &lt;a href=&quot;https://arxiv.org/abs/2602.11988&quot;&gt;&quot;Evaluating AGENTS.md&quot;&lt;/a&gt;, found that context files can actually &lt;em&gt;reduce&lt;/em&gt; agent success rates compared to providing no context at all, while increasing inference costs by over 20%. The key finding is that unnecessary requirements make tasks harder for agents. LLM-generated context files performed worst, while human-written files with minimal requirements did slightly better.&lt;/p&gt;
&lt;p&gt;Interestingly, &lt;a href=&quot;https://vercel.com/blog/agents-md-outperforms-skills-in-our-agent-evals&quot;&gt;Vercel&apos;s own evals&lt;/a&gt; tell a different story. They found that embedding documentation directly in &lt;code&gt;AGENTS.md&lt;/code&gt; dramatically outperformed skills, hitting 100% pass rates on build, lint, and test metrics while skills with default settings scored the same as having no docs at all (53%). The key difference is that &lt;code&gt;AGENTS.md&lt;/code&gt; content is always in the system prompt, so the agent doesn&apos;t have to decide whether to look something up. Skills require the agent to make a decision to invoke them, and that decision point is where things break down. Even with explicit instructions to use skills, they only got to 79%.&lt;/p&gt;
&lt;p&gt;So how do you reconcile those two findings? I think the arxiv paper is really about bloated, low-quality context hurting more than helping. And Vercel&apos;s results are about the right context, compressed aggressively (they went from 40KB to 8KB while keeping 100% accuracy), delivered passively. Both point to the same conclusion: less is more, and the best context is focused, curated, and always available.&lt;/p&gt;
&lt;p&gt;It&apos;s also worth mentioning &lt;a href=&quot;https://context7.com/&quot;&gt;Context7&lt;/a&gt; here. Context7 came before the skills ecosystem and is still really relevant. It&apos;s an MCP server that fetches up-to-date, version-specific library documentation directly into your prompt. So instead of baking framework docs into your &lt;code&gt;AGENTS.md&lt;/code&gt; or skills (where they&apos;ll go stale), Context7 pulls the latest docs on demand. I use it alongside skills, where skills handle project-specific conventions and Context7 handles third-party library documentation. They complement each other well.&lt;/p&gt;
&lt;p&gt;The takeaway from all of this is: keep your &lt;code&gt;AGENTS.md&lt;/code&gt; focused on what actually matters, compress aggressively, and use tools like Context7 for documentation that changes frequently. Don&apos;t dump everything you know into one file. The agents in the arxiv study &lt;em&gt;did&lt;/em&gt; follow the instructions and explore more thoroughly, but all that extra exploration didn&apos;t translate to better results when the instructions were bloated.&lt;/p&gt;
&lt;h2&gt;MCP Servers: Powerful but Painful&lt;/h2&gt;
&lt;p&gt;I have to talk about &lt;a href=&quot;https://modelcontextprotocol.io/&quot;&gt;Model Context Protocol (MCP)&lt;/a&gt; servers here because they&apos;re becoming a really important part of the agent ecosystem, but the experience of using them is still pretty rough.&lt;/p&gt;
&lt;p&gt;MCP servers let you give agents access to external tools and data sources: databases, APIs, browser automation, file systems, documentation fetchers like Context7, and more. The idea is great. Instead of the agent only being able to read and write files, it can interact with the outside world through a standardized protocol. In practice though, there are a few real pain points.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Context bloat just from being enabled.&lt;/strong&gt; Every MCP server you have enabled adds its tool definitions to the agent&apos;s context, even if you never use it in that session. If you&apos;ve got ten MCP servers configured, that&apos;s a lot of token budget being spent on tool descriptions before you&apos;ve even asked a question. This ties back to the &quot;less is more&quot; theme. Only enable the MCP servers you actually need for the project you&apos;re working on, and disable the rest.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Configuration is different everywhere.&lt;/strong&gt; Every tool has its own way of setting up MCP servers. Claude Code uses a &lt;code&gt;claude_desktop_config.json&lt;/code&gt; or &lt;code&gt;.mcp.json&lt;/code&gt;, VS Code has its own config format, Cursor does it differently, and so on. If you&apos;re using multiple tools (and you should be, as I talked about with the cross-model review workflow), you end up maintaining the same MCP server configuration in three or four different places. There&apos;s no shared standard for this yet.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Discovery is inconsistent across tools.&lt;/strong&gt; Some tools are getting better at this. &lt;a href=&quot;https://cursor.com/docs/context/mcp/directory&quot;&gt;Cursor has an MCP directory&lt;/a&gt; built right in, and &lt;a href=&quot;https://code.visualstudio.com/mcp&quot;&gt;VS Code has one too&lt;/a&gt;. But not all tools have caught up, and there&apos;s no universal &lt;code&gt;npx skills add&lt;/code&gt; equivalent for MCPs. Depending on which tool you&apos;re using, you might have a nice browsable directory or you might be searching GitHub and hoping for the best. Compare that to the skills ecosystem where &lt;a href=&quot;https://skills.sh&quot;&gt;skills.sh&lt;/a&gt; gives you a searchable directory and the Vercel CLI handles installation regardless of which agent you&apos;re using.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Security is a huge concern.&lt;/strong&gt; This is the one that really worries me. MCP servers can execute arbitrary code, make network requests, access your file system, and interact with external services. A malicious or compromised MCP server has way more attack surface than a malicious skill file. A skill file can only influence what the agent &lt;em&gt;thinks&lt;/em&gt; it should do. An MCP server can actually &lt;em&gt;do things&lt;/em&gt; on your machine. What&apos;s worse, some editors like VS Code ship MCP tools that haven&apos;t been through a thorough security review and are enabled by default. That&apos;s a lot of trust to place in tooling that most developers haven&apos;t audited.&lt;/p&gt;
&lt;p&gt;Here&apos;s what I recommend:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Only use MCP servers from trusted sources.&lt;/strong&gt; This is even more important than with skills. Check the source code, check who maintains it, and check how actively it&apos;s maintained.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Audit the permissions.&lt;/strong&gt; What can this server access? Does it need network access? File system access? Does it run with your user&apos;s full permissions? The less access it needs, the better.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pin versions.&lt;/strong&gt; Don&apos;t pull &lt;code&gt;latest&lt;/code&gt; for MCP servers. Pin to a specific commit or version so you know exactly what you&apos;re running. Review changes before updating.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Watch for prompt injection.&lt;/strong&gt; MCP servers return data that gets fed into the agent&apos;s context. If an MCP server returns data from an untrusted source (like user input from a database or content from the web), that data could contain prompt injection attacks that hijack the agent&apos;s behavior.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The MCP ecosystem is moving fast and the protocol itself is really well designed. But the tooling around discovery, cross-tool configuration, and security still has a long way to go. I&apos;m hopeful this gets better quickly because MCPs are genuinely powerful when they work.&lt;/p&gt;
&lt;h2&gt;Planning Mode: Think Before You Code&lt;/h2&gt;
&lt;p&gt;One thing I&apos;ve changed in my workflow is that I almost always run in planning mode. In Claude Code you can hit &lt;code&gt;Shift+Tab&lt;/code&gt; to toggle into plan mode, or just type &lt;code&gt;/plan&lt;/code&gt;. This puts the agent into a read-only or planning-first exploration mode where it analyzes your codebase and proposes a plan before touching any files.&lt;/p&gt;
&lt;p&gt;Why does this matter? Without planning mode, agents tend to just start writing code immediately. They&apos;ll make assumptions about your architecture, pick the wrong file to modify, or go down a path that doesn&apos;t fit your patterns. With planning mode, the agent explores first, reads the relevant files, understands the structure, asks clarifying questions, and then presents a plan for your approval before it writes a single line.&lt;/p&gt;
&lt;p&gt;This is especially valuable when you&apos;re working with skills. The agent loads the relevant skills during the planning phase and you can verify it&apos;s actually applying them before it starts coding. I&apos;ve caught plenty of cases where the agent&apos;s initial approach would have conflicted with our conventions, and a quick redirect during planning saved a lot of back-and-forth.&lt;/p&gt;
&lt;p&gt;I have to say, &lt;a href=&quot;https://www.cursor.com/&quot;&gt;Cursor&lt;/a&gt; has the best planning experience I&apos;ve used. I always get really great results from its planning mode. It&apos;s worth trying if you haven&apos;t already. The &lt;a href=&quot;https://docs.anthropic.com/en/docs/claude-cli&quot;&gt;Claude CLI&lt;/a&gt; and &lt;a href=&quot;https://docs.github.com/en/copilot/github-copilot-in-the-cli&quot;&gt;GitHub Copilot CLI&lt;/a&gt; are also solid options for terminal-based planning and coding. One thing to note: Claude&apos;s tooling centers &lt;code&gt;CLAUDE.md&lt;/code&gt; rather than &lt;code&gt;AGENTS.md&lt;/code&gt;, so keep that in mind if you&apos;re working across multiple tools.&lt;/p&gt;
&lt;p&gt;Steering with voice is something I&apos;ve been really enjoying. &lt;a href=&quot;https://code.visualstudio.com/updates/v1_109#_message-steering-and-queueing-experimental&quot;&gt;VS Code 1.109 added message steering and queueing&lt;/a&gt; as an experimental feature, and with the &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=ms-vscode.vscode-speech&quot;&gt;VS Code Speech extension&lt;/a&gt; you can use voice to kick off a task, bounce ideas around, or learn new concepts by talking through them with an agent. It&apos;s a really natural interaction model and it&apos;s how I like to start my planning sessions now.&lt;/p&gt;
&lt;p&gt;I also really like how &lt;a href=&quot;https://developers.googleblog.com/build-with-google-antigravity-our-new-agentic-development-platform/&quot;&gt;Google Antigravity&lt;/a&gt; handles planning. It generates a structured plan and then lets you leave comments directly on it, similar to commenting on a Google Doc. The agent incorporates your feedback without stopping its execution flow. That&apos;s a really nice interaction model. I wish all the tools would adopt something like that. And honestly, all of them would be better if you could print or share the plan more easily.&lt;/p&gt;
&lt;h3&gt;Plans as Architectural Documents&lt;/h3&gt;
&lt;p&gt;One thing I&apos;ve started thinking about differently is that plans aren&apos;t just throwaway artifacts. They&apos;re architectural documents. A good plan captures the &quot;why&quot; behind a set of changes: what problem you&apos;re solving, what approach you chose, what alternatives you considered. That&apos;s incredibly valuable context that usually gets lost once the code is written.&lt;/p&gt;
&lt;p&gt;I&apos;ve started saving plans alongside the code, either in a &lt;code&gt;docs/&lt;/code&gt; directory or in the PR description. Six months from now when someone asks &quot;why did we do it this way?&quot; that plan is the answer. You can also keep plans outside the codebase entirely, in a wiki or shared docs system, especially for bigger architectural decisions that span multiple repos. The point is: don&apos;t let that context disappear after the agent finishes coding.&lt;/p&gt;
&lt;p&gt;This idea of &quot;docs as the source of truth&quot; extends beyond plans. &lt;a href=&quot;https://github.com/snarktank/ralph&quot;&gt;Ralph&lt;/a&gt; is a really interesting project that takes PRDs (Product Requirements Documents) and turns them into the driving force of an autonomous agent loop. You write a PRD as a markdown doc, convert it into structured user stories, and Ralph picks up incomplete stories one at a time, implements them, runs quality checks, commits, and moves on. The PRD becomes the living spec that both humans and agents work from. I really like this pattern because it keeps the requirements front and center instead of buried in a ticket system. The PRD is the documentation, the acceptance criteria, and the progress tracker all in one.&lt;/p&gt;
&lt;h2&gt;OpenCode and Codex&lt;/h2&gt;
&lt;p&gt;I want to mention &lt;a href=&quot;https://opencode.ai/&quot;&gt;OpenCode&lt;/a&gt; here because it&apos;s a really solid alternative in the terminal agent space. It&apos;s open source, supports multiple LLM providers (75+ through Models.dev), and the TUI is really nice with multi-session support, LSP integration, and shareable session links.&lt;/p&gt;
&lt;p&gt;I also really like &lt;a href=&quot;https://openai.com/index/codex/&quot;&gt;Codex&lt;/a&gt;. For me it&apos;s one of the best experiences for agentic development right now. You kick off a task, iterate quickly, and focus on the end result rather than staring at every line of code. Some people call this &quot;vibing,&quot; and honestly that&apos;s a pretty good description. The key is that you&apos;re not just blindly trusting the output. You&apos;re pairing that fast iteration with the multi-agent review workflow I described earlier, so you get the speed of vibing with the code quality of a proper review process. That combination is really powerful.&lt;/p&gt;
&lt;p&gt;The reason I bring up all these tools is that it validates the agent-agnostic approach. If you invest time in good &lt;code&gt;AGENTS.md&lt;/code&gt; and skill files, you&apos;re not tied to any one tool. You can switch between Claude Code, Codex, Copilot, OpenCode, and whatever comes next depending on the task, and they all benefit from the same project knowledge.&lt;/p&gt;
&lt;h2&gt;The Full Agent Workflow: PRs, Reviews, and Feedback Loops&lt;/h2&gt;
&lt;p&gt;Where this really clicks is when you wire it all together into a continuous feedback loop. Here&apos;s the workflow I&apos;ve settled into:&lt;/p&gt;
&lt;h3&gt;1. Plan and Implement&lt;/h3&gt;
&lt;p&gt;Planning is the most important step. I use Claude or Codex for planning since they&apos;re really strong at architectural reasoning. Feed it all the context it needs: the ticket, acceptance criteria, relevant docs, architectural constraints. Let the agent explore the codebase, understand the architecture, and propose a plan. Then actually read the plan. Break it down step by step. Walk through the approach and iterate on it. Ask questions, push back on things that don&apos;t feel right, and have the agent revise until you&apos;re confident in the direction. This is where you catch bad approaches early, and the time you invest here saves you way more time later.&lt;/p&gt;
&lt;p&gt;For implementation, I&apos;ll use Claude, Codex, or Gemini depending on the task. Gemini has been really solid for frontend work in particular. Then for code review, I always use a different model than whatever did the implementation. If Claude wrote it, Codex or Copilot reviews it. If Codex wrote it, Claude reviews it. The whole point is getting a different perspective.&lt;/p&gt;
&lt;h3&gt;2. Cross-Model Review Before Committing&lt;/h3&gt;
&lt;p&gt;Once the agent has an implementation, have sub-agents on different models review the changes. This is the multi-agent review I talked about earlier. Check the current branch, make sure everything looks right, and iterate until you&apos;re happy with the quality. The bottleneck in any agentic workflow is going to be at review time. This is where you need the most powerful (and expensive) models to catch what the cheaper ones miss.&lt;/p&gt;
&lt;p&gt;It&apos;s really important not to let tech debt sneak in during this process. I always look at the diff while the agent is working, before every push, and always after. Don&apos;t just trust the agent and move on. Review the code as it&apos;s going in. If something looks off, stop and fix it now because it&apos;s way easier to address in the moment than after it&apos;s merged.&lt;/p&gt;
&lt;p&gt;If I&apos;m not sure about the implementation at this point, I always tell the agent to commit but don&apos;t push. This lets me iterate locally without anything going out to the team. I can review the diff, ask the agent to change things, commit again, and keep going until I&apos;m confident. Only when I&apos;m happy do I move to the next step.&lt;/p&gt;
&lt;h3&gt;3. Run Tests and E2E&lt;/h3&gt;
&lt;p&gt;Before you branch and push anything, have the agent run the full test suite. Unit tests, integration tests, whatever you&apos;ve got. If anything fails, the agent fixes it and runs them again. Don&apos;t push broken code.&lt;/p&gt;
&lt;p&gt;For frontend work, this is where the &lt;a href=&quot;https://github.com/vercel-labs/agent-browser&quot;&gt;agent-browser&lt;/a&gt; skill is one of my favorites. It lets the agent do end-to-end browser testing, actually navigating your app, filling out forms, clicking buttons, and verifying the UI works as expected. It&apos;s a really nice way to catch issues that unit tests miss before anything goes out for review.&lt;/p&gt;
&lt;p&gt;For .NET projects, the &lt;a href=&quot;https://aspire.dev/&quot;&gt;.NET Aspire&lt;/a&gt; MCP is a game changer here. It automatically launches all your resources (databases, caches, message brokers, everything), provides full logs for the entire stack, and makes running tests and agent code a breeze. Instead of manually spinning up dependencies or mocking everything, Aspire handles the orchestration so the agent can just run tests against a real environment.&lt;/p&gt;
&lt;h3&gt;4. Branch, Commit, and PR via GitHub CLI&lt;/h3&gt;
&lt;p&gt;I let the agent handle the entire &lt;a href=&quot;https://git-scm.com/&quot;&gt;git&lt;/a&gt; workflow using the &lt;a href=&quot;https://cli.github.com/&quot;&gt;GitHub CLI (&lt;code&gt;gh&lt;/code&gt;)&lt;/a&gt;. As defined in our &lt;code&gt;AGENTS.md&lt;/code&gt;, the agent follows &lt;a href=&quot;https://nvie.com/posts/a-successful-git-branching-model/&quot;&gt;Git Flow&lt;/a&gt;, creates a feature or bug branch, commits with a structured message format like &lt;code&gt;[feat] #123 Add retry logic to queue processor&lt;/code&gt; or &lt;code&gt;[fix] #456 Handle null reference in queue&lt;/code&gt;, and opens a PR filling out the PR template. The agent knows how to do all of this. You don&apos;t need to hand it specific commands.&lt;/p&gt;
&lt;p&gt;The agent writes better commit messages than I do half the time because it has the full diff context and the skill files telling it our conventions. No more &quot;fix stuff&quot; commits.&lt;/p&gt;
&lt;h3&gt;5. PR Review: Copilot, CodeQL, and Team&lt;/h3&gt;
&lt;p&gt;Once the PR is up, kick off the review process. GitHub Copilot automatically reviews it using your skill files and &lt;a href=&quot;https://github.com/FoundatioFx/Foundatio/blob/main/.github/workflows/copilot-setup-steps.yml&quot;&gt;&lt;code&gt;copilot-setup-steps.yml&lt;/code&gt;&lt;/a&gt; workflow. CodeQL runs static analysis for security and quality issues. And your team gets notified to review as well.&lt;/p&gt;
&lt;h3&gt;6. Address Review Comments&lt;/h3&gt;
&lt;p&gt;This is the part most people miss. Just tell the agent to go review the PR comments and CodeQL findings. It already knows how to use the &lt;a href=&quot;https://cli.github.com/&quot;&gt;GitHub CLI&lt;/a&gt; to pull that information. You don&apos;t need to hand it specific commands.&lt;/p&gt;
&lt;p&gt;Ask the agent to review all the PR comments to determine which ones are valid. For valid issues, have it do a proper root cause analysis and fix the underlying problem, not just patch the symptom. Make sure it adds or updates test coverage for any fixes and that the overall code quality is solid. Then push the update, and have the agent reply to and resolve the comments on the PR.&lt;/p&gt;
&lt;h3&gt;7. The Review Loop&lt;/h3&gt;
&lt;p&gt;Now repeat. Copilot and CodeQL review the new changes. Your team looks at the updates. Pull any new comments back into the agent. Fix, push, reply, resolve. Keep running this loop until the feedback is clean.&lt;/p&gt;
&lt;p&gt;For every PR, have the agent self-review the entire diff before the next round of human review. Sometimes stepping back and looking at the full picture catches things that incremental reviews miss. This should be a standard part of the loop, not just something you do for big changes.&lt;/p&gt;
&lt;h3&gt;8. The Compound Effect&lt;/h3&gt;
&lt;p&gt;This creates a real feedback loop:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Planning mode&lt;/strong&gt; with a strong model ensures it thinks before coding&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cross-model review&lt;/strong&gt; catches issues before you even push&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tests and E2E&lt;/strong&gt; verify everything works before branching&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GitHub CLI&lt;/strong&gt; automates the branch, commit, and PR workflow&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Copilot + CodeQL + team&lt;/strong&gt; review against your conventions&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Review comments&lt;/strong&gt; feed back into the agent for RCA and fixes&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Agent replies and resolves&lt;/strong&gt; comments on the PR directly&lt;/li&gt;
&lt;li&gt;Patterns that keep coming up get &lt;strong&gt;added to skills&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The loop tightens over time. As your skills get better, the initial code gets better, the reviews find fewer issues, and the whole cycle speeds up.&lt;/p&gt;
&lt;p&gt;You might ask, how is this process actually working? We&apos;ve shipped some really technical features to the &lt;a href=&quot;https://github.com/FoundatioFx/Foundatio&quot;&gt;Foundatio&lt;/a&gt; codebase and apps with a very high quality bar to production using this workflow. Right now we&apos;re iterating on a massive rewrite of Foundatio.Parsers and Foundatio.Repositories for Elasticsearch 9, and all tests are currently passing. This isn&apos;t a toy workflow for side projects. It&apos;s how we&apos;re shipping real, complex infrastructure code.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Just remember: there&apos;s a bad actor out there who also has agents, and they never sleep. Put security processes into your workflow. Even if it&apos;s a one-liner that audits all changes for security issues and OWASP vulnerabilities, that&apos;s better than nothing. The agents working against you are getting better at the same rate as the ones working for you.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;But the automated PR review pipeline is only half the story. The other half is what happens &lt;em&gt;before&lt;/em&gt; you even push.&lt;/p&gt;
&lt;h2&gt;Where Developers Fit: Multi-Agent Review and Iteration&lt;/h2&gt;
&lt;p&gt;So if agents are writing more and more of the code, what are developers actually doing? I&apos;ve been thinking about this a lot lately.&lt;/p&gt;
&lt;p&gt;From everything I&apos;ve seen, we&apos;re moving up the stack. Less time writing boilerplate, more time on the stuff that really matters: breaking down problems, reviewing architecture, defining acceptance criteria, and making sure the output is correct and secure. It&apos;s less &quot;person who writes all the code&quot; and more &quot;person who orchestrates and validates the work.&quot; And honestly? I think that&apos;s a pretty exciting shift.&lt;/p&gt;
&lt;p&gt;But I want to be clear: I&apos;m not just kicking things off and walking away. I still look at the architecture and implementation closely. I question edge cases and flag problems I see. I still influence the design a ton, pushing back on complexity, asking about security implications, challenging whether an approach is really the right one. That human feedback loop is still really important. The agents handle a lot of the execution, but the critical thinking, the &quot;wait, what about this scenario?&quot; questions, that&apos;s still very much on you as the developer. At the end of the day, this is still code that you are responsible for and it has to ship at a very high quality bar.&lt;/p&gt;
&lt;h3&gt;Multi-Agent Review Before Human Review&lt;/h3&gt;
&lt;p&gt;Here&apos;s what I&apos;ve been really excited about: instead of one agent producing code and immediately throwing it at a human reviewer, you have multiple agents review each other&apos;s work first. Think of it like having several reviewers with different perspectives do a first pass before the final review.&lt;/p&gt;
&lt;p&gt;As I mentioned in the workflow above, I always use a different model for review than whatever did the implementation. They have different strengths and blind spots. A review from a different model catches things the original one missed, just like two developers with different backgrounds looking at the same code. Feed that feedback back to the implementing agent, let it address everything, then review again with the second model. Back and forth until it&apos;s clean. After a few rounds of this ping-pong, the code quality is really solid before a human ever touches it.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I&apos;ve found that saying &quot;review all changes in this branch&quot; or &quot;review this PR&quot; helps a ton. Tell the agent to make sure every change was absolutely needed. Check for security issues, edge cases, readability, and performance problems. Being explicit about what you want reviewed makes the output way more useful. Tell the review agent to review as if the code needs world-class integration and is being deployed to production. Sounds simple, but it puts the review on a whole other level. The agent stops giving you surface-level &quot;looks good&quot; feedback and starts really digging into edge cases, error handling, and whether the code is actually robust enough. The bar you set in the prompt directly affects the quality of the review.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There are community projects formalizing this same concept. &lt;a href=&quot;https://burkeholland.github.io/anvil/&quot;&gt;Anvil&lt;/a&gt; is a Copilot CLI plugin that does adversarial review across up to three different AI models (GPT, Gemini, Claude) that each try to find bugs, security holes, and logic errors. I haven&apos;t used it yet, but it&apos;s doing exactly what I&apos;ve been doing manually and it&apos;s something I&apos;m actively planning to look into. The disagreement between models is the feature. If one model thinks the code is fine but another flags a problem, that&apos;s exactly what you want caught before a human reviews it.&lt;/p&gt;
&lt;p&gt;You can take this even further with specialized review passes: one agent focused on security auditing, another checking architectural patterns, another on code quality and naming conventions. Each one catches different things. By the time a human sits down to review, the obvious issues are already resolved. The human reviewer can focus on the high-level stuff: does this approach make sense? Does it fit the broader architecture? Are there edge cases the agents missed?&lt;/p&gt;
&lt;h3&gt;Breaking Down Acceptance Criteria Into Your Flow&lt;/h3&gt;
&lt;p&gt;One of the most underrated things you can do is feed your acceptance criteria directly into the agent workflow. Take your ticket or spec, break it into concrete acceptance criteria, and let the agent work through them one by one. The agent can even help you break down vague requirements into testable criteria, which is super useful when a ticket is a little hand-wavy.&lt;/p&gt;
&lt;p&gt;Then each criterion becomes a checkpoint. Did the agent&apos;s implementation actually satisfy it? You can have a review agent validate each criterion against the code changes. This gives you a structured way to verify completeness instead of just eyeballing a diff and hoping nothing got missed.&lt;/p&gt;
&lt;h3&gt;Testing and Human QA&lt;/h3&gt;
&lt;p&gt;After the multi-agent review cycle, you hand off to testing agents. These can generate test cases from your acceptance criteria, write unit and integration tests, and even run them. If the tests don&apos;t pass, the agent loops back and fixes the issues before a human ever sees them.&lt;/p&gt;
&lt;p&gt;If you don&apos;t have acceptance criteria yet, you can generate them quickly. Just describe what the feature should do and have the agent break it down into testable criteria. It&apos;s a really fast way to get structured coverage even when the spec is thin.&lt;/p&gt;
&lt;p&gt;But here&apos;s the thing: you still need human QA. Agents are really good at verifying specific behaviors, but they don&apos;t have the intuition for &quot;this feels wrong&quot; or &quot;this flow is confusing.&quot; Human QA catches the stuff that&apos;s technically correct but practically bad. The sweet spot is letting agents handle the mechanical verification so your QA team can focus on exploratory testing and user experience.&lt;/p&gt;
&lt;p&gt;There are skills like &lt;a href=&quot;https://github.com/pbakaus/impeccable&quot;&gt;Impeccable&lt;/a&gt; that are trying to help agents evaluate UI quality. It&apos;s not quite there yet in terms of &quot;taste,&quot; but it&apos;s getting closer every day. I think this is one of the areas where we&apos;ll see a lot of improvement soon.&lt;/p&gt;
&lt;p&gt;The next logical step here is agent skills specifically for QA. Imagine a skill that takes your acceptance criteria, builds automated tests from them (I really wish these lived in the repo alongside the code), and verifies regression scenarios. You could give higher priority to testing areas with changing requirements so the feedback cycle is even faster. This would let QA teams adapt to shifting specs without manually rewriting test plans every sprint.&lt;/p&gt;
&lt;h3&gt;Using Agents to Learn and Share Knowledge&lt;/h3&gt;
&lt;p&gt;One thing that doesn&apos;t get talked about enough is using agents as a learning tool. When I&apos;m working on an unfamiliar part of the codebase, I&apos;ll ask the agent to explain how something works, walk me through the data flow, or diagram the architecture. It&apos;s like having a senior dev who&apos;s already read every file sitting next to you.&lt;/p&gt;
&lt;p&gt;This is super useful for understanding the impact of a bug. Ask the agent to trace through the code and show you everywhere a change could have downstream effects. Have it do a proper root cause analysis and explain not just what went wrong but why. I&apos;ve found that agents are really good at this kind of investigation because they can hold the entire codebase in context and follow the threads way faster than I can by reading files one at a time.&lt;/p&gt;
&lt;p&gt;The knowledge sharing side is where it gets even more interesting. Ask the agent to generate mermaid diagrams of the architecture, data flows, or system interactions. These are incredibly useful for onboarding new team members, documenting complex systems, or explaining a bug fix in a PR description. Instead of spending an hour drawing a diagram by hand, the agent generates one in seconds and you can iterate on it.&lt;/p&gt;
&lt;p&gt;I&apos;ve started making this a regular part of my workflow. After finishing a complex feature or fixing a tricky bug, I&apos;ll ask the agent to write up a summary with diagrams explaining what changed and why. That documentation lives in the PR, in the docs, or in the skill files. It compounds over time and makes the whole team faster.&lt;/p&gt;
&lt;h3&gt;The Massive Leap in LLM Capabilities&lt;/h3&gt;
&lt;p&gt;I really cannot overstate how much these models have improved in just the past three to four months. The jump in code understanding, multi-file reasoning, and the ability to hold complex architectural context has been unlike anything I&apos;ve seen before. Tasks that agents would consistently botch six months ago? They handle them reliably now. The multi-agent review pattern I described above wouldn&apos;t have been practical even a few months ago because the individual agents weren&apos;t reliable enough to trust their reviews.&lt;/p&gt;
&lt;p&gt;I want to be honest though: none of this is perfect. Agents will hallucinate. They will lie to you with confidence. Code reviews from agents will miss things or flag false positives. But for everything they get wrong, they get a whole lot right. I&apos;ve found some really impactful bugs, security issues, and architectural problems by following this process that I might have missed on my own. And it&apos;s only going to get better with time.&lt;/p&gt;
&lt;h3&gt;SDL and Code Scanning Are More Important Than Ever&lt;/h3&gt;
&lt;p&gt;This is something I want to really emphasize: with agents writing more code, your Security Development Lifecycle (SDL) and code scanning practices are more important than ever, not less. It&apos;s really easy for an LLM to sneak things in. Not maliciously, but because it&apos;s optimizing for &quot;make the code work&quot; and doesn&apos;t always think about security implications the way a human would. It might introduce a SQL injection vulnerability, hardcode a credential pattern, add an overly permissive CORS policy, or pull in a dependency with known CVEs, all while confidently telling you the code is correct.&lt;/p&gt;
&lt;p&gt;This is why automated scanning needs to be a non-negotiable part of your pipeline:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Static analysis (SAST)&lt;/strong&gt; with tools like &lt;a href=&quot;https://codeql.github.com/&quot;&gt;CodeQL&lt;/a&gt;, &lt;a href=&quot;https://semgrep.dev/&quot;&gt;Semgrep&lt;/a&gt;, or &lt;a href=&quot;https://www.sonarsource.com/products/sonarqube/&quot;&gt;SonarQube&lt;/a&gt; should run on every PR. These catch the patterns that agents commonly introduce: injection vulnerabilities, insecure deserialization, hardcoded secrets, and more.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dependency scanning&lt;/strong&gt; with &lt;a href=&quot;https://docs.github.com/en/code-security/dependabot&quot;&gt;Dependabot&lt;/a&gt;, &lt;code&gt;npm audit&lt;/code&gt;, or &lt;code&gt;dotnet list package --vulnerable&lt;/code&gt; catches vulnerable packages the agent might add.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Secret scanning&lt;/strong&gt; should be enabled at the repo level. Agents sometimes generate placeholder secrets or copy patterns from training data that look like real credentials.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Copilot code review&lt;/strong&gt; adds another layer by reviewing against your skill files and conventions automatically on every PR.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The multi-agent review workflow I described helps here too. Having a second model review specifically for security issues catches a lot. But automated scanning is your safety net. Don&apos;t rely on any single layer. The whole point of defense in depth is that each layer catches what the others miss.&lt;/p&gt;
&lt;p&gt;So I think now is the time to invest in your agent infrastructure. The &lt;code&gt;AGENTS.md&lt;/code&gt; files, the skills, the review workflows, all of it gets more valuable as the models improve. You&apos;re not just building for today&apos;s capabilities. You&apos;re building the scaffolding that will compound as these models keep getting better. And based on the trajectory I&apos;ve seen recently, they&apos;re going to keep getting better fast.&lt;/p&gt;
&lt;h2&gt;Hooks: The Next Big Development&lt;/h2&gt;
&lt;p&gt;I think hooks are where the next big leap in agent workflows is going to come from. Right now, tools like &lt;a href=&quot;https://code.claude.com/docs/en/hooks-guide&quot;&gt;Claude Code&lt;/a&gt;, &lt;a href=&quot;https://code.visualstudio.com/docs/copilot/customization/hooks&quot;&gt;VS Code&lt;/a&gt; (preview), and &lt;a href=&quot;https://cursor.com/docs/agent/hooks&quot;&gt;Cursor&lt;/a&gt; support hooks that fire shell commands in response to events like tool calls, file edits, or commits. That&apos;s useful, but we&apos;re just scratching the surface.&lt;/p&gt;
&lt;p&gt;Imagine hooks that automatically run code formatting after every file edit. Or hooks that pause the session to ask you a clarifying question before the agent goes down a path you didn&apos;t intend. Or hooks that kick off a sub-agent review, like &lt;a href=&quot;https://burkeholland.github.io/anvil/&quot;&gt;Anvil&lt;/a&gt;&apos;s adversarial multi-model review, every time the agent finishes an implementation task. All of this while keeping the session alive so you don&apos;t lose context.&lt;/p&gt;
&lt;p&gt;The hooks themselves already support all of this today. You can build any of these right now. The missing piece is that there&apos;s no prebuilt ecosystem for hooks like &lt;a href=&quot;https://skills.sh&quot;&gt;skills.sh&lt;/a&gt; is for skills. You have to write them yourself. Here are some examples of what you can do:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Auto-formatting on save.&lt;/strong&gt; The agent writes code, a hook runs your formatter (Prettier, dotnet format, whatever) before the agent moves on. No more style nits in code review.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Security scanning.&lt;/strong&gt; A hook that runs a security scanner after every file edit. Catch secrets being committed, detect known vulnerable patterns, or flag changes to authentication and authorization code before the agent moves on.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dependency auditing.&lt;/strong&gt; When the agent adds a new package, a hook runs &lt;code&gt;npm audit&lt;/code&gt; or &lt;code&gt;dotnet list package --vulnerable&lt;/code&gt; to check for known vulnerabilities before it gets committed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Interactive checkpoints.&lt;/strong&gt; Hooks that prompt the user for input at key decision points. &quot;The agent wants to add a new dependency, do you approve?&quot; This keeps you in the loop without you having to watch every step.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Automated sub-agent review.&lt;/strong&gt; After the agent finishes a task, a hook automatically sends the changes to a review agent (or multiple review agents) before proceeding. This is basically &lt;a href=&quot;https://burkeholland.github.io/anvil/&quot;&gt;Anvil&lt;/a&gt;&apos;s pattern built into the workflow as a hook.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Test-on-commit hooks.&lt;/strong&gt; Before the agent commits, run the test suite. If tests fail, feed the failures back to the agent automatically. No human intervention needed for the fix cycle.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;License compliance.&lt;/strong&gt; A hook that checks any new dependencies against your approved license list. Block the agent from adding GPL code to your MIT project, for example.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Skill and docs updates.&lt;/strong&gt; After a refactor, a hook could prompt the agent to review and update relevant skill files and documentation.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&apos;d love to see a community hooks registry similar to skills.sh emerge. Prebuilt, vetted hooks for common patterns like security scanning, formatting, and compliance would save everyone a lot of time.&lt;/p&gt;
&lt;p&gt;GitHub is already moving in this direction with &lt;a href=&quot;https://github.com/github/gh-aw&quot;&gt;&lt;code&gt;gh-aw&lt;/code&gt; (GitHub Agentic Workflows)&lt;/a&gt;, which lets you write agentic workflows in natural language markdown and run them in GitHub Actions. It&apos;s read-only by default with sandboxed execution, input sanitization, and team-gating for critical operations. This is the kind of thing I&apos;m talking about, taking the agent workflow and making it a first-class part of your CI/CD pipeline.&lt;/p&gt;
&lt;p&gt;I also think we&apos;re going to see a lot more investment in sandboxing for agents. Right now, most agents run with your full user permissions, which is a pretty big trust surface. As agents get more autonomous and hooks let them do more without human intervention, proper sandboxing becomes critical. Containers, restricted file system access, network isolation, scoped API tokens. The tools that figure out how to give agents enough access to be useful while limiting the blast radius of mistakes (or compromises) are going to win.&lt;/p&gt;
&lt;p&gt;The tools that get hooks and sandboxing right are going to have a huge advantage. Hooks are what turn an agent from a tool you interact with into a workflow you orchestrate. I&apos;m really excited to see where this goes.&lt;/p&gt;
&lt;h2&gt;The CLAUDE.md Problem&lt;/h2&gt;
&lt;p&gt;If you&apos;re using Claude Code, you need to know that Anthropic has chosen not to support the &lt;code&gt;AGENTS.md&lt;/code&gt; standard. Claude Code uses its own &lt;code&gt;CLAUDE.md&lt;/code&gt; format instead. As of March 4, 2026, there&apos;s an &lt;a href=&quot;https://github.com/anthropics/claude-code/issues/6235&quot;&gt;open issue with nearly 3,000 upvotes&lt;/a&gt; requesting &lt;code&gt;AGENTS.md&lt;/code&gt; support, but no official response yet. It&apos;s frustrating because a growing set of major tools support the standard while Claude still requires its own parallel file.&lt;/p&gt;
&lt;p&gt;So if you want your agent instructions to work across Claude Code and everything else, you need to maintain both files. The easiest workaround is to just copy your &lt;code&gt;AGENTS.md&lt;/code&gt; content into a &lt;code&gt;CLAUDE.md&lt;/code&gt; file and keep them in sync. It&apos;s annoying, but it works. Some people use hard links (a way to make two filenames point to the same file on disk), but &lt;code&gt;git&lt;/code&gt; treats them as separate files anyway, so you&apos;re still managing two copies in version control. I&apos;m really hoping Anthropic just adds &lt;code&gt;AGENTS.md&lt;/code&gt; support so we can stop dealing with this.&lt;/p&gt;
&lt;h2&gt;Migrating from Copilot Instructions&lt;/h2&gt;
&lt;p&gt;If you&apos;re still using &lt;a href=&quot;https://docs.github.com/copilot/customizing-copilot/adding-custom-instructions-for-github-copilot&quot;&gt;&lt;code&gt;.github/copilot-instructions.md&lt;/code&gt;&lt;/a&gt;, I&apos;d recommend migrating to &lt;code&gt;AGENTS.md&lt;/code&gt; and skills. Use the most advanced model you can (like Opus), feed it the documentation for &lt;code&gt;AGENTS.md&lt;/code&gt; and agent skills, find existing skills or an &lt;code&gt;AGENTS.md&lt;/code&gt; that&apos;s really close to what you&apos;re thinking or is specific to the tech stack you&apos;re using as a base, tweak it in plan mode, and go from there. Tell it to be very surgical. The migration is straightforward since it&apos;s all just markdown. The big advantage is that you&apos;re no longer locked into one vendor&apos;s instruction format. Copilot and a growing set of other tools support &lt;code&gt;AGENTS.md&lt;/code&gt;, even though Claude Code still requires &lt;code&gt;CLAUDE.md&lt;/code&gt;. And the skill-based approach scales much better than a single monolithic instruction file.&lt;/p&gt;
&lt;h2&gt;The Future: Orchestration by Powerful Models&lt;/h2&gt;
&lt;p&gt;Looking ahead, I think the biggest shift is going to be orchestration by a powerful language model. Instead of you manually kicking off agents, reviewing, sending to another agent, and managing the loop yourself, a top-tier model acts as the conductor. It breaks down the task, delegates to specialized sub-agents (planning, implementation, code review, testing, security), coordinates their work in parallel, and synthesizes the results. You describe what you want, and the orchestrator figures out how to get there.&lt;/p&gt;
&lt;p&gt;We&apos;re already seeing this take shape. &lt;a href=&quot;https://code.visualstudio.com/updates/v1_109#_agent-orchestration&quot;&gt;VS Code 1.109 introduced agent orchestration&lt;/a&gt; as a first-class concept, where multiple specialized agents collaborate on complex tasks. Each sub-agent operates in its own context window (solving the context overflow problem), can use different models optimized for its role, and independent tasks run in parallel. The community has already built orchestration systems on top of this like &lt;a href=&quot;https://github.com/AmanPriyanshu/copilot-orchestra&quot;&gt;Copilot Orchestra&lt;/a&gt; and &lt;a href=&quot;https://github.com/AmanPriyanshu/copilot-atlas&quot;&gt;GitHub Copilot Atlas&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Distribution has always been the hardest part of any computing problem, and agent orchestration is no different. You&apos;re starting to see Google push this forward with Gemini, where the model itself handles the coordination across multiple agents and tools. I think this is the natural evolution of everything I&apos;ve described in this post. The workflow I&apos;m doing manually today (plan, implement, cross-model review, test, PR, review loop) is going to be something you describe once and an orchestrator runs end to end.&lt;/p&gt;
&lt;p&gt;The key ingredient is that the orchestrating model needs to be really powerful. It needs to understand your codebase, your conventions (through &lt;code&gt;AGENTS.md&lt;/code&gt; and skills), the strengths of different sub-agents, and when to escalate to a human. This is why investing in your agent infrastructure now matters so much. The orchestrators are coming, and the teams with the best &lt;code&gt;AGENTS.md&lt;/code&gt; files, skills, and review patterns are going to get the most out of them.&lt;/p&gt;
&lt;h3&gt;The Cost Rug Pull&lt;/h3&gt;
&lt;p&gt;I do think there&apos;s going to be a rug pull on costs at some point. Right now, models are really cheap with incredible value. Codex, Copilot, and Gemini subscriptions let you do a ton for very little money. But be careful with the &quot;newer is cheaper&quot; narrative. I&apos;ve read breakdowns of both Gemini and OpenAI where a newer model claims lower per-token pricing, but the model emits way more tokens to accomplish the same task, so the actual cost ends up being higher. Always look at total cost for a task, not just the per-token rate.&lt;/p&gt;
&lt;p&gt;This is why open source models are so important. You can run models locally today, and the latest developments around running models via Apple&apos;s Neural Engine are really exciting. Projects like &lt;a href=&quot;https://github.com/maderix/ANE&quot;&gt;ANE&lt;/a&gt; have potential to bring real speed, efficiency, and cost savings for local inference. Having the option to run locally means you&apos;re not entirely dependent on subscription pricing that could change at any time.&lt;/p&gt;
&lt;h3&gt;What&apos;s Coming Fast&lt;/h3&gt;
&lt;p&gt;I think we&apos;re going to see some really exciting iteration speeds on things that used to be painful. Localization, accessibility, documentation. Every major product is going to ship with an MCP server or skill. We&apos;re already seeing it with Stripe, Vercel, and shadcn. That trend is only going to accelerate.&lt;/p&gt;
&lt;p&gt;Relatedly, website traffic is also declining across the board. Look at what happened to &lt;a href=&quot;https://tailwindcss.com/&quot;&gt;Tailwind CSS&lt;/a&gt; and other documentation-heavy sites. A lot of this is because AI is being used for search now, and those models are being trained on your content. It&apos;s worth asking: does your website have an &lt;a href=&quot;https://llmstxt.org/&quot;&gt;&lt;code&gt;llms.txt&lt;/code&gt;&lt;/a&gt;? If not, you might want to think about how your content is being consumed in an AI-first world.&lt;/p&gt;
&lt;p&gt;Just remember, it&apos;s okay not to be the first. Things are moving so fast right now. Just be aware of what&apos;s out there and implement when there are clear winners.&lt;/p&gt;
&lt;h3&gt;Future Tech Debt (or the Lack of It)&lt;/h3&gt;
&lt;p&gt;This one is a little further out, but I think we&apos;re going to get to a place where if you have enough tests, requirements, and acceptance criteria, you can just regenerate your application. Point it at your existing data stores, your API contracts, your test suites, and let the agent rebuild the implementation from scratch. New tech debt appears until it gets regenerated again. It sounds wild, but it&apos;s the logical conclusion of everything I&apos;ve described in this post.&lt;/p&gt;
&lt;p&gt;That said, it&apos;s still going to be really important for a person to put tools and processes in place (with agent assistance) to make sure APIs aren&apos;t broken and users are happy. But will apps even be apps as we know them? Or will they be more like personal assistants feeding you data through your glasses or via voice? I think this is where OpenAI is going with their voice-driven Jarvis product. Just remember: your privacy and security should always be paramount.&lt;/p&gt;
&lt;h2&gt;Getting Better Over Time&lt;/h2&gt;
&lt;p&gt;The real value compounds. Here&apos;s how I think about the improvement cycle:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Start small.&lt;/strong&gt; An &lt;code&gt;AGENTS.md&lt;/code&gt; with build commands, project structure, and your top 5 coding conventions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Add skills as pain points emerge.&lt;/strong&gt; When you notice the agent consistently getting something wrong, that&apos;s a skill entry. Don&apos;t preemptively document everything.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Review skills during refactors.&lt;/strong&gt; Changing architecture? Update the skills. Dropping a dependency? Remove references to it. Skills are living documents.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use code review feedback as input.&lt;/strong&gt; If the same comment keeps coming up in PRs (from humans or agents), codify it in a skill.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Prune ruthlessly.&lt;/strong&gt; A shorter, accurate skill file outperforms a longer, stale one every time. The agent has a context window. Respect it.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The goal isn&apos;t to document everything. It&apos;s to document the right things, the patterns and decisions that make your codebase yours, so that AI agents can be productive contributors rather than generic code generators.&lt;/p&gt;
&lt;p&gt;One more thing worth keeping in mind: prompt placement and repetition matter more than people expect. I first saw this framed well in &lt;a href=&quot;https://x.com/realBigBrainAI/status/2027383248201089337&quot;&gt;this tweet&lt;/a&gt;, and it stuck with me because it matches what I&apos;ve seen in practice. This has real implications for how you structure your &lt;code&gt;AGENTS.md&lt;/code&gt;. Your most critical conventions should be stated clearly and prominently, not buried in a wall of text. If the agent has to wade through dozens of rules to find the important ones, the important ones lose their impact.&lt;/p&gt;
&lt;p&gt;If you&apos;re not using &lt;code&gt;AGENTS.md&lt;/code&gt; yet, I&apos;d really encourage you to try it. Start with build commands and your most important conventions. You&apos;ll be surprised how much better the AI output gets with even a little bit of context. Then try the cross-model review workflow: have one agent write, another review, and iterate until it&apos;s clean. The combination of good skills and multi-agent review has been a pretty huge improvement for the quality of code coming out of my projects.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Prompting is a skill worth investing in. A great prompt has three things: context (what you&apos;re working on, the tech stack, the constraints), intent (what you want the agent to do and why), and a definition of done (what a good result actually looks like). Be specific. &quot;Refactor this function&quot; is okay. &quot;Refactor this function to use the repository pattern from our Foundatio skill, keep backward compatibility, and add unit tests&quot; is way better. And if you&apos;re not getting good results, don&apos;t just ask the same thing louder. Come at it from a different angle. Try &quot;tell me everything wrong with approach X&quot; and then solve for it. Sometimes changing the prompt is like going for a walk and coming back with fresh eyes. A different framing can completely change the output.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And if you&apos;re already doing all of this, I&apos;d love to hear how you&apos;re handling the maintenance and pruning side of things. That&apos;s where I think the community still has a lot to figure out.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;For those just entering the field: there is a whole lot more to learn than just prompting, especially around security. You don&apos;t always have to be the first to do something, just execute well. Learn to prompt effectively, but also learn how these models actually work (it&apos;s next-word prediction, all the way down). Use AI as a tool. Trust that it will get you most of the way there, but know that it&apos;s always that last 10-20% that&apos;s the hardest. That&apos;s where your skills, your judgment, and your experience make all the difference.&lt;/p&gt;
&lt;p&gt;One last thing: this blog was converted from Hugo to Astro in a few hours via &lt;a href=&quot;https://openai.com/index/codex/&quot;&gt;OpenAI Codex&lt;/a&gt;. I didn&apos;t write a single line of code, just reviewed it and iterated. This blog post was written by Claude Code (Opus 4.6) with a skill built from my writing voice across all my prior blog posts, reviewed by the Codex app, and proofread and agentic-looped by yours truly. That&apos;s where we&apos;re at right now. Never stop learning, trust your gut, and ship it.&lt;/p&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>How-to get notifications when your mailbox is opened</title><link>https://blakeniemyjski.com/blog/how-to-get-notifications-when-your-mailbox-is-opened/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/how-to-get-notifications-when-your-mailbox-is-opened/</guid><pubDate>Thu, 17 Dec 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Have you ever waited endlessly for an important package or mail to be delivered?
Have you ever dealt with a package being stolen from your mailbox? In this
guide I&apos;ll show you how to see packages will arrive today and notifications
when it&apos;s delivered as shown below with the help of
&lt;a href=&quot;https://www.home-assistant.io&quot;&gt;Home Assistant&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/home-automation/mail-delivered-notification-600.jpg&quot; alt=&quot;Mail Delivered Notification&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This guide will also show you how to bring in mail and packages sensors from
various different delivery services to allow you to see your mail in the Media
Browser as well as provide additional context to the delivery notifications.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A huge shout out to &lt;a href=&quot;https://twitter.com/tekmaven/status/1329905040988004352&quot;&gt;Ryan Hoffman&lt;/a&gt; who
shared his experiences with the Ring mailbox sensor and how he was using the
&lt;a href=&quot;#camera-proxy-integration&quot;&gt;Camera Proxy Integration&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Configuring Integrations&lt;/h2&gt;
&lt;p&gt;In the sections below we will configure some integrations and email settings.&lt;/p&gt;
&lt;h3&gt;Installing Home Assistant Community Store (HACS)&lt;/h3&gt;
&lt;p&gt;If you haven&apos;t done so yet, please follow &lt;a href=&quot;https://hacs.xyz/docs/installation/manual&quot;&gt;these instructions&lt;/a&gt;
to install &lt;a href=&quot;https://hacs.xyz&quot;&gt;HACS&lt;/a&gt;. This will allow us to install and manage
third party integrations, plugins, themes and more.&lt;/p&gt;
&lt;h3&gt;Installing the Media Source integration&lt;/h3&gt;
&lt;p&gt;Installing the &lt;a href=&quot;https://www.home-assistant.io/integrations/media_source/&quot;&gt;Media Source integration&lt;/a&gt;
will allow us to view what email is arriving today as well as any image
snapshots we take when our mailbox is opened!&lt;/p&gt;
&lt;h3&gt;Installing Mail and Packages integration&lt;/h3&gt;
&lt;p&gt;Once HACS is installed, we need to install the
&lt;a href=&quot;https://github.com/moralmunky/Home-Assistant-Mail-And-Packages&quot;&gt;Mail and Packages Integration&lt;/a&gt;.
This integration uses IMAP to search your email account for specific emails by
different delivery services like &lt;code&gt;Amazon&lt;/code&gt;, &lt;code&gt;USPS&lt;/code&gt;, &lt;code&gt;FedEx&lt;/code&gt;, &lt;code&gt;UPS&lt;/code&gt;, &lt;code&gt;DHL&lt;/code&gt; and
&lt;a href=&quot;https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki#how-it-works&quot;&gt;more&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There is a complete guide located on the projects site on how to
&lt;a href=&quot;https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Configuration-and-Email-Settings&quot;&gt;install and configure&lt;/a&gt;
the integration.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I really like receiving the daily USPS Informed Delivery email notifications
with the images of mail being delivered so I can see if I even need to get
the mail for today or I can wait until I get a package I care about. It&apos;s
also helped me inquire when I didn&apos;t get a piece of mail it said was coming.
Having this all in Home Assistant as an animated gif with sensors has taken
it to a whole new level.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I recommend configuring the sensor list to track only what you are interested.
This will ensure the sensors update quickly only searching your email for the
delivery services you are most likely to use. This will create a bunch of
sensors for each delivery service (e.g., &lt;code&gt;sensor.mail_usps_mail&lt;/code&gt;,
&lt;code&gt;sensor.mail_amazon_packages&lt;/code&gt;).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Important: I search the all mail folder and archive all delivery email
notifications. If you delete the notification emails, the sensors
will not detect the arriving / delivered mail properly.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Finally, make sure you specify the image path to use the new media
integration! You can do this by setting the &lt;code&gt;Image Path&lt;/code&gt; to &lt;code&gt;/media/mail&lt;/code&gt;.
This will create a gif attachment &lt;code&gt;/media/mail/mail_today.gif&lt;/code&gt; of all the mail
being delivered to you that you can can view via the media browser or in a
notification!&lt;/p&gt;
&lt;p&gt;Here is what my configuration looked like:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/home-automation/mail-and-packages-settings-597.png&quot; alt=&quot;Installing Mail and Packages&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Camera Integration&lt;/h3&gt;
&lt;p&gt;I recommend having a camera pointing where packages are normally dropped off
(e.g., front porch) and mailbox if possible. This allows you to take a snapshot
when a package is delivered and use these in notifications.&lt;/p&gt;
&lt;p&gt;A smart doorbell with a video feed
has been working great for my setup. I use the
&lt;a href=&quot;https://www.amazon.com/Ubiquiti-UniFi-Protect-Doorbell-UVC-G4-DoorBell/dp/B08L3X9ZZX?tag=niemyjski-20&quot;&gt;Ubiquiti UniFi Protect G4 Doorbell&lt;/a&gt;
as it stores all the data locally. But you can use the
&lt;a href=&quot;https://store.google.com/us/product/nest_hello_doorbell&quot;&gt;Nest Hello&lt;/a&gt; or
&lt;a href=&quot;https://www.amazon.com/dp/B07WLP395R?tag=niemyjski-20&quot;&gt;Ring Video Doorbell&lt;/a&gt;. Just be careful
about your privacy settings and be aware of the cloud dependency.&lt;/p&gt;
&lt;p&gt;Depending on your camera you choose, you&apos;ll need to configure it in Home
Assistant.&lt;/p&gt;
&lt;h3&gt;Camera Proxy Integration&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;https://www.home-assistant.io/integrations/proxy/&quot;&gt;Camera Proxy integration&lt;/a&gt;
is a little known integration but worth it&apos;s weight in gold. This integration
lets us crop a camera into only the area we are interested in, thus removing a
ton of noise from an image. You make a camera containing only the area you are
interested. Below we turn the original camera content&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/home-automation/mail-camera-540.png&quot; alt=&quot;Original Camera&quot; /&gt;&lt;/p&gt;
&lt;p&gt;into&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/home-automation/mail-camera-proxy-265.jpg&quot; alt=&quot;Cropped Camera Proxy&quot; /&gt;&lt;/p&gt;
&lt;p&gt;In order to setup the image cropping your first need to know the offsets in
your existing image that you wish to crop. I&apos;d start by saving an image snapshot
from your camera feed and loading it into an image editing software like
&lt;a href=&quot;https://www.gimp.org&quot;&gt;GIMP&lt;/a&gt; that supports showing you the &lt;code&gt;X,Y&lt;/code&gt; coordinates
of where you clicked.&lt;/p&gt;
&lt;p&gt;Next, click on the upper left of where I wanted to start the crop. GIMP showed
me the &lt;code&gt;X1,Y1&lt;/code&gt; of &lt;code&gt;750,580&lt;/code&gt; when in the lower left toolbar. I then set &lt;code&gt;image_top&lt;/code&gt;
to &lt;code&gt;580&lt;/code&gt; and &lt;code&gt;image_left&lt;/code&gt; to &lt;code&gt;750&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Next, click on the lower left side where I wanted to end the crop. I got the
&lt;code&gt;X2,Y2&lt;/code&gt; of &lt;code&gt;1015,705&lt;/code&gt; when in the lower left toolbar. I then did some basic
subtraction and set &lt;code&gt;max_image_width&lt;/code&gt; and &lt;code&gt;max_stream_width&lt;/code&gt; to the value of
&lt;code&gt;X2-X1&lt;/code&gt; and set &lt;code&gt;max_image_height&lt;/code&gt; and &lt;code&gt;max_stream_height&lt;/code&gt; to the value of
&lt;code&gt;Y2-Y1&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Here is the yaml configuration that I used for my camera:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;- platform: proxy
  entity_id: camera.front_door
  name: Mailbox
  mode: crop
  max_image_width: 265
  max_image_height: 125
  max_stream_width: 265
  max_stream_height: 125
  image_top: 580
  image_left: 750
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can now reference this Camera Proxy integration as a normal camera named
&lt;code&gt;camera.mailbox&lt;/code&gt;!&lt;/p&gt;
&lt;h3&gt;Mailbox Sensor&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;If you already have a mailbox sensor you can skip this step.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I&apos;ve researched a lot of different solutions both DYI and paid products for
getting notifications when my mailbox was opened. Here was some of the things
I was looking for in a sensor:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Will it last the brutally cold winters and thousands of open and closes?&lt;/li&gt;
&lt;li&gt;Will it work in a metal faraday cage (e.g., metal mailbox)?&lt;/li&gt;
&lt;li&gt;What is the battery life?&lt;/li&gt;
&lt;li&gt;What is the max range the sensor data will transmit&lt;/li&gt;
&lt;li&gt;Can I get the sensor data locally?&lt;/li&gt;
&lt;li&gt;Will the company be around in five years?&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Ring Mailbox Sensor&lt;/h4&gt;
&lt;p&gt;I ended up going with &lt;a href=&quot;https://www.amazon.com/dp/B08J1NP9Z5?tag=niemyjski-20&quot;&gt;Ring Mailbox Sensor&lt;/a&gt;.
I was really hesitant at first given Ring cloud dependency and track record
around &lt;a href=&quot;https://arstechnica.com/tech-policy/2019/08/police-can-get-your-ring-doorbell-footage-without-a-warrant-report-says/&quot;&gt;camera privacy&lt;/a&gt;.
I&apos;ll cover this more in detail in a review later.&lt;/p&gt;
&lt;p&gt;However, It was the only solution that worked with my metal mailbox due to the
antenna and checked off most of the criteria.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/home-automation/mail-ring-antenna-1200.jpg&quot; alt=&quot;Ring Antenna&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The cable was also braided which gives me confidence it will last over time. I
also wrapped the cable with some extra tape and taped it to the bottom of the
mailbox and to the door.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/home-automation/mail-ring-sensor-1200.jpg&quot; alt=&quot;Ring Sensor&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I followed the Ring installation documentation and setup the light bridge as
well.&lt;/p&gt;
&lt;h4&gt;Ring Integration&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;This integration assumes you are already using &lt;a href=&quot;https://www.home-assistant.io/docs/mqtt/&quot;&gt;MQTT&lt;/a&gt;.
I&apos;d recommend using the &lt;a href=&quot;https://github.com/home-assistant/addons/blob/master/mosquitto/README.md&quot;&gt;official Mosquitto add-on&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Lets install the &lt;a href=&quot;https://github.com/tsightler/ring-mqtt-ha-addon&quot;&gt;Ring Devices via MQTT Add-on&lt;/a&gt;
from &lt;a href=&quot;https://github.com/hassio-addons/repository&quot;&gt;Home Assistant Community Add-ons&lt;/a&gt;
repository. This allows us to run Ring discovery locally and have our devices integrate
automatically with Home Assistant by taking advantage of all the awesome
community work that&apos;s been done. Behind the scenes the add-on uses auto discovery
to expose configured devices to Home Assistant automatically!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We have to install this addon because the official Ring integration doesn&apos;t
support the Ring light bridge that the mailbox sensor uses.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Navigate to &lt;a href=&quot;http://homeassistant.local:8123/hassio/store&quot;&gt;http://homeassistant.local:8123/hassio/store&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Click on the option menu in the upper right and select &lt;code&gt;Repositories&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Enter &lt;code&gt;https://github.com/tsightler/ring-mqtt-ha-addon&lt;/code&gt; and then click the &lt;code&gt;Add&lt;/code&gt; button.&lt;/li&gt;
&lt;li&gt;Click the close button to close the &lt;code&gt;Manage add-on repositories&lt;/code&gt; dialog.&lt;/li&gt;
&lt;li&gt;Select the &lt;code&gt;Ring Devices&lt;/code&gt; add-on or access via the url &lt;a href=&quot;http://homeassistant.local:8123/hassio/addon/03cabcc9_ring_mqtt/info&quot;&gt;http://homeassistant.local:8123/hassio/addon/03cabcc9_ring_mqtt/info&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Click the &lt;code&gt;Install&lt;/code&gt; button.&lt;/li&gt;
&lt;li&gt;Click the &lt;code&gt;Start&lt;/code&gt; button.&lt;/li&gt;
&lt;li&gt;Finally, open the Web UI once the addon is started and login to your Ring Account.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The Ring devices should now be discovered and shown under the MQTT Integration.
I had two devices created for me and a mailbox sensor
&lt;code&gt;binary_sensor.mailbox_motion&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Automations&lt;/h3&gt;
&lt;p&gt;The possibilities are endless for what you can do when the mailbox is opened.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I&apos;d recommend checking out &lt;a href=&quot;https://www.home-assistant.io/docs/automation/using_blueprints/&quot;&gt;Home Assistant Blueprints&lt;/a&gt;
for quickly importing and consuming automations.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;Send picture notification when mailbox is opened&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/home-automation/mail-delivered-notification-600.jpg&quot; alt=&quot;Mail Delivered Notification&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I&apos;m using the following &lt;a href=&quot;https://community.home-assistant.io/t/send-camera-snapshot-notification-on-motion/254565&quot;&gt;Home Assistant Blueprint&lt;/a&gt;
to send a notification when the &lt;code&gt;binary_sensor.mailbox_motion&lt;/code&gt; is triggered
using the &lt;code&gt;camera.mailbox&lt;/code&gt; for the camera source.&lt;/p&gt;
&lt;p&gt;I customized the Title to &lt;code&gt;Mailbox opened&lt;/code&gt; and used a template for
the Message of &lt;code&gt;Expecting {{ states(&quot;sensor.mail_usps_mail&quot;) }} items to be delivered.&lt;/code&gt;
This uses one of the sensors that our &lt;a href=&quot;#installing-mail-and-packages-integration&quot;&gt;Mail and Package integration&lt;/a&gt;
added.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/home-automation/mail-blueprint-notification-800.png&quot; alt=&quot;Mail Notification Blueprint&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;Store a media snapshot when mailbox is opened.&lt;/h4&gt;
&lt;p&gt;I&apos;m using the following &lt;a href=&quot;https://community.home-assistant.io/t/save-a-camera-snapshot-with-archive-when-motion-is-detected/256606&quot;&gt;Home Assistant Blueprint&lt;/a&gt;
to take a camera snapshots and save them to &lt;code&gt;/media/mail&lt;/code&gt; for easy viewing.&lt;/p&gt;
&lt;p&gt;I set the &lt;code&gt;File Path&lt;/code&gt; to &lt;code&gt;/media/mail/last_opened.jpg&lt;/code&gt; and the
&lt;code&gt;Archive File Path&lt;/code&gt; to
&lt;code&gt;/media/mail/archive/opened_{{ now().strftime(&quot;%Y%m%d-%H%M%S&quot;) }}.jpg&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/home-automation/mail-blueprint-snapshot-800.png&quot; alt=&quot;Mail Snapshot Blueprint&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;Automation ideas&lt;/h4&gt;
&lt;p&gt;The possibilities are endless for what notifications you could do when the
mailbox is opened like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Play a &lt;a href=&quot;/blog/how-to-send-text-to-speech-notifications-with-home-assistant-and-node-red&quot;&gt;Text-to-Speech (TTS) notification&lt;/a&gt; for &lt;code&gt;You Got Mail&lt;/code&gt; to any &lt;code&gt;media_player&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Stream the &lt;code&gt;camera.mailbox&lt;/code&gt; feed to a &lt;code&gt;media_player&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Turn on the outside lights if the mailbox is opened at night.&lt;/li&gt;
&lt;li&gt;Send &lt;a href=&quot;https://github.com/moralmunky/Home-Assistant-Mail-And-Packages/wiki/Example-Automations-and-Templates#delivery-summary-text-sensor-template-example&quot;&gt;notifications&lt;/a&gt; when mail is scheduled to be delivered.&lt;/li&gt;
&lt;li&gt;Trigger object recognition to see who is at your mailbox.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Lovelace integration&lt;/h3&gt;
&lt;p&gt;You can also use a picture card to display the last snapshot of who opened the
mailbox &lt;code&gt;/media/mail/last_opened.jpg&lt;/code&gt; as well as one for all the mail out for
delivery today with the &lt;code&gt;/media/mail/mail_today.gif&lt;/code&gt; source. I&apos;ve opened a
&lt;a href=&quot;https://github.com/home-assistant/frontend/discussions/8001&quot;&gt;feature request&lt;/a&gt;
to make this much easier!&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;I hope this article helps you be better informed about mail deliveries and what
packages to expect are coming. It&apos;s helped give me more confidence and saved
myself countless empty trips to the mailbox.&lt;/p&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>Disturbing trends in smart home products and services</title><link>https://blakeniemyjski.com/blog/disturbing-trends-in-smart-home-products-and-services/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/disturbing-trends-in-smart-home-products-and-services/</guid><pubDate>Thu, 03 Dec 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Over the last decade there has been some really great software products that
have been shut down with little to no warning. This is getting so common that
someone created a website called &lt;a href=&quot;https://killedbygoogle.com&quot;&gt;Killed by Google&lt;/a&gt;.
After Google &lt;a href=&quot;http://googlereader.blogspot.com/2013/03/powering-down-google-reader.html&quot;&gt;shutdown Reader in 2013&lt;/a&gt;, I spent months trying to find a replacement.
During this research I determined I should try to find solutions to services
I use everyday that wouldn&apos;t be shut down in some board room over profit
margins.&lt;/p&gt;
&lt;p&gt;About five years ago, I started researching smart home products and services
as well as automation software to use in my next home.  I started to notice the
following trends:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Software products and services being shutdown with no notice&lt;/li&gt;
&lt;li&gt;No software updates for promised features or security fixes&lt;/li&gt;
&lt;li&gt;Software updates that remove out of the box functionality (features/bricking)&lt;/li&gt;
&lt;li&gt;Free software that suddenly requires a paid subscription&lt;/li&gt;
&lt;li&gt;Companies are selling or hording your data.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For the sake of relevancy let&apos;s just cover what&apos;s happened since the start of
2019.&lt;/p&gt;
&lt;h2&gt;2019&lt;/h2&gt;
&lt;h3&gt;ecobee&lt;/h3&gt;
&lt;p&gt;When ecobee launched the &lt;a href=&quot;https://www.amazon.com/ecobee-Switch-Smart-Amazon-Built/dp/B0798LCLJ5?tag=niemyjski-20&quot;&gt;ecobee Switch+&lt;/a&gt;
I became super interested in them due to the shear amount of sensors in a small
device. They had also stated there would be API support as well as it would be
integrated as a remote temperature sensor for an
&lt;a href=&quot;https://www.amazon.com/ecobee4-Thermostat-Built-Sensor-Included/dp/B08BZTPMW1?tag=niemyjski-20&quot;&gt;ecobee smart thermostat&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It took most of 2019, before 15 of my ecobee switches got the firmware update
to be added to my thermostat but the delivered promise of api support never came.&lt;/p&gt;
&lt;h3&gt;Best Buy Insignia shut down&lt;/h3&gt;
&lt;p&gt;Best Buy &lt;a href=&quot;https://www.theverge.com/2019/9/6/20853671/best-buy-connect-insignia-smart-plug-wifi-freezer-mobile-app-shutdown-november-6&quot;&gt;quietly announced on &lt;strong&gt;September 6th&lt;/strong&gt;&lt;/a&gt;
that it was shutting down it&apos;s Insignia brand of smart home devices on
&lt;strong&gt;November 6th&lt;/strong&gt;. App access would no longer be available rendering most of the
products unusable.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The announcement has already been purged from the Best Buy Insignia website.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Lowe&apos;s Iris shut down&lt;/h3&gt;
&lt;p&gt;Lowe&apos;s Iris platform first launched in 2012, offered free and paid subscription
options. An &lt;a href=&quot;https://www.theverge.com/2019/2/2/18208407/lowes-iris-smart-home-platform-shutting-down-march-2019&quot;&gt;announcement on &lt;strong&gt;January 31st&lt;/strong&gt;&lt;/a&gt;
that it was shutting down on &lt;strong&gt;March 31st&lt;/strong&gt;, rendering devices dead.&lt;/p&gt;
&lt;p&gt;I&apos;m happy they offered a program to trade in any purchased devices for a
prepaid credit card so users could migrate to a different solution.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The announcement has already been purged from the Lowe&apos;s Iris website.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Chamberlain MyQ Breaking API Changes&lt;/h3&gt;
&lt;p&gt;When it comes to automating garage door openers there are few out of the box
solutions that work and are as popular as the
&lt;a href=&quot;https://www.amazon.com/Smart-Garage-Opener-Chamberlain-MYQ-G0301/dp/B075H7Z5L8?tag=niemyjski-20&quot;&gt;Chamberlain MyQ Smart Garage Door Opener&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;An open source project created a python library that many other open source
projects use to integrate with MyQ using the MyQ Api. Unfortunately the MyQ
team has broken this &lt;a href=&quot;https://github.com/arraylabs/pymyq/issues/27&quot;&gt;api multiple times&lt;/a&gt;
in 2019 causing automations to break.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I hesitate a little bit every time I see an MyQ app update on my phone. as
I&apos;m always wondering if this will trigger my opener firmware to be updated
and break my home automations.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It&apos;s been a cat and mouse game between smart home developers and the MyQ team.&lt;/p&gt;
&lt;h3&gt;Stringify shut down&lt;/h3&gt;
&lt;p&gt;Stringify was launched in 2014 and bought by Comcast in 2017. Fast forward 18
months later to &lt;strong&gt;April 8th&lt;/strong&gt;, Stringify &lt;a href=&quot;https://www.howtogeek.com/410576/stringify-is-shutting-down-after-five-years/&quot;&gt;announces via email&lt;/a&gt;
it will be shutting down new sign ups immediately and all data will be deleted
on &lt;strong&gt;July 8th&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&amp;lt;blockquote class=&quot;twitter-tweet&quot;&amp;gt;&amp;lt;a href=&quot;https://twitter.com/i/status/1115370840353202177&quot;&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;/p&gt;
&lt;h3&gt;Sonos bricking devices&lt;/h3&gt;
&lt;p&gt;In &lt;strong&gt;December&lt;/strong&gt; it was &lt;a href=&quot;https://www.theverge.com/2019/12/30/21042871/sonos-recycle-mode-trade-up-program-controversy&quot;&gt;uncovered&lt;/a&gt;
that Sonos was bricking devices upon agreeing to recycle your device or by
upgrading the speaker.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Recycle Mode is a state your device enters 21 days after recycling
confirmation in the Sonos app. In Recycle Mode, all data is erased and the
device is permanently deactivated so you can safely and securely dispose of
it. Once a device is in Recycle Mode, it cannot be reactivated.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This decision was &lt;a href=&quot;https://www.theverge.com/2020/3/5/21166777/sonos-ending-recycle-mode-trade-up-program-sustainability&quot;&gt;overturned&lt;/a&gt; in &lt;strong&gt;March 2020&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;Works with Nest shut down&lt;/h3&gt;
&lt;p&gt;Google &lt;a href=&quot;https://blog.google/products/google-nest/helpful-home/&quot;&gt;announced&lt;/a&gt; on
&lt;strong&gt;May 7th&lt;/strong&gt; that it would be shutting down &lt;code&gt;Works with Nest&lt;/code&gt; on &lt;strong&gt;August 31st&lt;/strong&gt;.
It was only after massive backlash that they &lt;a href=&quot;https://blog.google/products/google-nest/updates-works-with-nest/&quot;&gt;announced&lt;/a&gt;
that they would continue to support integrations until they were available in
the new API&apos;s backed by a single Google Account.&lt;/p&gt;
&lt;p&gt;Current users have to be super careful not to migrate their existing Nest
accounts to the new Google account for fear of losing integrations. I believe
this migration and integration support for various hubs will be completed in
2021.&lt;/p&gt;
&lt;h3&gt;Wink product availability&lt;/h3&gt;
&lt;p&gt;For over five months users in the Wink ecosystem couldn&apos;t purchase any new
&lt;a href=&quot;https://www.amazon.com/Wink-WNKHUB-2US-Smart-home-White/dp/B01KW8WGZQ?tag=niemyjski-20&quot;&gt;Wink devices&lt;/a&gt;
due to &lt;a href=&quot;https://www.howtogeek.com/409925/why-we-cant-recommend-wink-hubs-anymore/&quot;&gt;product availability issues&lt;/a&gt;.
This lasted from &lt;strong&gt;November 2018&lt;/strong&gt; through &lt;strong&gt;March 2019&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;2020&lt;/h2&gt;
&lt;h3&gt;August Connect firmware&lt;/h3&gt;
&lt;p&gt;In early 2020, I received a firmware update that caused all five of my
&lt;a href=&quot;https://www.amazon.com/dp/B0752VPHVK?tag=niemyjski-20&quot;&gt;August Smart Lock Pro Connect Hubs&lt;/a&gt;
from connecting to my Wi-Fi. Slowly but surely they&apos;ve came online but every
now and then I&apos;ll have one drop offline. Thankfully I have the Z-Wave version
and access the locks that way.&lt;/p&gt;
&lt;h3&gt;Dark Sky shut down&lt;/h3&gt;
&lt;p&gt;On &lt;strong&gt;March 31st&lt;/strong&gt; &lt;a href=&quot;https://www.theverge.com/2020/3/31/21201666/apple-acquires-weather-app-dark-sky-shut-down-android-wear-os-ios&quot;&gt;Apple acquired Dark Sky&lt;/a&gt;
and announced it would be shutting down it&apos;s API at the end of &lt;strong&gt;2021&lt;/strong&gt;. A lot
of smart home users used the API to power weather automations.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Apple did the right thing by giving users plenty of time to move off of the API.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;ecobee&lt;/h3&gt;
&lt;p&gt;As stated in the 2019 section, when remote thermostat sensors support was
rolled out to the &lt;a href=&quot;https://www.amazon.com/ecobee-Switch-Smart-Amazon-Built/dp/B0798LCLJ5?tag=niemyjski-20&quot;&gt;ecobee Switch+&lt;/a&gt;
via an firmware update. A few months later it was removed with a second firmware
update. Now only some of my switches are paired as a remote sensor to my
&lt;a href=&quot;https://www.amazon.com/ecobee4-Thermostat-Built-Sensor-Included/dp/B08BZTPMW1?tag=niemyjski-20&quot;&gt;ecobee smart thermostats&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To make matters worse there is an unresolved server side bug that keeps
renaming any switch that has a custom defined name back to the serial number of
the device. This should never happen to a consumer device paired with cloud
outages leaves a lot to be desired from ecobee when it comes to software.
I recommend using ecobee&apos;s HomeKit support to get around these limitations
where possible.&lt;/p&gt;
&lt;h3&gt;Home Assistant breaking changes and OS changes&lt;/h3&gt;
&lt;p&gt;In &lt;strong&gt;May&lt;/strong&gt; the Home Assistant team &lt;a href=&quot;https://www.home-assistant.io/blog/2020/05/26/installation-methods-and-community-guides-wiki/&quot;&gt;announced&lt;/a&gt;
that they were dropping support for generic linux installs without consulting
the community. This was a major change that left many users frustrated. It makes
sense that users should be using the managed Home Assistant operating system,
but a large portion of users installed it on Ubuntu and other linux operating
systems of which may have been used for other purposes.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;As the platform matures there will be less churn and breaking changes.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;As we near the 1.0 release of Home Assistant, we&apos;ve seen more integrations
be removed as well as changes to the API surface. Although these are good
changes they still require work by end users. This is to be expected of pre
version 1.0 software.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Although breaking changes do happen from time to time. You control the
updates on your own time, helping to limit breaking changes.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Chamberlain MyQ breaking API changes&lt;/h3&gt;
&lt;p&gt;For the second year running the &lt;a href=&quot;https://www.amazon.com/Smart-Garage-Opener-Chamberlain-MYQ-G0301/dp/B075H7Z5L8?tag=niemyjski-20&quot;&gt;Chamberlain MyQ Smart Garage Door Opener&lt;/a&gt; has had issues due to MyQ trying lock out users
from using the MyQ API. It has been a cat and mouse game between smart home
developers and the MyQ team.&lt;/p&gt;
&lt;h3&gt;Tuya firmware locking&lt;/h3&gt;
&lt;p&gt;I love &lt;a href=&quot;/blog/securing-smart-home-devices-with-esphome-and-home-assistant/&quot;&gt;flashing retail devices&lt;/a&gt;
from the stock firmware to ESPHome. I do this mainly for privacy and to ensure it
can be brought into Home Assistant easily. It appears there is a
&lt;a href=&quot;https://github.com/ct-Open-Source/tuya-convert/wiki/Collaboration-document-for-PSK-Identity-02&quot;&gt;new firmware&lt;/a&gt;
on some Tuya devices that is preventing flashing causing vendor lock in to
unknown third party services. I can&apos;t blame companies for wanting to lock down
the firmware, it just leaves little options for smart home integrations when
the company leaves the space.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Check out &lt;a href=&quot;https://www.youtube.com/watch?v=imKZbhJ8lvU&quot;&gt;this workaround&lt;/a&gt; for
how to flash devices that are locked down.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Logitech Harmony Express remote shut down&lt;/h3&gt;
&lt;p&gt;On &lt;strong&gt;July 9th&lt;/strong&gt; Logitech &lt;a href=&quot;https://support.logi.com/hc/en-us/community/posts/360048494053&quot;&gt;announced&lt;/a&gt;
it was discontinuing the 15 month old smart remote control that cost $250. It
would also be shutting down the Alexa service on &lt;strong&gt;September 30th&lt;/strong&gt;, rendering
the device useless to control by it&apos;s main selling feature.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Logitech is offering a free upgrade if you bought this product!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Wink Subscription&lt;/h3&gt;
&lt;p&gt;On &lt;strong&gt;May 20th&lt;/strong&gt; Wink &lt;a href=&quot;https://blog.wink.com/wink-blog/2020/5/6/introducing-wink-subscription&quot;&gt;announced&lt;/a&gt;
that it would start charging all users $4.99 a month. Failure to subscribe
would result in severely limited functionality, pretty much forcing users to
upgrade in order to add or edit devices, add automations, or use voice control.&lt;/p&gt;
&lt;h3&gt;IFTTT Subscription&lt;/h3&gt;
&lt;p&gt;On &lt;strong&gt;September 10th&lt;/strong&gt; IFTTT &lt;a href=&quot;https://ifttt.com/explore/introducing_ifttt_pro&quot;&gt;announced&lt;/a&gt;
a pro plan that costs $9.99 a month. The existing free plan would be severely
limited to 3 applets.&lt;/p&gt;
&lt;h3&gt;Smart Things Classic shut down&lt;/h3&gt;
&lt;p&gt;On &lt;strong&gt;June 24th&lt;/strong&gt; SmartThings &lt;a href=&quot;https://community.smartthings.com/t/update-on-smartthings-classic-shutdown/213037&quot;&gt;announced&lt;/a&gt;
that SmartThings classic will be shutting down on &lt;strong&gt;December 1st&lt;/strong&gt; . All users
who haven&apos;t migrated to the new SmartThings API will have major disruptions
(e.g., your integrations will be deleted).&lt;/p&gt;
&lt;h3&gt;Wemo cloud only&lt;/h3&gt;
&lt;p&gt;On &lt;strong&gt;May 26th&lt;/strong&gt; Wemo &lt;a href=&quot;https://www.androidpolice.com/2020/05/26/wemo-is-replacing-its-awful-network-based-smart-home-management-with-online-accounts/&quot;&gt;announced&lt;/a&gt;
&lt;code&gt;Wemo Accounts&lt;/code&gt;, a new way to manage all of your devices with an online account.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The new account system shifts all of the device management away from your
local network and onto cloud services, which should reduce problems with the
Wemo apps and integrations with third-party services. However, account
creation is currently broken, which is very on-brand for Wemo.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;OSRAM shut down&lt;/h3&gt;
&lt;p&gt;On &lt;strong&gt;March 9th&lt;/strong&gt;, OSRAM &lt;a href=&quot;https://www.osram-group.com/en/media/press-releases/pr-2020/09-03-2020&quot;&gt;announced&lt;/a&gt;
that it would be shutting down it&apos;s smart lighting systems servers on
&lt;strong&gt;August 31st, 2021&lt;/strong&gt;. They provided a &lt;a href=&quot;https://www.osram.com/cb/lightify/index.jsp&quot;&gt;faq&lt;/a&gt;
and recommended resetting all your devices and pair them to a Zigbee bridge.&lt;/p&gt;
&lt;h3&gt;Hue Bridge v1 shut down&lt;/h3&gt;
&lt;p&gt;On &lt;strong&gt;March 6th&lt;/strong&gt; Hue &lt;a href=&quot;https://www.theverge.com/circuitbreaker/2020/3/6/21167813/philips-hue-bridge-hub-internet-connectivity-discontinued-cloud-updates&quot;&gt;announced&lt;/a&gt;
that it would be shutting down internet connectivity for the first generation
bridge on &lt;strong&gt;April 30th&lt;/strong&gt;.  You can still use the bridge locally but they
recommended upgrading to the newer generation bridge.&lt;/p&gt;
&lt;h3&gt;Automatic shut down&lt;/h3&gt;
&lt;p&gt;On &lt;strong&gt;May 1st&lt;/strong&gt; Automatic Labs &lt;a href=&quot;https://www.theverge.com/2020/5/1/21244295/automatic-labs-vehicle-tracking-service-covid-19-pandemic-rebate-may-28th&quot;&gt;announced&lt;/a&gt;
that it would be shutting down it&apos;s vehicle-tracking service on &lt;strong&gt;May 28th&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;Android Things shut down&lt;/h3&gt;
&lt;p&gt;On &lt;strong&gt;December 18th&lt;/strong&gt; Google &lt;a href=&quot;https://developer.android.com/things/faq&quot;&gt;announced&lt;/a&gt;
that it would be shut down the sign up of Android Things new projects on
&lt;strong&gt;January 5th, 2021&lt;/strong&gt;. On &lt;strong&gt;January 5th, 2022&lt;/strong&gt; it will be shutting down the
service completely.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;It&apos;s worth looking at all the services and products that were shut down and
disappeared like they never even existed. Some of these products and service
had amassed hundreds of thousands of users. Is there an emerging pattern? Would
you ever trust this company again?&lt;/p&gt;
&lt;p&gt;I recommend buying devices that can be controlled locally though ESPHome, mqtt,
Thread, Zigbee or Z-Wave. Stay away from products that use a vendor specific
protocol especially if the company has a history of shutting down services or
products. I&apos;d also avoid large investments in new products by any company
unless they have a positive track record or can be controlled via a standard.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If an online service doesn&apos;t charge you a monthly fee, you are the product.
Products that have a free cloud dependency have to find a way to turn a
profit or they will shut down. Someone has to pay for the cloud services.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I do feel comfortable sticking with HomeKit supported devices as I can control
them locally and Apple has a good track record of privacy, supporting devices,
and part of HomeKit was &lt;a href=&quot;https://github.com/apple/HomeKitADK&quot;&gt;open sourced&lt;/a&gt; too.&lt;/p&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>Securing smart home devices with ESPHome and Home Assistant</title><link>https://blakeniemyjski.com/blog/securing-smart-home-devices-with-esphome-and-home-assistant/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/securing-smart-home-devices-with-esphome-and-home-assistant/</guid><pubDate>Sat, 17 Oct 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;When I first came across the &lt;a href=&quot;https://esphome.io&quot;&gt;ESPHome&lt;/a&gt; project a year or so
ago. I thought it was interesting and something that I would use if I was
building a smart device from scratch. I thought of all the things I could do,
but I didn&apos;t really like the idea of all the maintenance I could foresee.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;ESPHome is a system to control your ESP8266/ESP32 by simple yet powerful
configuration files and control them remotely through Home Automation systems.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It took some persuasion by &lt;a href=&quot;https://www.architectryan.com&quot;&gt;Ryan Hoffman&lt;/a&gt;, who
has been building a bunch of custom made ESPHome projects to show me what is
possible with ESPHome. I&apos;m starting to kick myself in the butt (better now than
never) that I didn&apos;t take time to check this project out sooner. I&apos;ve since
purchased multiple ESPHome devices such as the
&lt;a href=&quot;https://www.amazon.com/gp/product/B079MFTYMV?tag=niemyjski-20&quot;&gt;Gosund Mini WiFi Outlet&lt;/a&gt;
and &lt;a href=&quot;https://www.amazon.com/gp/product/B0718T232Z?tag=niemyjski-20&quot;&gt;HiLetgo ESP-WROOM-32 ESP32&lt;/a&gt;.
The next device that I think will work great for holiday lights is the
&lt;a href=&quot;https://www.amazon.com/Gosund-Protector-Controlled-Individually-Required/dp/B07P65GJS1?tag=niemyjski-20&quot;&gt;Gosund Smart Power Strip&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I think one really huge benefit that mostly goes unnoticed for for a larger part
of the smart home community is all the retail devices running ESPHome. This
means that you can buy devices you might not trust on your networks due to them
being from a company you&apos;ve never heard from and completely flashing the
firmware to something you&apos;ve created. Yes, there still could be software embedded
onto different chips but it&apos;s unlikely.&lt;/p&gt;
&lt;p&gt;In the sections below I will cover installing ESPHome to work with
&lt;a href=&quot;https://www.home-assistant.io&quot;&gt;Home Assistant&lt;/a&gt;, flashing a device, and
adding the device to Home Assistant.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;ESPHome can use mqtt so you can use it without Home Assistant too!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Benefits&lt;/h2&gt;
&lt;p&gt;Here are some of the benefits I&apos;ve noticed since using some of my first devices.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Open Source (Audited)&lt;/li&gt;
&lt;li&gt;Easy to manage with Over-the-air (OTA) updates&lt;/li&gt;
&lt;li&gt;Flash any ESPHome device (including retail devices)&lt;/li&gt;
&lt;li&gt;Cheap&lt;/li&gt;
&lt;li&gt;Rock Solid&lt;/li&gt;
&lt;li&gt;Rich local automation support right inside the firmware that can use Home Assistant states&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Installing the ESPHome add-on&lt;/h2&gt;
&lt;p&gt;Lets install the &lt;a href=&quot;https://github.com/esphome/hassio&quot;&gt;ESPHome add-on&lt;/a&gt; from
&lt;a href=&quot;https://github.com/hassio-addons/repository&quot;&gt;Home Assistant Community Add-ons&lt;/a&gt;
repository. This allows us to run ESPHome locally and have our devices integrate
automatically with Home Assistant by taking advantage of all the awesome
community work that&apos;s been done. Behind the scenes the add-on uses auto discovery
to expose configured devices to Home Assistant automatically!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;There is an &lt;a href=&quot;https://esphome.io/guides/getting_started_hassio.html&quot;&gt;official getting started&lt;/a&gt;
guide, but I found it slightly confusing and the steps below straight forward!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Navigate to &lt;a href=&quot;http://homeassistant.local:8123/hassio/store&quot;&gt;http://homeassistant.local:8123/hassio/store&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Click on the option menu in the upper right and select &lt;code&gt;Repositories&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Enter &lt;code&gt;https://github.com/hassio-addons/repository&lt;/code&gt; and then click the &lt;code&gt;Add&lt;/code&gt; button.&lt;/li&gt;
&lt;li&gt;Click the close button to close the &lt;code&gt;Manage add-on repositories&lt;/code&gt; dialog.&lt;/li&gt;
&lt;li&gt;Select the &lt;code&gt;ESPHome&lt;/code&gt; add-on or access via the url &lt;a href=&quot;http://homeassistant.local:8123/hassio/addon/a0d7b954_esphome/info&quot;&gt;http://homeassistant.local:8123/hassio/addon/a0d7b954_esphome/info&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Click the &lt;code&gt;Install&lt;/code&gt; button.&lt;/li&gt;
&lt;li&gt;Toggle the &lt;code&gt;Show in sidebar&lt;/code&gt; option for ease of use (Optional)&lt;/li&gt;
&lt;li&gt;Finally, click the &lt;code&gt;Start&lt;/code&gt; button.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;ESPHome should now be running as an add-on and you can access the ESPHome web ui
on via the sidebar or directly using
&lt;a href=&quot;http://homeassistant.local:8123/hassio/ingress/a0d7b954_esphome&quot;&gt;http://homeassistant.local:8123/hassio/ingress/a0d7b954_esphome&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;ESPHome devices&lt;/h2&gt;
&lt;p&gt;A huge misconception I had with ESPHome was that I had to build all my devices.
It wasn&apos;t until recently that it was pointed out to me that there are a ton of
cheap consumer devices from brands you&apos;ve never heard of, and more than likely
never have trusted to ever connect to your IoT/NoT network. This is super
awesome game changer! You can build your own device firmware, flash the
manufactures device knowing exactly what the device is doing, and then reflash
it Over-the-air (OTA) via ESPHome with a few clicks! This really saves on the
maintenance as well as having more trust for the security of the device.&lt;/p&gt;
&lt;p&gt;My first ESPHome compatible device was the &lt;a href=&quot;https://www.amazon.com/gp/product/B079MFTYMV?tag=niemyjski-20&quot;&gt;Gosund Mini WiFi Outlet&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/home-automation/esphome-gosund-smart-plug-695.png&quot; alt=&quot;Gosund Mini WiFi Outlet&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I&apos;d recommend checking out one of the following links to find retail devices
that work with ESPHome&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.amazon.com/s?k=esphome&amp;amp;tag=niemyjski-20&quot;&gt;Amazon ESPHome&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.amazon.com/s?k=smart+2.4GHz+WiFi+Only&amp;amp;tag=niemyjski-20&quot;&gt;Amazon 2.4GHz WiFi Only&lt;/a&gt; (&lt;code&gt;2.4GHz WiFi Only&lt;/code&gt; is a really good indication a device is running ESPHome, but double check)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://esphome-configs.io/devices/&quot;&gt;ESPHome Configs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://templates.blakadder.com/us.html&quot;&gt;TASMOTA devices&lt;/a&gt; (An ESPHome alternative)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once you have a device we need to flash it, in order to flash it we need to
configure some software and hardware.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Please note you may BRICK your device by flashing it or break the terms of
any device/licenses.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Hardware Requirements&lt;/h3&gt;
&lt;p&gt;I used a spare &lt;a href=&quot;https://www.amazon.com/ELEMENT-Element14-Raspberry-Pi-Motherboard/dp/B07P4LSDYV?tag=niemyjski-20&quot;&gt;Raspberry Pi 3 B+&lt;/a&gt;
that I had laying around, otherwise any Pi that supports Wifi will work like the
&lt;a href=&quot;https://www.amazon.com/CanaKit-Raspberry-4GB-Starter-Kit/dp/B07V5JTMV9?tag=niemyjski-20&quot;&gt;Raspberry Pi 4 Model B&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Please note that you can use any free hardware granted you have an unused wifi
adaptor. You just need to meet and it meets the requirements of
&lt;a href=&quot;https://github.com/ct-Open-Source/tuya-convert&quot;&gt;tuya-convert&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Installing tuya-convert on a Pi&lt;/h3&gt;
&lt;p&gt;In this section I&apos;ll be configuring the Raspberry Pi to run the flashing
software &lt;a href=&quot;https://github.com/ct-Open-Source/tuya-convert&quot;&gt;tuya-convert&lt;/a&gt;. Please
follow the steps below to get &lt;code&gt;tuya-convert&lt;/code&gt; up and running.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Download &lt;a href=&quot;https://www.raspberrypi.org/downloads/raspberry-pi-os/&quot;&gt;&lt;code&gt;Raspberry Pi OS&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Install the &lt;a href=&quot;https://www.raspberrypi.org/downloads/&quot;&gt;&lt;code&gt;Raspberry Pi Imager&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Insert a 2GB or bigger &lt;a href=&quot;https://www.amazon.com/dp/B07B9KTLJZ?tag=niemyjski-20&quot;&gt;SD Card&lt;/a&gt; into your computer.&lt;/li&gt;
&lt;li&gt;Launch the &lt;code&gt;Raspberry Pi Imager&lt;/code&gt; and image the SD Card with the downloaded &lt;code&gt;Raspberry Pi OS&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Remount the imaged SD Card.&lt;/li&gt;
&lt;li&gt;Navigate to the root of the SD Card and create a file called &lt;code&gt;ssh&lt;/code&gt; (e.g., &lt;code&gt;touch ssh&lt;/code&gt;).
This will allow you to &lt;code&gt;SSH&lt;/code&gt; into the Pi in a headless manner.&lt;/li&gt;
&lt;li&gt;Eject the SD Card and insert into your Pi.&lt;/li&gt;
&lt;li&gt;Ensure you have ethernet plugged into your Pi&lt;/li&gt;
&lt;li&gt;Power on the Pi and wait.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SSH&lt;/code&gt; into the Pi &lt;code&gt;ssh pi@10.0.0.100&lt;/code&gt; (you might need to figure out the ip using your router).&lt;/li&gt;
&lt;li&gt;Enter in the default password of &lt;code&gt;raspberry&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Install git using &lt;code&gt;sudo apt-get install git&lt;/code&gt; and enter &lt;code&gt;yes&lt;/code&gt; when prompted.&lt;/li&gt;
&lt;li&gt;Install &lt;code&gt;tuya-convert&lt;/code&gt; by cloning it with &lt;code&gt;git clone https://github.com/ct-Open-Source/tuya-convert.git&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Navigate to the &lt;code&gt;tuya-convert&lt;/code&gt; directory using &lt;code&gt;cd tuya-convert&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Install the dependencies by running &lt;code&gt;./install_prereq.sh&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now that &lt;code&gt;tuya-convert&lt;/code&gt; is installed, we need to switch gears and build our
custom firmware for our device.&lt;/p&gt;
&lt;h3&gt;Building the ESPHome Firmware&lt;/h3&gt;
&lt;p&gt;Now it&apos;s time to jump back into the ESPHome addon we configured earlier. You
can access the ESPHome web ui from the sidebar or directly using
&lt;a href=&quot;http://homeassistant.local:8123/hassio/ingress/a0d7b954_esphome&quot;&gt;http://homeassistant.local:8123/hassio/ingress/a0d7b954_esphome&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I&apos;ll be building the firmware for the
&lt;a href=&quot;https://www.amazon.com/gp/product/B079MFTYMV?tag=niemyjski-20&quot;&gt;Gosund Mini WiFi Outlet&lt;/a&gt;,
if you are using a different ESPHome device, you will need to tweak the
following steps slightly.&lt;/p&gt;
&lt;h4&gt;Create new ESPHome device&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;Click the Add button in the lower right hand side to bring up a
&lt;code&gt;Create New Node&lt;/code&gt; modal.&lt;/li&gt;
&lt;li&gt;Enter in the new Node Name and click &lt;code&gt;Next&lt;/code&gt;. I entered &lt;code&gt;gosund_wp3_02&lt;/code&gt; as this is my second device.&lt;/li&gt;
&lt;li&gt;Select the device type and and click &lt;code&gt;Next&lt;/code&gt;. I selected &lt;code&gt;Generic ESP8266&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Enter in your WiFi network information or some garbage text (we will configure them via secrets in a little bit) and click &lt;code&gt;Next&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/home-automation/esphome-dashboard-status-695.png&quot; alt=&quot;ESPHome Dashboard Status&quot; /&gt;&lt;/p&gt;
&lt;p&gt;It&apos;s worth pointing out that you may see a red line at the top of your device.
This means that your device is offline and hasn&apos;t communicated with ESPHome. If
it is green, that means the device is online.&lt;/p&gt;
&lt;p&gt;Also, if you look closely at this point to your Home Assistant installation,
you will see that an &lt;code&gt;esphome&lt;/code&gt; folder was created with a yaml file and folder
that matches your Node Name! This allows you to make edits in your editor of
choice and also commit these to source control!&lt;/p&gt;
&lt;h4&gt;Edit ESPHome Secrets&lt;/h4&gt;
&lt;p&gt;We will configure some secrets that will be merged into the firmware
configuration. This works much like the lovelace secrets work! You can do this
by clicking on the options menu in the upper right and selecting &lt;code&gt;Secrets Editor&lt;/code&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It&apos;s also worth pointing out that you can have ESPHome use your
&lt;a href=&quot;https://esphome.io/guides/faq.html#how-do-i-use-my-home-assistant-secrets-yaml&quot;&gt;Home Assistant secrets.yaml file&lt;/a&gt;!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The firmware I will be using uses the following secrets. This may be different
for the firmware you are using.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;wifi_ssid: &quot;my_secret_network_name&quot;
wifi_password: &quot;my_secret_network_password&quot;
wifi_fallback_password: &quot;my_secret_network_password&quot;
gosund_ota_password: &quot;&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After you&apos;ve configured the secrets hit &lt;code&gt;Save&lt;/code&gt; and &lt;code&gt;Close&lt;/code&gt; buttons respectively.&lt;/p&gt;
&lt;h4&gt;Edit ESPHome Device Firmware&lt;/h4&gt;
&lt;p&gt;Now that the secrets are configured lets click on the &lt;code&gt;Edit&lt;/code&gt; button of the new
device we created inside of ESPHome. I&apos;m going to copy the
&lt;a href=&quot;https://github.com/jonathanadams/esphome-configs/blob/7eb19efa29cc772f400df9ad582fe3a0be378b16/_devices/Gosund-WP3/Gosund-WP3.md&quot;&gt;firmware yaml&lt;/a&gt;
from github into this editor and click &lt;code&gt;Save&lt;/code&gt; and &lt;code&gt;Close&lt;/code&gt; buttons respectively.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This firmware supports &lt;code&gt;substitutions&lt;/code&gt;, which allows you to make variable
replacements to the firmware easily. I use &lt;code&gt;substitutions&lt;/code&gt; to change the
&lt;code&gt;device_name&lt;/code&gt; or &lt;code&gt;friendly_name&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Next, click on the ESPHome Device options in the upper right of we just edited
and select &lt;code&gt;Compile&lt;/code&gt;. This will open a modal and start compiling the firmware.
Once we the firmware is compiled there will be an option to &lt;code&gt;Download Binary&lt;/code&gt;.
Click this to save the firmware to your machine.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/home-automation/esphome-compiling-firmware-wp3-02-yaml-736.png&quot; alt=&quot;ESPHome Compile Firmware&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;Uploading the firmware to the Pi&lt;/h4&gt;
&lt;p&gt;The next step could be the most trickiest step of them all (if you are running
Windows), which is getting the firmware onto the pi. We need to copy the file
from the downloads folder to the &lt;code&gt;/home/pi/tuya-convert/files&lt;/code&gt; folder.&lt;/p&gt;
&lt;p&gt;This can be done by navigating to the downloads folder in your terminal and
entering: &lt;code&gt;scp gosund_wp3_02.bin pi@10.0.0.100:/home/pi/tuya-convert/files&lt;/code&gt; and
enter in the default password of &lt;code&gt;raspberry&lt;/code&gt; when asked.&lt;/p&gt;
&lt;h4&gt;Flashing...&lt;/h4&gt;
&lt;p&gt;Don&apos;t forget to plug in our device and hit the pairing button to enter setup
mode!&lt;/p&gt;
&lt;p&gt;After the device is in setup mode, switch back over to the Pi that we were
connected to earlier via SSH and run the &lt;code&gt;./start_flash.sh&lt;/code&gt; script&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pi@raspberrypi:/home/pi/tuya-convert# ./start_flash.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You may have to enter &lt;code&gt;y&lt;/code&gt;, to terminate some running service (e.g., &lt;code&gt;dnsmasq&lt;/code&gt;,
&lt;code&gt;mosquitto&lt;/code&gt;) when prompted. After a while you will be presented with several
options&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Available options:
  0) return to stock
  1) flash gosund_wp3_02.bin
  q) quit; do nothing
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Double check yourself at this step as you don&apos;t want to flash the wrong
firmware!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You will want to flash it with the binary you uploaded in the previous step!
Enter in the number of your firmware and follow any remaining prompts. After
the device reboots it will now show up in ESPHome highlighted in green and
you&apos;ll be asked to configure it in Home Assistant!&lt;/p&gt;
&lt;h5&gt;locked firmware&lt;/h5&gt;
&lt;p&gt;It appears there is a
&lt;a href=&quot;https://github.com/ct-Open-Source/tuya-convert/wiki/Collaboration-document-for-PSK-Identity-02&quot;&gt;new firmware&lt;/a&gt;
on some Tuya devices that is preventing flashing causing vendor lock in to
unknown third party services. There are two workarounds,
&lt;a href=&quot;https://siytek.com/how-to-flash-esp-devices-with-tasmota-using-raspberry-pi/#Soldering_the_wires&quot;&gt;one requires soldering&lt;/a&gt;
and the other solution &lt;a href=&quot;https://www.youtube.com/watch?v=imKZbhJ8lvU&quot;&gt;requires a 3D Printer&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;iframe
title=&quot;YouTube video player&quot;
src=&quot;https://www.youtube.com/embed/imKZbhJ8lvU&quot;
loading=&quot;lazy&quot;
allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&quot;
referrerpolicy=&quot;strict-origin-when-cross-origin&quot;
allowfullscreen&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;lt;/iframe&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Adding the flashed device to home assistant&lt;/h3&gt;
&lt;p&gt;The device will be automatically discovered by the ESPHome add-on and you&apos;ll
be asked to configure the device.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/home-automation/esphome-gosund-wp3-02-discovered-299.png&quot; alt=&quot;ESPHome Device Discovered&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Once you configure the device you&apos;ll have several device entities as shown below.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/home-automation/esphome-gosund-wp3-02-entities-341.png&quot; alt=&quot;ESPHome Device Entities&quot; /&gt;&lt;/p&gt;
&lt;p&gt;It&apos;s worth noting that you can use the physical button and the relay independently.
This is possible because you have full control over how your devices operate
when building your firmware!&lt;/p&gt;
&lt;h3&gt;Updating Device Firmware&lt;/h3&gt;
&lt;p&gt;If you need to update the device firmware later, it is a simple process. All
you need to do is navigate to the device in the ESPHome Dashboard and click
&lt;code&gt;Edit&lt;/code&gt;. After you&apos;ve made your changes, click the &lt;code&gt;Upload&lt;/code&gt; button.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/home-automation/esphome-gosund-wp3-01-update-1150.png&quot; alt=&quot;ESPHome Device Firmware Update&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This will compile and upload the firmware to the device Over-the-air (OTA) and
then start tailing the device logs. Just click the &lt;code&gt;Stop&lt;/code&gt; button after the
firmware is updated to close the modal.&lt;/p&gt;
&lt;h2&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;I&apos;ve really enjoyed using ESPHome so far. The overall ease of use and seamless
integration into Home Assistant feels really polished. I&apos;ve also noticed that
all of my ESPHome devices are really stable and rock solid! I&apos;ll be buying more
ESPHome devices for my projects.&lt;/p&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>Building and maintaining a smart home presentation</title><link>https://blakeniemyjski.com/blog/building-and-maintaining-a-smart-home-presentation/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/building-and-maintaining-a-smart-home-presentation/</guid><pubDate>Tue, 25 Aug 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Here is a recent recording of a new talk I recently gave at
&lt;a href=&quot;http://northdallas.net/meetings/building-and-maintaining-a-smart-home/&quot;&gt;North Dallas Developers&lt;/a&gt; on my experiences building and
maintaining a smart home.&lt;/p&gt;
&lt;p&gt;&amp;lt;iframe
title=&quot;YouTube video player&quot;
src=&quot;https://www.youtube.com/embed/A9HN4zc-75E&quot;
loading=&quot;lazy&quot;
allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&quot;
referrerpolicy=&quot;strict-origin-when-cross-origin&quot;
allowfullscreen&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;lt;/iframe&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Here is a high level overview of what is covered in the presentation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;First Steps
&lt;ul&gt;
&lt;li&gt;What are your Goals?&lt;/li&gt;
&lt;li&gt;Existing products and ecosystems&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Research
&lt;ul&gt;
&lt;li&gt;Future technologies&lt;/li&gt;
&lt;li&gt;Are you the product?&lt;/li&gt;
&lt;li&gt;How-to choose a contractor&lt;/li&gt;
&lt;li&gt;Costs&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/blog/lets-talk-about-smart-home-accessibility&quot;&gt;Accessibility&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Breaking Changes&lt;/li&gt;
&lt;li&gt;Companies will break you or sell your data&lt;/li&gt;
&lt;li&gt;Smart Home Hubs&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/tags/automation/&quot;&gt;Automations&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Feedback&lt;/h2&gt;
&lt;p&gt;Any feedback is greatly appreciated on how I can improve this talk. I had been
wanting to give one on smart homes for a while so decided to create one over
the weekend and present on it. Do you like the format? Was it helpful?&lt;/p&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>Speaking at North Dallas Developers</title><link>https://blakeniemyjski.com/blog/speaking-at-north-dallas-developers/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/speaking-at-north-dallas-developers/</guid><pubDate>Tue, 04 Aug 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I&apos;ll be giving a talk at &lt;a href=&quot;http://northdallas.net/meetings/building-and-maintaining-a-smart-home/&quot;&gt;North Dallas Developers&lt;/a&gt;
on Building and maintaining a smart home
on August 5, 2020 at 6:30 PM Central Standard Time.&lt;/p&gt;
&lt;p&gt;Visit the link above to RSVP to the virtual talk.&lt;/p&gt;
&lt;h3&gt;Overview&lt;/h3&gt;
&lt;p&gt;I&apos;ll be covering my experiences building and maintaining a smart home. Starting
from nothing to having a fully automated house can be overwhelming. We’ll cover
many topics like choosing future proof products (usability, security, etc..),
dealing with contractors, keeping a happy spouse with ever changing automations
and much more.&lt;/p&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>How-to: Using NFC Tags to trigger device or home automations</title><link>https://blakeniemyjski.com/blog/how-to-using-nfc-tags-to-trigger-device-or-home-automation/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/how-to-using-nfc-tags-to-trigger-device-or-home-automation/</guid><pubDate>Sun, 19 Jul 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This post will cover how I use &lt;a href=&quot;https://electronics.howstuffworks.com/nfc-tag1.htm&quot;&gt;NFC tags&lt;/a&gt;
with &lt;a href=&quot;https://www.home-assistant.io&quot;&gt;Home Assistant&lt;/a&gt; to trigger automations.
I&apos;ve laid out all the information below on how you can use NFC Tags to automate
other apps and devices as well!&lt;/p&gt;
&lt;p&gt;I really like using &lt;a href=&quot;https://en.wikipedia.org/wiki/Near-field_communication&quot;&gt;Near Field Communication (NFC)&lt;/a&gt;
for home or device based automations because it is really cheap and the
possibilities are endless as you can place them in any environmental condition
and on almost any physical object.&lt;/p&gt;
&lt;p&gt;Here are some of the ways you could use NFC Tags to trigger home automations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Place a tag next to your bed to activate good morning/night routines.&lt;/li&gt;
&lt;li&gt;Place a tag next to a door to automatically unlock a door (presence detection is hard).&lt;/li&gt;
&lt;li&gt;Place a tag on the lawn mower and have an automation to open the Garage Door.&lt;/li&gt;
&lt;li&gt;Place a tag under a bed to trigger an emergency action like trigger alarm.&lt;/li&gt;
&lt;li&gt;Place a tag outside next to patio furniture to turn on lights or music.&lt;/li&gt;
&lt;li&gt;Place a tag next to a lamp with a smart bulb to turn it on or off.&lt;/li&gt;
&lt;li&gt;Place a tag next to the cat litter to trigger a future reminder to change it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here are some of the ways you could use NFC Tags to trigger device automations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sharing Guest Wifi information!&lt;/li&gt;
&lt;li&gt;Place under a bed to trigger an emergency action like text emergency services.&lt;/li&gt;
&lt;li&gt;Place a tag by your relaxation place and automatically play relaxing music.&lt;/li&gt;
&lt;li&gt;Put one on your bag and tap it to trigger airplane mode or silence your phone.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;A huge shout out to &lt;a href=&quot;https://www.architectryan.com/&quot;&gt;Ryan Hoffman&lt;/a&gt; who inspired
me to look into using NFC after sharing his experiences with it. I&apos;m not sure
I would have stumbled upon NFC Tags otherwise.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;What Tag Should I Get?&lt;/h2&gt;
&lt;p&gt;There are many different types of &lt;a href=&quot;https://electronics.howstuffworks.com/nfc-tag1.htm&quot;&gt;NFC tags&lt;/a&gt;
that have different form factors, features (write locking, password protection,
number of writes, etc).&lt;/p&gt;
&lt;p&gt;I really like these &lt;a href=&quot;https://www.amazon.com/gp/product/B015EM34WY?tag=niemyjski-20&quot;&gt;heavy duty Type 2 NFC Tags&lt;/a&gt;
as they are waterproof, safe for outdoor use and supports all of the NFC
features you&apos;d expect (locking, multi-write, password protection). Additionally,
they come with 3M adhesive backing so you can literally stick them anywhere!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/home-automation/nfc-tag-door-lock-700.png&quot; alt=&quot;NFC Tag Near Door&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I could have done more research and found a tag that blended in better with my
door, please keep this in mind when picking out where you want to place these.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You can get NFC Tags in
&lt;a href=&quot;https://www.amazon.com/gp/product/B015UMPVNI?tag=niemyjski-20&quot;&gt;sticker form&lt;/a&gt;
as well. Just be sure it supports your all of your requirements. Here are some
factors you might want to consider:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Location: Some tags are not waterproof and some don&apos;t work on metal surfaces.&lt;/li&gt;
&lt;li&gt;Device support: Will this work with the devices I want to use it with (e.g., iPhone).&lt;/li&gt;
&lt;li&gt;Updatable: Do you need to update this tag once installed (eg., Can I lock it, or write to it multiple times)?&lt;/li&gt;
&lt;li&gt;Form Factor: Does this need to be a &lt;a href=&quot;https://www.amazon.com/gp/product/B015UMPVNI?tag=niemyjski-20&quot;&gt;sticker&lt;/a&gt;?&lt;/li&gt;
&lt;li&gt;Color: Does this blend in with the surrounding?&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;NFC Tag Tips&lt;/h2&gt;
&lt;p&gt;It&apos;s important to note that you should associate a name or identifier to a tag
or to the event name you trigger. I&apos;d &lt;strong&gt;strongly recommend&lt;/strong&gt; creating tags with
sequentially or with random characters like &lt;code&gt;nfc_tag_1&lt;/code&gt; and not with a specific
name like &lt;code&gt;office_lights_tag&lt;/code&gt;. The reason behind this is this because you can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Potentially leak information publicly as it can be read by anyone with an NFC device.&lt;/li&gt;
&lt;li&gt;Prevents you from reusing tags at a later date.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Finally, don&apos;t store anything you don&apos;t publicly want exposed (e.g., api keys,
IP addresses, ...). Tags can be copied / cloned with enough work. I&apos;ve yet to
figure out how to encrypt the payload of a tag but I&apos;ve seen encrypted tags
mentioned around in my various searches. Even still I wouldn&apos;t put anything
sensitive on a tag!&lt;/p&gt;
&lt;h2&gt;Triggering events when NFC is detected.&lt;/h2&gt;
&lt;p&gt;I&apos;m pretty new to NFC but in my adventures I&apos;ve only come across two ways to
trigger Home Assistant events when an NFC Tag is detected. You can do it via
the Shortcuts App or via &lt;a href=&quot;https://en.wikipedia.org/wiki/Deep_linking&quot;&gt;deep&lt;/a&gt;/universal
linking.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This article primarily focuses on iOS support as I don&apos;t have any android
devices to try on. The following links may need to be slightly tweaked.
Please leave a comment below on what worked for you and I&apos;ll update this article.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Please note that there are deep links for a wide range of
&lt;a href=&quot;http://x-callback-url.com/apps/&quot;&gt;applications&lt;/a&gt; that you could use NFC tags on.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Skip to the &lt;a href=&quot;#conclusion&quot;&gt;Conclusion&lt;/a&gt; if you are looking for my recommendations
on what solution to go with.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Tags&lt;/h3&gt;
&lt;p&gt;Since writing this article Home Assistant has added the support for
&lt;a href=&quot;https://www.home-assistant.io/integrations/tag/&quot;&gt;Tags&lt;/a&gt;. Home Assistant will
manage reading and writing tags for you which is really nice. However, it still
requires you to click on a banner to fire an event when you scan a tag.&lt;/p&gt;
&lt;p&gt;&amp;lt;iframe
title=&quot;YouTube video player&quot;
src=&quot;https://www.youtube.com/embed/Xc120lClUgA&quot;
loading=&quot;lazy&quot;
allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&quot;
referrerpolicy=&quot;strict-origin-when-cross-origin&quot;
allowfullscreen&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;lt;/iframe&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;iframe
title=&quot;YouTube video player&quot;
src=&quot;https://www.youtube.com/embed/xE7wm1bxRLs&quot;
loading=&quot;lazy&quot;
allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&quot;
referrerpolicy=&quot;strict-origin-when-cross-origin&quot;
allowfullscreen&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;lt;/iframe&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Deep/Universal links&lt;/h3&gt;
&lt;p&gt;For simplicity sake a deep/universal link will open an application with some
arguments contained in the link of which can trigger an action. You can read
more about the differences &lt;a href=&quot;https://stackoverflow.com/questions/35522618/universal-links-on-ios-vs-deep-links-url-schemes&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can do a &lt;a href=&quot;https://companion.home-assistant.io/docs/integrations/url-handler/&quot;&gt;wide range of actions&lt;/a&gt;
with links like Call Services, Fire Events or Send a Location.&lt;/p&gt;
&lt;p&gt;The advantage of using Deep/Universal links is it allows any authorized
user to execute a Home Assistant action with no configuration defined on
device.&lt;/p&gt;
&lt;h4&gt;Deep link&lt;/h4&gt;
&lt;p&gt;The following Deep link &lt;a&gt;&lt;code&gt;homeassistant://fire_event/nfc_tag_1&lt;/code&gt;&lt;/a&gt;
will launch the Home Assistant application if it&apos;s installed, fire an event
called &lt;code&gt;nfc_tag_1&lt;/code&gt; and show an alert dialog saying the &lt;code&gt;nfc_tag_1&lt;/code&gt; event has
been called.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/home-automation/home-assistant-ios-app-fired-event-397.png&quot; alt=&quot;Fired Event&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This is less than ideal as you don&apos;t really want a notification interrupting you.
The application opening is jarring enough.&lt;/p&gt;
&lt;h4&gt;Deep link without notification&lt;/h4&gt;
&lt;p&gt;Luckily we can take advantage of a feature called
&lt;a href=&quot;https://companion.home-assistant.io/docs/integrations/x-callback-url&quot;&gt;&lt;code&gt;x-callback-url&lt;/code&gt;&lt;/a&gt;.
This allows us to trigger application actions without a notification. We simply
need to modify the url to
&lt;a&gt;&lt;code&gt;homeassistant://x-callback-url/fire_event?eventName=nfc_tag_1&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;Universal link&lt;/h4&gt;
&lt;p&gt;Deep links work great as long as the application you are linking to is installed.
If it isn&apos;t, than the link will do nothing. This is one of the problems
&lt;a href=&quot;https://companion.home-assistant.io/docs/integrations/universal-links&quot;&gt;Universal links&lt;/a&gt;
solve by redirecting you to the App Store if the application isn&apos;t
currently installed.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;NOTE: Universal links like &lt;a href=&quot;https://www.home-assistant.io/ios/nfc/&quot;&gt;https://www.home-assistant.io/ios/nfc/&lt;/a&gt; are
registered when you install the application and works offline!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Here is the Universal link you&apos;d need to register
&lt;a href=&quot;https://www.home-assistant.io/ios/nfc/?url=homeassistant://x-callback-url/fire_event?eventName=nfc_tag_1&quot;&gt;&lt;code&gt;https://www.home-assistant.io/ios/nfc/?url=homeassistant://x-callback-url/fire_event?eventName=nfc_tag_1&lt;/code&gt;&lt;/a&gt;
to fire the action without a notification.&lt;/p&gt;
&lt;h4&gt;Writing links to NFC Tag&lt;/h4&gt;
&lt;p&gt;I used the free application called &lt;a href=&quot;https://apps.apple.com/us/app/nfc-tools/id1252962749&quot;&gt;NFC Tools&lt;/a&gt;
which is also available on the &lt;a href=&quot;https://play.google.com/store/apps/details?id=com.wakdev.wdnfc&amp;amp;hl=en_US&quot;&gt;Google Play Store&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Open the application and follow the steps below:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Select the &lt;code&gt;Write&lt;/code&gt; button.&lt;/li&gt;
&lt;li&gt;Select the &lt;code&gt;Add a record&lt;/code&gt; button.&lt;/li&gt;
&lt;li&gt;Select the &lt;code&gt;URL / URI&lt;/code&gt; option.&lt;/li&gt;
&lt;li&gt;Enter in your Deep/Universal link and select &lt;code&gt;OK&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Select the &lt;code&gt;Write / xx Bytes&lt;/code&gt; button to write the link to the NFC Tag.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/home-automation/nfc-tools-write-record-1200.png&quot; alt=&quot;Write&quot; /&gt;&lt;/p&gt;
&lt;p&gt;When your phone gets close to the NFC tag it will launch a banner prompting
you to click on it to activate the event.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/home-automation/home-assistant-nfc-tag-banner-1192.jpeg&quot; alt=&quot;Open in Home Assistant NFC Tag&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This isn&apos;t ideal but it&apos;s in place for
&lt;a href=&quot;https://developer.apple.com/documentation/corenfc/adding_support_for_background_tag_reading&quot;&gt;security reasons&lt;/a&gt;
so your phone doesn&apos;t automatically execute tasks.&lt;/p&gt;
&lt;h3&gt;Shortcuts&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://apps.apple.com/us/app/shortcuts/id1462947752&quot;&gt;Shortcuts&lt;/a&gt; is an iOS
application made by apple that allows you do device based tasks or automations
much like &lt;a href=&quot;https://play.google.com/store/apps/details?id=net.dinglisch.android.taskerm&amp;amp;hl=en_US&quot;&gt;Tasker&lt;/a&gt;
on Android.&lt;/p&gt;
&lt;p&gt;Shortcuts define tasks and automations in the app that can get triggered several
ways. Shortcut tasks can be shared across devices easily. However Shortcuts
Automations cannot be shared and is currently the only way to trigger an
action when an NFC tag is detected without any prompts.&lt;/p&gt;
&lt;p&gt;The nice thing about Shortcuts is you can have it automate anything (e.g.,
texting, application actions, video calling and more).&lt;/p&gt;
&lt;h4&gt;Shortcut&lt;/h4&gt;
&lt;p&gt;There are two kind of Shortcuts you can create. The first is referred to a
Shortcut and can be shared or triggered from automations or by NFC Tags. The
second is called Shortcut Automations and those get triggered automatically
based on some kind of action or state like coming into proximity to a NFC Tag.
Shortcut Automations cannot currently be shared as of iOS 13.&lt;/p&gt;
&lt;h5&gt;Creating a new Shortcut&lt;/h5&gt;
&lt;p&gt;We first need to create a new Shortcut in order for a NFC tag to trigger it.&lt;/p&gt;
&lt;p&gt;Open the Shortcuts application and follow the steps below:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Select the &lt;code&gt;Create Shortcut&lt;/code&gt; button.&lt;/li&gt;
&lt;li&gt;Select the &lt;code&gt;Add Action&lt;/code&gt; button.&lt;/li&gt;
&lt;li&gt;Select the &lt;code&gt;Apps&lt;/code&gt; button and select &lt;code&gt;Home Assistant&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Select the &lt;code&gt;Fire Event&lt;/code&gt; option.&lt;/li&gt;
&lt;li&gt;Enter in the event name you wish to trigger (e.g., &lt;code&gt;nfc_tag_1&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Select &lt;code&gt;Show More&lt;/code&gt; and enter in &lt;code&gt;{}&lt;/code&gt; for the &lt;code&gt;Event Data&lt;/code&gt;.
&lt;img src=&quot;/img/blog/home-automation/apple-new-shortcut-home-assistant-1200.png&quot; alt=&quot;New Shortcut&quot; /&gt;&lt;/li&gt;
&lt;li&gt;Select the &lt;code&gt;Next&lt;/code&gt; button.&lt;/li&gt;
&lt;li&gt;Enter in the event name as the Shortcut name (e.g., &lt;code&gt;nfc_tag_1&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Select the &lt;code&gt;Done&lt;/code&gt; button to save the Shortcut.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You can now select on the Shortcut button you just added to fire an event! We
can now write to an NFC Tag to tell it to run the Shortcut we just added when
the tag is read by our device.&lt;/p&gt;
&lt;h5&gt;Writing Shortcut to NFC Tag&lt;/h5&gt;
&lt;p&gt;I used the free application called &lt;a href=&quot;https://apps.apple.com/us/app/nfc-tools/id1252962749&quot;&gt;NFC Tools&lt;/a&gt;
to write the Shortcut to the NFC Tag.&lt;/p&gt;
&lt;p&gt;Open the application and follow the steps below:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Select the &lt;code&gt;Write&lt;/code&gt; button.&lt;/li&gt;
&lt;li&gt;Select the &lt;code&gt;Add a record&lt;/code&gt; button.&lt;/li&gt;
&lt;li&gt;Select the &lt;code&gt;Shortcut&lt;/code&gt; option.&lt;/li&gt;
&lt;li&gt;Enter in the name of the Shortcut and select &lt;code&gt;OK&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Select the &lt;code&gt;Write / xx Bytes&lt;/code&gt; button to write the link to the NFC Tag.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;When your phone gets close to the NFC tag it will launch a banner prompting
you to click on it to activate the event.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/home-automation/nfc-tools-nfc-tag-shortcuts-banner-1200.png&quot; alt=&quot;Open in ShortCuts NFC Tag&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This isn&apos;t ideal but it&apos;s in place for security reasons so your phone doesn&apos;t
automatically execute tasks.&lt;/p&gt;
&lt;h4&gt;Shortcuts Automations&lt;/h4&gt;
&lt;p&gt;As stated previously, Automations are setup in the Shortcut apps and cannot be
shared across devices. Which means this will become a lot of work to maintain if
you have more than one user who will be using NFC Tag for automations. The plus
side is you don&apos;t need to write anything to the NFC Tag to use these automations
and they run without any user interaction. I think this is the biggest win of
this approach.&lt;/p&gt;
&lt;p&gt;Open the Shortcuts application and follow the steps below:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Select the Automation tab.&lt;/li&gt;
&lt;li&gt;Select the &lt;code&gt;Add&lt;/code&gt; icon button.&lt;/li&gt;
&lt;li&gt;Select the &lt;code&gt;Create Personal Automation&lt;/code&gt; button.&lt;/li&gt;
&lt;li&gt;Select the &lt;code&gt;NFC&lt;/code&gt; option under the &lt;code&gt;Settings Category&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Select the &lt;code&gt;Scan&lt;/code&gt; button and scan your NFC Tag.&lt;/li&gt;
&lt;li&gt;Enter in the event name as the Shortcut name (e.g., &lt;code&gt;nfc_tag_1&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Select the &lt;code&gt;Next&lt;/code&gt; button.&lt;/li&gt;
&lt;li&gt;Select the &lt;code&gt;Apps&lt;/code&gt; button and select &lt;code&gt;Home Assistant&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Select the &lt;code&gt;Fire Event&lt;/code&gt; option.&lt;/li&gt;
&lt;li&gt;Enter in the event name you wish to trigger (e.g., &lt;code&gt;nfc_tag_1&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Select &lt;code&gt;Show More&lt;/code&gt; and enter in &lt;code&gt;{}&lt;/code&gt; for the &lt;code&gt;Event Data&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Select the &lt;code&gt;Next&lt;/code&gt; button.&lt;/li&gt;
&lt;li&gt;Turn off &lt;code&gt;Ask Before Running&lt;/code&gt;, unless you wish to be prompted.
&lt;img src=&quot;/img/blog/home-automation/apple-new-shortcut-automation-home-assistant-1200.png&quot; alt=&quot;New Shortcut Automation&quot; /&gt;&lt;/li&gt;
&lt;li&gt;Select the &lt;code&gt;Done&lt;/code&gt; button to save the Automation.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;When your phone gets close to the NFC tag it will automatically fire the event.&lt;/p&gt;
&lt;h2&gt;Home Assistant Automation&lt;/h2&gt;
&lt;p&gt;Here is a quick little Home Assistant Automation you can use that will get
triggered when your NFC Tag is read and the event is fired (e.g., &lt;code&gt;nfc_tag_1&lt;/code&gt;).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;- alias: Toggle light when event is nfc_tag_1 event is triggered
  trigger:
  - event_data: {}
    event_type: nfc_tag_1
    platform: event
  action:
  - service: light.turn_on
    data:
      entity_id:
        - light.office
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: this automation can be created very easily in Home Assistant
Automations User Interface (UI).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Future Plans&lt;/h2&gt;
&lt;p&gt;Here are some of the things I want to try todo in the future that I think we
may need to address in Home Assistant if it&apos;s not already available.&lt;/p&gt;
&lt;p&gt;I&apos;d like to find a way to ensure my tag can run from any device without leaking
implementation details on how to execute. I think this comes down to potentially
adding shared universal deep link that works on various platforms.&lt;/p&gt;
&lt;p&gt;Taking this a step further, it would be really nice to have a named access
token that any user can access to preform an action. I think of this as a named
short link of which I define the action that it can perform server side
(e.g., turn on this light or fire this action). These could even be used to
anonymously share state as well! But this would expose your Home Assistance
instance in some way.&lt;/p&gt;
&lt;p&gt;Some further research is needed to see if I can stack NFC Tag actions to have
multiple run at the same time or if only supported ones will be executed by
a specific device (e.g., If I put Shortcuts first, will it be skipped on
Android). This could be used to stack events that get fired or services called.&lt;/p&gt;
&lt;p&gt;Finally, it would be nice to figure out how to encrypt my tags just so I can
learn more!&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;UPDATE: Since writing this article Home Assistant has added the support
for &lt;a href=&quot;https://www.home-assistant.io/integrations/tag/&quot;&gt;Tags&lt;/a&gt;. I&apos;d recommend
using Tags but it still requires you to click on a notification banner to
to fire an event. You may want to use both Tags and
&lt;a href=&quot;#shortcuts-automations&quot;&gt;Shortcut Automations&lt;/a&gt; together by first configuring
Tags and then setting up the Shortcut Automation.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I&apos;m going to be using the &lt;a href=&quot;#universal-link&quot;&gt;Universal Link&lt;/a&gt; route with all my
tags as I define the configuration once on what the tag should do and anyone
authenticated with the Home Assistant app will be able to fire events.&lt;/p&gt;
&lt;p&gt;I do think that &lt;a href=&quot;#shortcuts-automations&quot;&gt;Shortcut Automations&lt;/a&gt; has the best user
experience by automatically triggering actions when NFC Tags are detected. But
they are not sharable/synced across devices or accounts makes this a non starter
unless you will always own a single Apple Device or you just love maintaining
automations long term. I do think I may use Shortcut Automations for some
automations of which extra interaction is not ideal like activating good night
routines without remembering to press on the banner.&lt;/p&gt;
&lt;p&gt;NFC Tags can be used for Home Automation or even device specific tasks so go
wild!&lt;/p&gt;
&lt;h2&gt;Extra reading&lt;/h2&gt;
&lt;p&gt;Here are some useful links that I came across in my NFC adventures:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.apple.com/documentation/corenfc/adding_support_for_background_tag_reading&quot;&gt;Adding Support for Background Tag Reading&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://security.stackexchange.com/questions/63483/how-do-nfc-tags-prevent-copying&quot;&gt;How do NFC tags prevent copying?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/SeanPM5/homeassistant-config/wiki/Apple-Shortcuts---Home-Assistant-iOS-Tutorial&quot;&gt;Apple Shortcuts Home Assistant iOS Tutorial&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.reddit.com/r/homeassistant/comments/cgqe52/multifunction_nfc_tags_with_home_assistant_and/eulvn13/&quot;&gt;Multi-function NFC Tags with Home Assistant and Tasker&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>How-to normalize home volume levels with Node-RED</title><link>https://blakeniemyjski.com/blog/how-to-normalize-home-volume-levels-with-node-red/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/how-to-normalize-home-volume-levels-with-node-red/</guid><pubDate>Fri, 29 May 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This post is going to cover normalizing all &lt;code&gt;media_player&lt;/code&gt; volume levels with
&lt;a href=&quot;https://www.home-assistant.io&quot;&gt;Home Assistant&lt;/a&gt; and &lt;a href=&quot;https://nodered.org&quot;&gt;Node-RED&lt;/a&gt;.
If you haven&apos;t already, I&apos;d recommend reading my previous blog post on
&lt;a href=&quot;/blog/how-to-send-text-to-speech-notifications-with-home-assistant-and-node-red&quot;&gt;sending Text-to-Speech notifications&lt;/a&gt; which
dives a bit deeper on the optional accessibility Subflow this Flow consumes.&lt;/p&gt;
&lt;p&gt;This automation normalizes all volume levels in my home based on the time of day
using the
&lt;a href=&quot;https://github.com/custom-components/alexa_media_player&quot;&gt;Amazon Alexa Media Player Integration&lt;/a&gt;
to bring in all my
&lt;a href=&quot;https://www.amazon.com/all-new-Echo/dp/B07NFTVP7P?tag=niemyjski-20&quot;&gt;Amazon Echo&lt;/a&gt;
and &lt;a href=&quot;https://www.amazon.com/ecobee-Switch-Smart-Amazon-Built/dp/B0798LCLJ5?tag=niemyjski-20&quot;&gt;ecobee Switch+&lt;/a&gt;
devices into Home Assistant.&lt;/p&gt;
&lt;p&gt;This is one of my favorite automations in my house and I think you will enjoy
it too! I wrote this automation for several reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I wanted all speakers to be quiet at bed time. This prevents someone from
getting blasted at 2:00 am in the morning.&lt;/li&gt;
&lt;li&gt;In the morning after I&apos;ve had my cup of coffee, I want to increase the volume
to an acceptable level.&lt;/li&gt;
&lt;li&gt;Automation allows for reuse via sub-flows. This means you can trigger it for
other use cases like friends are over and music is playing, increase or
decrease the volume.&lt;/li&gt;
&lt;li&gt;One less thing I have to think about, or constantly touch to change. In
todays pandemic, this is also one less thing you potentially have to clean!&lt;/li&gt;
&lt;li&gt;You can use this same routine for increasing the volume when triggering an
alarm.&lt;/li&gt;
&lt;li&gt;All speakers are at the same volume level when playing music on every speaker.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This Automation Flow is comprised of two different
&lt;a href=&quot;https://nodered.org/docs/user-guide/editor/workspace/Subflows&quot;&gt;Subflows&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Alexa Media Player Custom Component&lt;/h3&gt;
&lt;p&gt;If you haven&apos;t done so already, install the
&lt;a href=&quot;https://github.com/custom-components/alexa_media_player&quot;&gt;Alexa Media Player Custom Component&lt;/a&gt;
if you are using Alexa, otherwise skip this section.&lt;/p&gt;
&lt;p&gt;This addon is amazing as it normalizes all of your Alexa enabled devices such
as the
&lt;a href=&quot;https://www.amazon.com/all-new-Echo/dp/B07NFTVP7P?tag=niemyjski-20&quot;&gt;Amazon Echo&lt;/a&gt;
or &lt;a href=&quot;https://www.amazon.com/ecobee-Switch-Smart-Amazon-Built/dp/B0798LCLJ5?tag=niemyjski-20&quot;&gt;ecobee Switch+&lt;/a&gt;
into a &lt;code&gt;media_player&lt;/code&gt; that can be used by Home Assistant.&lt;/p&gt;
&lt;h3&gt;Service to set volume level.&lt;/h3&gt;
&lt;p&gt;This Automation Flow controls the volume levels by calling the
&lt;code&gt;media_player.volume_set&lt;/code&gt; service. I&apos;d recommend testing it first by opening
the Home Assistant Developer tools and navigating to the Services tab.&lt;/p&gt;
&lt;p&gt;Next, start playing some music on your &lt;code&gt;media_player&lt;/code&gt; and call the
&lt;code&gt;media_player.volume_set&lt;/code&gt; service with a payload of:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;entity_id: media_player.amazon_echo_plus
volume_level: 0.6
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you don&apos;t hear a volume change, try a different &lt;code&gt;volume_level&lt;/code&gt; in case the
the device is already at that level. If it&apos;s not then stop and check the logs.
This will help diagnose why the device isn&apos;t working and save you lots of time
before continuing.&lt;/p&gt;
&lt;h2&gt;Input Boolean to control automations&lt;/h2&gt;
&lt;p&gt;I highly recommend adding multiple
&lt;a href=&quot;https://www.home-assistant.io/integrations/input_boolean/&quot;&gt;Input Booleans&lt;/a&gt;
that controls if a specific automation or all automations can run. It&apos;s always
nice to be able to turn on or off a specific automation or all in the case of
maintenance. Here are the three &lt;code&gt;input_boolean&lt;/code&gt;s that are used in this
Automation Flow.&lt;/p&gt;
&lt;p&gt;If you choose a different name, be sure to update the code below.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;automation_enable:
  name: Enable Automations
  icon: mdi:home-automation

automation_normalize_volume:
  name: Automation - Automatically Normalize Volume throughout the day
  icon: mdi:volume-high

automation_notifications:
  name: Notify when an automation is triggered
  icon: mdi:home-automation
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Set Volume Level Subflow&lt;/h3&gt;
&lt;p&gt;This Subflow will get the current devices volume level, compare the current and
desired state. If changes are required, it will call the required service to
change the devices volume level.&lt;/p&gt;
&lt;p&gt;I then create a new Subflow with a status node, one input and one output. This
Subflow optionally depends on the
&lt;a href=&quot;/blog/how-to-send-text-to-speech-notifications-with-home-assistant-and-node-red#send-automation-speech-notification-subflow&quot;&gt;&lt;code&gt;Send Automation Speech Notification Subflow&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;graph TD
  InputNode(Input) --&amp;gt;  ACurrentStateNode(fas:fa-database Get Current Volume Level)
  ACurrentStateNode --&amp;gt; BFunctionNode(fas:fa-code Set Volume Level Payload)
  BFunctionNode --&amp;gt; CCallServiceNode(fas:fa-play Set Volume Level)
  BFunctionNode --&amp;gt; DCallSubflowNode(fas:fa-sliders-h Send Automation Speech Notification)
  CCallServiceNode --&amp;gt; OutputNode(Output 1)

  StatusNode(fas:fa-heartbeat Status: All) --&amp;gt; StatusOutputNode(Status)

style StatusNode fill:#ECF5FF
style StatusOutputNode fill:#FAFAFB

style InputNode fill:#FAFAFB
style ACurrentStateNode fill:#66ACFD
style BFunctionNode fill:#FBB68F
style CCallServiceNode fill:#66ACFD
style DCallSubflowNode fill:#FF75A1
style OutputNode fill:#FAFAFB
linkStyle default stroke-width:2px,fill:none,stroke:#CCD0D4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&apos;s break down the Subflow. The incoming message gets passed to the
&lt;code&gt;Get Current Volume Level&lt;/code&gt; Current State node to populate &lt;code&gt;msg.data&lt;/code&gt; with the
entity. This is then passed to the &lt;code&gt;Set Volume Level Payload&lt;/code&gt; node which checks
to see if any changes are required to meet the desired volume level and outputs
Text-to-Speech (TTS) status messages. If changes are required, the
&lt;code&gt;Set Volume Level&lt;/code&gt; node will call the &lt;code&gt;media_player.volume_set&lt;/code&gt; service.&lt;/p&gt;
&lt;p&gt;Here is the JavaScript code contained in the &lt;code&gt;Set Volume Level Payload&lt;/code&gt;
Function node to smartly adjust the volume only if it is needed.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const entity = msg.payload &amp;amp;&amp;amp; msg.payload.entity_id;
const attributes = msg.data &amp;amp;&amp;amp; msg.data.attributes
if (!entity || !attributes) {
    node.status({ fill: &quot;red&quot;, shape: &quot;dot&quot;, text: &quot;Invalid volume payload&quot; });
    return [null, null];
}

const desired_volume_level = msg.payload.volume_level || 0;
if (desired_volume_level &amp;lt; 0.0 || desired_volume_level &amp;gt; 1.0) {
    let message = &quot;Volume level must be between 0 and 1.&quot;;
    node.status({ fill: &quot;red&quot;, shape: &quot;dot&quot;, text: message });
    node.error(message);

    return [null, {
        payload: {
            entity_id: entity,
            message: message
        }
    }];
}

if (desired_volume_level === attributes.volume_level) {
    let message = &quot;Volume level is already at the desired level.&quot;;
    node.status({ fill: &quot;grey&quot;, shape: &quot;dot&quot;, text: message });
    node.log(message);
    return [null, {
        payload: {
            entity_id: entity,
            message: message
        }
    }];
}

const volumePayload = {
    payload: {
        data: {
            entity_id: entity,
            volume_level: desired_volume_level
        }
    }
};

let message = &quot;Setting Volume Level to &quot; + (desired_volume_level * 100) + &quot; percent.&quot;;
const speechPayload = {
    payload: {
        entity_id: entity,
        message: message
    }
};

node.status({ fill: &quot;green&quot;, shape: &quot;dot&quot;, text: message });
return [volumePayload, speechPayload];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you an see it will output a friendly speech notification payload that will
be sent to the &lt;code&gt;Send Automation Speech Notification&lt;/code&gt; Subflow. If you are not
using the &lt;code&gt;alexa_media_player&lt;/code&gt; Home Assistant addon, you may want to update the
you&apos;ll want to update &lt;code&gt;Set Volume Level Payload&lt;/code&gt; Function node to take in a
volume range you are expecting.&lt;/p&gt;
&lt;p&gt;You can trigger this Subflow by passing a message object with the
following payload. I recommend using a &lt;a href=&quot;https://nodered.org/docs/user-guide/nodes&quot;&gt;Inject Node&lt;/a&gt;
to test this out.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;entity_id&quot;: &quot;media_player.amazon_echo_plus&quot;,
  &quot;volume_level&quot;:0.4
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Please note, for my Alexa speakers. I use a volume level between the range of
&lt;code&gt;[0.0..0.10)&lt;/code&gt;. To mute, pass &lt;code&gt;0.0&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Set Volume Level on All Media Devices Subflow&lt;/h3&gt;
&lt;p&gt;This subflow will get all &lt;code&gt;media_player&lt;/code&gt;&apos; and then send them one by one to the
&lt;code&gt;Set Volume Level&lt;/code&gt; Subflow.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you are not using Alexa, you may need to tweak the &lt;code&gt;Get All Media Players&lt;/code&gt;
node as it&apos;s returning all devices that have a specific feature attribute
which says I support changing the volume level.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;graph TD
  InputNode(Input) --&amp;gt; AFunctionNode(fas:fa-code Parse Volume Level)
  AFunctionNode --&amp;gt; BGetEntitiesNode(fas:fa-search Get All Media Players)
  BGetEntitiesNode --&amp;gt; CChangeNode(fas:fa-random Set Volume Level Payload)
  CChangeNode --&amp;gt; DSubflowNode(fas:fa-sliders-h Set Volume Level)
  DSubflowNode --&amp;gt; OutputNode(Output)

  StatusNode(fas:fa-heartbeat Status: All) --&amp;gt; StatusOutputNode(Status)

style StatusNode fill:#ECF5FF
style StatusOutputNode fill:#FAFAFB

style InputNode fill:#FAFAFB
style AFunctionNode fill:#FBB68F
style BGetEntitiesNode fill:#66ACFD
style CChangeNode fill:#FEA530
style DSubflowNode fill:#FF75A1
style OutputNode fill:#FAFAFB

linkStyle default stroke-width:2px,fill:none,stroke:#CCD0D4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can trigger this Subflow by passing a message object with the
following &lt;code&gt;float&lt;/code&gt; (e.g. &lt;code&gt;0.1&lt;/code&gt;) payload. I recommend using a &lt;a href=&quot;https://nodered.org/docs/user-guide/nodes&quot;&gt;Inject Node&lt;/a&gt;
to test this out.&lt;/p&gt;
&lt;h3&gt;Normalize Volume Levels Everywhere Flow&lt;/h3&gt;
&lt;p&gt;This flow brings the two previous sub flows together to normalize all volume
levels based on the time of the day. In my home, I set the level to 30% from
9:00am to 9:00pm, otherwise I set to 10%.&lt;/p&gt;
&lt;p&gt;To have the flow set the volume levels at different times of the day, I use the
&lt;a href=&quot;https://flows.nodered.org/node/node-red-contrib-bigtimer&quot;&gt;BigTimer Node&lt;/a&gt;
with an &lt;code&gt;On Time&lt;/code&gt; of &lt;code&gt;09:00&lt;/code&gt; and a &lt;code&gt;Off Time&lt;/code&gt; of &lt;code&gt;21:00&lt;/code&gt;. Then I set the
&lt;code&gt;ON Msg&lt;/code&gt; to &lt;code&gt;.3&lt;/code&gt; and a &lt;code&gt;OFF Msg&lt;/code&gt; to &lt;code&gt;.1&lt;/code&gt;. When the timer turns on and turns off,
it will check to see if my automation flags are turned on. If they are, it will
enumerate over all media devices and set the volume based on the timers output
message (e.g., &lt;code&gt;.1&lt;/code&gt; or &lt;code&gt;.3&lt;/code&gt;).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;graph TD
  ABigTimerNode(fas:fa-clock Normalize Volume) --&amp;gt; BCurrentStateNode(fas:fa-database Automations Enabled?)
  BCurrentStateNode --&amp;gt; CCurrentStateNode(fas:fa-database Normalize Volume Levels?)
  CCurrentStateNode --&amp;gt; DRbeNode(fas:fa-scroll Only allow changed values)
  DRbeNode --&amp;gt; ECallSubflow(fas:fa-sliders-h Set Volume Level on All Media Devices Subflow)

  StatusNode(fas:fa-heartbeat Status: All) --&amp;gt; StatusOutputNode(Status)

style StatusNode fill:#ECF5FF
style StatusOutputNode fill:#FAFAFB

style ABigTimerNode fill:#3DB39F
style BCurrentStateNode fill:#66ACFD
style CCurrentStateNode fill:#66ACFD
style DRbeNode fill:#FEB95E
style ECallSubflow fill:#FF75A1

linkStyle default stroke-width:2px,fill:none,stroke:#CCD0D4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can &lt;a href=&quot;https://nodered.org/docs/user-guide/editor/workspace/import-export&quot;&gt;import this Flow&lt;/a&gt;
and all Subflows shown above by importing the following JSON.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[
    {
        &quot;id&quot;: &quot;7a62a2d3.c0f4a4&quot;,
        &quot;type&quot;: &quot;Subflow&quot;,
        &quot;name&quot;: &quot;Send Automation Speech Notification&quot;,
        &quot;info&quot;: &quot;&quot;,
        &quot;category&quot;: &quot;&quot;,
        &quot;in&quot;: [
            {
                &quot;x&quot;: 60,
                &quot;y&quot;: 100,
                &quot;wires&quot;: [
                    {
                        &quot;id&quot;: &quot;87411254.a6ed18&quot;
                    }
                ]
            }
        ],
        &quot;out&quot;: [
            {
                &quot;x&quot;: 1120,
                &quot;y&quot;: 100,
                &quot;wires&quot;: [
                    {
                        &quot;id&quot;: &quot;d44cc51a.be0668&quot;,
                        &quot;port&quot;: 0
                    }
                ]
            }
        ],
        &quot;status&quot;: {
            &quot;x&quot;: 220,
            &quot;y&quot;: 40,
            &quot;wires&quot;: [
                {
                    &quot;id&quot;: &quot;aef53056.742438&quot;,
                    &quot;port&quot;: 0
                }
            ]
        }
    },
    {
        &quot;id&quot;: &quot;89300b1.595bcf8&quot;,
        &quot;type&quot;: &quot;function&quot;,
        &quot;z&quot;: &quot;7a62a2d3.c0f4a4&quot;,
        &quot;name&quot;: &quot;Set Speech Payload&quot;,
        &quot;func&quot;: &quot;const entity = flow.get(\&quot;$parent.speech_entity_id\&quot;) || (msg.payload &amp;amp;&amp;amp; msg.payload.entity_id) || \&quot;media_player.office_echo_plus\&quot;;\nconst message = (msg.payload &amp;amp;&amp;amp; msg.payload.message) || \&quot;Automation provided no message\&quot;;\nconst announcement = msg.payload &amp;amp;&amp;amp; msg.payload.announcement;\n\nif (announcement) {\n    node.status({ fill: \&quot;green\&quot;, shape: \&quot;dot\&quot;, text: \&quot;Announce message:\&quot; + message });\n    return {\n        payload:{\n            data: {\n                message: message,\n                data: { \&quot;type\&quot;: \&quot;announce\&quot;, \&quot;method\&quot;: \&quot;all\&quot; },\n                target: !!entity ? [entity] : []\n            }\n        }\n    };\n}\n\nnode.status({ fill: \&quot;green\&quot;, shape: \&quot;dot\&quot;, text: \&quot;TTS message:\&quot; + message });\nreturn {\n    payload:{\n        data: {\n            message: message,\n            data: { type: \&quot;tts\&quot; },\n            target: [entity]\n        }\n    }\n};&quot;,
        &quot;outputs&quot;: 1,
        &quot;noerr&quot;: 0,
        &quot;x&quot;: 700,
        &quot;y&quot;: 100,
        &quot;wires&quot;: [
            [
                &quot;d44cc51a.be0668&quot;
            ]
        ]
    },
    {
        &quot;id&quot;: &quot;d44cc51a.be0668&quot;,
        &quot;type&quot;: &quot;api-call-service&quot;,
        &quot;z&quot;: &quot;7a62a2d3.c0f4a4&quot;,
        &quot;name&quot;: &quot;Send Speech Notification&quot;,
        &quot;server&quot;: &quot;61956bd4.93df44&quot;,
        &quot;version&quot;: 1,
        &quot;debugenabled&quot;: false,
        &quot;service_domain&quot;: &quot;notify&quot;,
        &quot;service&quot;: &quot;alexa_media&quot;,
        &quot;entityId&quot;: &quot;&quot;,
        &quot;data&quot;: &quot;&quot;,
        &quot;dataType&quot;: &quot;json&quot;,
        &quot;mergecontext&quot;: &quot;&quot;,
        &quot;output_location&quot;: &quot;payload&quot;,
        &quot;output_location_type&quot;: &quot;msg&quot;,
        &quot;mustacheAltTags&quot;: false,
        &quot;x&quot;: 950,
        &quot;y&quot;: 100,
        &quot;wires&quot;: [
            []
        ]
    },
    {
        &quot;id&quot;: &quot;6add9bc6.4c3624&quot;,
        &quot;type&quot;: &quot;api-current-state&quot;,
        &quot;z&quot;: &quot;7a62a2d3.c0f4a4&quot;,
        &quot;name&quot;: &quot;Speech Notifications?&quot;,
        &quot;server&quot;: &quot;61956bd4.93df44&quot;,
        &quot;version&quot;: 1,
        &quot;outputs&quot;: 2,
        &quot;halt_if&quot;: &quot;true&quot;,
        &quot;halt_if_type&quot;: &quot;bool&quot;,
        &quot;halt_if_compare&quot;: &quot;is&quot;,
        &quot;override_topic&quot;: false,
        &quot;entity_id&quot;: &quot;input_boolean.automation_notifications&quot;,
        &quot;state_type&quot;: &quot;habool&quot;,
        &quot;state_location&quot;: &quot;&quot;,
        &quot;override_payload&quot;: &quot;none&quot;,
        &quot;entity_location&quot;: &quot;&quot;,
        &quot;override_data&quot;: &quot;none&quot;,
        &quot;blockInputOverrides&quot;: false,
        &quot;x&quot;: 440,
        &quot;y&quot;: 140,
        &quot;wires&quot;: [
            [
                &quot;89300b1.595bcf8&quot;
            ],
            []
        ],
        &quot;outputLabels&quot;: [
            &quot;&quot;,
            &quot;enabled&quot;
        ]
    },
    {
        &quot;id&quot;: &quot;aef53056.742438&quot;,
        &quot;type&quot;: &quot;status&quot;,
        &quot;z&quot;: &quot;7a62a2d3.c0f4a4&quot;,
        &quot;name&quot;: &quot;&quot;,
        &quot;scope&quot;: null,
        &quot;x&quot;: 100,
        &quot;y&quot;: 40,
        &quot;wires&quot;: [
            []
        ]
    },
    {
        &quot;id&quot;: &quot;87411254.a6ed18&quot;,
        &quot;type&quot;: &quot;function&quot;,
        &quot;z&quot;: &quot;7a62a2d3.c0f4a4&quot;,
        &quot;name&quot;: &quot;Check for overrides&quot;,
        &quot;func&quot;: &quot;const alwaysSpeak = msg.payload &amp;amp;&amp;amp; msg.payload.announcement\nif (alwaysSpeak) {\n    return [msg, null];\n} else {\n    return [null, msg];\n}&quot;,
        &quot;outputs&quot;: 2,
        &quot;noerr&quot;: 0,
        &quot;x&quot;: 210,
        &quot;y&quot;: 100,
        &quot;wires&quot;: [
            [
                &quot;89300b1.595bcf8&quot;
            ],
            [
                &quot;6add9bc6.4c3624&quot;
            ]
        ],
        &quot;outputLabels&quot;: [
            &quot;Bypass notification&quot;,
            &quot;Check for notifications&quot;
        ]
    },
    {
        &quot;id&quot;: &quot;974adda.d2ea92&quot;,
        &quot;type&quot;: &quot;Subflow&quot;,
        &quot;name&quot;: &quot;Set Volume Level&quot;,
        &quot;info&quot;: &quot;Set Volume Level&quot;,
        &quot;category&quot;: &quot;&quot;,
        &quot;in&quot;: [
            {
                &quot;x&quot;: 80,
                &quot;y&quot;: 120,
                &quot;wires&quot;: [
                    {
                        &quot;id&quot;: &quot;4c86fd47.0875e4&quot;
                    }
                ]
            }
        ],
        &quot;out&quot;: [
            {
                &quot;x&quot;: 960,
                &quot;y&quot;: 100,
                &quot;wires&quot;: [
                    {
                        &quot;id&quot;: &quot;263d05a1.5b6672&quot;,
                        &quot;port&quot;: 0
                    }
                ]
            }
        ],
        &quot;status&quot;: {
            &quot;x&quot;: 240,
            &quot;y&quot;: 40,
            &quot;wires&quot;: [
                {
                    &quot;id&quot;: &quot;7a662cd5.162a24&quot;,
                    &quot;port&quot;: 0
                }
            ]
        }
    },
    {
        &quot;id&quot;: &quot;563c252e.ce31e4&quot;,
        &quot;type&quot;: &quot;function&quot;,
        &quot;z&quot;: &quot;974adda.d2ea92&quot;,
        &quot;name&quot;: &quot;Set Volume Level Payload&quot;,
        &quot;func&quot;: &quot;const entity = msg.payload &amp;amp;&amp;amp; msg.payload.entity_id;\nconst attributes = msg.data &amp;amp;&amp;amp; msg.data.attributes\nif (!entity || !attributes) {\n    node.status({ fill: \&quot;red\&quot;, shape: \&quot;dot\&quot;, text: \&quot;Invalid volume payload\&quot; });\n    return [null, null];\n}\n\nconst desired_volume_level = msg.payload.volume_level || 0;\nif (desired_volume_level &amp;lt; 0.0 || desired_volume_level &amp;gt; 1.0) {\n    let message = \&quot;Volume level must be between 0 and 1.\&quot;; \n    node.status({ fill: \&quot;red\&quot;, shape: \&quot;dot\&quot;, text: message });\n    node.error(message);\n    \n    return [null, { \n        payload: {\n            entity_id: entity,\n            message: message\n        }\n    }];\n}\n\nif (desired_volume_level === attributes.volume_level) {\n    let message = \&quot;Volume level is already at the desired level.\&quot;; \n    node.status({ fill: \&quot;grey\&quot;, shape: \&quot;dot\&quot;, text: message });\n    node.log(message);\n    return [null, { \n        payload: {\n            entity_id: entity,\n            message: message\n        }\n    }];\n}\n\nconst volumePayload = { \n    payload: {\n        data: {\n            entity_id: entity,\n            volume_level: desired_volume_level\n        } \n    }\n};\n\nlet message = \&quot;Setting Volume Level to \&quot; + (desired_volume_level * 100) + \&quot; percent.\&quot;;\nconst speechPayload = { \n    payload: {\n        entity_id: entity,\n        message: message\n    }\n};\n\nnode.status({ fill: \&quot;green\&quot;, shape: \&quot;dot\&quot;, text: message });\nreturn [volumePayload, speechPayload];&quot;,
        &quot;outputs&quot;: 2,
        &quot;noerr&quot;: 0,
        &quot;x&quot;: 510,
        &quot;y&quot;: 120,
        &quot;wires&quot;: [
            [
                &quot;263d05a1.5b6672&quot;
            ],
            [
                &quot;b41308c0.f41e9&quot;
            ]
        ],
        &quot;inputLabels&quot;: [
            &quot;Volume Percentage&quot;
        ],
        &quot;outputLabels&quot;: [
            &quot;Volume Level Payload&quot;,
            &quot;Speech Notification Payload&quot;
        ]
    },
    {
        &quot;id&quot;: &quot;263d05a1.5b6672&quot;,
        &quot;type&quot;: &quot;api-call-service&quot;,
        &quot;z&quot;: &quot;974adda.d2ea92&quot;,
        &quot;name&quot;: &quot;Set Volume Level&quot;,
        &quot;server&quot;: &quot;61956bd4.93df44&quot;,
        &quot;version&quot;: 1,
        &quot;debugenabled&quot;: false,
        &quot;service_domain&quot;: &quot;media_player&quot;,
        &quot;service&quot;: &quot;volume_set&quot;,
        &quot;entityId&quot;: &quot;&quot;,
        &quot;data&quot;: &quot;&quot;,
        &quot;dataType&quot;: &quot;json&quot;,
        &quot;mergecontext&quot;: &quot;&quot;,
        &quot;output_location&quot;: &quot;payload&quot;,
        &quot;output_location_type&quot;: &quot;msg&quot;,
        &quot;mustacheAltTags&quot;: false,
        &quot;x&quot;: 770,
        &quot;y&quot;: 100,
        &quot;wires&quot;: [
            []
        ]
    },
    {
        &quot;id&quot;: &quot;b41308c0.f41e9&quot;,
        &quot;type&quot;: &quot;Subflow:7a62a2d3.c0f4a4&quot;,
        &quot;z&quot;: &quot;974adda.d2ea92&quot;,
        &quot;name&quot;: &quot;&quot;,
        &quot;env&quot;: [],
        &quot;x&quot;: 830,
        &quot;y&quot;: 160,
        &quot;wires&quot;: [
            []
        ]
    },
    {
        &quot;id&quot;: &quot;4c86fd47.0875e4&quot;,
        &quot;type&quot;: &quot;api-current-state&quot;,
        &quot;z&quot;: &quot;974adda.d2ea92&quot;,
        &quot;name&quot;: &quot;Get Current Volume Level&quot;,
        &quot;server&quot;: &quot;61956bd4.93df44&quot;,
        &quot;version&quot;: 1,
        &quot;outputs&quot;: 1,
        &quot;halt_if&quot;: &quot;&quot;,
        &quot;halt_if_type&quot;: &quot;str&quot;,
        &quot;halt_if_compare&quot;: &quot;is&quot;,
        &quot;override_topic&quot;: true,
        &quot;entity_id&quot;: &quot;&quot;,
        &quot;state_type&quot;: &quot;str&quot;,
        &quot;state_location&quot;: &quot;&quot;,
        &quot;override_payload&quot;: &quot;none&quot;,
        &quot;entity_location&quot;: &quot;data&quot;,
        &quot;override_data&quot;: &quot;msg&quot;,
        &quot;blockInputOverrides&quot;: false,
        &quot;x&quot;: 250,
        &quot;y&quot;: 120,
        &quot;wires&quot;: [
            [
                &quot;563c252e.ce31e4&quot;
            ]
        ]
    },
    {
        &quot;id&quot;: &quot;7a662cd5.162a24&quot;,
        &quot;type&quot;: &quot;status&quot;,
        &quot;z&quot;: &quot;974adda.d2ea92&quot;,
        &quot;name&quot;: &quot;&quot;,
        &quot;scope&quot;: null,
        &quot;x&quot;: 120,
        &quot;y&quot;: 40,
        &quot;wires&quot;: [
            []
        ]
    },
    {
        &quot;id&quot;: &quot;76be143e.dbb0e4&quot;,
        &quot;type&quot;: &quot;Subflow&quot;,
        &quot;name&quot;: &quot;Set Volume Level on All Media Devices&quot;,
        &quot;info&quot;: &quot;&quot;,
        &quot;category&quot;: &quot;&quot;,
        &quot;in&quot;: [
            {
                &quot;x&quot;: 100,
                &quot;y&quot;: 120,
                &quot;wires&quot;: [
                    {
                        &quot;id&quot;: &quot;642c5f33.bd92a8&quot;
                    }
                ]
            }
        ],
        &quot;out&quot;: [
            {
                &quot;x&quot;: 1120,
                &quot;y&quot;: 120,
                &quot;wires&quot;: [
                    {
                        &quot;id&quot;: &quot;15d5ddae.8f64c2&quot;,
                        &quot;port&quot;: 0
                    }
                ]
            }
        ],
        &quot;env&quot;: [],
        &quot;color&quot;: &quot;#DDAA99&quot;,
        &quot;status&quot;: {
            &quot;x&quot;: 1120,
            &quot;y&quot;: 40,
            &quot;wires&quot;: [
                {
                    &quot;id&quot;: &quot;ba88f8d5.b5d648&quot;,
                    &quot;port&quot;: 0
                },
                {
                    &quot;id&quot;: &quot;15d5ddae.8f64c2&quot;,
                    &quot;port&quot;: 0
                }
            ]
        }
    },
    {
        &quot;id&quot;: &quot;f467a711.24ca3&quot;,
        &quot;type&quot;: &quot;ha-get-entities&quot;,
        &quot;z&quot;: &quot;76be143e.dbb0e4&quot;,
        &quot;server&quot;: &quot;61956bd4.93df44&quot;,
        &quot;name&quot;: &quot;Get All Media Players&quot;,
        &quot;rules&quot;: [
            {
                &quot;property&quot;: &quot;attributes.supported_features&quot;,
                &quot;logic&quot;: &quot;is&quot;,
                &quot;value&quot;: &quot;56253&quot;,
                &quot;valueType&quot;: &quot;num&quot;
            },
            {
                &quot;property&quot;: &quot;attributes.available&quot;,
                &quot;logic&quot;: &quot;is&quot;,
                &quot;value&quot;: &quot;true&quot;,
                &quot;valueType&quot;: &quot;bool&quot;
            }
        ],
        &quot;output_type&quot;: &quot;split&quot;,
        &quot;output_empty_results&quot;: true,
        &quot;output_location_type&quot;: &quot;msg&quot;,
        &quot;output_location&quot;: &quot;payload&quot;,
        &quot;output_results_count&quot;: 1,
        &quot;x&quot;: 480,
        &quot;y&quot;: 120,
        &quot;wires&quot;: [
            [
                &quot;1fe46c89.0e0543&quot;
            ]
        ]
    },
    {
        &quot;id&quot;: &quot;1fe46c89.0e0543&quot;,
        &quot;type&quot;: &quot;change&quot;,
        &quot;z&quot;: &quot;76be143e.dbb0e4&quot;,
        &quot;name&quot;: &quot;Set Volume Level Payload&quot;,
        &quot;rules&quot;: [
            {
                &quot;t&quot;: &quot;set&quot;,
                &quot;p&quot;: &quot;payload.volume_level&quot;,
                &quot;pt&quot;: &quot;msg&quot;,
                &quot;to&quot;: &quot;volume_level&quot;,
                &quot;tot&quot;: &quot;flow&quot;
            }
        ],
        &quot;action&quot;: &quot;&quot;,
        &quot;property&quot;: &quot;&quot;,
        &quot;from&quot;: &quot;&quot;,
        &quot;to&quot;: &quot;&quot;,
        &quot;reg&quot;: false,
        &quot;x&quot;: 730,
        &quot;y&quot;: 120,
        &quot;wires&quot;: [
            [
                &quot;15d5ddae.8f64c2&quot;
            ]
        ]
    },
    {
        &quot;id&quot;: &quot;642c5f33.bd92a8&quot;,
        &quot;type&quot;: &quot;function&quot;,
        &quot;z&quot;: &quot;76be143e.dbb0e4&quot;,
        &quot;name&quot;: &quot;Parse Volume Level&quot;,
        &quot;func&quot;: &quot;const volumeLevel = parseFloat(msg.payload);\nnode.status({ fill: \&quot;green\&quot;, shape: \&quot;dot\&quot;, text: \&quot;Volume Level: \&quot; + volumeLevel });\nflow.set(\&quot;volume_level\&quot;, volumeLevel);\nreturn msg;&quot;,
        &quot;outputs&quot;: 1,
        &quot;noerr&quot;: 0,
        &quot;x&quot;: 250,
        &quot;y&quot;: 120,
        &quot;wires&quot;: [
            [
                &quot;f467a711.24ca3&quot;
            ]
        ]
    },
    {
        &quot;id&quot;: &quot;15d5ddae.8f64c2&quot;,
        &quot;type&quot;: &quot;Subflow:974adda.d2ea92&quot;,
        &quot;z&quot;: &quot;76be143e.dbb0e4&quot;,
        &quot;name&quot;: &quot;&quot;,
        &quot;env&quot;: [],
        &quot;x&quot;: 970,
        &quot;y&quot;: 120,
        &quot;wires&quot;: [
            []
        ]
    },
    {
        &quot;id&quot;: &quot;ba88f8d5.b5d648&quot;,
        &quot;type&quot;: &quot;status&quot;,
        &quot;z&quot;: &quot;76be143e.dbb0e4&quot;,
        &quot;name&quot;: &quot;&quot;,
        &quot;scope&quot;: null,
        &quot;x&quot;: 140,
        &quot;y&quot;: 40,
        &quot;wires&quot;: [
            []
        ]
    },
    {
        &quot;id&quot;: &quot;3c07a459.ce903c&quot;,
        &quot;type&quot;: &quot;tab&quot;,
        &quot;label&quot;: &quot;Normalize Volume&quot;,
        &quot;disabled&quot;: false,
        &quot;info&quot;: &quot;Normalize All Volume&quot;
    },
    {
        &quot;id&quot;: &quot;e949c86f.c9ca9&quot;,
        &quot;type&quot;: &quot;api-current-state&quot;,
        &quot;z&quot;: &quot;3c07a459.ce903c&quot;,
        &quot;name&quot;: &quot;Automations Enabled?&quot;,
        &quot;server&quot;: &quot;61956bd4.93df44&quot;,
        &quot;version&quot;: 1,
        &quot;outputs&quot;: 2,
        &quot;halt_if&quot;: &quot;true&quot;,
        &quot;halt_if_type&quot;: &quot;bool&quot;,
        &quot;halt_if_compare&quot;: &quot;is&quot;,
        &quot;override_topic&quot;: false,
        &quot;entity_id&quot;: &quot;input_boolean.automation_enable&quot;,
        &quot;state_type&quot;: &quot;habool&quot;,
        &quot;state_location&quot;: &quot;&quot;,
        &quot;override_payload&quot;: &quot;none&quot;,
        &quot;entity_location&quot;: &quot;&quot;,
        &quot;override_data&quot;: &quot;none&quot;,
        &quot;blockInputOverrides&quot;: false,
        &quot;x&quot;: 560,
        &quot;y&quot;: 100,
        &quot;wires&quot;: [
            [
                &quot;ec5308f5.aefff&quot;
            ],
            []
        ],
        &quot;outputLabels&quot;: [
            &quot;&quot;,
            &quot;enabled&quot;
        ]
    },
    {
        &quot;id&quot;: &quot;ec5308f5.aefff&quot;,
        &quot;type&quot;: &quot;api-current-state&quot;,
        &quot;z&quot;: &quot;3c07a459.ce903c&quot;,
        &quot;name&quot;: &quot;Normalize Volume Levels?&quot;,
        &quot;server&quot;: &quot;61956bd4.93df44&quot;,
        &quot;version&quot;: 1,
        &quot;outputs&quot;: 2,
        &quot;halt_if&quot;: &quot;true&quot;,
        &quot;halt_if_type&quot;: &quot;bool&quot;,
        &quot;halt_if_compare&quot;: &quot;is&quot;,
        &quot;override_topic&quot;: false,
        &quot;entity_id&quot;: &quot;input_boolean.automation_normalize_volume&quot;,
        &quot;state_type&quot;: &quot;habool&quot;,
        &quot;state_location&quot;: &quot;&quot;,
        &quot;override_payload&quot;: &quot;none&quot;,
        &quot;entity_location&quot;: &quot;&quot;,
        &quot;override_data&quot;: &quot;none&quot;,
        &quot;blockInputOverrides&quot;: false,
        &quot;x&quot;: 810,
        &quot;y&quot;: 100,
        &quot;wires&quot;: [
            [
                &quot;bde48ad8.814ac&quot;
            ],
            []
        ],
        &quot;outputLabels&quot;: [
            &quot;&quot;,
            &quot;enabled&quot;
        ]
    },
    {
        &quot;id&quot;: &quot;dbf47096.3a7dc&quot;,
        &quot;type&quot;: &quot;inject&quot;,
        &quot;z&quot;: &quot;3c07a459.ce903c&quot;,
        &quot;name&quot;: &quot;&quot;,
        &quot;topic&quot;: &quot;&quot;,
        &quot;payload&quot;: &quot;on&quot;,
        &quot;payloadType&quot;: &quot;str&quot;,
        &quot;repeat&quot;: &quot;&quot;,
        &quot;crontab&quot;: &quot;&quot;,
        &quot;once&quot;: false,
        &quot;onceDelay&quot;: 0.1,
        &quot;x&quot;: 110,
        &quot;y&quot;: 80,
        &quot;wires&quot;: [
            [
                &quot;4608374e.2fd4d8&quot;
            ]
        ]
    },
    {
        &quot;id&quot;: &quot;6eace9df.a5aa68&quot;,
        &quot;type&quot;: &quot;inject&quot;,
        &quot;z&quot;: &quot;3c07a459.ce903c&quot;,
        &quot;name&quot;: &quot;&quot;,
        &quot;topic&quot;: &quot;&quot;,
        &quot;payload&quot;: &quot;off&quot;,
        &quot;payloadType&quot;: &quot;str&quot;,
        &quot;repeat&quot;: &quot;&quot;,
        &quot;crontab&quot;: &quot;&quot;,
        &quot;once&quot;: false,
        &quot;onceDelay&quot;: 0.1,
        &quot;x&quot;: 110,
        &quot;y&quot;: 120,
        &quot;wires&quot;: [
            [
                &quot;4608374e.2fd4d8&quot;
            ]
        ]
    },
    {
        &quot;id&quot;: &quot;4608374e.2fd4d8&quot;,
        &quot;type&quot;: &quot;bigtimer&quot;,
        &quot;z&quot;: &quot;3c07a459.ce903c&quot;,
        &quot;outtopic&quot;: &quot;&quot;,
        &quot;outpayload1&quot;: &quot;.3&quot;,
        &quot;outpayload2&quot;: &quot;.1&quot;,
        &quot;name&quot;: &quot;Normalize Volume&quot;,
        &quot;comment&quot;: &quot;Set volume to 30% from 9:00am to 9:00pm, otherwise set to 10%&quot;,
        &quot;lat&quot;: &quot;44.50&quot;,
        &quot;lon&quot;: &quot;-88.06&quot;,
        &quot;starttime&quot;: &quot;540&quot;,
        &quot;endtime&quot;: &quot;1260&quot;,
        &quot;startoff&quot;: &quot;0&quot;,
        &quot;endoff&quot;: 0,
        &quot;startoff2&quot;: &quot;&quot;,
        &quot;endoff2&quot;: &quot;&quot;,
        &quot;offs&quot;: 0,
        &quot;outtext1&quot;: &quot;&quot;,
        &quot;outtext2&quot;: &quot;&quot;,
        &quot;timeout&quot;: &quot;240&quot;,
        &quot;sun&quot;: true,
        &quot;mon&quot;: true,
        &quot;tue&quot;: true,
        &quot;wed&quot;: true,
        &quot;thu&quot;: true,
        &quot;fri&quot;: true,
        &quot;sat&quot;: true,
        &quot;jan&quot;: true,
        &quot;feb&quot;: true,
        &quot;mar&quot;: true,
        &quot;apr&quot;: true,
        &quot;may&quot;: true,
        &quot;jun&quot;: true,
        &quot;jul&quot;: true,
        &quot;aug&quot;: true,
        &quot;sep&quot;: true,
        &quot;oct&quot;: true,
        &quot;nov&quot;: true,
        &quot;dec&quot;: true,
        &quot;day1&quot;: 0,
        &quot;month1&quot;: 0,
        &quot;day2&quot;: 0,
        &quot;month2&quot;: 0,
        &quot;day3&quot;: 0,
        &quot;month3&quot;: 0,
        &quot;day4&quot;: 0,
        &quot;month4&quot;: 0,
        &quot;day5&quot;: 0,
        &quot;month5&quot;: 0,
        &quot;day6&quot;: 0,
        &quot;month6&quot;: 0,
        &quot;day7&quot;: &quot;&quot;,
        &quot;month7&quot;: &quot;&quot;,
        &quot;day8&quot;: &quot;&quot;,
        &quot;month8&quot;: &quot;&quot;,
        &quot;day9&quot;: &quot;&quot;,
        &quot;month9&quot;: &quot;&quot;,
        &quot;day10&quot;: &quot;&quot;,
        &quot;month10&quot;: &quot;&quot;,
        &quot;day11&quot;: &quot;&quot;,
        &quot;month11&quot;: &quot;&quot;,
        &quot;day12&quot;: &quot;&quot;,
        &quot;month12&quot;: &quot;&quot;,
        &quot;d1&quot;: 0,
        &quot;w1&quot;: 0,
        &quot;d2&quot;: 0,
        &quot;w2&quot;: 0,
        &quot;d3&quot;: 0,
        &quot;w3&quot;: 0,
        &quot;d4&quot;: 0,
        &quot;w4&quot;: 0,
        &quot;d5&quot;: 0,
        &quot;w5&quot;: 0,
        &quot;d6&quot;: 0,
        &quot;w6&quot;: 0,
        &quot;xday1&quot;: &quot;0&quot;,
        &quot;xmonth1&quot;: &quot;0&quot;,
        &quot;xday2&quot;: &quot;0&quot;,
        &quot;xmonth2&quot;: &quot;0&quot;,
        &quot;xday3&quot;: 0,
        &quot;xmonth3&quot;: 0,
        &quot;xday4&quot;: 0,
        &quot;xmonth4&quot;: 0,
        &quot;xday5&quot;: 0,
        &quot;xmonth5&quot;: 0,
        &quot;xday6&quot;: 0,
        &quot;xmonth6&quot;: 0,
        &quot;xd1&quot;: 0,
        &quot;xw1&quot;: 0,
        &quot;xd2&quot;: 0,
        &quot;xw2&quot;: 0,
        &quot;xd3&quot;: 0,
        &quot;xw3&quot;: 0,
        &quot;xd4&quot;: 0,
        &quot;xw4&quot;: 0,
        &quot;xd5&quot;: 0,
        &quot;xw5&quot;: 0,
        &quot;xd6&quot;: 0,
        &quot;xw6&quot;: 0,
        &quot;suspend&quot;: false,
        &quot;random&quot;: false,
        &quot;repeat&quot;: false,
        &quot;atstart&quot;: false,
        &quot;odd&quot;: false,
        &quot;even&quot;: false,
        &quot;x&quot;: 340,
        &quot;y&quot;: 100,
        &quot;wires&quot;: [
            [
                &quot;e949c86f.c9ca9&quot;
            ],
            [],
            []
        ]
    },
    {
        &quot;id&quot;: &quot;418865ec.975eec&quot;,
        &quot;type&quot;: &quot;inject&quot;,
        &quot;z&quot;: &quot;3c07a459.ce903c&quot;,
        &quot;name&quot;: &quot;&quot;,
        &quot;topic&quot;: &quot;&quot;,
        &quot;payload&quot;: &quot;auto&quot;,
        &quot;payloadType&quot;: &quot;str&quot;,
        &quot;repeat&quot;: &quot;&quot;,
        &quot;crontab&quot;: &quot;&quot;,
        &quot;once&quot;: false,
        &quot;onceDelay&quot;: 0.1,
        &quot;x&quot;: 110,
        &quot;y&quot;: 160,
        &quot;wires&quot;: [
            [
                &quot;4608374e.2fd4d8&quot;
            ]
        ]
    },
    {
        &quot;id&quot;: &quot;e314dd72.39afe&quot;,
        &quot;type&quot;: &quot;inject&quot;,
        &quot;z&quot;: &quot;3c07a459.ce903c&quot;,
        &quot;name&quot;: &quot;&quot;,
        &quot;topic&quot;: &quot;&quot;,
        &quot;payload&quot;: &quot;manual&quot;,
        &quot;payloadType&quot;: &quot;str&quot;,
        &quot;repeat&quot;: &quot;&quot;,
        &quot;crontab&quot;: &quot;&quot;,
        &quot;once&quot;: false,
        &quot;onceDelay&quot;: 0.1,
        &quot;x&quot;: 110,
        &quot;y&quot;: 200,
        &quot;wires&quot;: [
            [
                &quot;4608374e.2fd4d8&quot;
            ]
        ]
    },
    {
        &quot;id&quot;: &quot;bde48ad8.814ac&quot;,
        &quot;type&quot;: &quot;rbe&quot;,
        &quot;z&quot;: &quot;3c07a459.ce903c&quot;,
        &quot;name&quot;: &quot;Only allow changed values&quot;,
        &quot;func&quot;: &quot;rbe&quot;,
        &quot;gap&quot;: &quot;&quot;,
        &quot;start&quot;: &quot;&quot;,
        &quot;inout&quot;: &quot;out&quot;,
        &quot;property&quot;: &quot;payload&quot;,
        &quot;x&quot;: 480,
        &quot;y&quot;: 180,
        &quot;wires&quot;: [
            [
                &quot;4173c48c.381f84&quot;
            ]
        ]
    },
    {
        &quot;id&quot;: &quot;4173c48c.381f84&quot;,
        &quot;type&quot;: &quot;Subflow:76be143e.dbb0e4&quot;,
        &quot;z&quot;: &quot;3c07a459.ce903c&quot;,
        &quot;name&quot;: &quot;&quot;,
        &quot;env&quot;: [],
        &quot;x&quot;: 790,
        &quot;y&quot;: 180,
        &quot;wires&quot;: [
            []
        ]
    },
    {
        &quot;id&quot;: &quot;d45759b3.b76fe8&quot;,
        &quot;type&quot;: &quot;comment&quot;,
        &quot;z&quot;: &quot;3c07a459.ce903c&quot;,
        &quot;name&quot;: &quot;Set volume to 30% from 9:00am to 9:00pm, otherwise set to 10%&quot;,
        &quot;info&quot;: &quot;&quot;,
        &quot;x&quot;: 270,
        &quot;y&quot;: 40,
        &quot;wires&quot;: []
    },
    {
        &quot;id&quot;: &quot;61956bd4.93df44&quot;,
        &quot;type&quot;: &quot;server&quot;,
        &quot;z&quot;: &quot;&quot;,
        &quot;name&quot;: &quot;Home Assistant&quot;,
        &quot;legacy&quot;: false,
        &quot;addon&quot;: true,
        &quot;rejectUnauthorizedCerts&quot;: true,
        &quot;ha_boolean&quot;: &quot;y|yes|true|on|home|open&quot;,
        &quot;connectionDelay&quot;: true
    }
]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;NodeRed VS Home Assistant Automations&lt;/h2&gt;
&lt;p&gt;I personally prefer writing all my automations in NodeRed as it helps me
visually see the flow of the automation. I&apos;d love to hear what you like and if
you&apos;d like to see automations written using the built in Home Assistant
Automations.&lt;/p&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>How-to send Text-to-Speech notifications with Home Assistant and Node-RED</title><link>https://blakeniemyjski.com/blog/how-to-send-text-to-speech-notifications-with-home-assistant-and-node-red/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/how-to-send-text-to-speech-notifications-with-home-assistant-and-node-red/</guid><pubDate>Thu, 30 Apr 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Have you ever wanted to send Text-to-Speech (TTS) notifications with
&lt;a href=&quot;https://www.home-assistant.io&quot;&gt;Home Assistant&lt;/a&gt; and
&lt;a href=&quot;https://nodered.org&quot;&gt;Node-RED&lt;/a&gt;? In the sections below I&apos;m going to be using
&lt;a href=&quot;https://github.com/custom-components/alexa_media_player&quot;&gt;Amazon Alexa Media Player Integration&lt;/a&gt;
to send TTS notifications. Please note you can easily switch out the
&lt;a href=&quot;https://www.home-assistant.io/integrations/tts/&quot;&gt;TTS integration&lt;/a&gt; to the one
of your choosing and still use this Subflow (with a few tweaks).&lt;/p&gt;
&lt;p&gt;This is one of my favorite
&lt;a href=&quot;https://nodered.org/docs/user-guide/editor/workspace/subflows&quot;&gt;Subflows&lt;/a&gt; as it
can be used easily in a lot of automations. I use it in my automations to
announce:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Friendly state change notifications for accessibility (e.g., motion detected
in room abc, lights turning on in the current room, the garage door was unlocked).&lt;/li&gt;
&lt;li&gt;General announcements (e.g., welcome to our home, it&apos;s bedtime).&lt;/li&gt;
&lt;li&gt;Weather related announcements and warnings.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;If you are new to Node-RED, I&apos;d recommend checking out this awesome
&lt;a href=&quot;https://www.youtube.com/watch?v=SuoSXVqjyfc&amp;amp;list=PL-51DG-VULPqeoYGyIrainyWc-_MnFb-y&quot;&gt;YouTube Node-RED playlist&lt;/a&gt;
to get up to speed!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Alexa Media Player Custom Component&lt;/h3&gt;
&lt;p&gt;If you haven&apos;t done so already, install the
&lt;a href=&quot;https://github.com/custom-components/alexa_media_player&quot;&gt;Alexa Media Player Custom Component&lt;/a&gt;
if you are using Alexa, otherwise skip this section.&lt;/p&gt;
&lt;p&gt;This addon is amazing as it normalizes all of your Alexa enabled devices such
as the
&lt;a href=&quot;https://www.amazon.com/all-new-Echo/dp/B07NFTVP7P?tag=niemyjski-20&quot;&gt;Amazon Echo&lt;/a&gt;
or &lt;a href=&quot;https://www.amazon.com/ecobee-Switch-Smart-Amazon-Built/dp/B0798LCLJ5?tag=niemyjski-20&quot;&gt;ecobee Switch+&lt;/a&gt;
into a &lt;code&gt;media_player&lt;/code&gt; that can be used by Home Assistant. Once configured you
can simply verify the device you want to send a message is working by calling
the &lt;code&gt;notify.alexa_media&lt;/code&gt; service with the payload:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;message: &quot;Text-to-Speech is easy&quot;
data:
  type: &quot;tts&quot;
  target: [&quot;media_player.amazon_echo_plus&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you don&apos;t hear anything after calling this service, stop and check the
logs. This will help diagnose why you are not getting TTS notifications before
continuing.&lt;/p&gt;
&lt;h2&gt;Input Boolean to control automations&lt;/h2&gt;
&lt;p&gt;I highly recommend adding an &lt;a href=&quot;https://www.home-assistant.io/integrations/input_boolean/&quot;&gt;Input Boolean&lt;/a&gt;
that controls if audible notifications occur. It could get pretty annoying if
you are constantly hearing notifications for automations that are running
unless it&apos;s desired. Here is my &lt;code&gt;input_boolean&lt;/code&gt; that I use for this automation.
If you choose a different name, be sure to update the code below.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;automation_notifications:
  name: Notify when an automation is triggered
  icon: mdi:home-automation
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Send Automation Speech Notification Subflow&lt;/h2&gt;
&lt;p&gt;Lets open Node-RED and create a new Subflow with a
&lt;a href=&quot;https://nodered.org/docs/creating-nodes/status#note--status-node&quot;&gt;Status Node&lt;/a&gt;,
&lt;code&gt;Input Node&lt;/code&gt; and &lt;code&gt;Output Node&lt;/code&gt;. I really like using the &lt;code&gt;Status Node&lt;/code&gt; to capture
all statuses as it allows for a better experience using Subflows by showing you
a visual indication of the current status. Also, I like specifying an
&lt;code&gt;Output Node&lt;/code&gt; as it allows a node to be part of a series when the Subflow has
completed. It&apos;s worth noting that this &lt;code&gt;Output Node&lt;/code&gt; will only be called if the
notification is sent.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;graph TD
  InputNode(Input) --&amp;gt; AFunctionNode(fas:fa-code Check For Overrides)
  AFunctionNode --&amp;gt; BCurrentStateNode(fas:fa-database Speech Notifications?)
  AFunctionNode --&amp;gt; |Announcement Override| CFunctionNode(fas:fa-code Set Speech Payload)
  BCurrentStateNode --&amp;gt; CFunctionNode
  CFunctionNode --&amp;gt; DCallServiceNode(fas:fa-play Send Speech Notification)
  DCallServiceNode --&amp;gt; OutputNode(Output 1)

  StatusNode(fas:fa-heartbeat Status: All) --&amp;gt; StatusOutputNode(Status)

style StatusNode fill:#ECF5FF
style StatusOutputNode fill:#FAFAFB

style InputNode fill:#FAFAFB
style AFunctionNode fill:#FBB68F
style BCurrentStateNode fill:#66ACFD
style CFunctionNode fill:#FBB68F
style DCallServiceNode fill:#66ACFD
style OutputNode fill:#FAFAFB
linkStyle default stroke-width:2px,fill:none,stroke:#CCD0D4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&apos;s break down the Subflow. The incoming message gets passed to the
&lt;code&gt;Check For Overrides&lt;/code&gt; node. This node simply checks to see if the
&lt;code&gt;msg.payload.announcement&lt;/code&gt; is set to &lt;code&gt;true&lt;/code&gt;. If &lt;code&gt;true&lt;/code&gt;, then it will always send
the TTS notification. Otherwise it will call the &lt;code&gt;Speech Notifications?&lt;/code&gt;
&lt;code&gt;Current State Node&lt;/code&gt; to see if the &lt;code&gt;input_boolean.automation_notifications&lt;/code&gt; is
&lt;code&gt;true&lt;/code&gt;. To recap, we will only call &lt;code&gt;Set Speech Payload&lt;/code&gt; if it an &lt;code&gt;announcement&lt;/code&gt;
is &lt;code&gt;true&lt;/code&gt; or our &lt;code&gt;input_boolean.automation_notifications&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;Set Speech Payload&lt;/code&gt; node will return a new payload that the
&lt;code&gt;Send Speech Notification&lt;/code&gt; &lt;code&gt;Service Call Node&lt;/code&gt; is expecting. If you are not
using the &lt;code&gt;alexa_media_player&lt;/code&gt; Home Assistant addon, you&apos;ll want to update
both of these nodes for the correct payload and service call.&lt;/p&gt;
&lt;p&gt;You can then trigger this Subflow by passing a message object with the
following payload. I recommend using a &lt;a href=&quot;https://nodered.org/docs/user-guide/nodes&quot;&gt;Inject Node&lt;/a&gt;
to test this out.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
    &quot;announcement&quot;: true,
    &quot;entity_id&quot;: &quot;media_player.amazon_echo_plus&quot;,
    &quot;message&quot;: &quot;test&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When triggering this Subflow it&apos;s a good idea to pick the closest speaker to
where the automation is occurring.&lt;/p&gt;
&lt;p&gt;Here is the exported Node-RED subflow.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[
    {
        &quot;id&quot;: &quot;7a62a2d3.c0f4a4&quot;,
        &quot;type&quot;: &quot;subflow&quot;,
        &quot;name&quot;: &quot;Send Automation Speech Notification&quot;,
        &quot;info&quot;: &quot;&quot;,
        &quot;category&quot;: &quot;&quot;,
        &quot;in&quot;: [
            {
                &quot;x&quot;: 60,
                &quot;y&quot;: 100,
                &quot;wires&quot;: [
                    {
                        &quot;id&quot;: &quot;87411254.a6ed18&quot;
                    }
                ]
            }
        ],
        &quot;out&quot;: [
            {
                &quot;x&quot;: 1120,
                &quot;y&quot;: 100,
                &quot;wires&quot;: [
                    {
                        &quot;id&quot;: &quot;d44cc51a.be0668&quot;,
                        &quot;port&quot;: 0
                    }
                ]
            }
        ],
        &quot;status&quot;: {
            &quot;x&quot;: 220,
            &quot;y&quot;: 40,
            &quot;wires&quot;: [
                {
                    &quot;id&quot;: &quot;aef53056.742438&quot;,
                    &quot;port&quot;: 0
                }
            ]
        }
    },
    {
        &quot;id&quot;: &quot;89300b1.595bcf8&quot;,
        &quot;type&quot;: &quot;function&quot;,
        &quot;z&quot;: &quot;7a62a2d3.c0f4a4&quot;,
        &quot;name&quot;: &quot;Set Speech Payload&quot;,
        &quot;func&quot;: &quot;const entity = flow.get(\&quot;$parent.speech_entity_id\&quot;) || (msg.payload &amp;amp;&amp;amp; msg.payload.entity_id) || \&quot;media_player.amazon_echo_plus\&quot;;\nconst message = (msg.payload &amp;amp;&amp;amp; msg.payload.message) || \&quot;Automation provided no message\&quot;;\n\nnode.status({ fill: \&quot;green\&quot;, shape: \&quot;dot\&quot;, text: \&quot;TTS message:\&quot; + message });\nreturn {\n    payload:{\n        data: {\n            message: message,\n            data: { type: \&quot;tts\&quot; },\n            target: [entity]\n        }\n    }\n};&quot;,
        &quot;outputs&quot;: 1,
        &quot;noerr&quot;: 0,
        &quot;x&quot;: 700,
        &quot;y&quot;: 100,
        &quot;wires&quot;: [
            [
                &quot;d44cc51a.be0668&quot;
            ]
        ]
    },
    {
        &quot;id&quot;: &quot;d44cc51a.be0668&quot;,
        &quot;type&quot;: &quot;api-call-service&quot;,
        &quot;z&quot;: &quot;7a62a2d3.c0f4a4&quot;,
        &quot;name&quot;: &quot;Send Speech Notification&quot;,
        &quot;server&quot;: &quot;61956bd4.93df44&quot;,
        &quot;version&quot;: 1,
        &quot;debugenabled&quot;: false,
        &quot;service_domain&quot;: &quot;notify&quot;,
        &quot;service&quot;: &quot;alexa_media&quot;,
        &quot;entityId&quot;: &quot;&quot;,
        &quot;data&quot;: &quot;&quot;,
        &quot;dataType&quot;: &quot;json&quot;,
        &quot;mergecontext&quot;: &quot;&quot;,
        &quot;output_location&quot;: &quot;payload&quot;,
        &quot;output_location_type&quot;: &quot;msg&quot;,
        &quot;mustacheAltTags&quot;: false,
        &quot;x&quot;: 950,
        &quot;y&quot;: 100,
        &quot;wires&quot;: [
            []
        ]
    },
    {
        &quot;id&quot;: &quot;6add9bc6.4c3624&quot;,
        &quot;type&quot;: &quot;api-current-state&quot;,
        &quot;z&quot;: &quot;7a62a2d3.c0f4a4&quot;,
        &quot;name&quot;: &quot;Speech Notifications?&quot;,
        &quot;server&quot;: &quot;61956bd4.93df44&quot;,
        &quot;version&quot;: 1,
        &quot;outputs&quot;: 2,
        &quot;halt_if&quot;: &quot;true&quot;,
        &quot;halt_if_type&quot;: &quot;bool&quot;,
        &quot;halt_if_compare&quot;: &quot;is&quot;,
        &quot;override_topic&quot;: false,
        &quot;entity_id&quot;: &quot;input_boolean.automation_notifications&quot;,
        &quot;state_type&quot;: &quot;habool&quot;,
        &quot;state_location&quot;: &quot;&quot;,
        &quot;override_payload&quot;: &quot;none&quot;,
        &quot;entity_location&quot;: &quot;&quot;,
        &quot;override_data&quot;: &quot;none&quot;,
        &quot;blockInputOverrides&quot;: false,
        &quot;x&quot;: 440,
        &quot;y&quot;: 140,
        &quot;wires&quot;: [
            [
                &quot;89300b1.595bcf8&quot;
            ],
            []
        ],
        &quot;outputLabels&quot;: [
            &quot;&quot;,
            &quot;enabled&quot;
        ]
    },
    {
        &quot;id&quot;: &quot;aef53056.742438&quot;,
        &quot;type&quot;: &quot;status&quot;,
        &quot;z&quot;: &quot;7a62a2d3.c0f4a4&quot;,
        &quot;name&quot;: &quot;&quot;,
        &quot;scope&quot;: null,
        &quot;x&quot;: 100,
        &quot;y&quot;: 40,
        &quot;wires&quot;: [
            []
        ]
    },
    {
        &quot;id&quot;: &quot;87411254.a6ed18&quot;,
        &quot;type&quot;: &quot;function&quot;,
        &quot;z&quot;: &quot;7a62a2d3.c0f4a4&quot;,
        &quot;name&quot;: &quot;Check for overrides&quot;,
        &quot;func&quot;: &quot;const alwaysSpeak = msg.payload &amp;amp;&amp;amp; msg.payload.announcement\nif (alwaysSpeak) {\n    return [msg, null];\n} else {\n    return [null, msg];\n}&quot;,
        &quot;outputs&quot;: 2,
        &quot;noerr&quot;: 0,
        &quot;x&quot;: 210,
        &quot;y&quot;: 100,
        &quot;wires&quot;: [
            [
                &quot;89300b1.595bcf8&quot;
            ],
            [
                &quot;6add9bc6.4c3624&quot;
            ]
        ],
        &quot;outputLabels&quot;: [
            &quot;Bypass notification&quot;,
            &quot;Check for notifications&quot;
        ]
    },
    {
        &quot;id&quot;: &quot;61956bd4.93df44&quot;,
        &quot;type&quot;: &quot;server&quot;,
        &quot;z&quot;: &quot;&quot;,
        &quot;name&quot;: &quot;Home Assistant&quot;,
        &quot;legacy&quot;: false,
        &quot;addon&quot;: true,
        &quot;rejectUnauthorizedCerts&quot;: true,
        &quot;ha_boolean&quot;: &quot;y|yes|true|on|home|open&quot;,
        &quot;connectionDelay&quot;: true
    }
]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I hope this article inspired you to create your own reusable Subflows that you
can use in your various automations. I&apos;ve found this Subflow to be super useful,
let me know how it works out for you!&lt;/p&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>Let&apos;s talk about smart home accessibility</title><link>https://blakeniemyjski.com/blog/lets-talk-about-smart-home-accessibility/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/lets-talk-about-smart-home-accessibility/</guid><pubDate>Thu, 26 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I define accessibility as a critical necessity to be inclusive to everyone. No
matter how big or small the task, anyone should be able complete the task at
hand regardless of the disability (e.g., vision, hearing, mobility, device
access, language, etc).&lt;/p&gt;
&lt;p&gt;You might be wondering what does this have to do with smart homes and why should
I think or care about this? I think there are a few simple facts to consider:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You may already have a disability.&lt;/li&gt;
&lt;li&gt;You can dramatically improve the quality of life, safety and happiness of
those who have a disability when they are in your home.&lt;/li&gt;
&lt;li&gt;You never know when yourself or a loved one may have a disability. You
could one day wake up and not be able to walk, see or hear (I&apos;m serious). You
could have a stroke or get hit by a car dramatically changing your way of life
only to realize how unaccessible the world really is.&lt;/li&gt;
&lt;li&gt;We are aging every day and everyone will have some type of disability at some
point in your life.&lt;/li&gt;
&lt;li&gt;If all consumers think about this, it will be for the betterment of all while
bringing down costs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I hope this showcased a different perspective on life! Now, I&apos;m not saying we
should all get rid of our current smart devices or do major construction for
ramps, handrails or low countertops. But it is something you should keep in
mind when buying new products, buying a house or doing a remodel. Did you ever
consider this could be a reason why a house with a first floor bedroom is worth
more than a house with a second story bedroom?&lt;/p&gt;
&lt;p&gt;Also we can use our existing smart products (e.g., smart bulbs) to make a home
more accessible with a little bit of time. I&apos;ll cover this below in the
following sections.&lt;/p&gt;
&lt;h2&gt;Progressive Enhancement&lt;/h2&gt;
&lt;p&gt;Think of a single task or physical item that does something really well like a
light switch with on and off actions. A progressive enhancement would be to add
additional functionality without compromising the previous task/item. For
example, a switch or light should always be able to be turned on like any other
device, but it may be able to provide additional functionality like a night
light, motion detection, or play multimedia.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Automations are a progress enhancement and can improve accessibility.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Think about the ways you can use these added enhancements in accessible ways
but don&apos;t compromise on the main functionality of the device!&lt;/p&gt;
&lt;h2&gt;Switches&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;TLDR: Find a switch that will work great as just a switch, supports multiple
protocols if possible (e.g., API and HomeKit, Z-Wave) and has progressive
enhancements (motion, light) that don&apos;t take away from using the switch. Use
These sensors to create automations that can improve accessibility.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The single responsibility of a switch is to turn on and off a device. This should
continue to work unimpeded. A guest shouldn&apos;t know they are interacting with
a smart home, it should work like any other home to be accessible. Switches
with motion detection and smart home control can greatly increase the quality
and reduce the physical need to turn lights or devices on or off.&lt;/p&gt;
&lt;p&gt;I&apos;ve seen multiple products like &lt;a href=&quot;https://www.amazon.com/dp/B07912JY7Y?tag=niemyjski-20&quot;&gt;wall switch guards&lt;/a&gt;
and &lt;a href=&quot;https://www.thingiverse.com/search?q=light+switch+guard&amp;amp;type=things&amp;amp;sort=relevant&quot;&gt;3D printed guards&lt;/a&gt;
which only add to the problems.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.thingiverse.com/thing:2826763&quot;&gt;&lt;img src=&quot;/img/blog/home-automation/accessibility-light-switch-guard-800.jpg&quot; alt=&quot;Light Switch Guard&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;These guards were put into place because turning off the switch would cause
products like the &lt;a href=&quot;https://www.amazon.com/Philips-Ambiance-Equivalent-Compatible-Assistant/dp/B07351P1JK?tag=niemyjski-20&quot;&gt;Hue Smart Bulbs&lt;/a&gt;
from working as smart bulbs need constant power. The real solution to this
problem would be to use the &lt;a href=&quot;https://www.amazon.com/Philips-Dimmer-Switch-Installation-Free-Exclusively/dp/B076MGKTGS?tag=niemyjski-20&quot;&gt;official dimmer switch&lt;/a&gt;
or even better, replace the switch with a smart switch that is designed to act
like a normal switch but can also supply constant power to the smart bulb. One
switch that fits this bill is the &lt;a href=&quot;https://www.amazon.com/Inovelli-SmartThings-Monitoring-Technology-Notifications/dp/B07S1BMMGH?tag=niemyjski-20&quot;&gt;Inovelli Smart Switch&lt;/a&gt;.
With this switch you can disable the internal relay so constant power is
supplied to the bulb, while the on and off commands are forwarded to your hub.
You can then write an automation to turn on or off the bulb when the switch is
activated!&lt;/p&gt;
&lt;p&gt;I have a few different switches in my house after months of research to what
would have simple on/off controls, but give me the most flexibility with
progressive enhancements. Here are some of the switches that I currently use
and the enhancements I get from them:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.amazon.com/ecobee-Switch-Smart-Amazon-Built/dp/B0798LCLJ5?tag=niemyjski-20&quot;&gt;ecobee Switch+&lt;/a&gt;:
This switch provides many benefits like a light (path lighting and night light),
speaker (voice announcements, music, home alarm), microphone (control devices),
illuminance sensor (to conserve energy), motion detection (security, lighting
control), and temperature sensor. I integrated this switch into
&lt;a href=&quot;https://www.home-assistant.io&quot;&gt;Home Assistant&lt;/a&gt; with the
&lt;a href=&quot;https://www.home-assistant.io/integrations/homekit/&quot;&gt;HomeKit integration&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.amazon.com/GE-Occupancy-Required-SmartThings-26931/dp/B07226MG2T?tag=niemyjski-20&quot;&gt;GE Z-Wave Motion Switch&lt;/a&gt;:
This switch mainly provides motion detection and works with Z-Wave. I use it
mainly for motion detection. I do wish it exposed its internal light sensor
so I could get access to the rooms brightness for additional smartness. This
switch has been pretty reliable but every smart switch has its baggage too.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I have purchased and but have not installed the &lt;a href=&quot;https://www.amazon.com/Inovelli-SmartThings-Monitoring-Technology-Notifications/dp/B07S1BMMGH?tag=niemyjski-20&quot;&gt;Inovelli Smart Switch&lt;/a&gt;.
I plan on replacing my &lt;a href=&quot;https://www.amazon.com/GE-Enbrighten-SimpleWire-SmartThings-46201/dp/B07RRBT6W5?tag=niemyjski-20&quot;&gt;GE Z-Wave switch&lt;/a&gt;
in the house to control the garage lights. This will allow me to use the switch&apos;s
awesome LED lights on the side of the switch to tell me the current charging
status of the &lt;a href=&quot;https://www.tesla.com/model3&quot;&gt;Tesla Model 3&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Also, keep in mind that some switches may look nice and have &lt;a href=&quot;https://noonhome.com&quot;&gt;full touch screens&lt;/a&gt;
but these probably won&apos;t last as long as a normal switch and many people might
not be able to figure out how to use them. Will you have to explain how to
turn on a light to your grandparents or parents?&lt;/p&gt;
&lt;p&gt;Lastly, here is a list of some of the automations I&apos;ve used to improve
accessibility:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use the switch&apos;s motion sensors to automatically turn on the lights and off
when there is no more activity. I&apos;ve tried to be really smart about this, so
one never even has to speak or touch a switch, it is not perfect but getting
closer.&lt;/li&gt;
&lt;li&gt;Use the switch&apos;s motion sensors and speaker (or nearby speaker) to announce
switch state changes like (light turning on, motion detected, fireplace
turning on). This is behind an automation accessibility mode flag and is
useful to hear the state changes or what&apos;s going on in the room (and
debugging).&lt;/li&gt;
&lt;li&gt;Automatically turn on lights in the case of emergency (path way lighting).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Audio&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;TLDR: Think about ways to use both microphones and speakers with natural
language to augment the home. There&apos;s more to it than just whole home audio.
Your home can give context in scenarios where someone can&apos;t see or touch.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;When end users typically think about about about smart homes and audio devices,
they think about whole home audio, but this is not the only use case as I&apos;ll
cover below.&lt;/p&gt;
&lt;p&gt;Since whole home audio is all the rage and the biggest use case it is worth
addressing. I feel that it is only possible for the large majority today within
two ecosystems: &lt;a href=&quot;https://www.amazon.com/all-new-Echo/dp/B07NFTVP7P?tag=niemyjski-20&quot;&gt;Amazon via Echo&lt;/a&gt;
(or third party speakers like the
&lt;a href=&quot;https://www.amazon.com/Sonos-Controlled-Speaker-Amazon-2-Pack/dp/B07PP8QYFD?tag=niemyjski-20&quot;&gt;Sonos&lt;/a&gt;)
and &lt;a href=&quot;https://www.apple.com/shop/buy-homepod/homepod&quot;&gt;Apple via HomePod&lt;/a&gt;.
Both of these allow you to use voice activation to play music and have deep
application support. But they require an internet connection for streaming audio
and cost some serious money. Smart switches like the
&lt;a href=&quot;https://www.amazon.com/ecobee-Switch-Smart-Amazon-Built/dp/B0798LCLJ5?tag=niemyjski-20&quot;&gt;ecobee Switch+&lt;/a&gt;
mentioned above can play music, but only on a single device at a time.&lt;/p&gt;
&lt;p&gt;One accessibility item that people forget about is natural language commands.
If you are interacting with your smart home using voice, than make sure various
commands work like &lt;code&gt;Turn on the office lights&lt;/code&gt;, &lt;code&gt;Turn on the lights&lt;/code&gt; and &lt;code&gt;Lumos&lt;/code&gt;. This
second command is tricky, something I&apos;ve yet to get working very good.&lt;/p&gt;
&lt;p&gt;But you can do so much more with audio than just play music! Here is a sample
list of automations I&apos;m currently doing with audio:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Automatically mute devices based on time of day (privacy!).&lt;/li&gt;
&lt;li&gt;Automatically normalize audio based on time of day, so a notification doesn&apos;t
blare audio when someone is sleeping.&lt;/li&gt;
&lt;li&gt;Use facial and image recognition to make contextual announcements like who is
at the door (person with suggested age, glasses, etc), package delivered, cat
is back from its adventures!&lt;/li&gt;
&lt;li&gt;Use the switch&apos;s motion sensors and speaker (or nearby speaker) to announce
switch state changes like (light turning on, motion detected, fireplace
turning on). This is behind an automation accessibility mode flag and is
useful to hear the state changes or what&apos;s going on in the room (and
debugging).&lt;/li&gt;
&lt;li&gt;Announcements when tasks are completed (e.g., the washer or dryer has finished).&lt;/li&gt;
&lt;li&gt;Whole house notifications for announcements like dinner is ready or it is bed
time. This can be a little annoying for switches which might have audio
delivery delays as audio echos throughout the house.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Future automation goals relating to audio:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use directional audio notifications with sensor/location data to point
someone to the closest door in case of emergency.&lt;/li&gt;
&lt;li&gt;Use speakers to amplify security alarms and doorbells.&lt;/li&gt;
&lt;li&gt;Integrate with live weather advisories and keep informed about dangerous
weather conditions.&lt;/li&gt;
&lt;li&gt;Notifications when wildlife is outside (e.g., deer, ducks, etc.).&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/custom-components/alexa_media_player/wiki#automation-example&quot;&gt;Clear alexa history&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Play relaxing music, white noise or lullabies at bed time based on occupancy.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you are using &lt;a href=&quot;https://www.amazon.com/all-new-Echo/dp/B07NFTVP7P?tag=niemyjski-20&quot;&gt;Amazon Echo&lt;/a&gt;
devices and are using Home Assistant, I&apos;d highly recommend installing the
&lt;a href=&quot;https://github.com/custom-components/alexa_media_player&quot;&gt;Alexa Media Player Integration&lt;/a&gt;.
I use this to control all of my devices as well as do text to speech. Here is
the Home Assistant service call needed to send a message:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;service: notify.alexa_media
data:
  message: &quot;my message&quot;
  data:
    type: &quot;tts&quot;
  target: [&quot;media_player.YOUR_MEDIA_PLAYER&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I created a reusable &lt;a href=&quot;https://nodered.org/docs/user-guide/editor/workspace/subflows&quot;&gt;Node-Red Subflow&lt;/a&gt;
that calls this service and has &lt;code&gt;input_boolean&lt;/code&gt; and overrides for announcements.&lt;/p&gt;
&lt;h2&gt;Lighting&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;TLDR: There&apos;s more to lighting than just smart bulbs. Removing the need to
turn on or off a single light is a huge deal for those with little to no
mobility.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Lighting can be used to &lt;a href=&quot;https://en.wikipedia.org/wiki/Li-Fi&quot;&gt;transmit vital information&lt;/a&gt;
about current or future state. But the most often use case is to use color to
control room ambience or enhance a
&lt;a href=&quot;https://www.amazon.com/Philips-White-Color-Ambiance-Double/dp/B07GXB3S7Z?tag=niemyjski-20&quot;&gt;movie experience&lt;/a&gt;
through smart bulbs and &lt;a href=&quot;https://www2.meethue.com/en-us/p/hue-play-hdmi-sync-box-/046677555221&quot;&gt;hub&lt;/a&gt;.
The current two most popular lighting solutions &lt;a href=&quot;https://www.amazon.com/stores/node/20345611011?tag=niemyjski-20&quot;&gt;Hue&lt;/a&gt;
and &lt;a href=&quot;https://www.amazon.com/stores/node/9029830011?tag=niemyjski-20&quot;&gt;LIFX&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But you don&apos;t need smart bulbs to have great lighting. While I do have some
smart bulbs primarily for my lamps, I&apos;ve invested in smart switches to control
my lighting. I feel like this gives me way more control over the environment in
a future proof way that works with any lighting source, it also allows me to
control lights by the switch, motion, voice or application. Which is a huge win
as smart bulbs typically don&apos;t provide bright light and can lock you in on
controlling them. With this said, I do think that smart bulbs and devices with
integrated lights (switches, smoke detectors for pathway detection) can
increase accessibility more than just a light switch.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Keep in mind, smart bulbs don&apos;t work with most switches and don&apos;t even think
about using it with a dimmer switch.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;One advantage of a smart bulb is that some can change colors. With color, you
can show different states like danger, this way to safety, or work-in-progress.
It is important to try and choose
&lt;a href=&quot;http://web-accessibility.carnegiemuseums.org/design/color/&quot;&gt;accessible colors&lt;/a&gt;
that everyone can see!&lt;/p&gt;
&lt;p&gt;Here is a sample list of automations I&apos;m currently doing with lighting:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Turn on lights based motion, time of day, and outside and inside illuminance.
It is really important to respect turning on or off the lights by manual
press!&lt;/li&gt;
&lt;li&gt;Turn on lights when people are detected in the driveway / door.&lt;/li&gt;
&lt;li&gt;Turning off lights when the tv starts playing a movie.&lt;/li&gt;
&lt;li&gt;Turning on lights and fans when shower turns on and off 30 minutes after
shower stops.&lt;/li&gt;
&lt;li&gt;Turn on/off devices and lights when waking up or going to sleep.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Future automation goals relating to lighting:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;While my wired &lt;a href=&quot;https://www.amazon.com/S3000BWES-Protect-Carbon-Monoxide-Battery/dp/B00XV1RD0K?tag=niemyjski-20&quot;&gt;Nest Protect&lt;/a&gt;
smoke detectors currently provide some basic path way lighting. I would like
to have directional lighting to the exit based on occupancy. Think of bright
light a head of you and fading light behind you.&lt;/li&gt;
&lt;li&gt;Use a colored light to show busy status like in virtual meeting.&lt;/li&gt;
&lt;li&gt;Show current traffic conditions for possible delays based on calendar
location data.&lt;/li&gt;
&lt;li&gt;Have greater presence detection so the home knows where you are at.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Other Devices&lt;/h2&gt;
&lt;p&gt;There are other many other device types, just keep in mind the devices
responsibility and make sure it does it well. Progressive enhancements can be
used to power future automations which can improve the quality of life for all.&lt;/p&gt;
&lt;h2&gt;Smart Home Software&lt;/h2&gt;
&lt;p&gt;I think about accessibility not just on physical devices but the software too.
This could be the automations you wrote to the smartphone applications or
management hub (e.g., Home Assistant).&lt;/p&gt;
&lt;p&gt;We have a long way for software to be more inclusive from color schemes, easy
clickable buttons, and screen reader support.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/home-automation/accessibility-home-assistant-history-720.png&quot; alt=&quot;Home Assistant Accessibility&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I&apos;d like to see more work go into improving accessibility in Home Assistant (as
well as this blog) as shown above. I think this will be improved over time which
will be a huge win for the whole community.&lt;/p&gt;
&lt;h2&gt;Power failures&lt;/h2&gt;
&lt;p&gt;Lastly, what about power failures? Can we handle them in an accessible way
where interaction isn&apos;t needed like automatic power fail over? For
example do we handle the following scenarios:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Restoring to last state on power restart.&lt;/li&gt;
&lt;li&gt;Running critical devices like smoke detectors, wifi on batteries.&lt;/li&gt;
&lt;li&gt;Critical alerts like security, fridge and flood sensors while power is out.&lt;/li&gt;
&lt;li&gt;Letting others know we might need assistance.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I feel those that have the most to gain with a smart home are those with
disabilities and we need to make it easier. But, it feels almost impossible to
install, configure, and automate a smart home today. There are just too many
blocking factors such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Too many inaccessible product options and software.&lt;/li&gt;
&lt;li&gt;Limited knowledgeable professional installers.&lt;/li&gt;
&lt;li&gt;Steep learning curve to get a hub up and running, and integrate your devices.&lt;/li&gt;
&lt;li&gt;Steep Learning curve learning how to write automations, especially for the
majority who don&apos;t know how to program.&lt;/li&gt;
&lt;li&gt;Hard to have reliable and secure networks (Z-Wave, Zigbee, Wifi). Having
a network just for Internet of Things (IoT), is only available on high end
routers (e.g. &lt;a href=&quot;https://www.amazon.com/Ubiquiti-UniFi-Dream-Machine-UDM-US/dp/B081QNJFPV?tag=niemyjski-20&quot;&gt;Ubiquity UniFi Dream Machine&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Consumers need to be privacy aware in a time when paid products are
compromising user privacy for profit.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We need to take the steps to make Home Automation and Smart Homes more accessible
in this early adoption stage of Smart Homes.&lt;/p&gt;
&lt;p&gt;Also, be sure to check out volunteer opportunities like &lt;a href=&quot;https://www.bemyeyes.com&quot;&gt;Be My Eyes&lt;/a&gt;
for bringing sight to blind and low-vision people!&lt;/p&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>Lessons learned from working at home for over a decade</title><link>https://blakeniemyjski.com/blog/lessons-learned-from-working-at-home-for-over-a-decade/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/lessons-learned-from-working-at-home-for-over-a-decade/</guid><pubDate>Tue, 10 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;With the recent world events causing many to work from home, I figured what
better time than now to share my some of my tips. This may be your first time
working from home for an extended period of time, but I have been remote for
the past 13 years.&lt;/p&gt;
&lt;h2&gt;1. Start with a routine&lt;/h2&gt;
&lt;p&gt;Whether you are a morning person or not, get into a routine that works for you
and stick to it. This will help get you motivated for the day. I try my best to
go to bed at a reasonable hour so I can naturally wake up around 5/6 a.m. feeling
refreshed. I start my day enjoying
&lt;a href=&quot;https://www.amazon.com/gp/product/B00HURFGBO?tag=niemyjski-20&quot;&gt;two cups of organic coffee&lt;/a&gt;
with
&lt;a href=&quot;https://www.organicvalley.coop/products/cream/cream/heavy-whipping-cream-pasteurized-pint/&quot;&gt;Organic Valley Heavy Whipping Cream&lt;/a&gt;
and &lt;a href=&quot;https://www.amazon.com/dp/B0784YLJKP?tag=niemyjski-20&quot;&gt;Unflavored Collagen Peptides&lt;/a&gt;
while catching up on emails, blogs and world news. This really helps me wake up
and get ready for the work day.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I&apos;ve had my trusty &lt;a href=&quot;https://www.amazon.com/Keurig-B60-Special-Brewing-System/dp/B000AQSMPO?tag=niemyjski-20&quot;&gt;Keurig B60&lt;/a&gt;
for a decade or longer. I&apos;ve been waiting to find a good coffee/mocha machine
that is easy to clean and can be automated. If you have a machine that works
for you let me know.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A routine could be as simple as taking a shower, getting dressed for the day or
even going for a walk.&lt;/p&gt;
&lt;h2&gt;2. Create a daily goal&lt;/h2&gt;
&lt;p&gt;I start every day with a simple goal of what I want to accomplish. It can be
something I want to learn or a work task I want to get done like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Complete a feature request or comment on an issue&lt;/li&gt;
&lt;li&gt;Inbox zero&lt;/li&gt;
&lt;li&gt;Meditating for 10 minutes during lunch using &lt;a href=&quot;https://www.calm.com&quot;&gt;Calm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Reading a page of a book&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Just create a small goal that is accomplishable as it will make you feel more
productive.&lt;/p&gt;
&lt;h2&gt;3. Work asynchronously&lt;/h2&gt;
&lt;p&gt;If possible, try &lt;a href=&quot;https://blog.remote.com/why-you-should-be-doing-async-work/&quot;&gt;working asynchronously&lt;/a&gt;.
This means that you prioritize smaller similar tasks and complete them without
interruption. Unless a message is super important it&apos;s okay to respond to it a
few minutes later along with any other messages you get. It&apos;s proven that
&lt;a href=&quot;https://www.apa.org/research/action/multitask&quot;&gt;switching tasks&lt;/a&gt; is not
productive.&lt;/p&gt;
&lt;p&gt;If you have a hard time with time management, there are techniques like
&lt;a href=&quot;https://en.wikipedia.org/wiki/Pomodoro_Technique&quot;&gt;Pomodoro&lt;/a&gt; which may help you
stay focused.&lt;/p&gt;
&lt;h2&gt;4. Keep your calendar up-to-date&lt;/h2&gt;
&lt;p&gt;If you have a morning standup meeting (e.g., scrum) pay attention for meetings
or demos that may not have been sent via a calendar invite. I always add these
to a calendar with any applicable time zones. Not doing so may mean you miss an
important discussion.&lt;/p&gt;
&lt;p&gt;If you keep to a schedule and know when upcoming meetings are you might be able
to take a flexible lunch hour to get a haircut, mow the lawn before it rains,
or have a quick lunch with a friend.&lt;/p&gt;
&lt;p&gt;I also keep an eye out for coworker travel plans to the office. This allows you
to spend time with other remote employees with whom you communicate with daily.
It&apos;s super important to stay social even if that&apos;s not your personality! I
cherish all the memories of visiting the office and having fun with those who
you work with day to day.&lt;/p&gt;
&lt;h2&gt;5. Avoid burn out&lt;/h2&gt;
&lt;p&gt;It&apos;s very easy to get burned out by working super long hours to make up for
what might feel like an unproductive day or to get a task done. Just consider
the fact that working in an office may have more distractions than working from
home. Making it a super productive day even if it doesn&apos;t feel like it.&lt;/p&gt;
&lt;p&gt;There will always be a never ending supply of work, that will be there tomorrow.
So leave work at work and disconnect to enjoy family/personal time at the end
of the day. It really helps if you have an isolated space like a spare room
where you only work from. At the end of the day close the door and walk away.&lt;/p&gt;
&lt;h2&gt;6. All about the Optics&lt;/h2&gt;
&lt;p&gt;If you don&apos;t have a camera and will be working from home for a while, I&apos;d
recommend investing in a good
&lt;a href=&quot;https://www.amazon.com/Logitech-BRIO-Conferencing-Recording-Streaming/dp/B01N5UOYC4?tag=niemyjski-20&quot;&gt;Logitech BRIO camera&lt;/a&gt;
and
&lt;a href=&quot;https://www.amazon.com/Logitech-USB-Headset-H570e-Stereo/dp/B00MUTWMN2?tag=niemyjski-20&quot;&gt;Logitech H570e headset&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Also, make sure to check what is in your background of your camera and mute
yourself when not talking.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I use the built in
&lt;a href=&quot;https://www.amazon.com/Apple-MacBook-15-inch-512GB-Storage/dp/B07S58MHXF?tag=niemyjski-20&quot;&gt;Apple MacBook Pro&lt;/a&gt;
front facing camera and the
&lt;a href=&quot;https://www.amazon.com/Apple-AirPods-with-Charging-Case/dp/B07PXGQC1Q?tag=niemyjski-20&quot;&gt;Apple AirPods&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Just remember to look directly into the camera and smile ??. It goes a long way
to build relationships and show that you are listening! Meetings don&apos;t always
go smoothly and you are not the only one who may be experiencing technical
difficulties.&lt;/p&gt;
&lt;h2&gt;7. Take a break&lt;/h2&gt;
&lt;p&gt;It is perfectly normal to take a break after working a few hours, diagnosing a
hard problem, or you just need some fresh air. I find the most difficult problems
are resolved upon returning from a short break. It&apos;s like having a second pair
of eyes on a difficult problem.&lt;/p&gt;
&lt;p&gt;Get up and take a walk to the mailbox or around the block to clear the mind
(and pie in moderation never hurts).&lt;/p&gt;
&lt;p&gt;&amp;lt;iframe
title=&quot;YouTube video player&quot;
src=&quot;https://www.youtube.com/embed/oqGU1DkaA0M&quot;
loading=&quot;lazy&quot;
allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&quot;
referrerpolicy=&quot;strict-origin-when-cross-origin&quot;
allowfullscreen&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;lt;/iframe&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Every one needs a few minutes of me time to decompress!&lt;/p&gt;
&lt;h2&gt;8. Exercise your body, eyes, and mind&lt;/h2&gt;
&lt;p&gt;It&apos;s really important to stretch and get your exercise in after being sedentary.
I purchased a &lt;a href=&quot;https://www.btod.com/RA-246060NHRT.php&quot;&gt;standing desk&lt;/a&gt;
to help get me more active and I&apos;ve seen others use
&lt;a href=&quot;https://www.amazon.com/gp/product/B07WWHM18H?tag=niemyjski-20&quot;&gt;under desk treadmills&lt;/a&gt;
too.&lt;/p&gt;
&lt;p&gt;I&apos;ve always been told by my eye doctor to take breaks and to look outside in
the distance. It&apos;s not good for our eyes to constantly focus on screens that
are close to us.&lt;/p&gt;
&lt;p&gt;It really helps if you have an amazing office view! It&apos;s amazing what you&apos;ll
see when you take a look outside. In the video below I saw two male ducks
fighting for love one summer day (looks best full screen @ 4k, tune to 2:30 for
an up close fight).&lt;/p&gt;
&lt;p&gt;&amp;lt;iframe
title=&quot;YouTube video player&quot;
src=&quot;https://www.youtube.com/embed/b_WRAmt0ZOk&quot;
loading=&quot;lazy&quot;
allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&quot;
referrerpolicy=&quot;strict-origin-when-cross-origin&quot;
allowfullscreen&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;lt;/iframe&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Did you know that &lt;a href=&quot;https://www.ducks.org/conservation/waterfowl-research-science/waterfowl-mating-systems&quot;&gt;ducks form seasonal bonds&lt;/a&gt;?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Working out, meditating, and eating clean really helps keep your mind sharp
too! You have the added advantage of removing food temptations from your diet
by working at home!&lt;/p&gt;
&lt;h2&gt;9. Set family expectations&lt;/h2&gt;
&lt;p&gt;A lot has changed since I first started working remote in college. It can be
difficult for a partner or family member who has never worked remote before to
understand the work life dynamics. Although you might be home, you are not free
all the time for home tasks. I&apos;m guilty of forgetting to switch the laundry
during a break because I&apos;m so concentrated in my work.&lt;/p&gt;
&lt;p&gt;If you have dedicated work hours stick to them. If you have flexible hours,
than consider starting your day early so you can get tasks done that may need
to be done before the weekend starts. This may include mowing the lawn or
getting groceries.&lt;/p&gt;
&lt;h2&gt;10. Distractions&lt;/h2&gt;
&lt;p&gt;It can be easy to get distracted by the smallest of things! I&apos;d recommend
eliminating as many distractions as possible such as a messy desk or
&lt;a href=&quot;https://support.apple.com/en-us/HT208982&quot;&gt;enabling screen time&lt;/a&gt; on your phone.
Just make your environment comfy to be in for long periods of time without
added distractions.&lt;/p&gt;
&lt;p&gt;I&apos;ve found that distractions can change day to day, so try and learn what
&lt;a href=&quot;https://www.amazon.com/Triggers-Creating-Behavior-Lasts-Becoming-Person/dp/0804141231?tag=niemyjski-20&quot;&gt;triggers you&lt;/a&gt;.
For example, one day I might be able to concentrate while playing music while
others I need it to be dead silent.&lt;/p&gt;
&lt;p&gt;Here are two music playlists that oddly enough help me concentrate:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://open.spotify.com/playlist/1ZJpJahEFst7u8njXeGFyv?si=jSUsDUxtSX6mL4SWKpqRDw&quot;&gt;Relaxing Classical&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://open.spotify.com/playlist/37i9dQZF1DWVEt8B7a1H1M?si=pOfH3CElSfqIyxgNL07cqg&quot;&gt;Birds in the Forest&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Try narrowing down what music you can play at a reduced volume that doesn&apos;t
distract you in your day to day tasks. I&apos;ve automated my speakers to change
the sound levels during the day so I don&apos;t wake a sleeping bear ??.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I&apos;ve taken this a step further when I&apos;m having a really productive day. I will
start &lt;a href=&quot;https://www.amazon.com/gp/product/B00Y2CQRZY?tag=niemyjski-20&quot;&gt;diffusing&lt;/a&gt; a
&lt;a href=&quot;https://www.amazon.com/gp/product/B011D2QPF2?tag=niemyjski-20&quot;&gt;specific scent&lt;/a&gt;
(like mental focus). Scents
&lt;a href=&quot;https://www.scientificamerican.com/article/why-do-smells-trigger-memories/&quot;&gt;can trigger memories and emotions&lt;/a&gt;
which I find help me get back into a productive state of mind. The human body
is truly amazing.&lt;/p&gt;
&lt;p&gt;Lastly, it can really help to work with a coworker (e.g., pair program) to do a
task if you are struggling (we all have these days). This helps ensure active
participation while working through complex problems.&lt;/p&gt;
&lt;p&gt;&amp;lt;iframe
title=&quot;YouTube video player&quot;
src=&quot;https://www.youtube.com/embed/2AB9zPfXqQQ&quot;
loading=&quot;lazy&quot;
allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&quot;
referrerpolicy=&quot;strict-origin-when-cross-origin&quot;
allowfullscreen&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;lt;/iframe&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This doesn&apos;t mean that you will be twice as productive, but you will be more
productive than just yourself!&lt;/p&gt;
&lt;h2&gt;11. With Great Responsibility...&lt;/h2&gt;
&lt;p&gt;I absolutely love working from home in my own personal space. But one regret I
do have is not taking full advantage of working remote by leaving my house more
often. For example, I wish in the past decade that I:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Traveled to more remote destinations while working. In 2016, my wife and I
traveled and worked remotely in New Zealand for a month. This was an amazing
learning adventure! I&apos;d wake up early and work for several hours at the start
and end of the day. It was a blast experiencing different cultures and breath
taking scenery, but it takes extreme discipline.&lt;/li&gt;
&lt;li&gt;Taken a &lt;a href=&quot;https://dereklow.co/across-the-usa-by-train-for-just-213/&quot;&gt;cross country sight seeing train&lt;/a&gt;
while working through the scenic landscapes.&lt;/li&gt;
&lt;li&gt;Spent more time working from my parents who live a few hours away and are
not getting any younger.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>Installing Home Assistant on Ubuntu</title><link>https://blakeniemyjski.com/blog/installing-home-assistant-on-ubuntu/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/installing-home-assistant-on-ubuntu/</guid><pubDate>Mon, 09 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;NOTE: This post has been updated on May 26th to use Docker-CE instead of Snap.&lt;/p&gt;
&lt;p&gt;Home Assistant has &lt;a href=&quot;https://www.home-assistant.io/blog/2020/05/09/deprecating-home-assistant-supervised-on-generic-linux/&quot;&gt;stated&lt;/a&gt;
they will no longer support this installation method but there currently is
no other way to use external storage.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I &lt;a href=&quot;/blog/upgrading-home-assistant-hardware-in-2020&quot;&gt;recently upgraded&lt;/a&gt; my
&lt;a href=&quot;https://www.home-assistant.io&quot;&gt;Home Assistant&lt;/a&gt; server to an
&lt;a href=&quot;https://www.amazon.com/Intel-NUC-10-Performance-Kit/dp/B08357VWB2?tag=niemyjski-20&quot;&gt;Intel NUC 10 Performance Kit (NUC10i7FNH)&lt;/a&gt;
from an
&lt;a href=&quot;https://www.amazon.com/DLI-Atomic-Pi-Peripheral-Camera/dp/B07DVYDDV8?tag=niemyjski-20&quot;&gt;Atomic Pi&lt;/a&gt;.
I needed to manually install and configure Ubuntu on my hardware there is an
issue where you
&lt;a href=&quot;https://github.com/home-assistant/operating-system/issues/422&quot;&gt;cannot boot from the NVMe drive&lt;/a&gt;.
and I wanted more control over secondary storage and services.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you are looking for hardware recommendations to run Home Assistant or a
small server on please check out
&lt;a href=&quot;/blog/upgrading-home-assistant-hardware-in-2020&quot;&gt;Upgrading Home Assistant Hardware&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Updating Firmware&lt;/h2&gt;
&lt;p&gt;It is always a good idea to update to the latest firmware of any device you
purchase before starting to install the Operating System (OS). Firmware upgrades
can add additional features, hardware support, or fix bugs that could cause
installation failures.&lt;/p&gt;
&lt;p&gt;I started by navigating to the &lt;a href=&quot;https://downloadcenter.intel.com&quot;&gt;Intel download center&lt;/a&gt;
and searching for my model number (&lt;code&gt;NUC10i7FNH&lt;/code&gt;). I clicked around until I
found a page talking about available Bios updates. In my case it was called
&lt;a href=&quot;https://www.intel.com/content/www/us/en/support/articles/000005636/intel-nuc.html&quot;&gt;BIOS Update and Recovery Instructions&lt;/a&gt;
and contained detailed instructions on how to update the Bios.&lt;/p&gt;
&lt;h2&gt;Operating System&lt;/h2&gt;
&lt;p&gt;If you are only using Home Assistant and don&apos;t plan on running any other
services on your machine than I&apos;d recommend just using
&lt;a href=&quot;https://www.home-assistant.io/hassio/&quot;&gt;Home Assistant&lt;/a&gt; formally known as
&lt;code&gt;hassio&lt;/code&gt;. This will be automatically updated and managed for you, reducing your
maintenance which is always a good thing!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Since writing this article, Home Assistant has dropped support for Ubuntu and
only supports Debian. I &lt;strong&gt;HIGHLY&lt;/strong&gt; recommended to use Home Assistant OS.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;However, if you run want to run extra services on your machine like
facial/image recognition software you&apos;ll need to install Home Assistant on an
existing Operating System (OS). Additionally, there may be software or driver
limitations with the Home Assistant images like the issue with not being able
to &lt;a href=&quot;https://github.com/home-assistant/operating-system/issues/422&quot;&gt;boot from the NVMe drive&lt;/a&gt;
that will force you to install on an existing OS.&lt;/p&gt;
&lt;p&gt;I use the &lt;a href=&quot;https://ubuntu.com/download/server&quot;&gt;Ubuntu Server LTS&lt;/a&gt; server images
instead of Windows for the following reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The software is open source, free and used by many.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://askubuntu.com&quot;&gt;Active community support&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Ubuntu Server is light weight and works on many different hardware
architectures (e.g., &lt;code&gt;x86&lt;/code&gt;, &lt;code&gt;arm&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;LTS images are supported for long period of times and updating is a breeze.&lt;/li&gt;
&lt;li&gt;Documented installation steps for Home Assistant.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After you download latest Ubuntu Server LTS from the Ubuntu site. We need to
image an &lt;a href=&quot;https://www.amazon.com/dp/B07B9KTLJZ?tag=niemyjski-20&quot;&gt;MicroSD card&lt;/a&gt;
or &lt;a href=&quot;https://www.amazon.com/gp/product/B07D7Q41PM?tag=niemyjski-20&quot;&gt;USB Flash Drive&lt;/a&gt;)
with the downloaded image. I use &lt;a href=&quot;https://www.balena.io/etcher/&quot;&gt;Etcher&lt;/a&gt; to
format the drive with the downloaded Ubuntu image (iso).&lt;/p&gt;
&lt;h2&gt;Install&lt;/h2&gt;
&lt;p&gt;After inserting the imaged flash drive or MicroSD card into the computer, I
follow the standard install process for Ubuntu. I use the recommended settings
for formatting and partitioning the primary NVMe drive.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/home-automation/ubuntu-filesystem-setup-1200.jpg&quot; alt=&quot;Ubuntu Filesystem Setup&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I could also format the secondary drive during this step. But I always tend to
just format the secondary drive later as I can then immediately configure the
mount points after formatting the secondary drive.&lt;/p&gt;
&lt;p&gt;I always install and configure SSH when prompted. This makes it so much easier
to complete the install below using your main computer so make sure to import
your SSH identities when prompted.&lt;/p&gt;
&lt;p&gt;After the installer reboots the machine, you can &lt;code&gt;ssh IP_ADDRESS&lt;/code&gt; into the
machine and use your primary computer to finish the install. Please remember to
replace &lt;code&gt;IP_ADDRESS&lt;/code&gt; with the machines IP Address. You can get the IP Address
by resolving the hostname you gave the machine during setup, looking in your
router settings, or by running the following command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;hostname -I
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Just grab the first IP Address in the list of results.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You can also continue the steps below directly on the machine if you didn&apos;t
configure or know how to use SSH. Here is a
&lt;a href=&quot;https://www.youtube.com/watch?v=qWKK_PNHnnA&quot;&gt;beginners guide to SSH&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Next, the steps below require &lt;a href=&quot;https://en.wikipedia.org/wiki/Sudo&quot;&gt;&lt;code&gt;sudo&lt;/code&gt;&lt;/a&gt; to
execute privileged statements from the
&lt;a href=&quot;https://askubuntu.com/questions/506510/what-is-the-difference-between-terminal-console-shell-and-command-line&quot;&gt;terminal&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo -s
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Optional: Enable Canonical Livepatch Service&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://ubuntu.com/livepatch&quot;&gt;Livepatch Service&lt;/a&gt; allows you to apply
critical kernel patches without rebooting. Which lowers the amount of time
maintaining my install. You just need to get a free Livepatch key and enter in
the commands below (remember to replace &lt;code&gt;KEY&lt;/code&gt; with your Livepatch key).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;snap install canonical-livepatch
canonical-livepatch enable KEY
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Optional: Format and mount secondary storage&lt;/h2&gt;
&lt;p&gt;I always install a secondary storage device to place my backups and other data
on. I feel like this helps protect me from boot drive failures and makes my
life easier as all I&apos;d need to do is replace or reformat the failed primary drive
and I&apos;d still have access to my backups locally.&lt;/p&gt;
&lt;p&gt;The following steps work for any storage devices (e.g., SSD or MicroSD). I feel
like every time I need to format a drive in linux it&apos;s been so long that I
forget how to and then I spend forever figuring out how to do it. This time
around I came across this awesome
&lt;a href=&quot;https://askubuntu.com/questions/384062/how-do-i-create-and-tune-an-ext4-partition-from-the-command-line&quot;&gt;guide&lt;/a&gt;
which I&apos;ll follow below with some tweaks.&lt;/p&gt;
&lt;p&gt;I want to access the drive by a friendly name via &lt;code&gt;/mnt/data&lt;/code&gt; after it&apos;s been
formatted. For this to happen we first need to create that directory so in the
following steps we can mount our storage device to this folder.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;root@nuc:~# cd /mnt
root@nuc:/mnt# mkdir data
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we need to get the location of the new drive by executing the statement
below and looking for the &lt;code&gt;logical name&lt;/code&gt; which in my case is &lt;code&gt;/dev/sda&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;root@nuc:~# lshw -C disk
  *-disk
       description: ATA Disk
       product: Samsung SSD 860
       physical id: 0.0.0
       bus info: scsi@2:0.0.0
       logical name: /dev/sda
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we need to create the GUID Partition Table (GPT) and enter in &lt;code&gt;Yes&lt;/code&gt; to
the prompts when asked.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;root@nuc:~# parted /dev/sda mklabel gpt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we need to create the partition label&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;root@nuc:~# parted --align optimal /dev/sda mkpart primary ext4 0% 100%
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we need to create the partition&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;root@nuc:~# mkfs.ext4 /dev/sda1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we need to get the drives UUID identifer so we can safely mount it. If
you have a few in the list, we are looking only for our drive we&apos;ve identified
earlier (e.g., &lt;code&gt;sda1&lt;/code&gt;).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;root@nuc:~# ls -l /dev/disk/by-uuid/
total 0
lrwxrwxrwx 1 root root 10 Mar 9 02:40 87f48668-003f-42b2-b9b9-d588093c7d22 -&amp;gt; ../../sda1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, copy the UUID from the previous command (in my case
&lt;code&gt;87f48668-003f-42b2-b9b9-d588093c7d22&lt;/code&gt;). We&apos;ll need we need to update &lt;code&gt;fstab&lt;/code&gt;
to mount our device on boot.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;root@nuc:~# nano /etc/fstab
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once this text editor is open, navigate to the end of the file and add the
following line replacing my UUID &lt;code&gt;f58e18af-1fb2-4186-92f6-702c081a757e&lt;/code&gt; with
your own.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/dev/disk/by-uuid/f58e18af-1fb2-4186-92f6-702c081a757e /mnt/data ext4 defaults 0 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once this has been added press the &lt;code&gt;control+o&lt;/code&gt;, then press &lt;code&gt;enter&lt;/code&gt; key to
confirm and finally &lt;code&gt;control+x&lt;/code&gt; to exit nano.&lt;/p&gt;
&lt;p&gt;Next, we can remount everything defined in &lt;code&gt;/etc/fstab&lt;/code&gt; without rebooting by
running the statement below.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;root@nuc:~# mount -a
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Something that I struggled with in previous attempts was knowing what device a
folder was stored on. I
&lt;a href=&quot;https://unix.stackexchange.com/questions/11311/how-do-i-find-on-which-physical-device-a-folder-is-located&quot;&gt;came across&lt;/a&gt;
this awesome little command &lt;code&gt;df&lt;/code&gt; that can confirm that &lt;code&gt;/mnt/data&lt;/code&gt; is located
on the correct storage device (&lt;code&gt;/dev/sda&lt;/code&gt;).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;root@nuc:~# df /mnt
Filesystem     1K-blocks     Used Available Use% Mounted on
/dev/nvme0n1p2 959862832 10726424 900308248   2% /
root@nuc:~# df /mnt/data
Filesystem     1K-blocks  Used Available Use% Mounted on
/dev/sda1      960379920 77852 911447644   1% /mnt/data
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see the parent folder &lt;code&gt;/mnt&lt;/code&gt; is on my primary NVMe drive, while
&lt;code&gt;/mnt/data&lt;/code&gt; is located on our recently configured SSD drive (&lt;code&gt;/dev/sda&lt;/code&gt;)!&lt;/p&gt;
&lt;h2&gt;Optional: Place backups on secondary storage&lt;/h2&gt;
&lt;p&gt;If you don&apos;t have a second drive already configured. Please install one and
follow the steps above for
&lt;a href=&quot;#optional-format-and-mount-secondary-storage&quot;&gt;formatting and mounting secondary storage&lt;/a&gt;
before continuing.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;NOTE: You can allow store the &lt;code&gt;addons&lt;/code&gt; folder on a secondary drive following
the same steps below but replace &lt;code&gt;backup&lt;/code&gt; with &lt;code&gt;addons&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We need to create a our backup directories for Home Assistant by running the
commands below.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cd /mnt/data
root@nuc:~# mkdir /mnt/data/backup
root@nuc:~# mkdir /usr/share/hassio
root@nuc:~# mkdir /usr/share/hassio/backup
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we&apos;ll need we need to update &lt;code&gt;fstab&lt;/code&gt; to automatically mount our backup
folder on boot.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;root@nuc:~# nano /etc/fstab
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once this text editor is open, navigate to the end of the file and add the
following line&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/mnt/data/backup /usr/share/hassio/backup none defaults,bind 0 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once this has been added press the &lt;code&gt;control+o&lt;/code&gt;, then press &lt;code&gt;enter&lt;/code&gt; key to
confirm and finally &lt;code&gt;control+x&lt;/code&gt; to exit nano.&lt;/p&gt;
&lt;p&gt;Finally, we can remount everything defined in &lt;code&gt;/etc/fstab&lt;/code&gt; without rebooting by
running the statement below.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;root@nuc:~# mount -a
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Installing Home Assistant&lt;/h2&gt;
&lt;p&gt;This step installs Home Assistant following the
&lt;a href=&quot;https://www.home-assistant.io/hassio/installation/#alternative-install-home-assistant-supervised-on-a-generic-linux-host&quot;&gt;Generic Linux Host Guide&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apt-get update &amp;amp;&amp;amp; apt-get upgrade
apt-get install -y apparmor-utils apt-transport-https avahi-daemon ca-certificates jq curl dbus network-manager socat software-properties-common
systemctl disable ModemManager
apt-get purge modemmanager
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, follow the installation documentation for installing
&lt;a href=&quot;https://docs.docker.com/engine/install/ubuntu/&quot;&gt;Docker-CE&lt;/a&gt; and then run the
scripts below to finish installing Home Assistant.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;curl -sL https://raw.githubusercontent.com/home-assistant/supervised-installer/c674830d8ddc6af9d618755a7995af939dd73fde/installer.sh | bash -s
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It might take a minute or two for Home Assistant to get up and running. It has
to download all of the docker containers from the
&lt;a href=&quot;https://hub.docker.com/u/homeassistant&quot;&gt;docker registry&lt;/a&gt; and then run the
containers.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you want to know more about docker I&apos;d recommend watching this
&lt;a href=&quot;https://www.youtube.com/watch?v=pGYAg7TMmp0&quot;&gt;video&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You can access your Home Assistant install by navigating to
&lt;a href=&quot;http://IP_ADDRESS:8123&quot;&gt;http://IP_ADDRESS:8123&lt;/a&gt; in your browser of choice. Please remember to replace
&lt;code&gt;IP_ADDRESS&lt;/code&gt; with the machines IP Address. You can get the IP Address by running
the following command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;hostname -I
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Just grab the first IP Address in the list of results.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It&apos;s highly recommended that you configure your router to give this machine
a static IP Address. This ensures that the IP Address doesn&apos;t change when
the machine reboots.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Optional: Restore from previous backup&lt;/h2&gt;
&lt;p&gt;The first step is to create and download a backup from your previous install.
Once this is done we need to transfer it to the new machine we just configured.&lt;/p&gt;
&lt;p&gt;Create a new temporary Home Assistant account if you haven&apos;t done so already
and install the
&lt;a href=&quot;https://github.com/home-assistant/hassio-addons/tree/master/samba&quot;&gt;Samba share&lt;/a&gt;
official Home Assistant add-on by navigating to
&lt;a href=&quot;http://IP_ADDRESS:8123/hassio/addon/core_samba&quot;&gt;http://IP_ADDRESS:8123/hassio/addon/core_samba&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Next, configure a &lt;code&gt;password&lt;/code&gt; in configuration and click the &lt;code&gt;Save&lt;/code&gt; button. Then
click the &lt;code&gt;Start&lt;/code&gt; button to turn on the Samba add-on.&lt;/p&gt;
&lt;p&gt;Once it&apos;s up and running you need to connect to the Samba share and upload
the downloaded backup to the &lt;code&gt;backups&lt;/code&gt; folder.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you&apos;ve never connected to a Samba share before then please watch one of
the following videos:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=gwM9RaemCvE&quot;&gt;How to connect to Samba share on Windows 10&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=pZw6qhjR-HE&quot;&gt;How to connect to Samba share on macOS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;Next, navigate to the &lt;a href=&quot;http://IP_ADDRESS:8123/hassio/snapshots&quot;&gt;http://IP_ADDRESS:8123/hassio/snapshots&lt;/a&gt; snapshots page
and click the refresh icon in the upper right. The snapshot will be listed in
the available snapshots. Click on the snapshot and then selected
&lt;code&gt;Wipe &amp;amp; Restore&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;After a few minutes home assistant will restart and you&apos;ll have all of your
existing configuration and addons from your previous install!&lt;/p&gt;
&lt;h2&gt;Optional: Configure Z-Wave Integration&lt;/h2&gt;
&lt;p&gt;If you are using Z-Wave, you&apos;ll also want to update your
&lt;a href=&quot;https://www.home-assistant.io/docs/z-wave/installation#configuration&quot;&gt;Z-Wave configuration&lt;/a&gt;
&lt;code&gt;usb_path&lt;/code&gt;. If you haven&apos;t done so already plugin your Z-Wave controller
(e.g., &lt;a href=&quot;https://www.amazon.com/gp/product/B00X0AWA6E?tag=niemyjski-20&quot;&gt;Aeotec Z-Stick Gen5&lt;/a&gt;)
into any usb port and run the following command to get the USB UUID.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;root@nuc:~# ls /dev/serial/by-id/
usb-0658_0200-if00
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The command returned &lt;code&gt;usb-0658_0200-if00&lt;/code&gt; a UUID for my USB device.&lt;/p&gt;
&lt;p&gt;Next, we need go and update the Z-Wave configuration yaml using your favorite
editor.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;zwave:
  usb_path: /dev/serial/by-id/usb-0658_0200-if00
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;If you configured Z-Wave using the integrations page, you may need to remove
and readd the integration in order to set the USB Path.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Finally, save the configuration file and restart Home Assistant to pick up the
new configuration settings.&lt;/p&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>Upgrading Home Assistant hardware in 2020</title><link>https://blakeniemyjski.com/blog/upgrading-home-assistant-hardware-in-2020/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/upgrading-home-assistant-hardware-in-2020/</guid><pubDate>Tue, 03 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I&apos;ve upgraded or swapped my &lt;a href=&quot;https://www.home-assistant.io&quot;&gt;Home Assistant&lt;/a&gt;
hardware more times than I want to count. I want to share the experiences I&apos;ve
learned along the way when choosing hardware to run Home Assistant locally. I
always try choose hardware is lower power and will take up limited space in my
&lt;a href=&quot;https://www.amazon.com/Tripp-Lite-Enclosure-Switch-Depth-SRW9U/dp/B013FDFLDO?tag=niemyjski-20&quot;&gt;9U Server Cabinet&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Skip to &lt;a href=&quot;#march-2020&quot;&gt;March 2020&lt;/a&gt; for a summary of what I recently upgraded
to and the &lt;a href=&quot;#conclusion&quot;&gt;Conclusion&lt;/a&gt; if you are looking for current hardware
recommendations.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;July 2018&lt;/h3&gt;
&lt;p&gt;In early 2018, I spent a massive amount of time researching what hardware and
software I wanted to use for my smart home. My requirements were to use
hardware that I could repurpose if needed and open source home automation
software that was widely supported, low maintenance, low power and cheap. The
&lt;a href=&quot;https://www.amazon.com/ELEMENT-Element14-Raspberry-Pi-Motherboard/dp/B07P4LSDYV?tag=niemyjski-20&quot;&gt;Raspberry Pi 3 B+&lt;/a&gt; was the clear
winner at the time with Home Assistant meeting the other requirement.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Hardware&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.amazon.com/ELEMENT-Element14-Raspberry-Pi-Motherboard/dp/B07P4LSDYV?tag=niemyjski-20&quot;&gt;Raspberry Pi 3 B+&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;$38&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.amazon.com/dp/B07B9KTLJZ?tag=niemyjski-20&quot;&gt;Samsung PRO Endurance 64GB&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;$18&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.amazon.com/gp/product/B07BTHNW9W?tag=niemyjski-20&quot;&gt;Miuzei Case&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;$17&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total&lt;/td&gt;
&lt;td&gt;$73&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;I followed &lt;a href=&quot;https://www.home-assistant.io/hassio/installation/&quot;&gt;this guide&lt;/a&gt; to
install Home Assistant on the Pi. It was fully manged and worked great as I
didn&apos;t want to have to manage an Operating System too.&lt;/p&gt;
&lt;p&gt;By early 2019, I was looking for something new for multiple reasons:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;I had multiple drive failures due to high number of writes going to a
&lt;a href=&quot;https://www.amazon.com/gp/product/B06XX29S9Q?tag=niemyjski-20&quot;&gt;basic MicroSD card&lt;/a&gt; that
wasn&apos;t designed to handle the number of Home Assistant writes for sensor
data. I upgraded to the &lt;a href=&quot;https://www.amazon.com/dp/B07B9KTLJZ?tag=niemyjski-20&quot;&gt;Samsung PRO Endurance 64GB MicroSD card&lt;/a&gt;
which helped solve this problem.&lt;/li&gt;
&lt;li&gt;Any power interruptions could cause an SD Card to be unbootable. This can
happen if a write is happening to the SD Card when the power loss occurs. I
had to restore from a saved backup multiple times which is a real pain and
not something you look forward to.&lt;/li&gt;
&lt;li&gt;Too many docker add-ons and integrations running was causing resource
constraint issues where integrations were slow to respond. This was greatly
magnified anytime an integration that required a network connection was
offline due to configured long default timeout times in software and not
handling &lt;a href=&quot;https://www.somethingsimilar.com/2013/01/14/notes-on-distributed-systems-for-young-bloods/#backpressure&quot;&gt;backpressure&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Rebooting for updates or integrations was painful.&lt;/li&gt;
&lt;li&gt;The acceptance factor was dropping.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;When purchasing flash memory it&apos;s highly recommended to choose storage that
is optimized for throughput (reads/writes) as well as total number of writes
(TBW).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;April 2019&lt;/h2&gt;
&lt;p&gt;One April morning I was super excited to see an announcement for a new
Single Board Computer (SBC) that had&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;16GB&lt;/code&gt; of onboard Flash Memory. This solved the issue with power interruptions.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;2GB&lt;/code&gt; of ram. Not only was it faster, but it was twice the amount of memory
compared to the 3B+.&lt;/li&gt;
&lt;li&gt;Faster &lt;code&gt;x86&lt;/code&gt; hardware which meant that it could out preform the Pi in every
way as well as run add-ons like VS Code (which didn&apos;t yet support ARM).&lt;/li&gt;
&lt;li&gt;Runs on only &lt;code&gt;4-15 watts TDP&lt;/code&gt;! Didn&apos;t require too much more power to run.&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Hardware&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.amazon.com/DLI-Atomic-Pi-Peripheral-Camera/dp/B07DVYDDV8?tag=niemyjski-20&quot;&gt;Atomic Pi&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;$36&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.amazon.com/SMAKN-Switching-Supply-Adapter-100-240/dp/B01N4HYWAM?tag=niemyjski-20&quot;&gt;Power Supply&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;$10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reused &lt;a href=&quot;https://www.amazon.com/dp/B07B9KTLJZ?tag=niemyjski-20&quot;&gt;Samsung PRO Endurance 64GB&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.thingiverse.com/tag:Atomic_Pi&quot;&gt;3D Printed Case&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total&lt;/td&gt;
&lt;td&gt;$46&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;This was a clear winner and checked all my boxes but it has some definite pain
points like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No &lt;a href=&quot;https://www.home-assistant.io/hassio/installation/&quot;&gt;fully managed Home Assistant Images&lt;/a&gt;
from Home Assistant.&lt;/li&gt;
&lt;li&gt;The only &quot;official&quot; device images were bloated developer OS Images. I tend to
only install clean images from Ubuntu or Microsoft directly so I know I&apos;m
getting a clean experience free from any &quot;paid&quot; vendor software. That&apos;s not
to say these images weren&apos;t already clean. I just want to know what I&apos;m using.&lt;/li&gt;
&lt;li&gt;No documentation on how to install Home Assistant on it other than
&lt;a href=&quot;https://www.home-assistant.io/hassio/installation/#alternative-install-home-assistant-supervised-on-a-generic-linux-host&quot;&gt;this&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Some Atomic Pi&apos;s might have issues with it requiring a hard power off for
reboot. I found this out when I helped get my neighbor up and running on an
Atomic Pi.&lt;/li&gt;
&lt;li&gt;The primary &lt;code&gt;16GB&lt;/code&gt; drive is an &lt;code&gt;eMMC&lt;/code&gt; which is known to have limited write
lifespan. It&apos;s fine for putting an operating system on but not something that
is always writing to it. So you need to configure Home Assistant to write to
the MicroSD card. It could be corrupted but it&apos;s much much less likely to
cause issues. In fact, I&apos;ve had 5 more power outages than I&apos;d like and I
didn&apos;t run into any issues.&lt;/li&gt;
&lt;li&gt;You have to know and maintain the linux install.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I was also able to repurpose the Raspberry Pi 3 B+! I installed
&lt;a href=&quot;https://octoprint.org&quot;&gt;OctoPrint&lt;/a&gt; on it and it controls my
&lt;a href=&quot;https://www.amazon.com/Comgrow-Creality-Printer-Upgrade-Certified/dp/B07GYRQVYV?tag=niemyjski-20&quot;&gt;Ender 3 Pro&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;By early 2020, I was starting to hit some of the same issues I ran into with the
Atomic Pi:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The addons and integrations I was running was just too much for the hardware
constraints. I currently have over 800 entities in my House and Garage with
many sensors including Facial Recognition which is all happening locally.
When backpressure occurred with networked entities on top of all of my local
processing... I started to get the feeling that some of my
&lt;a href=&quot;https://nodered.org&quot;&gt;Node-RED&lt;/a&gt; automations weren&apos;t running.&lt;/li&gt;
&lt;li&gt;Software reboots for updates or adding new integrations takes too long.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With these issues I was on the lookout for a new SBC that had much more
processing power while still had a low TDP power envelope. I knew I still had
quite a bit of life in the Atomic PI but long term this was not the solution
for me.&lt;/p&gt;
&lt;p&gt;As for the Atomic Pi, I&apos;m thinking of running some analysis services on it. It
would be really cool to buy two more and make a Kubernetes cluster! What are
your thoughts?&lt;/p&gt;
&lt;h2&gt;March 2020&lt;/h2&gt;
&lt;p&gt;I started researching the next hardware that would replace the Atomic Pi. I
wanted to take a look at all options since this next choice I would be living
with for as long as possible.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I could have continued to use with the Atomic Pi for quite a while longer.
Especially if time was taken to profile and optimize automations and
integrations. As the saying goes &lt;code&gt;time is money&lt;/code&gt; and I think a lot of time
would be sunk in just this.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Since the Atomic Pi came out there was a new
&lt;a href=&quot;https://www.amazon.com/Raspberry-Model-2019-Quad-Bluetooth/dp/B07TD42S27?tag=niemyjski-20&quot;&gt;Raspberry Pi 4 Model B&lt;/a&gt;
released. I knew it would roughly be the same experience as the Atomic PI, but
impossible to compare benchmarks due to different CPU architectures. From the
benchmarks I&apos;ve seen (not comparing apples to apples) I knew it would be close.
It would still have the same storage issues as described previously and it
might still have
&lt;a href=&quot;https://www.raspberrypi.org/forums/viewtopic.php?t=245553&quot;&gt;USB hardware issues with the Aeotec Z-Stick&lt;/a&gt;.
I need something more powerful that will last for years to come.&lt;/p&gt;
&lt;p&gt;I was looking for AMD to release a
&lt;a href=&quot;https://www.howtogeek.com/524327/ryzen-4000-will-your-next-gaming-laptop-be-amd-instead-of-intel/&quot;&gt;Ryzen 4000 series embedded device&lt;/a&gt;
but this just doesn&apos;t seem to be happening and it&apos;s very unlikely one can build
a super low powered tiny desktop SBC any time soon. The
&lt;a href=&quot;https://www.amd.com/en/products/embedded-ryzen-series&quot;&gt;Embedded Ryzen series&lt;/a&gt;
just don&apos;t seem like it will cut it in processing power either which is a real
shame.&lt;/p&gt;
&lt;p&gt;This lead me to look at Intel&apos;s latest 10th Generation Intel NUCs (&lt;code&gt;NUC 10&lt;/code&gt;)
that were released in December of 2019. The processors are built on 14nm++,
are faster and also contain hardware security fixes make it worth considering
over previous generations (&lt;code&gt;NUC 6&lt;/code&gt;, &lt;code&gt;NUC 8&lt;/code&gt;). One cravat is that the &lt;code&gt;NUC 10&lt;/code&gt;
actually has a slower graphics cards (GPU) than previous generations. This
should only affect you if you are wanting to use the integrated card for
heavy machine learning or other compute operation. I really had hoped it was a
bit more powerful but it will work.&lt;/p&gt;
&lt;p&gt;The 10th Generation met all my needs (&lt;code&gt;6 CPU Cores @ 28 watt TDP&lt;/code&gt;) but I wanted
to be a bit more future proof with newer standards. I initially ruled out the
&lt;code&gt;NUC 10&lt;/code&gt; for these reasons and was waiting to upgrade to the
&lt;a href=&quot;https://www.tomshardware.com/news/intels-nuc-11s-with-tiger-lake-cpus-spotted-panther-canyon-has-pcie-40&quot;&gt;11th Generation Tiger Lake NUC&lt;/a&gt;
later this year. Tiger Lake is supposed to feature 10nm technology, faster DDR4
memory, PCI Express 4.0, HDMI 2.1 and a new much faster Xe GPU. Also, I know
that they are coming out with a version that will take any PCI Express x16
graphics card. This would be really awesome for machine learning locally. I&apos;m
just cringing what the price tag and power consumption of such a rig would be.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;TIP: Check out this awesome
&lt;a href=&quot;https://lamw.github.io/intel-nuc-decoder/&quot;&gt;Intel NUC tracking spreadsheet&lt;/a&gt;
if you want to stay up-to-date on what NUCs are available.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;But this all changed, last week when there was an awesome deal on an &lt;code&gt;NUC 10&lt;/code&gt;
(NUC10i7FNH) kit with 64GB of memory included. A few of us were lucky enough
to &lt;a href=&quot;https://www.reddit.com/r/intelnuc/comments/f7y9p9/i_was_going_to_wait_for_tiger_lake_but_this_seems&quot;&gt;snag it&lt;/a&gt;
at $605 USD.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Hardware&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.amazon.com/dp/B0833SJQBH?tag=niemyjski-20&quot;&gt;Intel NUC 10 Performance Kit (NUC10i7FNH)&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;$605&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.amazon.com/gp/product/B07MFZY2F2?tag=niemyjski-20&quot;&gt;Samsung 970 EVO Plus 1TB - M.2 NVMe&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;$220&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.amazon.com/gp/product/B078DPCY3T?tag=niemyjski-20&quot;&gt;Samsung 860 EVO 1TB - SSD&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;$155&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total&lt;/td&gt;
&lt;td&gt;$980&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Compared to the previous two solutions this is definitely the most expensive
and a bit overkill. I went with two drives because I want the 860 EVO for
backups as well as large data sets facial/image recognition. I could have saved
$179 on by going with 500GB drives instead. I realize that I might not ever use
this amount of storage, but I don&apos;t want to run out. I can also use this memory
in a future machine or some of it for network attached storage (NAS).&lt;/p&gt;
&lt;p&gt;The current downsides of the NUC 10 are as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cost&lt;/li&gt;
&lt;li&gt;Higher power usage at 28 watt TDP (can peak much higher for short duration).&lt;/li&gt;
&lt;li&gt;Unable to use the &lt;a href=&quot;https://www.home-assistant.io/hassio/installation/&quot;&gt;Intel-NUC system images&lt;/a&gt;
as they currently don&apos;t support (as a result you have to maintain a linux
install):
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/home-assistant/operating-system/issues/422&quot;&gt;Unable to boot from the NVMe drive&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/home-assistant/operating-system/blob/dev/Documentation/boards/nuc.md&quot;&gt;Limited driver support&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;To my knowledge you cannot attach secondary drives for storage.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I should have spent some time trying to install it on my NUC to see if there
were any other issues or if I&apos;d run into driver issues but I was quite turned
off by the lack of NVMe support and couldn&apos;t get any answer on the
&lt;a href=&quot;https://discord.gg/c5DvZ4e&quot;&gt;official discord channel&lt;/a&gt; on secondary drives.&lt;/p&gt;
&lt;p&gt;So far the NUC 10 has been working amazing and everything is so much faster and
working much better than the previous hardware!&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;If you are looking for a SBC/small machine to run Home Assistant on I think
there are some great options out there. Even though I choose an Intel NUC 10
series doesn&apos;t mean you have to (I have some insane requirements). I&apos;d
recommend starting small if you are just starting out. Below are my
recommendations (based on the machines I have used) if you are looking for a
machine today.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;NOTE: If you don&apos;t care about power usage or form factor you can
&lt;a href=&quot;https://pcpartpicker.com/builds/#X=0,51593&amp;amp;period=1y&quot;&gt;build a machine on the cheap&lt;/a&gt;.
I would build based on the Ryzen 4000 series when it is released.&lt;/p&gt;
&lt;p&gt;There are also many other small form factor machines available that are much
cheaper. But the technology stack may contain hardware vulnerabilities, may
no longer be supported, or may lack community adoption/support. So please
keep this in mind when picking a solution.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Budget $75 USD&lt;/h2&gt;
&lt;p&gt;I really like the
&lt;a href=&quot;https://www.amazon.com/Raspberry-Model-2019-Quad-Bluetooth/dp/B07TD42S27?tag=niemyjski-20&quot;&gt;Raspberry Pi 4 Model B&lt;/a&gt;.
But I cannot recommend it until the &lt;a href=&quot;https://www.raspberrypi.org/forums/viewtopic.php?t=245553&quot;&gt;USB hardware issues with the Aeotec Z-Stick&lt;/a&gt;
are worked out. Also, I&apos;d only consider it if you have a power backup solution
so you don&apos;t run into any power failure issues described above. It has cases
you can buy from any retailer and you can use the managed
&lt;a href=&quot;https://www.home-assistant.io/hassio/installation/&quot;&gt;system images&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Due to the hardware issues that have been present since launch (some have been
resolved), I can only recommend the Atomic Pi which is the same exact cost but
features onboard flash storage and is x86 hardware. The main con being you have
to install and manage linux.&lt;/p&gt;
&lt;h3&gt;Atomic Pi&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Hardware&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.amazon.com/DLI-Atomic-Pi-Peripheral-Camera/dp/B07DVYDDV8?tag=niemyjski-20&quot;&gt;Atomic Pi&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;$36&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.amazon.com/SMAKN-Switching-Supply-Adapter-100-240/dp/B01N4HYWAM?tag=niemyjski-20&quot;&gt;Power Supply&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;$10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.amazon.com/dp/B07B9KTLJZ?tag=niemyjski-20&quot;&gt;Samsung PRO Endurance 64GB&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;$18&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.thingiverse.com/tag:Atomic_Pi?tag=niemyjski-20&quot;&gt;3D Printed Case&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total&lt;/td&gt;
&lt;td&gt;$64&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Budget $575 USD&lt;/h2&gt;
&lt;p&gt;I know there is a big price jump up from the previous tier. I just feel like it
is not even worth considering a non quad core cheaper solution like the i3 NUC.
In my experiences I&apos;ve ran into to many issues with lack of processing power.&lt;/p&gt;
&lt;p&gt;If you can&apos;t hold out until the Tiger Lake NUC is released I&apos;d buy an Intel
NUC 10 with only a single SSD
(&lt;a href=&quot;https://github.com/home-assistant/operating-system/issues/422&quot;&gt;as you can&apos;t boot off the NVMe drive&lt;/a&gt;).
You could then use the built in system image and let it manage everything for
you.&lt;/p&gt;
&lt;p&gt;As of writing this the NUC 10 (NUC10i5FNH) seems to have very limited stock so
I listed the NUC 8.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Hardware&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.amazon.com/Intel-NUC-Mainstream-Kit-NUC8i5BEH/dp/B07GX59NY8?tag=niemyjski-20&quot;&gt;Intel NUC 8 Mainstream Kit (NUC8i5BEH)&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;$439&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.amazon.com/Corsair-Memory-2400MHz-SODIMM-CMSX16GX4M2A2400C16/dp/B019MRBKYG?tag=niemyjski-20&quot;&gt;Corsair Memory Kit 16GB DDR4 2400MHz&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;$65&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.amazon.com/gp/product/B07864WMK8?tag=niemyjski-20&quot;&gt;Samsung 860 EVO 250GB - SSD&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;$60&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total&lt;/td&gt;
&lt;td&gt;$564&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Budget $850 USD&lt;/h2&gt;
&lt;p&gt;If you can&apos;t hold out until the Tiger Lake NUC is released I&apos;d buy an Intel
NUC 10 with a backup drive. Here is a recent review by
&lt;a href=&quot;https://www.anandtech.com/show/15571/intel-nuc10i7fnh-frost-canyon-review&quot;&gt;AnandTech&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Hardware&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.amazon.com/Intel-NUC-10-Performance-Kit/dp/B08357VWB2?tag=niemyjski-20&quot;&gt;Intel NUC 10 Performance Kit (NUC10i7FNH)&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;$605&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.amazon.com/gp/product/B07N124XDS?tag=niemyjski-20&quot;&gt;Samsung 32GB DDR4 2666MHz RAM&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;$122&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.amazon.com/gp/product/B07M7Q21N7?tag=niemyjski-20&quot;&gt;Samsung 970 EVO Plus 500GB - M.2 NVMe&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;$116&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total&lt;/td&gt;
&lt;td&gt;$843&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;You could bring the price down by swapping out different components but I feel
this offers you the biggest bang for your buck and leaves you open to future
upgrades via additional memory and storage.&lt;/p&gt;
&lt;p&gt;Please stay tuned for my next guide on installing
[Home Assistant on Ubuntu]](/blog/installing-home-assistant-on-ubuntu).&lt;/p&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>Thinking about smart home power usage</title><link>https://blakeniemyjski.com/blog/thinking-about-smart-home-power-usage/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/thinking-about-smart-home-power-usage/</guid><pubDate>Thu, 13 Feb 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I&apos;ve spent a great deal of my life admiring nature and thinking of ways we can
do to reduce our footprint. I&apos;d like to think I&apos;ve taken steps both physically
and monetarily to do so.&lt;/p&gt;
&lt;h2&gt;The Dreaded Power Bill&lt;/h2&gt;
&lt;p&gt;Which is why one Wisconsin Winter day I opened my utility (gas and electric)
bill and had a not so pleasant moment. Complete sticker shock at the $410 USD
single month total, which was our highest bill yet. I almost immediately
thought this can&apos;t be right, only to get the power bill break down only to
see some &lt;strong&gt;mistakes had been&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Power Bill Insights&lt;/h2&gt;
&lt;p&gt;I analyzed the electric bill by looking at the kWh break down. This was telling
me a couple things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;The &lt;a href=&quot;https://www.tesla.com/model3&quot;&gt;Telsa Model 3&lt;/a&gt; was starting to charge
every night at 10:00pm. This is exactly when time of use enters the
cheapest rate (or so I thought)!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I was not entered into Three-Tier Time-of-Use program like I had previously
thought. &lt;a href=&quot;https://blog.aurorasolar.com/how-time-of-use-rates-work/&quot;&gt;Time-of-Use (TOU) billing&lt;/a&gt;
allows you to have cheaper or more expensive billing periods based on
historic grid usage.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Time of use rates are a type of electricity rate used by some utilities
for billing customers. Under a TOU rate, customers pay different prices per
kilowatt hour (kWh) of electricity that they use, depending on when they
use it. Pricing varies by time of day, and can also vary based on the day
of the week (weekend or weekday) and the time of year
(&lt;a href=&quot;https://blog.aurorasolar.com/how-time-of-use-rates-work/&quot;&gt;Aurora&lt;/a&gt;).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I thought I had enabled this eight months previously by entering into the
time of day program website. However the opt requests must go straightly to
the garbage, because it was never processed. It just took a call to the power
company to get it enabled. &lt;strong&gt;I learned a valuable lesson to always follow up
to ensure things get done!&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Some power hourly usage spikes looked like they could be caused by something
running on a schedule. I immediately started thinking about the thermostat
comfort settings.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The max kWh usage excluding Tesla charging seemed like average use.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This lead me to reexamine what I was doing as well as look into products that
might help me determine what is consuming electricity.&lt;/p&gt;
&lt;h2&gt;Measuring&lt;/h2&gt;
&lt;p&gt;I was talking to one of my awesome neighbors about how our bill had gone way up
from the previous month. I was happy to know I wasn&apos;t alone and there are tools
that can help. I was lent a
&lt;a href=&quot;https://www.amazon.com/P3-International-P4460-Electricity-Monitor/dp/B000RGF29Q?tag=niemyjski-20&quot;&gt;Kill A Watt Electric Monitor&lt;/a&gt;
to help measure kWh usage by devices that are plugged in.&lt;/p&gt;
&lt;p&gt;I had never seen one of these before and it blew my mind that this existed. It
monitors the electricity usage of the device plugged into it and reports many
facts like price (per hour, day, week, month, year) based on single use rate.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Advice: The longer you measure the more accurate your results will be!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This really helped me track down what my fridge, freezer, 3D Printer, smart
device (Alexa, Gaming Consoles), and server rack were costing per month. It was
eye opening to see the the &lt;a href=&quot;https://www.amazon.com/dp/B07PBGN2WX?tag=niemyjski-20&quot;&gt;Amazon Echo 3rd Gen speaker&lt;/a&gt;
was costing me a few dollars per month to run (calculated using $0.10 per kWh).
The real shocker was when I decided to measure only the
&lt;a href=&quot;https://www.amazon.com/Smart-Enabled-Google-Assistant-HomeKit/dp/B01NBI0A6R?tag=niemyjski-20&quot;&gt;Wemo mini smart plug&lt;/a&gt;
with nothing connected was costing me $0.31/day.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/home-automation/kill-a-watt-wemo-1200.jpg&quot; alt=&quot;Kill A Watt - Wemo&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I do want to remeasure this to verify as it does seem a bit excessive on the
usage front.&lt;/p&gt;
&lt;p&gt;I luckily already upgraded every single light bulb to the lowest wattage LED
bulb while maintaining bright white light! If you haven&apos;t done this, then I
recommend to &lt;a href=&quot;https://www.inchcalculator.com/lighting-energy-cost-calculator/&quot;&gt;calculate light bulb costs per room&lt;/a&gt;
and keep this in back of your mind. It will help you purchase efficient LED
bulbs and think about how to optimize the power usage of each room.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Residential LEDs -- especially ENERGY STAR rated products -- use at least 75%
less energy, and last 25 times longer, than incandescent lighting
(&lt;a href=&quot;https://www.energy.gov/energysaver/save-electricity-and-fuel/lighting-choices-save-you-money/led-lighting&quot;&gt;energy.gov&lt;/a&gt;).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The one thing &lt;strong&gt;I haven&apos;t figured out is how much energy is my hard wired smart
devices costing me (e.g., smart switches)?&lt;/strong&gt; I was betting that if I looked at
the product details it would be unlikely to tell me how much it costs to
operate! So was there already a monitoring solution that could help me?&lt;/p&gt;
&lt;h3&gt;Near Real Time Data From Power Company&lt;/h3&gt;
&lt;p&gt;Last year my utilities all upgraded to smart meters. These meters report usage
data wirelessly as well as via &lt;a href=&quot;https://zigbeealliance.org&quot;&gt;Zigbee&lt;/a&gt;. I really
like this as it&apos;s a win for everyone! The utility companies don&apos;t have to drive
around, the environment is less polluted by internal combustion engines (ICE),
and end users can see near real time usage.&lt;/p&gt;
&lt;p&gt;I was discussing solutions with &lt;a href=&quot;https://www.architectryan.com/&quot;&gt;Ryan Hoffman&lt;/a&gt;,
and he showed me his power companies near real time usage using these meters
and I was soo jealous.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/home-automation/power-company-near-real-time-usage-1200.jpeg&quot; alt=&quot;Power Company Near Real Time Usage&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This was exactly what I needed so I called once again the power company as well
reached out on twitter (squeaky mouse gets the cheese) and asked how to get
Zigbee read-only access. I was basically told there was no Zigbee hardware
support on any of their devices and they had already evaluated this. I was
upset to say the least as I had already read the
&lt;a href=&quot;https://fccid.io/R7PEG1R1S6/User-Manual/Manual-2789321&quot;&gt;meters user manual&lt;/a&gt;
and knew this was not true. After some frustration and explaining how they were
incorrectly informed... I was eventually called back by a meter tech who had
researched this exact scenario. Long story short, there are some limitations
needed to be worked through to enable it for everyone and it&apos;s still a ways out.&lt;/p&gt;
&lt;h3&gt;Energy Monitoring Device&lt;/h3&gt;
&lt;p&gt;When I learned that I can&apos;t use the power companies near real time data, there
had to be a way to get it from inside of the house. I started searching for
solutions and came across a lot of solutions but not very many met my criteria:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Let me see my usage in near real time (eventual consistency :D)&lt;/li&gt;
&lt;li&gt;Let me query the data so I can make informed decisions&lt;/li&gt;
&lt;li&gt;Low maintenance&lt;/li&gt;
&lt;li&gt;Easy to install&lt;/li&gt;
&lt;li&gt;Work with existing devices and software.&lt;/li&gt;
&lt;li&gt;Low power consumption of measuring device.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;[Optional, but not happy about it]&lt;/em&gt; work offline&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There were only two devices I&apos;ve found that match all or most of this criteria.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The &lt;a href=&quot;https://www.amazon.com/Aeotec-Aeon-Labs-ZW095-ZW095/dp/B00XD8WZX6?tag=niemyjski-20&quot;&gt;Aeotec Z-Wave Home Energy Meter&lt;/a&gt;
uses Z-Wave to report usage to Home Assistant. I really like that it works
completely offline and I have a friend that has had much success using this!&lt;/li&gt;
&lt;li&gt;The &lt;a href=&quot;https://www.amazon.com/Sense-Energy-Monitor-Solar-Electricity/dp/B075K51T9X?tag=niemyjski-20&quot;&gt;Sense&lt;/a&gt;
uses out dated 2.4 GHz WIFI 3 to report usage to the cloud. You can then
pull this data into &lt;a href=&quot;https://www.home-assistant.io&quot;&gt;Home Assistant&lt;/a&gt;. I don&apos;t
like that this requires an internet connection, but the pros are pretty
unique. Sense can support detecting most devices (I&apos;ve read it can have
problems detecting LED devices) and can let you know when that device turns
on or off. I like that it also supports Solar!&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I&apos;ve yet to purchase any solution yet, but I&apos;m leaning towards the Aeotec. It
is 1/3 the price of the Sense and it runs locally. I just hate giving up solar
option and detecting devices. I will try and review the solution I pick in a
future post.&lt;/p&gt;
&lt;h2&gt;Short Term Solutions&lt;/h2&gt;
&lt;p&gt;If your time is precious I would suggest starting in this order.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Communicate with the whole house hold about current usage patterns and how
to find a happy medium.&lt;/li&gt;
&lt;li&gt;Optimize your furnace schedules so more zones run at the same time and update
the comfort settings to be warmer or colder depending on environment.&lt;/li&gt;
&lt;li&gt;If your usage (e.g., charging a car) can be scheduled, then opt into
Time-Of-Use based billing through your utility company.
&lt;img src=&quot;/img/blog/home-automation/ecobee-eco-plus-time-of-use-1200.jpg&quot; alt=&quot;ecobee eco plus time of use&quot; /&gt;
Also make sure to update your devices to optimize Time-Of-Use! I enrolled
all of my &lt;a href=&quot;https://www.amazon.com/ecobee-SmartThermostat-Voice-Control-Black/dp/B07NQT85FC?tag=niemyjski-20&quot;&gt;ecobee thermostats&lt;/a&gt;
into &lt;a href=&quot;https://www.ecobee.com/en-us/eco-plus/&quot;&gt;eco+&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Measure your usage and find the quick ways to reduce energy. Turn off things
you are not using (e.g., the coffee maker).&lt;/li&gt;
&lt;li&gt;Write automation routines or turn on settings that turns off bulbs when
there is no motion/occupancy. Remember smart bulbs always require power.&lt;/li&gt;
&lt;li&gt;Upgrade to LED bulbs.&lt;/li&gt;
&lt;li&gt;Seal windows for winter and look into getting more insulation installed.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Long Term Solutions&lt;/h2&gt;
&lt;p&gt;I&apos;m starting down this path today and here are the steps I&apos;m currently planning
on taking.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Better measuring of devices with a &lt;a href=&quot;https://www.amazon.com/Aeotec-Aeon-Labs-ZW095-ZW095/dp/B00XD8WZX6?tag=niemyjski-20&quot;&gt;smart home energy metering solution&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Analyzing device power and user usage data and writing smart automations
that reduce power usage without restricting limiting comfort. Some examples
include:
&lt;ul&gt;
&lt;li&gt;Run the pond air pump at off peak times or when people are observing the
pond. I think this can be done with the &lt;a href=&quot;https://www.amazon.com/Aeotec-Security-controller-electricity-consumption/dp/B00MBIRF5W?tag=niemyjski-20&quot;&gt;Aeotec 40 amp smart switch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Only turn on half of the room lights during high peak time (e.g., kitchen
sink, a lamp).&lt;/li&gt;
&lt;li&gt;Advanced furnace zone scheduling learning off calendars and history.&lt;/li&gt;
&lt;li&gt;Better climate management by opening blinds when the sun is shining in on
a cold day, but leaving it closed when it&apos;s hot out.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Buying lower energy smart devices. I&apos;m starting to analyze everything I buy
to ensure they are as efficient as possible. I&apos;m currently running my smart
home on an &lt;a href=&quot;https://www.amazon.com/DLI-Atomic-Pi-Peripheral-Camera/dp/B07DVYDDV8?tag=niemyjski-20&quot;&gt;Atomic Pi&lt;/a&gt;
which runs Home Assistant and much more using only 4-15 watts. I&apos;m planning
on upgrading it to a low powered AMD Ryzen 4000 machine or a 11th generation
Intel NUC (15 watt &lt;a href=&quot;https://www.youtube.com/watch?v=yDWO177BjZY&quot;&gt;TPD&lt;/a&gt;).
I have been considering the &lt;a href=&quot;https://www.amazon.com/Intel-NUC-10-Performance-Kit/dp/B08357VWB2?tag=niemyjski-20&quot;&gt;10th generation Intel NUC&lt;/a&gt;
as well if I need to upgrade before the 11th generation devices arrive.&lt;/li&gt;
&lt;li&gt;I want to install either Solar Panels and Batteries to help offset my power
usage and ensure it&apos;s coming from a green source. I&apos;ve been evaluating
replacing my aging garage roof with a &lt;a href=&quot;https://www.tesla.com/solarroof&quot;&gt;Solar Roof&lt;/a&gt;
and a &lt;a href=&quot;https://www.tesla.com/powerwall&quot;&gt;Power wall&lt;/a&gt; as users have indicated
huge savings when using Time-Of-Use Billing! Here is my
&lt;a href=&quot;https://ts.la/blake51079&quot;&gt;Tesla referral&lt;/a&gt; if you are thinking of doing this
too!&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I hope I gave you ways to measure and reduce consumption! Do everything you can
to reduce and reuse in your life, leaving it better for future generations!&lt;/p&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>Hello Home Automation, How-to detect battery levels with Node-RED</title><link>https://blakeniemyjski.com/blog/hello-home-automation-how-to-detect-battery-levels-with-node-red/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/hello-home-automation-how-to-detect-battery-levels-with-node-red/</guid><pubDate>Mon, 03 Feb 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I&apos;ve decided to start off 2020 by converting my blog to &lt;a href=&quot;https://gohugo.io&quot;&gt;Hugo&lt;/a&gt;
and start blogging again! This is my first of many posts on home automation!
This post is going to cover generically detecting battery levels with
&lt;a href=&quot;https://www.home-assistant.io&quot;&gt;Home Assistant&lt;/a&gt; and &lt;a href=&quot;https://nodered.org&quot;&gt;Node-RED&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;TLDR: Use the &lt;a href=&quot;https://nodered.org/docs/user-guide/nodes#function&quot;&gt;Function node&lt;/a&gt;
defined in the &lt;a href=&quot;https://gist.github.com/niemyjski/afc28ed47ae137fe5e245b245fd6c910&quot;&gt;Node-RED snippet here&lt;/a&gt;
to properly handle all the edge cases.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I decided to share my experiences based on seeing what others were doing in this
&lt;a href=&quot;https://www.reddit.com/r/homeassistant/comments/emvk9y/how_do_you_monitor_sensor_battery_levels_looking/&quot;&gt;thread&lt;/a&gt;.
For the most part, I saw users not accounting for edge cases or the &lt;a href=&quot;https://nodered.org/docs/user-guide/nodes#switch&quot;&gt;Switch node&lt;/a&gt;
wasn&apos;t going to work how they thought it was going to work.&lt;/p&gt;
&lt;p&gt;Two of the main gotchas I&apos;ve come across with device types that report battery
state:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;storing it in different locations (&lt;code&gt;msg.state&lt;/code&gt;, &lt;code&gt;msg.payload.attributes[&quot;Battery Level&quot;]&lt;/code&gt;, etc.)&lt;/li&gt;
&lt;li&gt;differing values/data types (&lt;code&gt;off&lt;/code&gt;, &lt;code&gt;Charging&lt;/code&gt;, &lt;code&gt;&quot;100&quot;&lt;/code&gt;, &lt;code&gt;100&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This really makes it difficult to use the Switch or &lt;a href=&quot;https://nodered.org/docs/user-guide/nodes#change&quot;&gt;Change node&lt;/a&gt;
with conditional logic as it might not behave &lt;a href=&quot;https://www.destroyallsoftware.com/talks/wat&quot;&gt;how you think it does&lt;/a&gt;.
This happened to me when I was debugging my first few attempts and getting &lt;code&gt;10&lt;/code&gt;
outputted in a &lt;code&gt;100&lt;/code&gt; bucket via the &lt;code&gt;gt&lt;/code&gt; condition. The best way I&apos;ve found to
handle these scenarios is with a &lt;a href=&quot;https://nodered.org/docs/user-guide/nodes#function&quot;&gt;Function node&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Home Assistant Feedback and Edge Cases&lt;/h2&gt;
&lt;p&gt;I think it may be worth opening a GitHub issue for this. It sure would be nice
if devices that can charge have their charging state normalized, battery level
state in the same spot with a last changed in attribute. Additionally it would
be nice to see something for dead batteries or if the device just hasn&apos;t reported
in a long time. I say this because I&apos;ve have many devices I know are charged
and on, but they are being reported as &lt;code&gt;off&lt;/code&gt;. What do you think?&lt;/p&gt;
&lt;h3&gt;Examples&lt;/h3&gt;
&lt;p&gt;Off, clearly the battery has a level and it lasts 10 years and it&apos;s reported in the nest app as OK.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{&quot;entity_id&quot;:&quot;binary_sensor.nest_protect_battery_health&quot;,&quot;state&quot;:&quot;off&quot;,&quot;attributes&quot;:{&quot;friendly_name&quot;:&quot;Nest Battery&quot;,&quot;device_class&quot;:&quot;battery&quot;},&quot;last_changed&quot;:&quot;2020-01-11T11:42:50.976581+00:00&quot;,&quot;last_updated&quot;:&quot;2020-01-11T11:42:50.976581+00:00&quot;,&quot;context&quot;:{&quot;id&quot;:&quot;2a2f5c1503cd44c7bb3ebd9be69a4144&quot;,&quot;parent_id&quot;:null,&quot;user_id&quot;:null},&quot;timeSinceChangedMs&quot;:2731510}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Charging - battery level stored in attributes.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{&quot;entity_id&quot;:&quot;sensor.battery_state_2&quot;,&quot;state&quot;:&quot;Charging&quot;,&quot;attributes&quot;:{&quot;Battery Level&quot;:52,&quot;friendly_name&quot;:&quot;Battery State&quot;,&quot;icon&quot;:&quot;mdi:battery-charging-40&quot;,&quot;device_class&quot;:&quot;battery&quot;},&quot;last_changed&quot;:&quot;2020-01-11T11:43:06.616903+00:00&quot;,&quot;last_updated&quot;:&quot;2020-01-11T11:43:06.616903+00:00&quot;,&quot;context&quot;:{&quot;id&quot;:&quot;a9cc54cd33cf4cf1954c4cd2f2f633a8&quot;,&quot;parent_id&quot;:null,&quot;user_id&quot;:null},&quot;timeSinceChangedMs&quot;:2715885}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Same device as Charging example above but duplicate battery device sensor with state in the correct location.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{&quot;entity_id&quot;:&quot;sensor.battery_level_2&quot;,&quot;state&quot;:&quot;52&quot;,&quot;attributes&quot;:{&quot;Battery State&quot;:&quot;Charging&quot;,&quot;unit_of_measurement&quot;:&quot;%&quot;,&quot;friendly_name&quot;:&quot;Battery Level&quot;,&quot;icon&quot;:&quot;mdi:battery-charging-40&quot;,&quot;device_class&quot;:&quot;battery&quot;},&quot;last_changed&quot;:&quot;2020-01-11T11:43:06.599389+00:00&quot;,&quot;last_updated&quot;:&quot;2020-01-11T11:43:06.599389+00:00&quot;,&quot;context&quot;:{&quot;id&quot;:&quot;60469c2964504dc4bb11cbf5a307ceb9&quot;,&quot;parent_id&quot;:null,&quot;user_id&quot;:null},&quot;timeSinceChangedMs&quot;:3766731}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;August lock reported from august integration missing information.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{&quot;entity_id&quot;:&quot;binary_sensor.august_door_locked_battery_level&quot;,&quot;state&quot;:&quot;off&quot;,&quot;attributes&quot;:{&quot;friendly_name&quot;:&quot;August Door Lock Battery Level&quot;,&quot;device_class&quot;:&quot;battery&quot;},&quot;last_changed&quot;:&quot;2020-01-11T11:42:50.892228+00:00&quot;,&quot;last_updated&quot;:&quot;2020-01-11T11:42:50.892228+00:00&quot;,&quot;context&quot;:{&quot;id&quot;:&quot;567d9c771c91440c826ed26d14187513&quot;,&quot;parent_id&quot;:null,&quot;user_id&quot;:null},&quot;timeSinceChangedMs&quot;:2731591}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;August lock as reported from Z-Wave.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{&quot;entity_id&quot;:&quot;sensor.august_asl_03_smart_lock_battery_level&quot;,&quot;state&quot;:&quot;100&quot;,&quot;attributes&quot;:{&quot;node_id&quot;:2,&quot;value_index&quot;:0,&quot;value_instance&quot;:1,&quot;value_id&quot;:&quot;72057594077773825&quot;,&quot;unit_of_measurement&quot;:&quot;%&quot;,&quot;friendly_name&quot;:&quot;August ASL-03 Smart Lock Battery Level&quot;,&quot;device_class&quot;:&quot;battery&quot;},&quot;last_changed&quot;:&quot;2020-01-11T11:47:39.195031+00:00&quot;,&quot;last_updated&quot;:&quot;2020-01-11T11:47:39.195031+00:00&quot;,&quot;context&quot;:{&quot;id&quot;:&quot;406c8c33527a4daf8d9b23531953d54f&quot;,&quot;parent_id&quot;:null,&quot;user_id&quot;:null},&quot;timeSinceChangedMs&quot;:2443740}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Handling the edge cases&lt;/h4&gt;
&lt;p&gt;The best way to handle all the edge cases above is with a &lt;a href=&quot;https://nodered.org/docs/user-guide/nodes#function&quot;&gt;Function node&lt;/a&gt;.
It will be extremely tough to handle normalizing the battery level with any
other node type with out a really crazy series.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It should be everyone&apos;s goal to keep automations as simple as possible. This
keeps the maintenance super low and the acceptance factor has high
as possible!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So lets get started by creating a new Function node with &lt;code&gt;6&lt;/code&gt; outputs.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// NOTE: We could probably look at the unit_of_measurement if specified to properly normalize the battery level.
// Check for battery level as it&apos;s set when state is Charging,Not Charging,etc...
const batteryLevelAttr = msg.payload.attributes &amp;amp;&amp;amp; msg.payload.attributes[&quot;Battery Level&quot;];
const state = !isNaN(batteryLevelAttr) ? batteryLevelAttr : msg.payload.state;
const level = parseInt(state);
if (!isFinite(level)) {
    return [msg];
} else if (level &amp;lt; 10) {
    return [null, msg];
} else if (level &amp;lt; 25) {
    return [null, null, msg];
} else if (level &amp;lt; 50) {
    return [null, null, null, msg];
} else if (level &amp;lt; 75) {
    return [null, null, null, null, msg];
} else {
    return [null, null, null, null, null, msg];
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By creating 6 outputs and outputting the message to the desired output, we can
wire up routines to fire on only the battery level ranges we care about
(e.g., 0-10%, 10-25%, etc.)!&lt;/p&gt;
&lt;h2&gt;Bringing it all together&lt;/h2&gt;
&lt;p&gt;I&apos;ve created a Node-RED flow to get every device that supports battery levels
and output them to a Debug Node using the above Function Node.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/home-automation/node-red-home-assistant-battery-level-flow.png&quot; alt=&quot;Node-RED Home Assistant Battery Level Flow&quot; /&gt;&lt;/p&gt;
&lt;p&gt;You can grab the complete &lt;a href=&quot;https://gist.github.com/niemyjski/afc28ed47ae137fe5e245b245fd6c910&quot;&gt;Node-RED snippet here&lt;/a&gt;.&lt;/p&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>Speaking at BAM</title><link>https://blakeniemyjski.com/blog/speaking-at-bam/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/speaking-at-bam/</guid><pubDate>Tue, 19 Feb 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I&apos;ll be giving a talk at
&lt;a href=&quot;https://www.meetup.com/BAMDataScience/events/257346637/&quot;&gt;From BAM-Big Data, Advanced Analytics, Machine Learning Scaling Your Applications&lt;/a&gt;
and Skill Set on January 15th!&lt;/p&gt;
&lt;h3&gt;Overview&lt;/h3&gt;
&lt;p&gt;Blake will lead a discussion regarding the struggles around starting and using
open source projects to build software solutions for customers. Going from
having no customers, to doing 3+ billion requests per month in Azure was a
sometimes painful experience, especially on a limited budget. Blake will
discuss what he has learned, as well as talk about data acquisition and
maintenance, and talk about problems working with large data sets.&lt;/p&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>Speaking at Central Wisconsin It Conference</title><link>https://blakeniemyjski.com/blog/speaking-at-central-wisconsin-it-conference/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/speaking-at-central-wisconsin-it-conference/</guid><pubDate>Thu, 04 Oct 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I&apos;ll be giving a talk at &lt;a href=&quot;https://www.cwitc.org&quot;&gt;Central Wisconsin IT Conference&lt;/a&gt;
on &lt;a href=&quot;https://github.com/exceptionless/MediaKit/tree/master/presentations/exceptionless&quot;&gt;Exception Driven Development&lt;/a&gt;
on October 6th!&lt;/p&gt;
&lt;h3&gt;Overview&lt;/h3&gt;
&lt;p&gt;What is &quot;Exception Driven Development&quot;? If customer-facing issues are your top
priority, then you already adhere to the primary principle of Exception Driven
Development.&lt;/p&gt;
&lt;p&gt;It&apos;s a pretty simple concept: Collect application data, especially regarding
errors and crashes, and then focus on fixing those problems that your users are
actually experiencing. Join me for 60 minutes and I&apos;ll walk you through what
you&apos;re doing wrong with your current log collection approach, and how you
should be doing it. Additionally, we&apos;ll cover visualizing errors in a more
meaningful manner that eliminates all the noise.&lt;/p&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>Speaking at Northeast Wisconsin Code Camp</title><link>https://blakeniemyjski.com/blog/speaking-at-the-northeast-wisconsin-code-camp/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/speaking-at-the-northeast-wisconsin-code-camp/</guid><pubDate>Thu, 22 Mar 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I&apos;ll be giving a talk at &lt;a href=&quot;https://newcodecamp.com&quot;&gt;Northeast Wisconsin Code Camp&lt;/a&gt;
on &lt;a href=&quot;https://github.com/exceptionless/MediaKit/tree/master/presentations/exceptionless&quot;&gt;Exception Driven Development&lt;/a&gt;
on March 24th!!&lt;/p&gt;
&lt;h3&gt;Overview&lt;/h3&gt;
&lt;p&gt;What is &quot;Exception Driven Development&quot;? If customer-facing issues are your top
priority, then you already adhere to the primary principle of Exception Driven
Development.&lt;/p&gt;
&lt;p&gt;It&apos;s a pretty simple concept: Collect application data, especially regarding
errors and crashes, and then focus on fixing those problems that your users are
actually experiencing. Join me for 60 minutes and I&apos;ll walk you through what
you&apos;re doing wrong with your current log collection approach, and how you
should be doing it. Additionally, we&apos;ll cover visualizing errors in a more
meaningful manner that eliminates all the noise.&lt;/p&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>Speaking at Milwaukee Code Camp</title><link>https://blakeniemyjski.com/blog/speaking-at-milwaukee-code-camp/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/speaking-at-milwaukee-code-camp/</guid><pubDate>Sat, 11 Nov 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I&apos;ll be giving a talk at &lt;a href=&quot;https://www.milwaukeecodecamp.com/&quot;&gt;Milwaukee Code Camp&lt;/a&gt;
on &lt;a href=&quot;https://github.com/exceptionless/MediaKit/tree/master/presentations/exceptionless&quot;&gt;Exception Driven Development&lt;/a&gt;
on November 11, 2017&lt;/p&gt;
&lt;h3&gt;Overview&lt;/h3&gt;
&lt;p&gt;What is &quot;Exception Driven Development&quot;? If customer-facing issues are your top
priority, then you already adhere to the primary principle of Exception Driven
Development.&lt;/p&gt;
&lt;p&gt;It&apos;s a pretty simple concept: Collect application data, especially regarding
errors and crashes, and then focus on fixing those problems that your users are
actually experiencing. Join me for 60 minutes and I&apos;ll walk you through what
you&apos;re doing wrong with your current log collection approach, and how you
should be doing it. Additionally, we&apos;ll cover visualizing errors in a more
meaningful manner that eliminates all the noise.&lt;/p&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>Speaking at North Dallas .NET User Group</title><link>https://blakeniemyjski.com/blog/speaking-at-the-north-dallas-net-user-group/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/speaking-at-the-north-dallas-net-user-group/</guid><pubDate>Mon, 01 May 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I&apos;ll be giving a talk at &lt;a href=&quot;https://northdallas.net/meetings/exception-driven-development/&quot;&gt;North Dallas .NET User Group&lt;/a&gt;
on &lt;a href=&quot;https://github.com/exceptionless/MediaKit/tree/master/presentations/exceptionless&quot;&gt;Exception Driven Development&lt;/a&gt;
on November 11, 2017&lt;/p&gt;
&lt;h3&gt;Overview&lt;/h3&gt;
&lt;p&gt;What is &quot;Exception Driven Development&quot;? If customer-facing issues are your top
priority, then you already adhere to the primary principle of Exception Driven
Development.&lt;/p&gt;
&lt;p&gt;It&apos;s a pretty simple concept: Collect application data, especially regarding
errors and crashes, and then focus on fixing those problems that your users are
actually experiencing. Join me for 60 minutes and I&apos;ll walk you through what
you&apos;re doing wrong with your current log collection approach, and how you
should be doing it. Additionally, we&apos;ll cover visualizing errors in a more
meaningful manner that eliminates all the noise.&lt;/p&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>Speaking at Milwaukee .NET Conference</title><link>https://blakeniemyjski.com/blog/speaking-at-the-milwaukee-net-conference/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/speaking-at-the-milwaukee-net-conference/</guid><pubDate>Mon, 29 Aug 2016 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I&apos;ll be giving a talk at &lt;a href=&quot;https://www.centare.com/mke-dot-net/&quot;&gt;Milwaukee .NET Conference&lt;/a&gt;
on &lt;a href=&quot;https://github.com/exceptionless/MediaKit/tree/master/presentations/exceptionless&quot;&gt;Exception Driven Development&lt;/a&gt;
on November 11, 2017&lt;/p&gt;
&lt;h3&gt;Overview&lt;/h3&gt;
&lt;p&gt;What is &quot;Exception Driven Development&quot;? If customer-facing issues are your top
priority, then you already adhere to the primary principle of Exception Driven
Development.&lt;/p&gt;
&lt;p&gt;It&apos;s a pretty simple concept: Collect application data, especially regarding
errors and crashes, and then focus on fixing those problems that your users are
actually experiencing. Join me for 60 minutes and I&apos;ll walk you through what
you&apos;re doing wrong with your current log collection approach, and how you
should be doing it. Additionally, we&apos;ll cover visualizing errors in a more
meaningful manner that eliminates all the noise.&lt;/p&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>The best JavaScript IDE</title><link>https://blakeniemyjski.com/blog/the-best-javascript-ide/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/the-best-javascript-ide/</guid><pubDate>Fri, 17 Oct 2014 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;Disclaimer: I was asked to write a review by WebStorm for a free open source
license. With that said you will always get my honest feedback about the pros
and cons about these editors.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;About a month ago I started writing the new &lt;a href=&quot;https://angularjs.org/&quot;&gt;AngularJS&lt;/a&gt;
based spa app for &lt;a href=&quot;https://github.com/exceptionless/Exceptionless&quot;&gt;Exceptionless 2.0&lt;/a&gt;.
While learning about AngularJS I looked around and used several editors to see
which one worked the best. I&apos;ll break down the editors that I used and conclude
which one I liked the best.&lt;/p&gt;
&lt;h2&gt;&lt;a href=&quot;https://atom.io/&quot;&gt;Atom&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I used the free atom editor for a day on my Windows 8.1 desktop. One thing that
really stood out about the editor is that it&apos;s very plugin based which is
awesome. You can download themes easily and add new IDE features like current
line highlighting, linting in seconds. I really liked this editor for editing
a few files but it really fell short when you needed auto completion for files
outside of the current view. For example it I never got auto completion inside
of my angular directives (E.G., ng-click) when editing my html markup or auto
completion for anything defined outside of the current JavaScript editor
document. It also seemed like I had to remember a lot of shortcuts to run
various commands like running grunt tasks etc.. I just felt like Atom needs
more time to mature.&lt;/p&gt;
&lt;h3&gt;Pros&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Free&lt;/li&gt;
&lt;li&gt;Cross-platform&lt;/li&gt;
&lt;li&gt;Many plugins available.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Cons&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Poor auto completion&lt;/li&gt;
&lt;li&gt;Left feeling like this is just notepad on steroids.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;&lt;a href=&quot;https://www.sublimetext.com/&quot;&gt;Sublime Text&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I used sublime for a day on my MacBook pro and I liked this editor. This is a
freeware/payware editor and feels more polished than Atom but I felt like it
had the same pitfalls of Atom (multiple file auto completion issues and lots of
keyboard commands to do various editor functions.&lt;/p&gt;
&lt;h3&gt;Pros&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Cross-platform&lt;/li&gt;
&lt;li&gt;Many plugins available.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Cons&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Poor auto completion&lt;/li&gt;
&lt;li&gt;Notepad on steroids.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;&lt;a href=&quot;https://www.visualstudio.com/&quot;&gt;Visual Studio with ReSharper&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I&apos;ve used Visual Studio 2013 (VS) and Resharper for many years and love it! The
latest updates to VS included a grunt task runner and more which got me really
excited! I really liked the fact that I could have all my code in one editor
and quickly make updates to my backend while working on the front end.&lt;/p&gt;
&lt;p&gt;However, I quickly found out that my VS came to a crawl on my desktop. It
seemed that ReSharper started to index all of my node components files (there
are thousands of files). Even worse it then added all of those tests it had
detected to the Test Runner when I selected the solution and clicked run all
tests (which then resulted in thousands of error dialogs saying an error
running the test occurred). At this point I was about to give up on VS IDE for
this task but I decided to try it for the rest of the day. I found that the
Intellisense was good all around but I suffered random freezes and just
couldn&apos;t take it anymore.&lt;/p&gt;
&lt;p&gt;Thinking back if ReSharper didn&apos;t index all of those files that I didn&apos;t need
it would have been a much better experience. But you must also take into the
fact that VS is very resource intensive especially on large projects and will
kill your battery quickly.&lt;/p&gt;
&lt;h3&gt;Pros&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;All source code in one spot for easy editing&lt;/li&gt;
&lt;li&gt;Very good auto completion and many plugins available.&lt;/li&gt;
&lt;li&gt;Grunt task runner via third party add in.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Cons&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Very expensive&lt;/li&gt;
&lt;li&gt;Only works on Microsoft Windows.&lt;/li&gt;
&lt;li&gt;Will kill your laptop battery quickly&lt;/li&gt;
&lt;li&gt;Sluggish due to third party addons.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;&lt;a href=&quot;https://www.jetbrains.com/webstorm/&quot;&gt;WebStorm&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I started using the thirty day trial of WebStorm and quickly fell in love. The
auto completion is amazing and works everywhere. You can also quickly access
the terminal without leaving the IDE to run bower and npm commands. You can
also run grunt or gulp tasks with ease as they made it a first class editor
feature by adding a tool window for it (VS has this but it&apos;s not as baked in
yet). I also had a few random minor annoyances with freezes with the editor
and the support was amazing and a product update released last week fixed the
issues. I can&apos;t speak for the support of the others but I would say good luck
about getting support for VS.&lt;/p&gt;
&lt;p&gt;There are a few things I didn&apos;t like about WebStorm is that it felt like a
chore to find and change settings. I really hope the team takes some time and
updates the whole menu layout to be more like VS. I&apos;d also like more themes. I
also found it a chore to copy files and folders as there smart refactor became
a pain a few times (or it works really great). Sometimes you just want to copy
a service or directive and use that as a starting point for edits. I found
myself opening up OSX Finder or Windows Explorer and renaming the files.
Finally, I felt like the other editors had better themes and I didn&apos;t like the
idea of installing Java on my machines.&lt;/p&gt;
&lt;h3&gt;Pros&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Amazing auto completion and editor features (E.G., Zen Coding).&lt;/li&gt;
&lt;li&gt;Builtin Grunt and Gulp task runners.&lt;/li&gt;
&lt;li&gt;Free for Students and Open Source projects.&lt;/li&gt;
&lt;li&gt;Fast and helpful support.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Pros&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Finding and changing settings seemed unintuitive.&lt;/li&gt;
&lt;li&gt;Smart refactoring is a two edged sword.&lt;/li&gt;
&lt;li&gt;Requires Java (only if you don&apos;t like Java).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Winner&lt;/h2&gt;
&lt;p&gt;Without question I&apos;ll be using WebStorm for the considerable future!&lt;/p&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>Exceptionless Goes Open Source</title><link>https://blakeniemyjski.com/blog/exceptionless-goes-open-source/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/exceptionless-goes-open-source/</guid><pubDate>Tue, 18 Feb 2014 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://exceptionless.com/assets/Octocat-300x249.png&quot; alt=&quot;Octocat&quot; /&gt;
&lt;strong&gt;It&apos;s a big day at Exceptionless.&lt;/strong&gt;
We are super excited to announce that we are
&lt;a href=&quot;https://github.com/exceptionless/Exceptionless&quot;&gt;open sourcing the Exceptionless code&lt;/a&gt;!
That&apos;s right, now you can hack on our real-time error reporting tool yourself.&lt;/p&gt;
&lt;p&gt;Too many apps are throwing too many errors out there, resulting in confused
users, lost business, and endless frustration.&lt;/p&gt;
&lt;p&gt;We believe Exceptionless can help the development community become more in-tune
with their code by making those errors more transparent, trackable, and
squashable. More importantly, we want to support developers building and
shipping better code for their &lt;strong&gt;users&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Cool, Where Do I Start?&lt;/h2&gt;
&lt;p&gt;Check out the &lt;a href=&quot;https://github.com/exceptionless/Exceptionless&quot;&gt;Exceptionless Github Repository&lt;/a&gt;,
and make sure to &lt;a href=&quot;https://github.com/exceptionless/Exceptionless/blob/master/CONTRIBUTING.md&quot;&gt;read about contributing&lt;/a&gt;
if you plan on helping us improve the project.&lt;/p&gt;
&lt;p&gt;You&apos;ll find instructions on getting started there, including setting up your
hosting environment if you want to host things yourself. If you would rather
host with us, we offer &lt;a href=&quot;https://exceptionless.com/pricing/&quot;&gt;easy and affordable plans&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you have a feature request or issue, let us know on the
&lt;a href=&quot;https://github.com/exceptionless/Exceptionless/issues&quot;&gt;issue tracker&lt;/a&gt;. We are
&lt;strong&gt;100% committed&lt;/strong&gt; to supporting the project.&lt;/p&gt;
&lt;h2&gt;Why Open Source?&lt;/h2&gt;
&lt;p&gt;In short, we want to see what the community can do with our baby, which we
consider a great development tool. The open source movement has provided
innovation throughout the industry, and we cannot tell you how excited we are
to be a part of it.&lt;/p&gt;
&lt;p&gt;We hope you will take it, add to it, suggest great new features, and report
bugs, but most of all we hope you will use it to** build better apps for the world**.&lt;/p&gt;
&lt;p&gt;The Exceptionless Team will continue to work on a road map of features and
improvements, all while providing support to developers that want to contribute.&lt;/p&gt;
&lt;h3&gt;Planned features/enhancements&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Refactor client so that the base client is a PCL library thus supporting WinRT and Mono.&lt;/li&gt;
&lt;li&gt;Implement search features using ElasticSearch.&lt;/li&gt;
&lt;li&gt;JavaScript client for reporting client side errors.&lt;/li&gt;
&lt;li&gt;Refactor the API and UI to be completely separate layers and rewrite the UI as a SPA app using AngularJS.&lt;/li&gt;
&lt;li&gt;Add a server side plugin system that allows new functionality to be easily added like HipChat notifications.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Ideas for the community&lt;/h3&gt;
&lt;p&gt;These include just a few of feature requests, etc, that aren&apos;t in the team&apos;s
immediate pipeline that you might consider tackling.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Clients for Ruby, Java, Python, PHP, etc&lt;/li&gt;
&lt;li&gt;Ability to log non-exception data&lt;/li&gt;
&lt;li&gt;Teams within organizations&lt;/li&gt;
&lt;li&gt;Bookmarks&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Lets make something awesome&lt;/h3&gt;
&lt;p&gt;We have spent a lot of time on Exceptionless, but together with the community
we can take it to the next level. Check out the
&lt;a href=&quot;https://github.com/exceptionless/Exceptionless&quot;&gt;GitHub repo&lt;/a&gt; today, and happy
coding!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Watch out, exceptions!&lt;/strong&gt;&lt;/p&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>Implementing GoTo Line functionality in a Visual Studio Custom Editor</title><link>https://blakeniemyjski.com/blog/implementing-goto-line-functionality-in-a-visual-studio-custom-editor/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/implementing-goto-line-functionality-in-a-visual-studio-custom-editor/</guid><pubDate>Fri, 03 Jun 2011 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;While implementing a custom &lt;a href=&quot;/programming/programming/announcing-the-codesmith-generator-6-0-visual-studio-template-editor/&quot;&gt;CodeSmith Generator Template Editor into Visual Studio 2010&lt;/a&gt;.
I ran into an issue with getting the GoTo Line functionality to work properly.
According to &lt;a href=&quot;https://social.msdn.microsoft.com/Forums/en-US/vsx/thread/b8cbb1de-ab99-48af-ae5a-78808ef3208c&quot;&gt;Microsoft&lt;/a&gt;
this functionality needs to be provided by the custom editor as well as the
GoTo Line dialog will need to be implemented as there is no &quot;official public
extension point&quot;. This makes sense if you are taking over control of the editor
but I feel that Microsoft should expose this dialog. I was able to implement
this functionality by adding a command to the &lt;code&gt;IMenuCommandService&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var mcs = GetService(typeof (IMenuCommandService)) as IMenuCommandService;
if (null != mcs)
    addCommand(mcs, VSConstants.GUID_VSStandardCommandSet97, (int) VSConstants.VSStd97CmdID.Goto, OnGoTo, OnQueryGoTo);
....
private static void addCommand(
    IMenuCommandService mcs, Guid menuGroup, int cmdID, EventHandler commandEvent, EventHandler queryEvent
) {}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, I see if the command should be enabled by seeing if the current document
has any lines of text. Then I make a call to the &quot;internal&quot; Microsoft Dialog
GoTo dialog that provides validation and help text. Please note that this code
requires the following import block and is subject to change in future versions
of Visual Studio.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using Microsoft.Internal.VisualStudio.PlatformUI;
private void OnQueryGoTo(object sender, EventArgs e)
{
    var command = (OleMenuCommand)sender;
    command.Enabled = EditorControl.LineCount &amp;gt; 0;
}

private void OnGoTo(object sender, EventArgs e)
{
    var lineNumber = GetGoToLineNumberFromDialog();
    if (lineNumber &amp;lt; 0)
        return;

    ((IVsFindTarget) this).NavigateTo(
        new TextSpan[] { new TextSpan { iStartLine = lineNumber, iStartIndex = 0,
                                        iEndLine = lineNumber, iEndIndex = 0 }});
}

private int GetGoToLineNumberFromDialog()
{
    var dataSource = new UIDataSource();
    dataSource.AddBuiltInProperty(&quot;MinimumLine&quot;, 1);
    dataSource.AddBuiltInProperty(&quot;MaximumLine&quot;, EditorControl.LineCount);
    dataSource.AddBuiltInProperty(&quot;CurrentLine&quot;, EditorControl.CurrentLine);
    WindowHelper.AddHelpTopic(dataSource, &quot;vs.gotoline&quot;);
    if (WindowHelper.ShowModalElement(VSConstants.GUID_VSStandardCommandSet97, 231, dataSource) == 1)
    {
        var result = new UIObject(dataSource[&quot;CurrentLine&quot;]);
        return (((int)result.Data) - 1);
    }

    return -1;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If anyone comes across a better way to accomplish this, by all means let me know!&lt;/p&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>How-to open a Visual Studio Document Window using the Visual Studio API</title><link>https://blakeniemyjski.com/blog/how-to-open-a-visual-studio-document-window-using-the-visual-studio-api/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/how-to-open-a-visual-studio-document-window-using-the-visual-studio-api/</guid><pubDate>Fri, 03 Jun 2011 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I had spent considerable time trying to open a new document window and then set
the document text to the result of the generated template text. I found this to
be an almost impossible task and proposed this on the
&lt;a href=&quot;https://social.msdn.microsoft.com/Forums/en-US/vsx/thread/6923ecd3-bcbd-4c63-a8ee-58f39d62dff3/#71947d56-e28a-49f7-abf5-78f47453fb6e&quot;&gt;Microsoft Visual Studio Extensibility forums&lt;/a&gt;.
The response that I got was that after looking into this functionality the best
approach would be to save the text to a file and then open the document window.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I looked into this, I think trying to open one of the built in editors, in a
normal editor tab populated with data that does not exist on disk is going to
be a pointless battle, the system is set up to deal with files, generally from
disk.&lt;/p&gt;
&lt;p&gt;I don&apos;t see any good (read easy or even logical) way to open an &apos;empty&apos; editor
that is fed data from memory. There is likely a way to do this, the folks on
the editor forum may have suggestions, but I don&apos;t see any clear path to it and
if you find it I foresee you having to fight a lot of battles as you are trying
to do something far from the &apos;mainstream&apos;.&lt;/p&gt;
&lt;p&gt;I looked into the File -&amp;gt; New File scenario since it is similar but even that
uses a temporary file on disk.  Saving the content you want to display to disk
in the users temp directory and opening that file would probably be 500% easier
than trying to do what you want to do above&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;One to One could more than likely use &lt;code&gt;EnvDTE.get_IsOpenFile()&lt;/code&gt; or
&lt;code&gt;EnvDTE.OpenFile()&lt;/code&gt; to accomplish this task, but I choose the following method
to open a new Document Window. One first must check to see if the document is
already open and if it isn&apos;t make the call to open it. We return the
&lt;code&gt;IVsWindowFrame&lt;/code&gt; from this method, as it allows one to do additional operations
on this opened document. Note that the &lt;code&gt;ServiceFactory.ServiceProvider&lt;/code&gt; returns
an instance of &lt;code&gt;EnvDTE.DTE&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public static IVsWindowFrame OpenDocumentInNewWindow(string filePath) {
  if(String.IsNullOrEmpty(filePath) || !File.Exists(filePath))
    return null;

  IVsUIHierarchy hierarchy;
  uint itemId;
  IVsWindowFrame frame = null;
  if (!VsShellUtilities.IsDocumentOpen(ServiceFactory.ServiceProvider, filePath,
      VSConstants.LOGVIEWID_Primary, out hierarchy, out itemId, out frame))
  {
    VsShellUtilities.OpenDocument(ServiceFactory.ServiceProvider, filePath,
      VSConstants.LOGVIEWID_Primary, out hierarchy, out itemId, out frame);
  }

  if (frame != null)
    frame.Show();

  return frame;
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>Announcing The CodeSmith Generator 6.0 Visual Studio Template Editor</title><link>https://blakeniemyjski.com/blog/announcing-the-codesmith-generator-6.0-visual-studio-template-editor/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/announcing-the-codesmith-generator-6.0-visual-studio-template-editor/</guid><pubDate>Fri, 03 Jun 2011 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;We are very excited to announce that CodeSmith Generator 6.0 will feature a
built-in Visual Studio CodeSmith Generator Template Editor on top of our
existing &lt;a href=&quot;https://www.codesmithtools.com/product/generator&quot;&gt;Visual Studio Integration&lt;/a&gt;.
The team has been hard at work integrating a new Template Editor into our
Visual Studio integration story. Without further ado:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/generator-6-visual-studio-integration.png&quot; alt=&quot;VS Integration&quot; /&gt;&lt;/p&gt;
&lt;p&gt;As you can see from the screenshot above, the CodeSmith Generator Template
Editor is sitting inside of Visual Studio. This means you can use a familiar
Development Interface to develop, edit and generate templates.Also noticeable
is our new property grid which replaces the visual studio property grid
(please note this is in the same property tool window as the built in property
window). This allows much more flexibility and power to-do things like
dynamically filter properties or only show required properties. The image below
shows the result of clicking generate for the currently shown editor template.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/generator-6-visual-studio-integration-generation.png&quot; alt=&quot;VS Integration&quot; /&gt;&lt;/p&gt;
&lt;p&gt;We still have more work to-do on the Visual Studio Integration but we couldn&apos;t
wait so share the news and get your initial feedback. We will be releasing more
information over the next few weeks on all of the awesome features and additions
to CodeSmith Generator 6.0!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;CodeSmith Generator 6.0 is currently in development, and is scheduled for
release in Q3 of 2011.&lt;/em&gt;&lt;/p&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>Extending the CodeSmith Generator PLINQO templates</title><link>https://blakeniemyjski.com/blog/extending-the-codesmith-generator-plinqo-templates/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/extending-the-codesmith-generator-plinqo-templates/</guid><pubDate>Tue, 18 Jan 2011 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The following guide will walk you through the basics on how-to extend the
&lt;a href=&quot;https://www.codesmithtools.com/product/generator&quot;&gt;CodeSmith Generator&lt;/a&gt;
&lt;a href=&quot;https://www.codesmithtools.com/product/frameworks&quot;&gt;PLINQO Templates&lt;/a&gt; to fit
your custom development needs. In this example we will create a new Webpage for
each entity that we generate. This page will contain a LinqDataSource control
and an ASP.NET GridView control that will display data from a corresponding
entity that we generated. I always recommend that you use the PLINQO API when
writing templates that interact with code generated by PLINQO as this ensures
that the code generated has the proper names and associations and or types.&lt;/p&gt;
&lt;h2&gt;Find relevant templates to use as a starting point&lt;/h2&gt;
&lt;p&gt;The first step I always use is to determine if an
&lt;a href=&quot;https://www.codesmithtools.com/product/generator#templates&quot;&gt;existing template&lt;/a&gt;
has a close fit to your end goal so you save time writing the template. In this
example I choose to pick the Entity.Editable.cst template that is located in the
CSharp or VisualBasic\Internal Folder. This template is a very basic template
that shows how to query off of the underlying PLINQO Template API. After I had
determined that this was the template file I wanted to use, I created a new
folder next to the internal folder called CustomTemplates. This name really
doesn&apos;t matter but I choose this name to get started. Next, I copied the ASP.NET
sample templates out of the the ASP.NET samples folder and renamed it to
Entity.GridView respectively.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/generator-plinqo-template-explorer.png&quot; alt=&quot;PLINQO Template Explorer&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Once the files were renamed, I copied the import statements and properties that
were defined in the Entity.Editable.cst template and copied them to my
Entity.GridView templates. Since the paths were relative and used the same
hierarchy. I was able to get away with not updating the file paths. I removed
the extra properties and declarations that I knew I would not be needing. I was
left with the following in the three Entity.GridView templates:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;%@AssemblyName=&quot;Dbml&quot;Path=&quot;..\..\Common&quot;%&amp;gt;
&amp;lt;%@ImportNamespace=&quot;LinqToSqlShared.DbmlObjectModel&quot;%&amp;gt;
&amp;lt;%@PropertyCategory=&quot;1.Mapping&quot;Name=&quot;Database&quot; Type=&quot;LinqToSqlShared.DbmlObjectModel.Database&quot;Optional=&quot;False&quot; Description=&quot;Database instance. Must be set by parent template&quot;%&amp;gt;
&amp;lt;%@PropertyCategory=&quot;1.Mapping&quot;Name=&quot;Type&quot; Type=&quot;LinqToSqlShared.DbmlObjectModel.Type&quot;Optional=&quot;False&quot; Description=&quot;The Type instance for this entity. Must be set by parent template&quot;%&amp;gt;
&amp;lt;%@MapName=&quot;CSharpAlias&quot; Src=&quot;System-CSharpAlias.csmap&quot; Reverse=&quot;False&quot; Description=&quot;Convert system data types to c# alias&quot;%&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Design first in Visual Studio than create a dynamic template&lt;/h2&gt;
&lt;p&gt;Once this was done, I went inside of generated PLINQO Visual Studio solution and
created a new sample ASPX page that contained the an GridView control and a
LinqDataSource control. I then went and pasted this syntax for the SourceView,
Code Behind and the Designer file into the respected template after I had the
look and feel that I wanted. The EntityGridView.aspx.cst template looked like
this when I started:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;asp:GridView ID=&quot;GridView1&quot; runat=&quot;server&quot; AutoGenerateColumns=&quot;False&quot; DataKeyNames=&quot;CategoryId&quot; DataSourceID=&quot;LinqDataSource1&quot;&amp;gt;
  &amp;lt;Columns&amp;gt;
    &amp;lt;asp:BoundField DataField=&quot;CategoryId&quot; HeaderText=&quot;CategoryId&quot; SortExpression=&quot;CategoryId&quot;/&amp;gt;
    &amp;lt;asp:BoundField DataField=&quot;Name&quot; HeaderText=&quot;Name&quot; SortExpression=&quot;Name&quot;/&amp;gt;
    &amp;lt;asp:BoundField DataField=&quot;Description&quot; HeaderText=&quot;Description&quot; SortExpression=&quot;Description&quot;/&amp;gt;
  &amp;lt;/Columns&amp;gt;
&amp;lt;/asp:GridView&amp;gt;

&amp;lt;asp:LinqDataSource ID=&quot;LinqDataSource1&quot; runat=&quot;server&quot; ContextTypeName=&quot;PetShop.Web&quot; EnableDelete=&quot;True&quot; EnableInsert=&quot;True&quot; EnableUpdate=&quot;True&quot; EntityTypeName=&quot;&quot; TableName=&quot;Category&quot;&amp;gt;
&amp;lt;/asp:LinqDataSource&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I then started converting the static text below into a dynamic template. I
looked at the existing Entity.Editable.cst template to see how they queried the
API and then I updated the template to use the existing template properties.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;asp:GridView ID=&quot;GridView2&quot; runat=&quot;server&quot; AutoGenerateColumns=&quot;False&quot; DataKeyNames=&quot;CategoryId&quot; DataSourceID=&quot;LinqDataSource1&quot;&amp;gt;
  &amp;lt;Columns&amp;gt;
  &amp;lt;% foreach(Column column in Type.Columns){ %&amp;gt;
    &amp;lt;asp:BoundField DataField=&quot;&amp;lt;%=column.Member%&amp;gt;&quot; HeaderText=&quot;&amp;lt;%=column.Member%&amp;gt;&quot;
    &amp;lt;%if(column.IsReadOnly.HasValue &amp;amp;&amp;amp; column.IsReadOnly.Value){ %&amp;gt;ReadOnly=&quot;True&quot;&amp;lt;%}%&amp;gt; SortExpression=&quot;&amp;lt;%=column.Member%&amp;gt;&quot;/&amp;gt;
  &amp;lt;%}%&amp;gt;
  &amp;lt;/Columns&amp;gt;
&amp;lt;/asp:GridView&amp;gt;
&amp;lt;asp:LinqDataSource ID=&quot;LinqDataSource2&quot; runat=&quot;server&quot; ContextTypeName=&quot;&amp;lt;%=Database.ContextNamespace%&amp;gt;.&amp;lt;%=Database.Class%&amp;gt;&quot; EnableDelete=&quot;True&quot; EnableInsert=&quot;True&quot; EnableUpdate=&quot;True&quot; EntityTypeName=&quot;&quot; TableName=&quot;&amp;lt;%=Type.Name%&amp;gt;&quot;&amp;gt;
&amp;lt;/asp:LinqDataSource&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see we updated the template to use the specified Type that is defined
in the DBML File and not a table type that we specified using SchemaExplorer.
The &lt;em&gt;Type &lt;em&gt;Object should be thought of as the entity that has been generated
with the&lt;/em&gt;Columns&lt;/em&gt; Property as the properties in the entity. Each object has
properties defined that relate closely to that of which makes up the DBML markup.
Once the template is built you will have Intellisense to help you discover the
properties and methods that you can use.&lt;/p&gt;
&lt;h2&gt;Resolve DataKeyNames using IndexedEnumerable&lt;/h2&gt;
&lt;p&gt;As you can see that we still need to update the &lt;a href=&quot;https://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.gridview.datakeynames.aspx&quot;&gt;DataKeyNames&lt;/a&gt;
which takes a list of primary keys in a comma delimited String. For this we will
use the &lt;a href=&quot;https://www.codesmithtools.com/product/generator&quot;&gt;IndexedEnumerable&lt;/a&gt;
feature that was added in CodeSmith Generator 5.1 to make it easier to build up
the delimited list.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;%
string keyNames = String.Empty;
foreach(var column in Type.PrimaryKeyColumns.AsIndexedEnumerable()) {
  keyNames += String.Format(&quot;{0}{1}&quot;,column.Value.Member,!column.IsLast?&quot;,&quot;:String.Empty);
}%&amp;gt;

&amp;lt;asp:GridView ID=&quot;GridView2&quot; runat=&quot;server&quot; AutoGenerateColumns=&quot;False&quot; DataKeyNames=&quot;&amp;lt;%=keyNames%&amp;gt;&quot; DataSourceID=&quot;LinqDataSource1&quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now the template has been converted completely to use the PLINQO API.&lt;/p&gt;
&lt;h2&gt;How-to use control logic to exclude Foreign Key columns in the GridView&lt;/h2&gt;
&lt;p&gt;I&apos;ll quickly show you how one could add conditional statements into the
templates to ignore foreign keys. Inside of the main loop where you are adding
a BoundField control to the Columns Collection of the GridViewControl we will
update the syntax to the following.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;%
var association=Type.GetForeignKeyAssociation(column);
if (association==null) { %&amp;gt;
  &amp;lt;asp:BoundField DataField=&quot;&amp;lt;%=column.Member%&amp;gt;&quot; HeaderText=&quot;&amp;lt;%=column.Member%&amp;gt;&quot; &amp;lt;%if(column.IsReadOnly.HasValue &amp;amp;&amp;amp; column.IsReadOnly.Value) {%&amp;gt;ReadOnly=&quot;True&quot;&amp;lt;%}%&amp;gt; SortExpression=&quot;&amp;lt;%=column.Member%&amp;gt;&quot;/&amp;gt; &amp;lt;%} else {%&amp;gt; &amp;lt;!-- We are ignoring &amp;lt;%= column.Member %&amp;gt; as it is a foreign key. --&amp;gt;
&amp;lt;%} }%&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If an association doesn&apos;t exist for the current column than the method call to
GetForeignKeyAssociation will return null.&lt;/p&gt;
&lt;h2&gt;Creating the Master Template&lt;/h2&gt;
&lt;p&gt;Finally, we will update the Entities.cst template or you could also create your
own &lt;a href=&quot;https://www.codesmithtools.com/product/generator&quot;&gt;master template&lt;/a&gt;
which will drive our three newly added custom templates. The first step is to
register the sub templates inside of your master templates.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;%@Register Name=&quot;EntityGridViewSourceViewTemplate&quot; Template=&quot;CustomTemplates\Entity.GridView.aspx.cst&quot;%&amp;gt;
&amp;lt;%@Register Name=&quot;EntityGridViewCodeBehindTemplate&quot;Template=&quot;CustomTemplates\Entity.GridView.aspx.cs.cst&quot;%&amp;gt;
&amp;lt;%@Register Name=&quot;EntityGridViewDesignerTemplate&quot;Template=&quot;CustomTemplates\Entity.GridView.aspx.designer.cs.cst&quot;%&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then I looked through the Entities.cst template and found existing method calls
like CreateEntityBaseClass(database) inside of the Generate Method. I then added
my own method calledCreateEntityGridViews(database) and then copied and edited
an existing method (CreateInterfaces). This method will initialize and render
out the sub templates.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public void CreateEntityGridViews(Database database) {
  EntityGridViewSourceViewTemplate vt = this.Create&amp;lt;EntityGridViewSourceViewTemplate&amp;gt;();
  this.CopyPropertiesTo(vt);
  vt.Database = database;

  EntityGridViewCodeBehindTemplate bt = this.Create&amp;lt;EntityGridViewCodeBehindTemplate&amp;gt;();
  this.CopyPropertiesTo(bt);
  bt.Database = database;

  EntityGridViewDesignerTemplate dt = this.Create&amp;lt;EntityGridViewDesignerTemplate&amp;gt;();
  this.CopyPropertiesTo(dt);
  dt.Database = database;

  foreach(Table tableMap in database.Tables) {
    Stopwatch watch = Stopwatch.StartNew();
    Debug.WriteLine(String.Format(&quot;Creating Entity GridView for &apos;{0}&apos; ...&quot;, tableMap.Type.Name));

    string fileName = Path.Combine(BaseDirectory, tableMap.Type.Name + &quot;.aspx&quot;);
    vt.Type = tableMap.Type;
    vt.RenderToFile(fileName, true);
    Response.WriteLine(fileName);

    string parentFileName = Path.Combine(BaseDirectory, tableMap.Type.Name + &quot;.aspx.cs&quot;);
    bt.Type = tableMap.Type;
    bt.RenderToFile(parentFileName, fileName, true);
    Response.WriteLine(parentFileName);

    parentFileName = Path.Combine(BaseDirectory, tableMap.Type.Name + &quot;.aspx.designer.cs&quot;);
    dt.Type = tableMap.Type;
    dt.RenderToFile(parentFileName, fileName, true);
    Response.WriteLine(parentFileName);

    Debug.WriteLine(String.Format(&quot;Created &apos;{0}&apos; in {1} ms.&quot;, tableMap.Type.Name, watch.Elapsed.TotalMilliseconds.ToString()));
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Download Samples&lt;/h2&gt;
&lt;p&gt;Click on the following link to download the &lt;a href=&quot;/files/media/file/Extending.PLINQO.zip&quot;&gt;three sample templates&lt;/a&gt;.&lt;/p&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>How-to: Create a new Oracle User</title><link>https://blakeniemyjski.com/blog/how-to-create-a-new-oracle-user/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/how-to-create-a-new-oracle-user/</guid><pubDate>Wed, 12 Jan 2011 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Every now and then I need to create a new &lt;a href=&quot;https://www.oracle.com/index.html&quot;&gt;Oracle&lt;/a&gt;
user to test the &lt;a href=&quot;https://www.codesmithtools.com/product/generator&quot;&gt;CodeSmith Generator&lt;/a&gt;
OracleSchemaProvider. I wanted to outline the steps that I use to create a new
user as well as change the password of an existing user.&lt;/p&gt;
&lt;p&gt;The first step one needs to take is to create a new user but you may not
remember your login information if it has been a while. At this point I always
open up SQL Plus from the start menu and enter in the following user name when
prompted.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/ as sysdba
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This allows you to connect as the sysdba user without remembering any credentials.
Please note that the passwords and/or steps below may not be the best practice
for password security but all of my machines are behind a firewall and are on
virtual machines for testing with no access to the world wide web. At this step
you can change any of the user&apos;s passwords in oracle like the sys account. Here
is the command to reset the SYS users password.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;alter user sys identified by NEW_PASSWORD;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you are ready to create a new user by entering in the following statement.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;create user USER_NAME identified by NEW_PASSWORD;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next we will unlock the user and grant connection access so they can login.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;alter user USER_NAME account unlock;
grant connect to USER_NAME;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, I grant some more rights to the user so we can import backups and not
have to worry about permissions. &lt;em&gt;Please note that I wouldn&apos;t recommend doing
this on a production database. Please see this
&lt;a href=&quot;https://www.cuddletech.com/articles/oracle/node36.html&quot;&gt;documentation&lt;/a&gt; for
setting the proper permissions.&lt;/em&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;grant resource to USER_NAME;
alter user USER_NAME quota unlimited on users;
&lt;/code&gt;&lt;/pre&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>Microsoft Windows SharePoint Services 3.0: Software Development Kit (SDK)</title><link>https://blakeniemyjski.com/blog/microsoft-windows-sharepoint-services-3.0-software-development-kit-sdk/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/microsoft-windows-sharepoint-services-3.0-software-development-kit-sdk/</guid><pubDate>Tue, 28 Dec 2010 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The Windows SharePoint Services 3.0 software development kit (SDK) has been
released and contains conceptual overviews, programming tasks, samples, and
references to guide you in developing solutions based on Microsoft Windows
SharePoint Services 3.0.&lt;/p&gt;
&lt;h2&gt;Source&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.microsoft.com/downloads/details.aspx?familyid=05e0dd12-8394-402b-8936-a07fe8afaffd&amp;amp;displaylang=en&quot;&gt;Microsoft&lt;/a&gt;&lt;/p&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>Beginning Silverlight 4 in C#</title><link>https://blakeniemyjski.com/blog/beginning-silverlight-4-in-csharp/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/beginning-silverlight-4-in-csharp/</guid><pubDate>Sun, 08 Aug 2010 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/img/blog/review/silverlight.gif&quot; alt=&quot;Beginning Silverlight 4 in C#&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I have read many technical books over the years and this is one is one of those
books that you know you better keep handy. I really like how Robert has this
book laid out and how he introduces you to Silverlight. I&apos;ve been a .NET guy
since version 1.0. But like most developers, I haven&apos;t had the chance to really
dive into Microsoft&apos;s latest technologies like Silverlight until they have a
few versions released.&lt;/p&gt;
&lt;p&gt;I found that the book is laid out very nicely; the first few chapters will
introduce you to the tools (Visual Studio 2010). Then it will lead you into
what Silverlight is and how to use it. What I really liked about this book is
that it gave you background information about previous versions of Silverlight.
This is a great way to learn Silverlight as you soon realize the early gotchas
of the framework so if you are developing a new Silverlight application or
joining a team tasked with upgrading a Silverlight 2.0 application. You will be
ready to go.&lt;/p&gt;
&lt;p&gt;Beginning Silverlight 4 will walk you through data binding, layout management,
Silverlight controls and much more. It takes a slightly different but much
appreciated way of presenting information to the reader. Each section offers
two methods of implementation that contain pictures code samples to solve the
presented task; usually declaratively and in code. I like this approach because
it allows you to learn the approach that&apos;s fits your coding style.&lt;/p&gt;
&lt;p&gt;I would recommend this book to anyone wanting to learn Silverlight. You should
be knowledgeable in C# before picking up this book! My only recommendation for
Robert in a second edition is I wish the code that was presented in the book
was in color!&lt;/p&gt;
&lt;p&gt;Remember to pick up a copy on &lt;a href=&quot;https://www.amazon.com/Beginning-Silverlight-4-Robert-Lair/dp/1430229888&quot;&gt;Amazon&lt;/a&gt;
or &lt;a href=&quot;https://www.apress.com/book/view/9781430229889&quot;&gt;Apress&lt;/a&gt;.&lt;/p&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>How-to: Rename your generated CSLA properties</title><link>https://blakeniemyjski.com/blog/how-to-rename-your-generated-csla-properties/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/how-to-rename-your-generated-csla-properties/</guid><pubDate>Tue, 03 Aug 2010 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In the following example, I&apos;ll show you how to use the &lt;a href=&quot;https://www.codesmithtools.com/product/frameworks#csla&quot;&gt;CodeSmith Generator CSLA template&apos;s&lt;/a&gt;
powerful renaming options. Many of you might have a legacy database with those
ugly prefixes, or things you wish you could refactor but just can&apos;t. Luckily,
you&apos;re in luck as &lt;a href=&quot;https://www.codesmithtools.com/product/generator&quot;&gt;CodeSmith Generator&lt;/a&gt;
is going to save the day once again! By default we will strip any invalid
characters from your property names like prefixed digits etc... Please note
that these methods also work for renaming class names, but there are other
methods that we will cover later that are is the recommended way to specify a
class name.&lt;/p&gt;
&lt;p&gt;In this example, I&apos;m going to be renaming column names in the following
database table:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE [dbo].[Rename](
  [RenameID] [int] NOT NULL,
  [RenameName] [nvarchar](50) NOT NULL,
  [RenameA] [nvarchar](50) NOT NULL,
  [Name] [nvarchar](50) NOT NULL,
  [RenameAge] [int] NOT NULL,
 CONSTRAINT [PK_Rename] PRIMARY KEY CLUSTERED
(
   [RenameID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

GO
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/csla-sql-rename-table.png&quot; alt=&quot;Rename Table SQL&quot; /&gt;&lt;/p&gt;
&lt;p&gt;As you can see this would cover a few use cases when it comes to legacy database
tables. By default when this is generated the code will look like this. Please
note that all code has been simplified to show the results only.&lt;/p&gt;
&lt;h3&gt;CSharp&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;public System.Int32 RenameID { get; set; }
public System.String RenameName { get; set; }
public System.String RenameA { get; set; }
public System.String Name { get; set; }
public System.Int32 RenameAge { get; set; }
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Visual Basic&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;Public Property RenameID() As System.Int32
End Property

Public Property RenameName() As System.String
End Property

Public Property RenameA() As System.String
End Property

Public Property Name() As System.String
End Property

Public Property RenameAge() As System.Int32
End Property
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see the name&apos;s haven&apos;t changed at all. I&apos;ll now show you how to
rename the property names!&lt;/p&gt;
&lt;h2&gt;Method 1&lt;/h2&gt;
&lt;p&gt;This method can be a lot of work, but for renaming a few columns is the quickest
and easiest way to rename property names. Inside of the CSLA Template folder,
there will be a folder called Common. Open this folder and double click on the
KeywordRenameAlias file. In the future, we would like to make this a per project
setting as this method renames everything generated by the templates. To create
a per project setting use a different method or create a copy of the templates
for a specific project.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/csla-templates-common-folder.png&quot; alt=&quot;CSLA Templates Common Folder&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I&apos;m going to show you how to quickly rename the RenameAge column to Age and the
RenameA column to Location. As you can see there are already a few existing entries.
Well add the two entries and then hit the save icon. Then we will go back and
regenerate.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/csla-keyword-rename-alias-generator-map-editor.png&quot; alt=&quot;Keyword Rename Alias Generator Map Editor&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The generated code will be updated with our new names!&lt;/p&gt;
&lt;h3&gt;CSharp&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;public System.Int32 RenameID { get; set; }
public System.String RenameName { get; set; }
public System.String Location{ get; set; }
public System.String Name { get; set; }
public System.Int32 Age { get; set; }
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Visual Basic&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;Public Property RenameID() As System.Int32
End Property

Public Property RenameName() As System.String
End Property

Public Property Location() As System.String
End Property

Public Property Name() As System.String
End Property

Public Property Age() As System.Int32
End Property
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We will now revert the KeywordRenameAlias mapping file back to it&apos;s default values.&lt;/p&gt;
&lt;h2&gt;Method 2&lt;/h2&gt;
&lt;p&gt;This method works much like Method one, instead your settings are stored as
extended properties inside of SQL Server. We will open SQL Management Studio
and right click on the column in question and select properties. Then we will
add an extended property called CS_Alias with the value being the desired name.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/csla-sql-column-properties-renamea-alias.png&quot; alt=&quot;Column Properties - RenameA&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;EXEC sys.sp_addextendedproperty @name=N&apos;CS_Alias&apos;, @value=N&apos;Location&apos; , @level0type=N&apos;SCHEMA&apos;,@level0name=N&apos;dbo&apos;, @level1type=N&apos;TABLE&apos;,@level1name=N&apos;Rename&apos;, @level2type=N&apos;COLUMN&apos;,@level2name=N&apos;RenameA&apos;
GO
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Method 3&lt;/h2&gt;
&lt;p&gt;This method is very powerful and could save you a bunch of time! However, it
currently requires you to open up the source solution located in the Source folder,
make one change and recompile. We will make this much easier to do in a later
version. Inside of the CodeSmith.SchemaHelper Project, open up the Configuration
class. Then find the following line:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;NamingProperty = new NamingProperty {
  EntityNaming = EntityNaming.Singular,
  TableNaming = TableNaming.Mixed,
  ColumnNaming = ColumnNaming.Preserve
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will want to change the ColumnNaming value to this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;NamingProperty = new NamingProperty {
  EntityNaming = EntityNaming.Singular,
  TableNaming = TableNaming.Mixed,
  ColumnNaming = ColumnNaming.RemoveTablePrefix
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When ColumnNaming is set to RemoveTablePrefix, sit back and be amazed at how
much cleaner your properties become automatically! When this is set, Method 1
and Method 2 will run and take preference. After Method 1 or Method 2 run, if
the property name matches the cleaned table name and the renaming character
length is greater than one character(for uniqueness) then we will strip the
table name from the property name, then rerun Method 1 and Method 2. Please
note: any association names will not be renamed via this method.&lt;/p&gt;
&lt;p&gt;After regenerating, your properties should look something like this:&lt;/p&gt;
&lt;h3&gt;CSharp&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;public System.Int32 Identification { get; set; }
public System.String Name1 { get; set; }
public System.String RenameA { get; set; }
public System.String Name2 { get; set; }
public System.Int32 Age { get; set; }
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Visual Basic&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;Public Property Identification() As System.Int32
End Property

Public Property Name1() As System.String
End Property

Public Property RenameA() As System.String
End Property

Public Property Name2 () As System.String
End Property

Public Property Age() As System.Int32
End Property
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You might be wondering why RenameID was changed into Identification. This is
because the table prefix was stripped leaving ID, ID is predefined in Method 1
by default, and thus was renamed to Identification. As you can see if your
column name is RenameA, then you will need to use Method 1 or Method 2 as we
won&apos;t rename anything with this method that would result in a really short name.&lt;/p&gt;
&lt;p&gt;Don&apos;t worry, we didn&apos;t change the generated stored procedures or parameterized
Sql column names because of these methods!!!&lt;/p&gt;
&lt;h3&gt;CSharp&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;const string commandText = &quot;INSERT INTO [dbo].[Rename] ([RenameID], [RenameName], [RenameA], [Name], [RenameAge]) VALUES (@p_RenameID, @p_RenameName, @p_RenameA, @p_Name, @p_RenameAge)&quot;;

using (SqlConnection connection = new SqlConnection(ADOHelper.ConnectionString)) {
  connection.Open();
  using(SqlCommand command = new SqlCommand(commandText, connection)) {
    command.Parameters.AddWithValue(&quot;@p_RenameID&quot;, this.Identification);
    command.Parameters.AddWithValue(&quot;@p_RenameName&quot;, this.Name1);
    command.Parameters.AddWithValue(&quot;@p_RenameA&quot;, this.RenameA);
    command.Parameters.AddWithValue(&quot;@p_Name&quot;, this.Name2);
    command.Parameters.AddWithValue(&quot;@p_RenameAge&quot;, this.Age);

    //result: The number of rows changed, inserted, or deleted. -1 for select statements; 0 if no rows were affected, or the statement failed.
    int result = command.ExecuteNonQuery();
    if (result == 0)

    throw new DBConcurrencyException(&quot;The entity is out of date on the client. Please update the entity and try again. This could also be thrown if the sql statement failed to execute.&quot;);
    LoadProperty(_originalIdentificationProperty, this.Identification);
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Visual Basic&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;Const commandText As String = &quot;INSERT INTO [dbo].[Rename] ([RenameID], [RenameName], [RenameA], [Name], [RenameAge]) VALUES (@p_RenameID, @p_RenameName, @p_RenameA, @p_Name, @p_RenameAge)&quot;

Using connection As New SqlConnection(ADOHelper.ConnectionString)
  connection.Open()

  Using command As New SqlCommand(commandText, connection)
    command.Parameters.AddWithValue(&quot;@p_RenameID&quot;, Me.Identification)
    command.Parameters.AddWithValue(&quot;@p_RenameName&quot;, Me.Name1)
    command.Parameters.AddWithValue(&quot;@p_RenameA&quot;, Me.RenameA)
    command.Parameters.AddWithValue(&quot;@p_Name&quot;, Me.Name2)
    command.Parameters.AddWithValue(&quot;@p_RenameAge&quot;, Me.Age)

    &apos;result: The number of rows changed, inserted, or deleted. -1 for select statements; 0 if no rows were affected, or the statement failed.
    Dim result As Integer = command.ExecuteNonQuery()
    If (result = 0) Then
      Throw new DBConcurrencyException(&quot;The entity is out of date on the client. Please update the entity and try again. This could also be thrown if the sql statement failed to execute.&quot;)
    End If
  End Using

  LoadProperty(_originalIdentificationProperty, Me.Identification)
End Using
&lt;/code&gt;&lt;/pre&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>How-to: Use a Stored Procedure extended property to preserve custom logic in generated CSLA code.</title><link>https://blakeniemyjski.com/blog/how-to-use-a-stored-procedure-extended-property-to-preserve-custom-logic-in-generated-csla-code/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/how-to-use-a-stored-procedure-extended-property-to-preserve-custom-logic-in-generated-csla-code/</guid><pubDate>Fri, 30 Jul 2010 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In the &lt;a href=&quot;/programming/csla/how-to-use-partial-methods-to-preserve-custom-csla-code-and-promote-active-regeneration/-promote-active-regeneration.aspx&quot;&gt;previous article&lt;/a&gt;
on how to use the partial methods in the &lt;a href=&quot;https://www.codesmithtools.com/product/frameworks#csla&quot;&gt;CodeSmith Generator CSLA templates&lt;/a&gt;
to preserve custom code mostly focused on the code side of things. For those of
you who might be using Stored Procedures might be wondering... How do I
accomplish preserving my custom stored procedure logic? Don&apos;t worry, CodeSmith
already though of this and didn&apos;t leave you out in the dark! The CSLA templates
by default will not automatically execute the generated stored procedures but
it can! To do this you need to set the AutoExecuteStoredProcedures property to
True. Now, when your database related metadata changes so will your stored
procedures.&lt;/p&gt;
&lt;p&gt;This is a great feature until you need to implement custom logic into your
stored procedure. By default we will generate logic that will prevent a stored
procedure from being overwritten if you have AutoExecuteStoredProcedures turned
to true or you try to drop the stored procedure. Here is the example that we
are going to be modifying that will result in the same exact output as our
previous sample.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SET NUMERIC_ROUNDABORT OFF
GO
SET ANSI_PADDING, ANSI_WARNINGS, CONCAT_NULL_YIELDS_NULL, ARITHABORT, QUOTED_IDENTIFIER, ANSI_NULLS ON
GO
IF EXISTS (SELECT * FROM tempdb..sysobjects WHERE id=OBJECT_ID(&apos;tempdb..#tmpErrors&apos;)) DROP TABLE #tmpErrors
GO
CREATE TABLE #tmpErrors (Error int)
GO
SET XACT_ABORT ON
GO
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
GO
BEGIN TRANSACTION
GO
PRINT N&apos;Dropping CSLA_Calendar_Select&apos;
GO
IF EXISTS(SELECT 1 FROM fn_listextendedproperty (NULL, &apos;SCHEMA&apos;, &apos;dbo&apos;, &apos;PROCEDURE&apos;, &apos;CSLA_Calendar_Select&apos;, default, default) WHERE name = &apos;CustomProcedure&apos; and value = &apos;1&apos;)
BEGIN
    RAISERROR (&apos;The procedure CSLA_Calendar_Select has an Extended Property &quot;CustomProcedure&quot; which means is has been customized. Please review and remove the property if you wish to drop the procedure.&apos;,16,1)
    INSERT INTO #tmpErrors (Error) SELECT 1
END
GO

IF OBJECT_ID(N&apos;CSLA_Calendar_Select&apos;) IS NOT NULL
  DROP PROCEDURE CSLA_Calendar_Select

GO
IF @@ERROR!=0 AND @@TRANCOUNT&amp;gt;0 ROLLBACK TRANSACTION
GO

IF @@TRANCOUNT=0 BEGIN INSERT INTO #tmpErrors (Error) SELECT 1 BEGIN TRANSACTION END
GO

IF EXISTS (SELECT * FROM #tmpErrors) ROLLBACK TRANSACTION
GO

IF @@TRANCOUNT&amp;gt;0 BEGIN
PRINT &apos;The stored procedure drop has succeeded&apos;
COMMIT TRANSACTION
END
ELSE PRINT &apos;The stored procedure drop has failed&apos;
GO

DROP TABLE #tmpErrors
GO

--region [dbo].[CSLA_Calendar_Select]

------------------------------------------------------------------------------------------------------------------------
-- Generated By:   Blake Niemyjski using CodeSmith: v5.2.3, CSLA Templates: v3.0.0.1888, CSLA Framework: v3.8.4
-- Procedure Name: [dbo].[CSLA_Calendar_Select]
------------------------------------------------------------------------------------------------------------------------

SET NUMERIC_ROUNDABORT OFF
GO
SET ANSI_PADDING, ANSI_WARNINGS, CONCAT_NULL_YIELDS_NULL, ARITHABORT, QUOTED_IDENTIFIER, ANSI_NULLS ON
GO
IF EXISTS (SELECT * FROM tempdb..sysobjects WHERE id=OBJECT_ID(&apos;tempdb..#tmpErrors&apos;)) DROP TABLE #tmpErrors
GO
CREATE TABLE #tmpErrors (Error int)
GO
SET XACT_ABORT ON
GO
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
GO
BEGIN TRANSACTION
GO

PRINT N&apos;Creating [dbo].[CSLA_Calendar_Select]&apos;
GO

IF EXISTS(SELECT 1 FROM fn_listextendedproperty (NULL, &apos;SCHEMA&apos;, &apos;dbo&apos;, &apos;PROCEDURE&apos;, &apos;CSLA_Calendar_Select&apos;, default, default) WHERE name = &apos;CustomProcedure&apos; and value = &apos;1&apos;)
    BEGIN
        RAISERROR (&apos;The procedure [dbo].[CSLA_Calendar_Select] has an Extended Property &quot;CustomProcedure&quot; which means is has been customized. Please review and remove the property if you wish to create the stored procedure.&apos;,16,1)
        INSERT INTO #tmpErrors (Error) SELECT 1
    END
GO

CREATE PROCEDURE [dbo].[CSLA_Calendar_Select]
  @p_ID nvarchar(50) = NULL,
  @p_CalendarName nvarchar(50) = NULL,
  @p_Name nvarchar(50) = NULL,
  @p_EventStart datetime = NULL,
  @p_EventEnd datetime = NULL,
  @p_Resource nvarchar(150) = NULL
AS

SET TRANSACTION ISOLATION LEVEL READ COMMITTED

SELECT
  [ID],
  [CalendarName],
  [Name],
  [EventStart],
  [EventEnd],
  [Resource]
FROM
    [dbo].[Calendar]
WHERE
  ([ID] = @p_ID OR @p_ID IS NULL)
  AND ([CalendarName] = @p_CalendarName OR @p_CalendarName IS NULL)
  AND ([Name] = @p_Name OR @p_Name IS NULL)
  AND ([EventStart] = @p_EventStart OR @p_EventStart IS NULL)
  AND ([EventEnd] = @p_EventEnd OR @p_EventEnd IS NULL)
  AND ([Resource] = @p_Resource OR @p_Resource IS NULL)

GO
IF @@ERROR!=0 AND @@TRANCOUNT&amp;gt;0 ROLLBACK TRANSACTION
GO

IF @@TRANCOUNT=0 BEGIN INSERT INTO #tmpErrors (Error) SELECT 1 BEGIN TRANSACTION END
GO

IF EXISTS (SELECT * FROM #tmpErrors) ROLLBACK TRANSACTION
GO
IF @@TRANCOUNT&amp;gt;0 BEGIN
PRINT &apos;Stored procedure creation succeeded.&apos;
COMMIT TRANSACTION
END
ELSE PRINT &apos;Stored procedure creation failed.&apos;
GO
DROP TABLE #tmpErrors
GO

--endregion
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&apos;s open up SQL Management Studio and run the script above. Once you run the
query above, you can see that the table was dropped and recreated.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Dropping CSLA_Calendar_Select
The stored procedure drop has succeeded
Creating [dbo].[CSLA_Calendar_Select]
Stored procedure creation succeeded.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that you can see that the stored procedure is being overwritten, let&apos;s
modify the stored procedure to return the same results as our previous example.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ALTER PROCEDURE [dbo].[CSLA_Calendar_Select]
  @p_ID nvarchar(50) = NULL,
  @p_CalendarName nvarchar(50) = NULL,
  @p_Name nvarchar(50) = NULL,
  @p_EventStart datetime = NULL,
  @p_EventEnd datetime = NULL,
  @p_Resource nvarchar(150) = NULL
AS

SET TRANSACTION ISOLATION LEVEL READ COMMITTED

SELECT
  [ID],
  [CalendarName],
  [Name],
  [EventStart],
  [EventEnd],
  [Resource]
FROM
    [dbo].[Calendar]
WHERE
  ([ID] = @p_ID OR @p_ID IS NULL)
  AND ([CalendarName] = @p_CalendarName OR @p_CalendarName IS NULL)
  AND ([Name] = @p_Name OR @p_Name IS NULL)
  AND ([EventStart] = @p_EventStart OR @p_EventStart IS NULL)
  AND ([EventEnd] = @p_EventEnd OR @p_EventEnd IS NULL)
  AND ([Resource] = @p_Resource OR @p_Resource IS NULL)
  AND [EventStart] &amp;gt;= CONVERT(varchar,DATEADD(MONTH,DATEDIFF(MONTH,0,GETDATE()),0),101)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since you modified the stored procedure, you want to mark it as a custom Stored
Procedure. If you choose not to do this, then you will need to modify this
stored procedure any time you wish to regenerate or drop and recreate this
stored procedure. This can be done by right clicking on the stored procedure,
selecting properties and then selecting the Extended Properties node. You will
want to create a new extended property with a name of CustomProcedure and a
value of 1. The following SQL Script will also create the extended property.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/csla-calendar-select-sql-stored-procedure-properties.png&quot; alt=&quot;Stored Procedure Properties&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;EXEC sys.sp_addextendedproperty @name=N&apos;CustomProcedure&apos;, @value=N&apos;1&apos; , @level0type=N&apos;SCHEMA&apos;,@level0name=N&apos;dbo&apos;, @level1type=N&apos;PROCEDURE&apos;,@level1name=N&apos;CSLA_Calendar_Select&apos;
GO
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now when you execute the SQL script to drop and create the stored procedure it
will error out and preserve your changes!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Dropping CSLA_Calendar_Select
Msg 50000, Level 16, State 1, Line 3
The procedure CSLA_Calendar_Select has an Extended Property &quot;CustomProcedure&quot; which means is has been customized. Please review and remove the property if you wish to drop the procedure.

(1 row(s) affected)
The stored procedure drop has failed
Creating [dbo].[CSLA_Calendar_Select]
Msg 50000, Level 16, State 1, Line 4
The procedure [dbo].[CSLA_Calendar_Select] has an Extended Property &quot;CustomProcedure&quot; which means is has been customized. Please review and remove the property if you wish to create the stored procedure.

(1 row(s) affected)
Msg 2714, Level 16, State 3, Procedure CSLA_Calendar_Select, Line 13
There is already an object named &apos;CSLA_Calendar_Select&apos; in the database.

(1 row(s) affected)
Stored procedure creation failed.
&lt;/code&gt;&lt;/pre&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>How-to: Use partial methods to preserve custom CSLA code and promote active regeneration.</title><link>https://blakeniemyjski.com/blog/how-to-use-partial-methods-to-preserve-custom-csla-code-and-promote-active-regeneration/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/how-to-use-partial-methods-to-preserve-custom-csla-code-and-promote-active-regeneration/</guid><pubDate>Thu, 29 Jul 2010 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;One of the best features of code generation is the reality of being able to
actively generate you code while preserving custom changes. This allows you to
implement custom logic while constantly making changes to your metadata and
getting the latest template changes.
&lt;a href=&quot;https://www.codesmithtools.com/product/generator&quot;&gt;CodeSmith Generator&lt;/a&gt; has
worked hard on making this happen in both the
&lt;a href=&quot;https://www.codesmithtools.com/product/frameworks&quot;&gt;PLINQO&lt;/a&gt; and
&lt;a href=&quot;https://www.codesmithtools.com/product/frameworks#csla&quot;&gt;CSLA Templates&lt;/a&gt;.
The CSLA templates offer many partial method overrides to make your life easier.
It is recommended that you place any partial methods that you implement in the
non-generated partial class; this is always the ClassName.cs or ClassName.vb
source code file.&lt;/p&gt;
&lt;p&gt;A list of partial methods can be found at the bottom of the every generated
partial class. Here is an example of the generated partial methods.&lt;/p&gt;
&lt;h3&gt;CSharp&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#region DataPortal partial methods
partial void OnCreating(ref bool cancel);
partial void OnCreated();
partial void OnFetching(CalendarCriteria criteria, ref bool cancel);
partial void OnFetched();
partial void OnMapping(SafeDataReader reader, ref bool cancel);
partial void OnMapped();
partial void OnUpdating(ref bool cancel);
partial void OnUpdated();
partial void OnAddNewCore(ref Calendar item, ref bool cancel);
#endregion
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Visual Basic&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#Region &quot;DataPortal partial methods&quot;

Partial Private Sub OnCreating(ByRef cancel As Boolean)
End Sub

Partial Private Sub OnCreated()
End Sub

Partial Private Sub OnFetching(ByVal criteria As CalendarCriteria, ByRef cancel As Boolean)
End Sub

Partial Private Sub OnFetched()
End Sub

Partial Private Sub OnMapping(ByVal reader As SafeDataReader, ByRef cancel As Boolean)
End Sub

Partial Private Sub OnMapped()
End Sub

Partial Private Sub OnUpdating(ByRef cancel As Boolean)
End Sub

Partial Private Sub OnUpdated()
End Sub

Partial Private Sub OnAddNewCore(ByVal item As Calendar, ByRef cancel As Boolean)
End Sub

#End Region
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example we will take an EditableRootList called CalendarList, whose
purpose is to hold a list of Calendar business objects. The default generated
code will select all calendar entries with a specific criterion. The code below
is the code that is generated in the CalendarList.DataAccess partial class:&lt;/p&gt;
&lt;h3&gt;CSharp&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;private void DataPortal_Fetch(CalendarCriteria criteria)
{
    bool cancel = false;
    OnFetching(criteria, ref cancel);
    if (cancel) return;

    RaiseListChangedEvents = false;

    // Fetch Child objects.
    string commandText = String.Format(&quot;SELECT [ID], [CalendarName], [Name], [EventStart], [EventEnd], [Resource] FROM [dbo].[Calendar] {0}&quot;, ADOHelper.BuildWhereStatement(criteria.StateBag));
    using (SqlConnection connection = new SqlConnection(ADOHelper.ConnectionString))
    {
        connection.Open();
        using (SqlCommand command = new SqlCommand(commandText, connection))
        {
            command.Parameters.AddRange(ADOHelper.SqlParameters(criteria.StateBag));

            using(var reader = new SafeDataReader(command.ExecuteReader()))
            {
                if(reader.Read())
                {
                    do
                    {
                        this.Add(new Calendar(reader));
                    } while(reader.Read());
                }
            }
        }
    }

    RaiseListChangedEvents = true;

    OnFetched();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Visual Basic&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;Private Shadows Sub DataPortal_Fetch(ByVal criteria As CalendarCriteria)
    Dim cancel As Boolean = False
    OnFetching(criteria, cancel)
    If (cancel) Then
        Return
    End If

    RaiseListChangedEvents = False

    &apos; Fetch Child objects.
    Dim commandText As String = String.Format(&quot;SELECT [ID], [CalendarName], [Name], [EventStart], [EventEnd], [Resource] FROM [dbo].[Calendar] {0}&quot;, ADOHelper.BuildWhereStatement(criteria.StateBag))
    Using connection As New SqlConnection(ADOHelper.ConnectionString)
        connection.Open()
        Using command As New SqlCommand(commandText, connection)
            command.Parameters.AddRange(ADOHelper.SqlParameters(criteria.StateBag))
            Using reader As SafeDataReader = New SafeDataReader(command.ExecuteReader())
                If reader.Read() Then
                    Do
                        Me.Add(New Calendar(reader))
                    Loop While reader.Read()
                End If
            End Using
        End Using
    End Using

    RaiseListChangedEvents = True

    OnFetched()
End Sub
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see that this isn&apos;t optimal because we only want to select all
Calendar entries from the first of the current month. In the real word we would
only want to select maybe a month of two or data... This can easily be done by
overriding the partial method and modifying the sql statement. We will now go
to the partial non generated class (CalendarList.cs or CalendarList.vb):&lt;/p&gt;
&lt;h3&gt;CSharp&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;#region Custom Data Access

partial void OnFetching(CalendarCriteria criteria, ref bool cancel)
{
    RaiseListChangedEvents = false;

    // Fetch Child objects.
    string commandText = String.Format(&quot;SELECT [ID], [CalendarName], [Name], [EventStart], [EventEnd], [Resource] FROM [dbo].[Calendar] {0} AND [EventStart] &amp;gt;= CONVERT(varchar,DATEADD(MONTH,DATEDIFF(MONTH,0,GETDATE()),0),101)&quot;, ADOHelper.BuildWhereStatement(criteria.StateBag));
    using (SqlConnection connection = new SqlConnection(ADOHelper.ConnectionString))
    {
        connection.Open();
        using (SqlCommand command = new SqlCommand(commandText, connection))
        {
            command.Parameters.AddRange(ADOHelper.SqlParameters(criteria.StateBag));

            using (var reader = new SafeDataReader(command.ExecuteReader()))
            {
                if (reader.Read())
                {
                    do
                    {
                        this.Add(new Calendar(reader));
                    } while (reader.Read());
                }
            }
        }
    }

    RaiseListChangedEvents = true;

    // Cancel the existing Dal method.
    cancel = true;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Visual Basic&lt;/h3&gt;
&lt;p&gt;#Region &quot;Custom Data Access&quot;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#Region &quot;Custom Data Access&quot;
Private Sub OnFetching(ByVal criteria As CategoryCriteria, ByRef cancel As Boolean)
    RaiseListChangedEvents = False

    &apos; Fetch Child objects.
    Dim commandText As String = String.Format(&quot;SELECT [ID], [CalendarName], [Name], [EventStart], [EventEnd], [Resource] FROM [dbo].[Calendar] {0} AND [EventStart] &amp;gt;= CONVERT(varchar,DATEADD(MONTH,DATEDIFF(MONTH,0,GETDATE()),0),101)&quot;, ADOHelper.BuildWhereStatement(criteria.StateBag))
    Using connection As New SqlConnection(ADOHelper.ConnectionString)
        connection.Open()
        Using command As New SqlCommand(commandText, connection)
            command.Parameters.AddRange(ADOHelper.SqlParameters(criteria.StateBag))
            Using reader As SafeDataReader = New SafeDataReader(command.ExecuteReader())
                If reader.Read() Then
                    Do
                        Me.Add(New Calendar(reader))
                    Loop While reader.Read()
                End If
            End Using
        End Using
    End Using

    RaiseListChangedEvents = True

    &apos;Cancel the existing Dal method.
    cancel = True
End Sub
#End Region
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see all we needed to do was implement the OnFetching partial method
and override the sql query that was then sent to the database server. You can
do anything you want in this partial method. The last thing to do is set the
reference cancel variable to true if you wish to have everything after the
partial method call ignored. If you set this to false then the existing
generated code will also run. In this case, we want to cancel out. A scenario
where you would want to return false, is if you wanted to do some processing
before accessing the database.&lt;/p&gt;
&lt;p&gt;If you come across something that you wish had a partial method, please log it
on the
&lt;a href=&quot;https://github.com/codesmithtools/Templates/issues&quot;&gt;CodeSmith template issue tracker&lt;/a&gt;.&lt;/p&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>Breaking CSLA 4.0 property changes when using private backing fields.</title><link>https://blakeniemyjski.com/blog/breaking-csla-4.0-property-changes-when-using-private-backing-fields/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/breaking-csla-4.0-property-changes-when-using-private-backing-fields/</guid><pubDate>Wed, 28 Jul 2010 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I came across this &lt;a href=&quot;https://www.lhotka.net/cslabugs/edit_bug.aspx?id=813&quot;&gt;bug last night&lt;/a&gt;,
which was occurring right as &lt;a href=&quot;https://www.lhotka.net/weblog/CSLA4Release.aspx&quot;&gt;CSLA 4&lt;/a&gt;
was released. It had to deal with the
&lt;a href=&quot;https://www.codesmithtools.com/product/frameworks#csla&quot;&gt;CodeSmith CSLA templates&lt;/a&gt;
when you set &lt;code&gt;UseMemberVariables&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt;. The error is quite obscure and not
very intuitive as the signs of a bug are only found when you check to see if
your business object IsValid. The scenario in this case is when you set a
required managed property, the rules never fully evaluate the set value. A
bunch of other side effects could occur as well. Here is the error you could
receive:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CategoryId required

Expected:True

But was:False
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here is the code that was causing the error:&lt;/p&gt;
&lt;h2&gt;CSharp&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;protected override void AddBusinessRules()
{
  BusinessRules.AddRule(new Csla.Rules.CommonRules.Required(_categoryIdProperty));
}

private static readonly PropertyInfo&amp;lt;System.String&amp;gt; _categoryIdProperty = RegisterProperty&amp;lt;System.String&amp;gt;(p =&amp;gt; p.CategoryId, String.Empty);
private System.String _categoryId = _categoryIdProperty.DefaultValue;
public System.String CategoryId
{
  get { return GetProperty(_categoryIdProperty, _categoryId); }
  set { SetProperty(_categoryIdProperty, ref _categoryId, value); }
}
[Test]
private void CreateCategory(string categoryID)
{
  Category category = Category.NewCategory();
  category.CategoryId = categoryID;
  Assert.IsTrue(category.IsValid, category.BrokenRulesCollection.ToString());
  category = category.Save();
  Assert.IsTrue(category.CategoryId == categoryID);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Visual Basic&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;Protected Overrides Sub AddBusinessRules()
  BusinessRules.AddRule(New Csla.Rules.CommonRules.Required(_categoryIdProperty))
End Sub

Private Shared ReadOnly _categoryIdProperty As PropertyInfo(Of System.String) = RegisterProperty(Of System.String)(Function(p As Category) p.CategoryId, String.Empty)
Private _categoryId As System.String = _categoryIdProperty.DefaultValue
Public Property CategoryId() As System.String

  Get
    Return GetProperty(_categoryIdProperty, _categoryId)
  End Get

  Set (value As System.String)
    SetProperty(_categoryIdProperty, _categoryId, value)

  End Set
End Property

&amp;lt;test()&amp;gt; _
Private Sub CreateCategory(ByVal categoryID As String)
  Dim category As Category = PetShop.Tests.ParameterizedSQL.Category.NewCategory()
  category.CategoryId = categoryID
  Assert.IsTrue(category.IsValid, category.BrokenRulesCollection.ToString())
  category = category.Save()
  Assert.IsTrue(category.CategoryId = categoryID)
End Sub
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see the code looks and works fine in CSLA 3.8 but it isn&apos;t the case
in 4.0. Once I tracked down what was causing the error, I saw that there wasn&apos;t
any checks in CSLA4 to see if the property had the &lt;code&gt;RelationshipTypes.PrivateField&lt;/code&gt;
overload when you are calling the &lt;code&gt;GetProperty&lt;/code&gt; or &lt;code&gt;SetProperty&lt;/code&gt; methods with a
backing field. The long term fix when using private member backing fields is to
set the &lt;code&gt;RelationshipTypes&lt;/code&gt; to &lt;code&gt;RelationshipTypes.PrivateField&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;CSharp&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;private static readonly PropertyInfo&amp;lt;System.String&amp;gt; _categoryIdProperty = RegisterProperty&amp;lt;System.String&amp;gt;(p =&amp;gt; p.CategoryId, String.Empty, RelationshipTypes.PrivateField);
private System.String _categoryId = _categoryIdProperty.DefaultValue;
public System.String CategoryId
{
  get { return GetProperty(_categoryIdProperty, _categoryId); }
  set { SetProperty(_categoryIdProperty, ref _categoryId, value); }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Visual Basic&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;Private Shared ReadOnly _categoryIdProperty As PropertyInfo(Of System.String) = RegisterProperty(Of System.String)(Function(p As Category) p.CategoryId, String.Empty, RelationshipTypes.PrivateField)
Private _categoryId As System.String = _categoryIdProperty.DefaultValue
Public Property CategoryId() As System.String
  Get
    Return GetProperty(_categoryIdProperty, _categoryId)
  End Get

  Set (value As System.String)
    SetProperty(_categoryIdProperty, _categoryId, value)
  End Set
End Property
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This has been fixed in a nightly build of the CodeSmith CSLA Templates and will
be in &lt;a href=&quot;https://github.com/codesmithtools/Templates/releases/tag/CSLA-3.0.1&quot;&gt;version 3.0.1&lt;/a&gt;
of the CodeSmith CSLA Templates. The CSLA team will also be releasing version
4.0.1 of CSLA sometime soon. If you don&apos;t have this overload set in CSLA 4.0.1
then you will receive the following exception:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Properties with private backing fields must be marked as
RelationshipTypes.PrivateField&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If you are using the CodeSmith CSLA templates, remember to update to the latest
templates and regenerate. If you are not, well you should be using the
CodeSmith CSLA Templates as it is about to &lt;strong&gt;save you a ton of time&lt;/strong&gt; adding
the RelationshipTypes.PrivateField overload to all of your properties!&lt;/p&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>How-to: Upgrade a Community Server website thats running  2.1, 2007 or 2008 site to Community Server 2008 or Telligent Community 5.5</title><link>https://blakeniemyjski.com/blog/how-to-upgrade-a-community-server-website-thats-running-2.1-2007-or-2008-site-to-community-server-2008-or-telligent-community-5.5/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/how-to-upgrade-a-community-server-website-thats-running-2.1-2007-or-2008-site-to-community-server-2008-or-telligent-community-5.5/</guid><pubDate>Tue, 27 Jul 2010 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I have done a few of these upgrades for a few big sites like bink.nu
and &lt;a href=&quot;https://sober.com&quot;&gt;Sober.com&lt;/a&gt;. Here is the safest way without a ton of
headaches. Please contact me if you need help, I&apos;m willing to do the upgrade
for people as well... Please do SQL backups after every step!&lt;/p&gt;
&lt;h2&gt;Database Instructions&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Upgrade from Community Server 2.1 to 2007.0&lt;/li&gt;
&lt;li&gt;Create a SQL backup.&lt;/li&gt;
&lt;li&gt;Run &lt;a href=&quot;https://www.red-gate.com/products/sql-development/sql-compare/&quot;&gt;SQL Compare&lt;/a&gt; against a fresh database of cs2007 to the upgraded database and make sure everything transferred correctly.&lt;/li&gt;
&lt;li&gt;If there were changes, create a SQL backup.&lt;/li&gt;
&lt;li&gt;Run the upgrade from 2007.0 to the last service pack release of 2007. (I think it was SP3).&lt;/li&gt;
&lt;li&gt;Create a SQL backup.&lt;/li&gt;
&lt;li&gt;Upgrade from CS 2007 SP3 to CS 2008.&lt;/li&gt;
&lt;li&gt;Create a SQL backup.&lt;/li&gt;
&lt;li&gt;Run &lt;a href=&quot;https://www.red-gate.com/products/sql-development/sql-compare/&quot;&gt;SQL Compare&lt;/a&gt; against a freshly created CS 2008 database against the upgrade database to ensure everything is correct.&lt;/li&gt;
&lt;li&gt;If there were changes, create a SQL backup.&lt;/li&gt;
&lt;li&gt;Upgrade to CS 2008.5 using the upgrade utility.&lt;/li&gt;
&lt;li&gt;Create a SQL backup.&lt;/li&gt;
&lt;li&gt;Upgrade to latest service pack.&lt;/li&gt;
&lt;li&gt;Run &lt;a href=&quot;https://www.red-gate.com/products/sql-development/sql-compare/&quot;&gt;SQL Compare&lt;/a&gt; against a freshly created CS 2008.5 SP3 database against the upgrade database to ensure everything is correct.&lt;/li&gt;
&lt;li&gt;--------------- Continue here if you are going to the latest version of TC 5.5-------&lt;/li&gt;
&lt;li&gt;Upgrade to TC 5.5.&lt;/li&gt;
&lt;li&gt;Run &lt;a href=&quot;https://www.red-gate.com/products/sql-development/sql-compare/&quot;&gt;SQL Compare&lt;/a&gt; against a freshly created CS TC 5.5 database against the upgrade database to ensure everything is correct.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Site File upgrade&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;The important thing here is that you need to do the upgrade process in order and make notes of changes. When doing this for a live database, do a run through of everything and make notes of things changing or failing and get them resolved a head of time.&lt;/li&gt;
&lt;li&gt;For the upgrade to 2007, read the readme and make sure you copy the storage files into the correct spot.
&lt;ol&gt;
&lt;li&gt;I would extract the complete 2007 site files and copy the photo storage folders and everything to the correct spot.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;To upgrade to 2008, you need to run the upgrade utility.
&lt;ol&gt;
&lt;li&gt;I would extract the complete 2008 site files and read the read me. I would copy the site files to the correct spots as they were.&lt;/li&gt;
&lt;li&gt;Once you are running this tool (somewhat buggy), I would make sure that you check the log file and copy over the file storage in the utilities folder to the file storage folder on the destination site.&lt;/li&gt;
&lt;li&gt;Then I would copy the old file storage folders to the same locations in the new site... I&apos;ve had cases where they are hard linked and then you get missing files. I would only ever delete these folders after completely upgraded and you have them backed up...&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;To upgrade to 2008.5, I&apos;m pretty sure it&apos;s using the upgrade utility again, go back to step 3.&lt;/li&gt;
&lt;li&gt;To upgrade to TC 5.5, you need to extract the clean website (to a new directory).
&lt;ol&gt;
&lt;li&gt;Next, copy over only the file storage folder (leave the old file storage folders \photos, \blogs, \forums\ for now (just to be safe).&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Final Steps&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Copy the files from steps 4 or 5 to the web server.&lt;/li&gt;
&lt;li&gt;Replace permissions on all upgraded files.&lt;/li&gt;
&lt;li&gt;You will need to make sure that all of your custom scripts are updated accordingly.&lt;/li&gt;
&lt;li&gt;Make sure all images and links are working.&lt;/li&gt;
&lt;/ol&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>How-to: Preserve column names in generated PLINQO entities</title><link>https://blakeniemyjski.com/blog/how-to-preserve-column-names-in-generated-plinqo-entities/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/how-to-preserve-column-names-in-generated-plinqo-entities/</guid><pubDate>Tue, 27 Jul 2010 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;There may come a time where you need to rename a column in your generated
entity for whatever reason. The following tip and trick article will show you
how to quickly accomplish this.&lt;/p&gt;
&lt;p&gt;Let&apos;s assume you have a generated property for the column &lt;code&gt;AccountId&lt;/code&gt; and it is
defined like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[System.CodeDom.Compiler.GeneratedCode(&quot;CodeSmith&quot;, &quot;5.0.0.0&quot;)]
private int _accountId = default(int);

/// &amp;lt;summary&amp;gt;
/// Gets the AccountId column value.
/// &amp;lt;/summary&amp;gt;
[System.Data.Linq.Mapping.Column(Name = &quot;AccountId&quot;, Storage = &quot;_accountId&quot;, DbType = &quot;int NOT NULL IDENTITY&quot;, IsPrimaryKey = true, IsDbGenerated = true, CanBeNull = false)]
[System.Runtime.Serialization.DataMember(Order = 1)]
[System.CodeDom.Compiler.GeneratedCode(&quot;CodeSmith&quot;, &quot;5.0.0.0&quot;)]
public int AccountId
{
    get { return _accountId; }
    set
    {
        if (_accountId != value)
        {
            OnAccountIdChanging(value);
            SendPropertyChanging(&quot;AccountId&quot;);
            _accountId = value;
            SendPropertyChanged(&quot;AccountId&quot;);
            OnAccountIdChanged();
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you take a look at the DBML file, it will look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Type Name=&quot;Account&quot;&amp;gt;
  &amp;lt;Column Name=&quot;AccountId&quot; Storage=&quot;_accountId&quot; Type=&quot;System.Int32&quot; DbType=&quot;int NOT NULL IDENTITY&quot; IsPrimaryKey=&quot;true&quot; IsDbGenerated=&quot;true&quot; CanBeNull=&quot;false&quot; /&amp;gt;
&amp;lt;/Type&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Say you want to change the name of the property and have it preserved across
generation. You will need to open the generated DBML file with the designer and
change the name. I recommend always opening up the DBML file with an Xml Text
editor (right click on the dbml file and select open with) and update the
definition like this.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Please note that opening the DBML file in the class designer will cause
custom DBML data to be lost!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Type Name=&quot;Account&quot;&amp;gt;
  &amp;lt;Column Name=&quot;AccountId&quot; Member=&quot;Id&quot; Storage=&quot;_id&quot; Type=&quot;System.Int32&quot; DbType=&quot;int NOT NULL IDENTITY&quot; IsPrimaryKey=&quot;true&quot; IsDbGenerated=&quot;true&quot; CanBeNull=&quot;false&quot; /&amp;gt;
&amp;lt;/Type&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice that an Member Attribute was added and the Storage Attribute value was
changed. These are the two attributes that you will need to modify in order to
rename properties in the DBML file. If you regenerate and take a look at the
generated partial class. You will see that the AccountID property was dropped
and the Id Property was created:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[System.CodeDom.Compiler.GeneratedCode(&quot;CodeSmith&quot;, &quot;5.0.0.0&quot;)]
private int _id = default(int);

/// &amp;lt;summary&amp;gt;
/// Gets the AccountId column value.
/// &amp;lt;/summary&amp;gt;
[System.Data.Linq.Mapping.Column(Name = &quot;AccountId&quot;, Storage = &quot;_id&quot;, DbType = &quot;int NOT NULL IDENTITY&quot;, IsPrimaryKey = true, IsDbGenerated = true, CanBeNull = false)]
[System.Runtime.Serialization.DataMember(Order = 12)]
[System.CodeDom.Compiler.GeneratedCode(&quot;CodeSmith&quot;, &quot;5.0.0.0&quot;)]
public int Id
{
    get { return _id; }
    set
    {
        if (_id != value)
        {
            OnIdChanging(value);
            SendPropertyChanging(&quot;Id&quot;);
            _id = value;
            SendPropertyChanged(&quot;Id&quot;);
            OnIdChanged();
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>How-to: Upgrade your existing CodeSmith Generator CSLA templates from version 3.8 to 4.0</title><link>https://blakeniemyjski.com/blog/how-to-upgrade-your-existing-codesmith-generator-csla-templates-from-version-3.8-to-4.0/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/how-to-upgrade-your-existing-codesmith-generator-csla-templates-from-version-3.8-to-4.0/</guid><pubDate>Mon, 26 Jul 2010 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;We recently released a new major version of the
&lt;a href=&quot;https://www.codesmithtools.com/product/frameworks#csla&quot;&gt;CodeSmith Generator CSLA templates&lt;/a&gt;
that add support for many new features including Visual Studio 2010, CSLA 4.0
and Silverlight 4.0 support. They can be downloaded
&lt;a href=&quot;https://github.com/codesmithtools/Templates/releases&quot;&gt;here&lt;/a&gt;. When upgrading to
the templates and you are going from CSLA 3.8 to the latest CSLA 3.8.4 templates,
there really aren&apos;t any changes you need to make other than updating your
CodeSmith Project File template location to point to the new version of the
templates. The easiest way to do this is to open up Visual Studio and double
click on the CodeSmith Project File and do a replace all on that file path as
this will update all file paths in all CodeSmith Project Files.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/csla-templates-replace-path.png&quot; alt=&quot;CSLA Template Replace Path&quot; /&gt;&lt;/p&gt;
&lt;p&gt;If you are upgrading from CSLA 3.8 to CSLA 4.0 then additional work is
required. The first step is to convert your solution to Visual Studio 2010 and
set all the projects to use the .NET Framework v4.0.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/csla-petshop-update-dotnet-target-framework.png&quot; alt=&quot;PetShop Update .NET Target Framework&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Secondly, you will need to do is go into your CodeSmith Project Settings and
change the &lt;code&gt;FrameworkVersion&lt;/code&gt; Property from &lt;code&gt;v35&lt;/code&gt; to &lt;code&gt;v40&lt;/code&gt;. If you are
planning on using Silverlight it is best to set the
&lt;strong&gt;IncludeSilverlightSupport&lt;/strong&gt; property to true to save you additional merging
steps.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/csla-entities-csp-edit-output.png&quot; alt=&quot;Edit Output - Entities.csp&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The next step is to regenerate the files; you will notice that there are some
build errors. This is due to the entity partial classes are not regenerated and
there are some required changes for Rules or for Silverlight. If you didn&apos;t
make any changes to the non-generated partial classes (E.G. &lt;code&gt;Account.cs&lt;/code&gt; or
&lt;code&gt;Account.vb&lt;/code&gt;), then it is safe to delete the entire class and regenerate.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; if your Location property is set to a full path, I recommend setting
it to &lt;code&gt;**.\**&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If this is not the case then I recommend backing up your solution if it is not
under source control (&lt;strong&gt;highly recommended&lt;/strong&gt;). I would copy the CodeSmith
Project File to a new folder and use a program like
&lt;a href=&quot;https://www.scootersoftware.com/features.php&quot;&gt;Beyond Compare&lt;/a&gt; to merge the
partial classes together. Since you have already regenerated, the generated
partial classes will be in sync with CSLA 4.0 and won&apos;t require a diff so your
favorite diffing program will tell you that there is no work needing to be
done.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/csla-account-csharp-text-compare.png&quot; alt=&quot;Account.cs - Text Compare&quot; /&gt;&lt;/p&gt;
&lt;p&gt;As you can see from the diff above, you will need to diff the changes. I&apos;d
recommend putting all of your Custom Code in region blocks as this makes
merging much easier. This shouldn&apos;t ever need to be merged again unless CSLA
introduces some breaking changes.&lt;/p&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>CodeSmith Generator supports Microsoft Visual Studio 2010 RTM!</title><link>https://blakeniemyjski.com/blog/codesmith-generator-supports-microsoft-visual-studio-2010-rtm/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/codesmith-generator-supports-microsoft-visual-studio-2010-rtm/</guid><pubDate>Mon, 19 Apr 2010 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;We have been working with Microsoft over the past few months to ensure that
&lt;a href=&quot;https://www.codesmithtools.com/product/generator&quot;&gt;CodeSmith Generator&lt;/a&gt; works
with &lt;a href=&quot;https://www.microsoft.com/visualstudio/en-us/&quot;&gt;Microsoft Visual Studio 2010&lt;/a&gt;.
The currently released version of CodeSmith Generator 5.x already supports
Visual Studio 2010 Integration. Although the current support is titled as beta
we have ran into very few Visual Studio 2010 bugs and will completely stand
behind Visual Studio 2010.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/visual-studio-2010-logo.png&quot; alt=&quot;Visual Studio 2010&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Microsoft and Microsoft logo&apos;s are trademarks of Microsoft Corporation.&lt;/em&gt;&lt;/p&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>Unit testing the CodeSmith Generator CSLA generated template code</title><link>https://blakeniemyjski.com/blog/unit-testing-the-codesmith-generator-csla-generated-template-code/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/unit-testing-the-codesmith-generator-csla-generated-template-code/</guid><pubDate>Thu, 08 Apr 2010 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;During the development of our
&lt;a href=&quot;https://www.codesmithtools.com/product/frameworks#csla&quot;&gt;CodeSmith Generator CSLA templates&lt;/a&gt;
we have spent a lot time unit testing the generated code to ensure everything
was working correctly.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/csla-petshop-visual-studio-unit-test-session.png&quot; alt=&quot;PetShop Unit Test Session&quot; /&gt;&lt;/p&gt;
&lt;p&gt;In the future we would like to generate basic CRUD unit tests for any project.
We decided to unit test the PetShop database to go along with our PetShop
Sample application. We have a solution both in VB and C# that can be found in
(&lt;code&gt;Documents\CodeSmith\Samples\v5.3\Projects\Framework-Samples\Csla&lt;/code&gt;) when you
install CodeSmith Generator or grab the latest version from
&lt;a href=&quot;https://github.com/codesmithtools/templates&quot;&gt;git&lt;/a&gt;. We have unit tests
for all the Data Access implementations as well as collections. We test
primarily against two tables, Category and LineItem.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/csla-petshop-unit-test-visual-studio-solution.png&quot; alt=&quot;PetShop Solution&quot; /&gt;&lt;/p&gt;
&lt;p&gt;These &lt;a href=&quot;https://www.nunit.org/?p=download&quot;&gt;NUnit tests&lt;/a&gt; will give you a good
understanding of how different operations are performed in CSLA. Feel free to
submit to CodeSmith Generator support or &lt;a href=&quot;https://www.codesmithtools.com/product/generator&quot;&gt;CodeSmith Community Forums&lt;/a&gt;
any unit tests you think we don&apos;t have. If you find a bug in the CSLA
templates, its a huge help to the community and us if you submit a failing unit
test for the PetShop sample application that can reproduce your bug. This
ensures we have more testability and no regressions in future versions. However,
any unit test will be appreciated along with the respected sample schema.
When contributing new unit tests, please try to follow our common testing
pattern for consistency that outputs a description and how long it took to
run the test.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/csla-petshop-unit-test-csharp-code.png&quot; alt=&quot;Unit Testing Code&quot; /&gt;&lt;/p&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>How-to: Use Microsoft SQL Server CLR Functions and Functions in your CodeSmith Generator Templates</title><link>https://blakeniemyjski.com/blog/how-to-use-microsoft-sql-server-clr-functions-and-functions-in-your-codesmith-generator-templates/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/how-to-use-microsoft-sql-server-clr-functions-and-functions-in-your-codesmith-generator-templates/</guid><pubDate>Tue, 26 Jan 2010 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In a previous&lt;a href=&quot;https://www.codesmithtools.com/product/generator&quot;&gt;article&lt;/a&gt;,
I had mentioned that we added support for &lt;a href=&quot;https://msdn.microsoft.com/en-us/library/ms131094.aspx&quot;&gt;Microsoft SQL Server CLR Stored procedures&lt;/a&gt;
and &lt;a href=&quot;https://msdn.microsoft.com/en-us/library/ms186755.aspx&quot;&gt;Functions&lt;/a&gt;in the
release of &lt;a href=&quot;https://www.codesmithtools.com/product/generator&quot;&gt;CodeSmith Generator 5.2&lt;/a&gt;.
Since then there has been a few questions since then on how to add this to your
existing templates. The great news is, you can add the functionality in a few
easy changes.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/generator-extended-property-editor-dbo-aspnet-check-schema-version.png&quot; alt=&quot;Extended Property Editor&quot; /&gt;&lt;/p&gt;
&lt;p&gt;To enable SQL function support you need to set &lt;code&gt;IncludeFunctions=&quot;True&quot;&lt;/code&gt; on any
types inheriting from SchemaObjectBase (E.G. CommandSchema,
CommandSchemaCollection, DatabaseSchema...).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/generator-include-functions-attribute.png&quot; alt=&quot;IncludeFunctions Attribute&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Once this has been done you will see SQL functions be added to your User Interface
Command pickers as well as show up in the API like DatabaseSchema.Commands.&lt;/p&gt;
&lt;h2&gt;How do I check to see what type of SQL Function it a command is?&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;em&gt;CS_IsCLR&lt;/em&gt;: Returns true if the command is a &lt;a href=&quot;https://msdn.microsoft.com/en-us/library/ms190790.aspx&quot;&gt;CLR Procedure&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;CS_IsScalarFunction&lt;/em&gt;: Returns true if the command is a &lt;a href=&quot;https://msdn.microsoft.com/en-us/library/ms174318.aspx&quot;&gt;Scalar Function&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;CS_IsTableValuedFunction&lt;/em&gt;: Returns true if the command is a &lt;a href=&quot;https://technet.microsoft.com/en-us/library/ms191165.aspx&quot;&gt;Table-Valued Function&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;CS_IsInlineTableValuedFunction&lt;/em&gt;: Returns true if the command is a &lt;a href=&quot;https://technet.microsoft.com/en-us/library/ms189294.aspx&quot;&gt;Inline Table-Valued Function&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;CS_IsMultiStatementTableValuedFunction&lt;/em&gt;: Returns true if the command is a &lt;a href=&quot;https://www.sqlteam.com/article/intro-to-user-defined-functions-updated&quot;&gt;Multi-Statement Table-Valued Function&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It is also easy to take different actions in your template logic based on the type of function.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;% if(!IsTableValuedFunction) { %&amp;gt;
  cmd.CommandText = &quot;[&amp;lt;%= SourceCommand.Owner %&amp;gt;].[&amp;lt;%= SourceCommand.Name %&amp;gt;]&quot;;
  cmd.CommandType = CommandType.StoredProcedure;
&amp;lt;% } else {%&amp;gt;
  cmd.CommandText = &quot;SELECT * FROM [&amp;lt;%= SourceCommand.Owner %&amp;gt;].[&amp;lt;%= SourceCommand.Name %&amp;gt;](&amp;lt;%=BuildArgumentList()%&amp;gt;)&quot;;
  cmd.CommandType = CommandType.Text;
&amp;lt;%}%&amp;gt;

public bool IsTableValuedFunction
{
  get
  {
    if (SourceCommand == null || !SourceCommand.ExtendedProperties.Contains(&quot;CS_IsTableValuedFunction&quot;))
      return false;
    bool temp;
    bool.TryParse(SourceCommand.ExtendedProperties[&quot;CS_IsTableValuedFunction&quot;].Value.ToString(), out temp);
    return temp;
  }
}
&amp;lt;%}%&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;What templates can I observe these changes in?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;We have completely updated the Command wrapper templates to fully support SQL Functions. Please download the latest build of CodeSmith Generator 5.2 and give them a try.&lt;/p&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>How-to: Upgrade your existing PLINQO or CSLA CodeSmith Generator Templates</title><link>https://blakeniemyjski.com/blog/how-to-upgrade-your-existing-plinqo-or-csla-codesmith-generator-templates/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/how-to-upgrade-your-existing-plinqo-or-csla-codesmith-generator-templates/</guid><pubDate>Tue, 26 Jan 2010 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This article will help you upgrade your solutions templates safely and easily
to the latest major or minor version. The latest templates can be found on our
&lt;a href=&quot;https://github.com/codesmithtools/Templates/releases&quot;&gt;repository&lt;/a&gt; and
the latest nightly build can be found &lt;a href=&quot;https://www.codesmithtools.com/product/generator&quot;&gt;here&lt;/a&gt;.
The first thing to do is to download the template framework you wish to update.&lt;/p&gt;
&lt;p&gt;After the templates have been downloaded and extracted. Remove or Replace the
old templates with the new ones keeping the same folder structure or simply
update your
&lt;a href=&quot;https://www.codesmithtools.com/product/generator&quot;&gt;CodeSmith Generator Project File(s)&lt;/a&gt;
to point to the updated templates. If you have custom made changes, this is
where you merge in your changes.&lt;/p&gt;
&lt;p&gt;I recommend using our templates via &lt;a href=&quot;https://github.com/codesmithtools/Templates&quot;&gt;git&lt;/a&gt;.
This will ensure easy upgradability while keeping any custom changes. To update
all you need to do is run &lt;code&gt;git pull&lt;/code&gt; from the command line.&lt;/p&gt;
&lt;p&gt;The next step is to remove any template dependent references from your Visual
Studio Project so the new references can be updated. These references would
include the CodeSmith.Data assembly if you are using PLINQO or the Csla
assembly. Both of these assemblies can be found in the Common folder in the
root of the template directory. This can be done by clicking on your project
references and clicking remove. The references will be automatically added when
you run the templates. Please note that if the paths are the same then you can
skip this step, but I recommend completing this step just to be on the safe
side.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/csla-templates-remove-reference-visual-studio.png&quot; alt=&quot;Remove Reference&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Lastly, if you have any build breaks fix them by compiling. You shouldn&apos;t have
any build breaks but this can happen if you are migrating to the next major
version (E.G. 4.0).&lt;/p&gt;
&lt;p&gt;If you encounter any bugs after upgrading please let us know by
&lt;a href=&quot;https://www.codesmithtools.com/contactus&quot;&gt;contacting support&lt;/a&gt;.&lt;/p&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>How-to: Use Microsoft SQL Server Functions in your CodeSmith Generator Templates</title><link>https://blakeniemyjski.com/blog/how-to-use-microsoft-sql-server-functions-in-your-codesmith-generator-templates/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/how-to-use-microsoft-sql-server-functions-in-your-codesmith-generator-templates/</guid><pubDate>Mon, 28 Dec 2009 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://www.codesmithtools.com/product/generator&quot;&gt;CodeSmith Generator&lt;/a&gt; now
supports &lt;a href=&quot;https://msdn.microsoft.com/en-us/library/ms186755.aspx&quot;&gt;Microsoft SQL ServerFunctions&lt;/a&gt;
and &lt;a href=&quot;https://msdn.microsoft.com/en-us/library/ms131094.aspx&quot;&gt;Microsoft SQL Server CLR Functions&lt;/a&gt;
with the release of &lt;a href=&quot;https://www.codesmithtools.com/product/generator&quot;&gt;CodeSmith Generator 5.2&lt;/a&gt;.
I will quickly show you how to add
&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/ms186755.aspx&quot;&gt;Microsoft SQL Server Function&lt;/a&gt;
support to your CodeSmith Generator template.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/generator-studio-sql-functions.png&quot; alt=&quot;Generator SQL Functions&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The first thing you need to do in order to use SQL Functions is to set the
&lt;code&gt;IncludeFunctions&lt;/code&gt; Property on any type that derives from
&lt;a href=&quot;https://www.codesmithtools.com/help/Default.aspx##CodeSmithAPI.chm/SchemaExplorer.SchemaObjectBase.html&quot;&gt;SchemaObjectBase&lt;/a&gt;.
The following property types are capable of showing functions when you add the
&lt;code&gt;IncludeFunctions=&quot;True&quot;&lt;/code&gt; setting to the property:
&lt;a href=&quot;https://www.codesmithtools.com/help/Default.aspx##CodeSmithAPI.chm/SchemaExplorer.CommandSchema.html&quot;&gt;CommandSchema&lt;/a&gt;,
&lt;a href=&quot;https://www.codesmithtools.com/help/Default.aspx##CodeSmithAPI.chm/SchemaExplorer.CommandSchemaCollection.html&quot;&gt;CommandSchemaCollection&lt;/a&gt;
and &lt;a href=&quot;https://www.codesmithtools.com/help/Default.aspx##CodeSmithAPI.chm/SchemaExplorer.DatabaseSchema.html&quot;&gt;DatabaseSchema&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;%@ Property Name=&quot;SourceDatabase&quot; Type=&quot;SchemaExplorer.DatabaseSchema&quot; Category=&quot;Required&quot; Description=&quot;Database that contain the stored procedures.&quot; IncludeFunctions=&quot;True&quot; %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now your SQL Functions will show up in SchemaExplorer when you iterate over your commands.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;% foreach (CommandSchema command in SourceDatabase.Commands) { %&amp;gt;
Name: &amp;lt;%= command.Name %&amp;gt;
&amp;lt;% } %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We have also added five extended properties to the CommandSchema. They are as
follows:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;em&gt;CS_IsCLR&lt;/em&gt;*: Returns true if the command is a &lt;a href=&quot;https://msdn.microsoft.com/en-us/library/ms190790.aspx&quot;&gt;CLR Procedure&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;CS_IsScalarFunction&lt;/em&gt;*: Returns true if the command is a &lt;a href=&quot;https://msdn.microsoft.com/en-us/library/ms174318.aspx&quot;&gt;Scalar Function&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;CS_IsTableValuedFunction&lt;/em&gt;*: Returns true if the command is a &lt;a href=&quot;https://technet.microsoft.com/en-us/library/ms191165.aspx&quot;&gt;Table-Valued Function&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;CS_IsInlineTableValuedFunction&lt;/em&gt;*: Returns true if the command is a &lt;a href=&quot;https://technet.microsoft.com/en-us/library/ms189294.aspx&quot;&gt;Inline Table-Valued Function&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;CS_IsMultiStatementTableValuedFunction&lt;/em&gt;*: Returns true if the command is a &lt;a href=&quot;https://www.sqlteam.com/article/intro-to-user-defined-functions-updated&quot;&gt;Multi-Statement Table-Valued Function&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;A normal stored procedure will always return an int value as the &lt;a href=&quot;https://msdn.microsoft.com/en-us/library/ms174998.aspx&quot;&gt;return value&lt;/a&gt;. However, a scalar function can return any data type.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;/file/blog/generator-sql-functions.cst&quot;&gt;attached template&lt;/a&gt; will show you
how to get at this information.&lt;/p&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>How-to: Build a custom UITypeEditor</title><link>https://blakeniemyjski.com/blog/how-to-build-a-custom-uitypeeditor/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/how-to-build-a-custom-uitypeeditor/</guid><pubDate>Tue, 13 Oct 2009 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Recently, I built a
&lt;a href=&quot;https://www.codesmithtools.com/product/generator&quot;&gt;CodeSmith Generator&lt;/a&gt;
sample UITypeEditor that allows a user to enter in custom data into a DropDownList.
In the following article I&apos;ll show you what you need to do in order to build
your own UITypeEditor. First you need to create a public class that will hold
the data of the drop down list. In this example I named my class
&lt;code&gt;DropDownListProperty&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class DropDownListProperty
{
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next we will need to add the properties and the constructors.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class DropDownListProperty
{
  private List&amp;lt;string&amp;gt; _values = new List&amp;lt;string&amp;gt;();

  public DropDownListProperty()
  {
    SelectedItem = &quot;None&quot;;
  }

  public DropDownListProperty(List&amp;lt;String&amp;gt; values)
  {
    if(values.Count &amp;gt; 0)
      SelectedItem = values[0];
    else
      SelectedItem = &quot;None&quot;;

    Values = values;
  }

  public List&amp;lt;string&amp;gt; Values
  {
    get
    {
      if (_values == null)
        _values = new List&amp;lt;String&amp;gt;();

      return _values;
    }
    set
    {
      if(value != null)
        _values = value;
    }
  }

  [Browsable(false)]
  public string SelectedItem { get; set; }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You&apos;ll notice that we have a public property called SelectedItem. This property
will hold the initial value which will be the selected value when a user selects
a choice. By default we set this to &quot;None&quot; in the constructor. We also set an
attribute on the property &lt;code&gt;Browsable(false)&lt;/code&gt;. This tells the PropertyGrid not to
display this property.&lt;/p&gt;
&lt;p&gt;We now want to override the ToString() method to the DropDownListProperty so the
dropdown displays the current selected value.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/// &amp;lt;summary&amp;gt;
/// The value that we return here will be shown in the property grid.
/// &amp;lt;/summary&amp;gt;
/// &amp;lt;returns&amp;gt;&amp;lt;/returns&amp;gt;
public override string ToString()
{
  return SelectedItem;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now it is time to implement the class that controls how my class is displayed
in the property grid. We will want to create a class that inherits from
UITypeEditor.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/// &amp;lt;summary&amp;gt;
/// Provides a user interface for selecting a state property.
/// &amp;lt;/summary&amp;gt;
public class DropDownListPropertyEditor : UITypeEditor
{
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next we will add a private member variable named &lt;code&gt;_service&lt;/code&gt;. We will need to
declare this member variable because we will want to tie into an event in a
little bit. Now it is time to override the &lt;code&gt;EditValue&lt;/code&gt; method.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/// &amp;lt;summary&amp;gt;
/// Displays a list of available values for the specified component than sets the value.
/// &amp;lt;/summary&amp;gt;
/// &amp;lt;param name=&quot;context&quot;&amp;gt;An ITypeDescriptorContext that can be used to gain additional context information.&amp;lt;/param&amp;gt;
/// &amp;lt;param name=&quot;provider&quot;&amp;gt;A service provider object through which editing services may be obtained.&amp;lt;/param&amp;gt;
/// &amp;lt;param name=&quot;value&quot;&amp;gt;An instance of the value being edited.&amp;lt;/param&amp;gt;
/// &amp;lt;returns&amp;gt;The new value of the object. If the value of the object hasn&apos;t changed, this method should return the same object it was passed.&amp;lt;/returns&amp;gt;
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
  if (provider != null)
  {
    // This service is in charge of popping our ListBox.
    _service = ((IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService)));

    if (_service != null &amp;amp;&amp;amp; value is DropDownListProperty)
    {
      var property = (DropDownListProperty) value;

      var list = new ListBox();
      list.Click += ListBox_Click;

      foreach (string item in property.Values)
      {
        list.Items.Add(item);
      }

      // Drop the list control.
      _service.DropDownControl(list);

      if (list.SelectedItem != null &amp;amp;&amp;amp; list.SelectedIndices.Count == 1)
      {
        property.SelectedItem = list.SelectedItem.ToString();
        value =  property;
      }
    }
  }

  return value;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It is important not to be overwhelmed by the code above. The object value that
is passed in is the DropDownListProperty class that holds our data. All we need
to do is some safe type checking (value is DropDownListProperty) and then cast
the value. The _service variable holds the property grid control that we are
interacting with.&lt;/p&gt;
&lt;p&gt;We create a ListBox object as that will hold our list of data (Values property
from the DropDownListProperty class). It also exposes a Click event that will
allow us to know when someone clicks on the drop down list. We will add an event
handler ListBox_Click to the Click event so we can close the drop down list.
If we skipped this step then the list would always be shown.&lt;/p&gt;
&lt;p&gt;The next few lines just adds all our data into the Listbox and calls
DropDownControl(Control). This shows the populated ListBox control.&lt;/p&gt;
&lt;p&gt;Finally we will set the SelectedItem to the Item that the user selected.&lt;/p&gt;
&lt;p&gt;It is time to add the method that we wired up to the Click event.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private void ListBox_Click(object sender, EventArgs e)
{
  if(_service != null)
    _service.CloseDropDown();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The last piece to this puzzle is to override the GetEditStyle method and return
that we want to display a DropDown UITypeEditorEditStyle&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/// &amp;lt;summary&amp;gt;
/// Gets the editing style of the &amp;lt;see cref=&quot;EditValue&quot;/&amp;gt; method.
/// &amp;lt;/summary&amp;gt;
/// &amp;lt;param name=&quot;context&quot;&amp;gt;An ITypeDescriptorContext that can be used to gain additional context information.&amp;lt;/param&amp;gt;
/// &amp;lt;returns&amp;gt;Returns the DropDown style, since this editor uses a drop down list.&amp;lt;/returns&amp;gt;
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
  // We&apos;re using a drop down style UITypeEditor.
  return UITypeEditorEditStyle.DropDown;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally we will go back and add a Editor attribute to the DropDownListProperty
class. This will tell the PropertyGrid that when this property type is loaded to
use the new UITypeEditor class we created.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Editor(typeof(DropDownListPropertyEditor), typeof(System.Drawing.Design.UITypeEditor))]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For more information please check out our
&lt;a href=&quot;https://github.com/codesmithtools/templates&quot;&gt;repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/file/blog/generator-ui-type-editor-sample.zip&quot;&gt;Attached&lt;/a&gt; is the source code
for these two classes.&lt;/p&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>It&apos;s the littlest of things that we do, that make CodeSmith Generator shine.</title><link>https://blakeniemyjski.com/blog/its-the-littlest-of-things-that-we-do-that-make-codesmith-generator-shine/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/its-the-littlest-of-things-that-we-do-that-make-codesmith-generator-shine/</guid><pubDate>Mon, 12 Oct 2009 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;During each release of &lt;a href=&quot;https://www.codesmithtools.com/product/generator&quot;&gt;CodeSmith Generator&lt;/a&gt;,
one thing we always do is test upgrade scenarios between each versions and
check for backwards compatibility among other tests. Recently, I was amazed at
how much change has taken place between CodeSmith Generator 4.x and the latest
CodeSmith Generator 5.1.x release when it comes to the user interface.
CodeSmith Generator has made a huge effort to make CodeSmith Generator rock
solid, crazy fast, and easy to use. I&apos;d like to give everyone a quick inside
look as to how I think CodeSmith Generator has accomplished this.&lt;/p&gt;
&lt;p&gt;Our goal from the beginning was to make CodeSmith Generator a top notch
dependable tool. I think we have accomplished this through various forms.
In the previous version of CodeSmith Generator a user would have to report
every bug they found by using a custom built in component,
&lt;a href=&quot;https://www.codesmithtools.com/contactus&quot;&gt;CodeSmith Support&lt;/a&gt;. We have found that
most of the time you&apos;re in a hurry and stop using the tool or don&apos;t report the
issue. We have spent the time to make bug reporting much easier and provide us
with as much possible as you want us using &lt;a href=&quot;https://exceptionless.com&quot;&gt;Exceptionless&lt;/a&gt;.
So we can fix the bug with as little user intervention as possible. You can
also choose in the options to send us feedback automatically, or you can choose
the default option to send feedback at your digression.&lt;/p&gt;
&lt;p&gt;When a crash occurred in CodeSmith Generator 4.x you had the option to send in
a crash report via the following dialog. We have found that the previous
approach doesn&apos;t allow us to reproduce the issue as easily and most of the time
doesn&apos;t provide enough information.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/generator-4-crash-report-dialog.png&quot; alt=&quot;Crash Report Dialog&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Since the introduction of CodeSmith Generator 5.1.x we have added new
functionality to CodeSmith Generator that allows the user to selectively choose
which pieces of information are sent back to us. This dialog is the default
dialog that can be added to any existing solution when using Exceptionless.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/generator-studio-4-error-dialog.png&quot; alt=&quot;CodeSmith Studio Error Dialog&quot; /&gt;&lt;/p&gt;
&lt;p&gt;If you click on the click here link you will be presented with the following
dialog that lets you choose which pieces of information you want to send back.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/generator-studio-4-error-dialog-details.png&quot; alt=&quot;CodeSmith Studio Error Dialog Details&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This additional information allows our project managers and team see what
issues end users like yourself are experiencing so we can get a bug fix out
quickly. If you provide your email address, we will contact you and let you
know that we are looking into the issue. We have gathered a lot of feedback
through the use of error reporting that has changed how we as a company work on
bugs in the product.  User feedback is very important to us. We review all user
feedback that comes into CodeSmith Generator via
&lt;a href=&quot;https://www.codesmithtools.com/contactus&quot;&gt;CodeSmith Support&lt;/a&gt;. With the latest
build of CodeSmith Generator, It has become even easier to contact CodeSmith
Generator and let us know what you think. From the Help menu select feedback
and you are prompted with the following dialog that allows you to ask a
question, submit an idea or bug report. We have also included the functionality
to search previous submitted ideas and vote on them.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/generator-5-send-feedback-dialog.png&quot; alt=&quot;Send Feedback Dialog&quot; /&gt;&lt;/p&gt;
&lt;p&gt;You might be thinking to yourself, CodeSmith Generator might be stable, but is
it faster with the new release. The answer to that question is YES. We have
spent a great deal of time focusing on Template Caching to ensure you never
need to recompile a template unless a change has taken place. We have also
introduced a new feature we call deep load. Deep loading allows us to grab all
your schema information in advance so we don&apos;t need to make multiple round
trips back to your database. We have also spent time working with performance
and memory profilers tweaking CodeSmith Generator for optimal performance. If
you come across a scenario where you think CodeSmith is slow, please let us
know by submitting a bug report.  We have made improvements to the user
interface across the board to allow you to know exactly how CodeSmith Generator
is working. Take for example, previously when CodeSmith Generator started up
you were prompted with a dialog like the following when samples were extracted:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/generator-4-extract-samples.png&quot; alt=&quot;CodeSmith 4.1 Extract Samples Progress&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This dialog would freeze CodeSmith Generator, so naturally you would think that
CodeSmith Generator had stopped working. We have since changed this to be
multithreaded and also display you a progress bar.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/generator-5-extract-samples.png&quot; alt=&quot;CodeSmith 5.1 Extract Samples Progress&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Another major change we have made is to simplify configuration.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/generator-4-options.png&quot; alt=&quot;Generator 4.1 Options&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Can&apos;t you tell this dialog was made by a developer ;). Let us know what you
think of the new configuration dialog!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/generator-5-options.png&quot; alt=&quot;Generator 5.1 Options&quot; /&gt;&lt;/p&gt;
&lt;p&gt;We love being organized and providing you with all of our samples applications
and templates. Below is a comparison between what you would have seen in
CodeSmith Generator 4.x and CodeSmith Generator 5.x. Besides adding and
updating a lot of the templates. We have now also made it easier to find the
template you are looking for.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/generator-4-template-explorer.png&quot; alt=&quot;Generator 4 Template Explorer&quot; /&gt;
&lt;img src=&quot;/img/blog/generator-5-template-explorer.png&quot; alt=&quot;Generator 5 Template Explorer&quot; /&gt;&lt;/p&gt;
&lt;p&gt;In CodeSmith Generator 5.1 we have also included the sample applications to
most of the Framework Templates. You can find this in the
&lt;code&gt;Documents\CodeSmith\Samples\v5.1\Projects\Framework-Samples&lt;/code&gt; folder on your
computer.  I hope that if you were a previous user of CodeSmith Generator and
you try out the latest version of 5.x you notice these major changes as well as
the minor changes like threading enhancements so you can navigate around
CodeSmith Generator features much quicker.&lt;/p&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>CodeSmith Generator CSLA Templates v1.0 released!</title><link>https://blakeniemyjski.com/blog/codesmith-generator-csla-templates-v1.0-released/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/codesmith-generator-csla-templates-v1.0-released/</guid><pubDate>Wed, 29 Jul 2009 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Over a week ago we released our new &lt;a href=&quot;https://www.codesmithtools.com/product/frameworks#csla&quot;&gt;CodeSmith Generator CSLA Templates&lt;/a&gt;.
I highly recommend everyone that uses &lt;a href=&quot;https://cslanet.com&quot;&gt;CSLA&lt;/a&gt;
to try them out as they will save you a ton of time. Also you&apos;ll notice very
quickly that it follows the CSLA best practices and has other features that you
have been looking for like a working Data Access Layer (DAL). The best part is
that you won&apos;t have to worry about learning something new (the generated code)
if you are already familiar with the &lt;a href=&quot;https://cslanet.com&quot;&gt;CSLA Framework&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Notable features&lt;/h2&gt;
&lt;p&gt;Working data access layer with concurrency support.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Support for completely overriding the generated rules.&lt;/li&gt;
&lt;li&gt;Generates three partial classes per business object.&lt;/li&gt;
&lt;li&gt;Generates a common business, data and UI layer.&lt;/li&gt;
&lt;li&gt;Quick start.&lt;/li&gt;
&lt;li&gt;Implements all 14 &lt;a href=&quot;https://store.lhotka.net&quot;&gt;object types&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Detects and generates properties and child objects for the following database relationships (One to Zero or One, One to One, One to Many, Many to One, Many to Many).&lt;/li&gt;
&lt;li&gt;All DateTime data types are generated as Csla.SmartDate&lt;/li&gt;
&lt;li&gt;Correctly handles Binary Database fields (E.G Images, TimeStamp)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/codesmithtools/Templates/releases&quot;&gt;Download the templates&lt;/a&gt;&lt;/p&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>How-to: Debugging CodeSmith Generator on Microsoft Windows 7</title><link>https://blakeniemyjski.com/blog/how-to-debugging-codesmith-generator-on-microsoft-windows-7/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/how-to-debugging-codesmith-generator-on-microsoft-windows-7/</guid><description>We have updated the Tips &amp; Tricks: Debugging CodeSmith Generator with the changes needed to debug CodeSmith Generator on Microsoft Windows 7.</description><pubDate>Wed, 03 Jun 2009 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;We have updated the
&lt;a href=&quot;https://www.codesmithtools.com/product/generator&quot;&gt;Tips &amp;amp; Tricks: Debugging CodeSmith Generator&lt;/a&gt;
with the changes needed to debug CodeSmith Generator on
&lt;a href=&quot;https://www.microsoft.com/en-us/windows&quot;&gt;Microsoft Windows 7&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here is the quick overview of what you need to-do to enable debugging on a
Microsoft Windows 7 machine.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Update the Just-In-Time debugger setting
&lt;code&gt;DbgJITDebugLaunchSetting&lt;/code&gt;. The setting is found in the registry at
&lt;code&gt;HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Set the value of &lt;code&gt;DbgJITDebugLaunchSetting&lt;/code&gt; to &lt;code&gt;2&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If you are using a 64bit operating system then you must also set the same
key &lt;code&gt;DbgJITDebugLaunchSetting&lt;/code&gt; in this folder
&lt;code&gt;HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\.NETFramework&lt;/code&gt; to &lt;code&gt;2&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If you run into any issues try running CodeSmith Generator Studio and Visual
Studio as an administrator.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now when CodeSmith Generator enters a break point you will see something like
this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/generator-visual-studio-just-in-time-debugger.png&quot; alt=&quot;Visual Studio Just-In-Time Debugger&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Click &lt;code&gt;Yes, Debug CodeSmithStudio&lt;/code&gt; to continue debugging.&lt;/p&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>CodeSmith Generator CSLA Templates Part 2: The Quick Start</title><link>https://blakeniemyjski.com/blog/codesmith-generator-csla-templates-part-2-the-quick-start/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/codesmith-generator-csla-templates-part-2-the-quick-start/</guid><pubDate>Thu, 21 May 2009 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In &lt;a href=&quot;/programming/csla/codesmith-generator-csla-templates-part-1-the-introduction/&quot;&gt;Part 1: The Introduction&lt;/a&gt;
was an overview of the
&lt;a href=&quot;https://www.codesmithtools.com/product/frameworks#csla&quot;&gt;CodeSmith Generator CSLA templates&lt;/a&gt;
we are developing at &lt;a href=&quot;https://www.codesmithtools.com/&quot;&gt;CodeSmith&lt;/a&gt;. In this
post, I am going to go over how to use the CSLA quick start and the new
improvements resulting from customer feedback (Keep the feedback coming!).&lt;/p&gt;
&lt;p&gt;First launch CodeSmith Generator Studio and under the Frameworks node open the
CSLA Folder.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/csla-generator-studio-5-folder.png&quot; alt=&quot;CSLA Folder&quot; /&gt;&lt;/p&gt;
&lt;p&gt;There are a couple of folders in the CodeSmith.CSLA folder&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Folder Name&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Common&lt;/td&gt;
&lt;td&gt;Contains the supporting CSLA assemblies and the Visual Studio templates to create the project.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CSharp&lt;/td&gt;
&lt;td&gt;Contains the C# templates.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Source&lt;/td&gt;
&lt;td&gt;Contains the source code to the quick start and the API that the templates use.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VB&lt;/td&gt;
&lt;td&gt;Doesn&apos;t exist yet but will contain the VB.NET templates.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Now double click on the quick start template, the property grid should now be
populated with the quick start properties.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/csla-templates-quickstart-generator-studio-5.png&quot; alt=&quot;CSLA QuickStart Template&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Next select &lt;code&gt;...&lt;/code&gt; button for the &lt;code&gt;Source Database&lt;/code&gt; property and select your
database you wish to generate against. In this example I will select the
PetShop database. The quick start will now populate the property grid most
common settings. From here configure the settings to your liking and click
&lt;code&gt;Generate&lt;/code&gt;. Microsoft Visual Studio should now launch unless you set
&lt;code&gt;Launch Visual Studio&lt;/code&gt; property to &lt;code&gt;false&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/csla-petshop-visual-studio-solution.png&quot; alt=&quot;PetShop Solution&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Next right click the PetShop.UI project and select Set as Startup Project&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/csla-petshop-visual-studio-solution-startup-project.png&quot; alt=&quot;PetShop Startup Project&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Now when you select build or debug your project will start generating.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/csla-petshop-visual-studio-solution-explorer-generated.png&quot; alt=&quot;PetShop Generated Solution&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The UI Project is a place holder project for you to start building your
application. So now that you have the general idea on getting up and running
quickly with the quick start lets see what it takes to customize the type of
each entity. Lets right click the Entities.csp template and select Manage
Outputs.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/csla-petshop-visual-studio-solution-manage-outputs.png&quot; alt=&quot;PetShop Manage Outputs&quot; /&gt;&lt;/p&gt;
&lt;p&gt;After the manage outputs dialog comes up double click on the Entities.cst item.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/img/blog/csla-petshop-entities-edit-output.png&quot; alt=&quot;Edit Output - Entities&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Earlier in the CSLA beta we had 14 different outputs instead of one. Now all
the settings are in one output (Entities.cst). Say you wanted to change the
types of your entities from an EditableRoot to a EditableRootChild object. Just
use the familiar table picker and change it. It will check to make sure there
is no conflict and that any child lists are populated. For example if you had
no entities selected and you added a table to &lt;code&gt;EditableChild&lt;/code&gt; it would look
through all the lists and if a list entity was not found it would add the table
to the EditableChildList. This makes it extremely easy to configure your
Business objects.&lt;/p&gt;
&lt;p&gt;Please give us your feedback on the new CSLA templates!&lt;/p&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>A fix for the Microsoft .Net Framework 3.5 SP1 Fatal Execution Error (VS Silent Death Bug).</title><link>https://blakeniemyjski.com/blog/a-fix-for-the-microsoft-.net-framework-3.5-sp1-fatal-execution-error-vs-silent-death-bug/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/a-fix-for-the-microsoft-.net-framework-3.5-sp1-fatal-execution-error-vs-silent-death-bug/</guid><pubDate>Wed, 20 May 2009 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;We released &lt;a href=&quot;https://www.codesmithtools.com/product/generator&quot;&gt;CodeSmith Generator 5.1&lt;/a&gt;
with the new requirements of the &lt;a href=&quot;https://www.microsoft.com/en-us/download/details.aspx?id=21&quot;&gt;Microsoft .Net Framework 3.5&lt;/a&gt;.
One of our experiences in upgrading to the .Net Framework 3.5 is issues we have
run into like our customers reporting Visual Studio disappearing or closing
extremely quick when they try to generate code using CodeSmith&apos;s Visual Studio
Integration. The following link
&lt;a href=&quot;http://huagati.blogspot.com&quot;&gt;Tools - Part 11 - Add-ins - Attempting to work around the &quot;VS Silent Death&quot; bug&lt;/a&gt;
describes this issue in detail. Since his blog content is currently offline
I&apos;ll quote his content.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;One very annoying and elusive problem that sometimes slam down on VS2008 / .net
3.5 SP1 developers is the &quot;VS Silent Death&quot; bug. It manifests itself by simply
making the Visual Studio IDE &apos;disappear&apos; at a random point such as when
opening a project or when opening a certain type of file in Visual Studio.&lt;/p&gt;
&lt;p&gt;The annoying part is the lack of feedback from VS that leaves many baffled. The
VS IDE just &quot;disappears&quot; without any feedback, although there is always an entry
in the Windows event log (app log) for each crash stating something along the
lines of &lt;code&gt;*.NET Runtime version 2.0.50727.1434 - Fatal Execution Engine Error (79FEC5F0) (80131506)&lt;/code&gt; or &lt;code&gt;.NET Runtime version 2.0.50727.3053 - Fatal Execution Engine Error (7A035E00) (80131506)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The elusive part of the problem is that for some users it happens only on rare
occasions, while for others it can occur with relatively high frequency. It can
happen for a specific project or solution on one machine while the same
project/solution works fine on an identical machine next to it (and vice versa).
This makes it tricky to reproduce and troubleshoot &quot;on demand&quot;.&lt;/p&gt;
&lt;p&gt;A mitigating factor is if any tools or add-ins used reference the .net 3.5
framework, so it can happen more frequently when VS add-ins are installed but
also when using certain features in VS2008/.net 3.5 such as ASP.Net MVC, Entity
Framework, Linq-to-SQL, WPF, etc. The fact that third party add-ins are commonly
affected has made it easy to put the blame on the add-ins; Microsoft&apos;s
PowerCommands, Huagati DBML/EDMX Tools, Jetbrains Resharper, Ankh, Gallio, and
many other third party add-ins have been affected and sometimes blamed for
causing the crashes. So has the WPF designer, L2S designer, MVC libraries etc.
However, debugging* and research* points to corrupt NGen images and assembly
load problems.&lt;/p&gt;
&lt;h2&gt;Finally Microsoft wakes up&lt;/h2&gt;
&lt;p&gt;There have been numerous reports* to Microsoft on this issue through Microsoft
Connect but despite the reporting party supplying crash dump files for several
of the reports they have all been closed with &quot;not reproducible&quot;. In fact, until
very recently I have not found a single acknowledgement from Microsoft of this
bug, even when actively pursuing an answer (i.e. asking them directly). A few
days ago a Microsoft blogger, Jim Nakashima, wrote in his msdn blog that they
have reproduced it when working with MVC in Azure projects and that the CLR
team is working on a fix:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/archive/blogs/jnak/potential-crash-in-vs-when-using-the-mvc-rc-on-windows-azure&quot;&gt;Potential Crash in VS when using the MVC RC on Windows Azure&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Fantastic. Great. Terrific.&lt;/p&gt;
&lt;p&gt;So far there&apos;s no &quot;official&quot; acknowledgement from MSFT of this bug or ETA for a
hot fix on &lt;a href=&quot;https://docs.microsoft.com/en-us/collaborate/feedback-items-manage&quot;&gt;docs.microsoft.com&lt;/a&gt;.
Not even Microsoft&apos;s primary communication channels to the developer community
(a.k.a. &lt;a href=&quot;https://weblogs.asp.net/scottgu/asp-net-mvc-1-0-release-candidate-now-available&quot;&gt;Scott Guthrie&lt;/a&gt;
or &lt;a href=&quot;https://www.hanselman.com&quot;&gt;Scott Hanselman&apos;s blog&lt;/a&gt;) mention the problem
except in user comments.&lt;/p&gt;
&lt;h2&gt;Update&lt;/h2&gt;
&lt;p&gt;Microsoft has a patch, KB963676 that addresses this issue. After installing it
on my repro-system it looks promising; I have not yet been able to trigger the
&apos;VS death&apos; after applying the patch. No word (yet) on when it will be
publicly available, but I assume that affected parties can request this hot patch
through MS Support by referring to the MSKB #.&lt;/p&gt;
&lt;h2&gt;Update 2&lt;/h2&gt;
&lt;p&gt;The patch is now available for download from Microsoft connect. See Jim
Nakashima&apos;s announcement at
&lt;a href=&quot;https://docs.microsoft.com/en-us/archive/blogs/jnak/fix-available-asp-net-mvc-rc-crash-in-a-windows-azure-cloud-service-project&quot;&gt;Fix available: ASP.Net MVC RC Crash in a Windows Azure Cloud Service Project&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Source:
&lt;a href=&quot;http://huagati.blogspot.com&quot;&gt;Tools - Part 11 - Add-ins - Attempting to work around the &quot;VS Silent Death&quot; bug&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;The Fix&lt;/h2&gt;
&lt;p&gt;A current workaround for this issue is to apply the Microsoft Visual Studio Patch
which is no longer available.  It was referenced by &lt;code&gt;DownloadID=16827&lt;/code&gt;, after
installing reboot your computer.&lt;/p&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>How-to: Register a keyboard shortcut (hot key) in a .NET Windows Forms Application.</title><link>https://blakeniemyjski.com/blog/how-to-register-a-keyboard-shortcut-hot-key-in-a-.net-windows-forms-application/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/how-to-register-a-keyboard-shortcut-hot-key-in-a-.net-windows-forms-application/</guid><pubDate>Tue, 19 May 2009 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;There may come a time when you want to implement some extra features or maybe
an Easter egg into your application using keyboard shortcuts. I was recently
tasked with creating an Easter egg so we could test some functionality in CodeSmith.
After countless searches looking for an elegant solution (I found a few ugly
solutions and some nicer ones) I came across Method #3. I&apos;ll First touch on the
one I didn&apos;t choose to go with.&lt;/p&gt;
&lt;h2&gt;Method #1&lt;/h2&gt;
&lt;p&gt;The following code block will make an unmanaged call to the user32.dll to register
the hotkey. I would never go this approach, but if you were like me and couldn&apos;t
find anything in the time frame allotted then you might end up implementing this.&lt;/p&gt;
&lt;p&gt;First you will need to update your using block.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;using System.Runtime.InteropServices;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then add the following code to your form.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/// &amp;lt;summary&amp;gt; The RegisterHotKey function defines a system-wide hot key &amp;lt;/summary&amp;gt;
/// &amp;lt;param name=&quot;hWnd&quot;&amp;gt;Handle to the window that will receive WM_HOTKEY messages
/// generated by the hot key.&amp;lt;/param&amp;gt;
/// &amp;lt;param name=&quot;id&quot;&amp;gt;Specifies the identifier of the hot key.&amp;lt;/param&amp;gt;
/// &amp;lt;param name=&quot;fsModifiers&quot;&amp;gt;Specifies keys that must be pressed in combination with the key
/// specified by the &apos;virtualKey&apos; parameter in order to generate the WM_HOTKEY message.&amp;lt;/param&amp;gt;
/// &amp;lt;param name=&quot;virtualKey&quot;&amp;gt;Specifies the virtual-key code of the hot key&amp;lt;/param&amp;gt;
/// &amp;lt;returns&amp;gt;&amp;lt;c&amp;gt;true&amp;lt;/c&amp;gt; if the function succeeds, otherwise &amp;lt;c&amp;gt;false&amp;lt;/c&amp;gt;&amp;lt;/returns&amp;gt;
/// &amp;lt;seealso cref=&quot;https://msdn.microsoft.com/en-us/library/ms646309(VS.85).aspx&quot;/&amp;gt;
[DllImport(&quot;user32.dll&quot;)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint virtualKey);

[DllImport(&quot;user32.dll&quot;)]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);

public static int MOD_CONTROL = 0x2;
public static int MOD_SHIFT = 0x4;
public static int WM_HOTKEY = 0x312;

private static int keyId;
public static void RegisterHotKey(Form f, Keys key)
{
   int modifiers = 0;

   if ((key &amp;amp; Keys.Control) == Keys.Control)
       modifiers = modifiers | MOD_CONTROL;

   if ((key &amp;amp; Keys.Shift) == Keys.Shift)
       modifiers = modifiers | MOD_SHIFT;

   Keys k = key &amp;amp; ~Keys.Control &amp;amp; ~Keys.Shift;

   Func&amp;lt;bool&amp;gt; ff = () =&amp;gt;
                       {
                           keyId = f.GetHashCode();
                           RegisterHotKey(f.Handle, keyId, (uint) modifiers, (uint) k);

                           return true;
                       };

   if (f.InvokeRequired)
       f.Invoke(ff);
   else
       ff();
}

public static void UnregisterHotKey(Form f)
{
   try
   {
       Func&amp;lt;bool&amp;gt; ff = () =&amp;gt;
                           {
                               UnregisterHotKey(f.Handle, keyId);

                               return true;
                           };

       if (f.InvokeRequired)
           f.Invoke(ff);
       else
           ff();
   }
   catch (Exception ex)
   {
       Debug.WriteLine(ex.ToString());
   }
}

protected override void WndProc(ref Message m)
{
   base.WndProc(ref m);

   if (m.Msg == WM_HOTKEY)
       throw new Exception(&quot;You found an Easter Egg.&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then in your Form Load event add the following code.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Keys k = Keys.F2 | Keys.Control;
RegisterHotKey(this, k);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I found this example &lt;a href=&quot;https://www.pinvoke.net/default.aspx/user32/RegisterHotKey.html&quot;&gt;here&lt;/a&gt;,
Please note that I did some refactoring to it.&lt;/p&gt;
&lt;h2&gt;Method #2&lt;/h2&gt;
&lt;p&gt;I also found this &lt;a href=&quot;https://stackoverflow.com/questions/48935/c-using-wpf-and-net-35-how-can-i-register-a-global-hot-key-to-say-ctrlshiftlett&quot;&gt;WPF solution&lt;/a&gt;
that I haven&apos;t tried but should work. This looks like the best approach for
implementing this functionality in WPF as I have seen people use the first method.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public WindowMain()
{
   InitializeComponent();

   this.InputBindings.Add(new InputBinding(MyAppCommands.SaveAll, new KeyGesture(Key.F2, ModifierKeys.Control)););

   CommandBinding cb = new CommandBinding(MyAppCommands.SaveAll);
   cb.Executed += SaveAllDocuments;

   this.CommandBindings.Add(cb );
}

private void SaveAllDocuments(object obSender, ExecutedRoutedEventArgs e)
{
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Method #3&lt;/h2&gt;
&lt;p&gt;The approach I went I thought was the best way as it is small and elegant piece
of code. It is completely safe to implement as it doesn&apos;t have any hooks into
native windows dlls. The initial idea came from
&lt;a href=&quot;https://www.codeguru.com/csharp/.net/net_general/keyboard/article.php/c4639&quot;&gt;here&lt;/a&gt;,
to implement this code just place it anywhere in your Form.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;protected override bool ProcessCmdKey(ref Message message, Keys keys)
{
    switch (keys)
    {
        case Keys.F2 | Keys.Control:
            //Process action here.
            return false;
    }

    return false;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Method #4&lt;/h2&gt;
&lt;p&gt;A &lt;a href=&quot;https://erikej.blogspot.com&quot;&gt;friend&lt;/a&gt; recently sent me &lt;a href=&quot;https://www.syncfusion.com/FAQ/windowsforms/faq_c46c.aspx&quot;&gt;this example&lt;/a&gt;
right before publishing this. I have never seen this approach but never the
less it is an example of how to implement this functionality.&lt;/p&gt;
&lt;h4&gt;CSharp&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;public class MyMainForm : System.Windows.Forms.Form, IMessageFilter
{
     const int WM_KEYDOWN = 0x100;
     const int WM_KEYUP = 0x101;

     public bool PreFilterMessage(ref Message m)
     {
          Keys keyCode = (Keys)(int)m.WParam &amp;amp; Keys.KeyCode;
          if(m.Msg == WM_KEYDOWN &amp;amp;&amp;amp; keyCode == Keys.Escape)
          {
               Console.WriteLine(&quot;Ignoring Escape...&quot;);
               return true;
          }
          return false;
     }

     private void MyMainForm_Load(object sender, System.EventArgs e)
     {
          Application.AddMessageFilter(this);
     }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;VB.NET&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;Public Class MyMainForm
     Inherits System.Windows.Forms.Form
     Implements IMessageFilter

     Private WM_KEYDOWN As Integer = &amp;amp;H100
     Private WM_KEYUP As Integer = &amp;amp;H101

     Public Function PreFilterMessage(ByRef m As Message) As Boolean
          Dim keyCode As Keys = CType(CInt(m.WParam), Keys) And Keys.KeyCode
          If m.Msg = WM_KEYDOWN And keyCode = Keys.Escape Then
               Console.WriteLine(&quot;Ignoring Escape...&quot;)
               Return True
          End If
          Return False
     End Function &apos;PreFilterMessage

     Private Sub MyMainForm_Load(sender As Object, e As System.EventArgs)
          Application.AddMessageFilter(Me)
     End Sub &apos;MyMainForm_Load
End Class &apos;MyMainForm
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Please let me know if you find any issues in the sample code above.&lt;/p&gt;
</content:encoded><author>Blake Niemyjski</author></item><item><title>Microsoft Visual Studio 2010 Beta 1 is coming to MSDN on May 18th.</title><link>https://blakeniemyjski.com/blog/microsoft-visual-studio-2010-beta-1-is-coming-to-msdn-on-may-18th/</link><guid isPermaLink="true">https://blakeniemyjski.com/blog/microsoft-visual-studio-2010-beta-1-is-coming-to-msdn-on-may-18th/</guid><pubDate>Fri, 15 May 2009 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;So far I have been very impressed with the CTP builds of
&lt;a href=&quot;https://www.microsoft.com/visualstudio/en-us/&quot;&gt;Microsoft Visual Studio 2010&lt;/a&gt;.
There are some really welcoming changes that are apart of Visual Studio 2010
like UI Unit tests and a completely new IDE written in WPF. The only area where
I thought was lacking was the responsiveness of the application. Last night a
&lt;a href=&quot;https://docs.microsoft.com/en-us/archive/blogs/jasonz/&quot;&gt;few members of the Visual Studio team&lt;/a&gt;
announced that one of there main goals for Beta 1 and RTM is on Performance.
I am very excited to hear this.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;During these last weeks we&apos;ve made a lot of progress, I&apos;m sure you&apos;re going
to feel the product is a lot snappier than the builds we provided at the PDC.
But of course I&apos;m never satisfied -- there are even more wins coming
later. I worked with many different teams to help us with our startup, with
UI transitions, with memory usage, with threading issues -- especially
with how WPF and our main thread synchronize&quot; &lt;a href=&quot;https://docs.microsoft.com/en-us/archive/blogs/ricom/visual-studio-working-on-performance&quot;&gt;Rico Mariani&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/archive/blogs/dannawi/visual-studio-2010-beta-1-available-for-the-msdn-subscribers-on-monday-may-18th&quot;&gt;Jihad Dannawi&lt;/a&gt;
from Microsoft Australia reports that MSDN will have the latest bits of Visual
Studio 2010 on May 18th, So keep an eye open.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://msdn.microsoft.com/subscriptions/downloads/default.aspx?pv=18:370&quot;&gt;MSDN Download link&lt;/a&gt;&lt;/p&gt;
</content:encoded><author>Blake Niemyjski</author></item></channel></rss>