MoreRSS

site iconGarrit FrankeModify

a generalist DevOps Engineer
Please copy the RSS to your reader, or quickly subscribe to:

Inoreader Feedly Follow Feedbin Local Reader

Rss preview of Blog of Garrit Franke

Custom Entities in Home Assistant

2025-12-11 08:00:00

My local gym has a gauge on their website that shows an approximation of how many people are currently there. Being an avid Home Assistant user, of course I had to pipe that data into my dashboard somehow. For that, I created a simple n8n workflow that scrapes the data off the gym's site and dumps it into a custom entity.

Because creating a custom entity was not quite so straight forward as I'd hoped, I wanted to share how I did it.

I ended up using the input_number integration (which seems to come with Home Assistant) and defined an entity that I could reference through the API:

`yaml

configuration.yaml

inputnumber: easyfitnessauslastung: name: EasyFitness Auslastung initial: 0 min: 0 max: 100 step: 1 `

After reloading the configuration, the entity was ready to be written to.

As mentioned, I'm using a self-hosted instance of n8n on a Raspberry Pi to automate this. Using their Home Assistant Integration, you can simply address the new entity and update its state. And just like that, I was able to add a new Gauge to my dashboard!

Making family IT support effortless (and free)

2025-09-15 08:00:00

Yesterday was one of those days where a family member had an issue with their computer. "It doesn't open when I click it" was all the information I had going into it. Debugging that through the telephone is just not happening.

I always stayed away from TeamViewer, because I was never really sure if that had a free plan, and the possibility of finding out mid-session that they don't have one was reason enough to save the hassle of yelling the installation instructions through the phone.

I remember hearing about RustDesk in a podcast a couple of years ago. The host claimed it to be a hassle-free open source replacement for TeamViewer, but by the time the next IT-support session arrived, I already forgot about it. Until yesterday!

Yesterday, I just didn't get enough information to be useful. I just needed a way to remotely access the computer, and that's then I remembered the promise of RustDesk. Going in blind, I quickly installed the client on my laptop and sent a mail with the instructions to my family member:

  1. Click here (a download link to the executable file)
  2. Press the windows key
  3. Type "rust" (to search for the file)
  4. Press enter
  5. Tell me the ID and the onetime password on the screen

These instructions are fool-proof! And sure enough, they worked. I logged in and debugged the issue. No paywall, no corporate bullsh**, just a simple client that just works as intended out of the box. I was delighted to see that the plan has worked out. Going forward, Rustdesk will be my primary choice for remote IT support.

If you're interested: for some reason the browser window on their screen was resized to a tiny box near the edge of the screen, so whenever they clicked a bookmarked link on their desktop, instead of "nothing happening", they just didn't see the page opening in the browser.

TL;DR

Use RustDesk instead of TeamViewer for a free and easy way to access your family's computer remotely.

git diff --ignore-all-space makes code review way easier

2025-06-11 08:00:00

I just learned a cool trick that I want to share. Let's review the diff of a file using git diff. I redacted most of it, but you probably found yourself in the situation of extremely long changes before:

` diff --git a/lib/ui/screens/detail/components/body/eventbody.dart b/lib/ui/screens/detail/components/body/eventbody.dart index d19d70a..1a61380 100644 --- a/lib/ui/screens/detail/components/body/eventbody.dart +++ b/lib/ui/screens/detail/components/body/eventbody.dart @@ -3,6 +3,7 @@ class EventBody extends StatelessWidget { final EventDetails details; @@ -18,35 +19,43 @@ class EventBody extends StatelessWidget {

@override Widget build(BuildContext context) => Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - EventInfoView( - location: details.location, - start: details.start, - end: details.end, - certified: details.certified, - paid: details.paid, - points: null, // Already shown in app bar + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + EventInfoView( + location: details.location, + start: details.start, + end: details.end, + certified: details.certified, + paid: details.paid, + points: null, // Already shown in app bar + ), + const SizedBox(height: 24), + Html( + data: details.description, + style: { + 'body': Style( + margin: Margins.zero, ... } `

But do you spot what has ACTUALLY been changed? In a real world scenario, it probably took you a while before you realised that it's the result of formatting the entire file. Nobody cares about whitespace when reviewing code. Or rather, your brain should not take the burden of having to care about that. That's what linters are for!

Let's look at the same diff with the --ignore-all-space (shorthand -w) flag activated:

diff --git a/lib/ui/screens/detail/components/body/event_body.dart b/lib/ui/screens/detail/components/body/event_body.dart index d19d70a..1a61380 100644 --- a/lib/ui/screens/detail/components/body/event_body.dart +++ b/lib/ui/screens/detail/components/body/event_body.dart @@ -3,6 +3,7 @@ class EventBody extends StatelessWidget { final EventDetails details; @@ -29,6 +30,14 @@ class EventBody extends StatelessWidget { points: null, // Already shown in app bar ), const SizedBox(height: 24), + Html( + data: details.description, + style: { + 'body': Style( + margin: Margins.zero, + ), + }, + ), Text(details.description, style: context.theme.textTheme.body2Regular), if (details.registrationUrl != null || details.programUrl != null) const SizedBox(height: 16), if (details.registrationUrl != null) ...[

Huh, so it's NOT just a formatted file. All whitespace changes have been stripped out, and you only see the changes that are relevant for the review. Neat!

Many tools also support ignoring whitespace. GitLab let's you disable Show whitespace changes in the merge request diff viewer. VSCode has the diffEditor.ignoreTrimWhitespace setting. So, if you want to make this the default in your tools, chances are there's an option for only showing relevant changes.

A bit of a sloppy post, but I hope this is useful to someone. Happy code reviewing!

No matter what you do, always leave a breadcrumb

2025-05-20 08:00:00

This applies to work tasks just as much as it applies to hobbies. No matter what you do, always create some sort of breadcrumb that you or someone else can pick up down the line.

If you build a feature into your app, you've generated an output, and therefore value. But what if you bang your head against the wall and don't make any progress? Write about it. Write about your challenges, document what you've learned and share it with your peers. Not reaching a set goal doesn't mean failure. It's an opportunity to create value in a way you didn't anticipate.

If you're out on a run and a thought pops into your head, record a memo on your phone, or better yet: keep a tiny notebook with you at all times. Thoughts slip in and out of our heads at a very rapid pace. Capturing thoughts, even if you don't share them with anyone else, is a great way to generate value out of thin air.

If you're into crafty hobbies like woodworking, painting or baking, you likely know how important it is to finish a project. No matter how good or bad the outcome is, every finished project is an artifact that you can look back at later and see the progress you've made.

Output always means value, even if it's not immediately apparent. Just keep on laying down those breadcrumbs.

About Container Interfaces

2025-03-25 08:00:00

There are a couple of interfaces that container orchestration systems (like Kubernetes) implement to expose certain behavior to their container workloads. I will only be talking about Kubernetes in this post since it's the orchestrator I'm most comfortable with, but some interfaces are also implemented in other orchestrators (like HashiCorp Nomad) too, which makes the interfaces cross-platform.

Container Storage Interface (CSI)

Storage behavior used to be built into Kubernetes. The container storage interface (CSI) defines a unified interface to manage storage volumes, regardless of the orchestrator (as long as they implement the CSI). This makes it way easier for third-party storage providers to expose data to Kubernetes. If a storage provider implements this interface, orchestrators can use it to provision volumes to containers. Notable storage providers are:

A full list of CSI drivers can be found here.

Container Runtime Interface (CRI)

The Container Runtime Interface (CRI) is an API that allows Kubernetes to use different container runtimes without needing to recompile the entire Kubernetes codebase. Before CRI, Kubernetes had a direct integration with Docker, making it difficult to use alternative container runtimes.

CRI defines the API between Kubernetes components (specifically kubelet) and container runtimes. This abstraction allows Kubernetes to support multiple container runtimes simultaneously, giving users the flexibility to choose the runtime that best fits their needs. Some popular container runtimes that implement the CRI include:

  • containerd - The industry-standard container runtime that powers Docker and is maintained by the CNCF
  • CRI-O - A lightweight runtime specifically designed for Kubernetes
  • Kata Containers - A secure container runtime that uses hardware virtualization for stronger isolation

With CRI, switching between different runtimes becomes more straightforward, allowing operators to optimize for security, performance, or compatibility based on their specific requirements.

Container Network Interface (CNI)

The Container Network Interface (CNI) defines a standard for configuring network interfaces for Linux containers. Similar to CSI and CRI, the CNI was created to decouple Kubernetes from specific networking implementations, allowing for a pluggable architecture.

CNI plugins are responsible for allocating IP addresses to pods and ensuring proper network connectivity between pods, nodes, and external networks. They implement features like network policies, load balancing, and network security. Some popular CNI plugins include:

  • Calico - Known for its performance, flexibility, and strong network policy support
  • Cilium - Uses eBPF for high-performance networking and security
  • Flannel - Simple overlay network focused on ease of use
  • AWS VPC CNI - Integrates pods directly with Amazon VPC networking

Each CNI plugin has its strengths and is suitable for different use cases. For example, Calico excels at enforcing network policies, Cilium is optimized for performance and observability, while Flannel is valued for its simplicity.

Wrapping Up

One thing I've always admired about Kubernetes is its pluggable architecture. These standardized interfaces (CSI, CRI, and CNI) showcase how well-designed the system really is. Instead of building everything into the core, the Kubernetes team made the smart decision to create extension points that allow the community to innovate without touching the core codebase.

The great news? You don't have to swap out all these components or even understand them deeply to use Kubernetes effectively. While the array of options might seem daunting at first glance, most Kubernetes distributions (like EKS, GKE, AKS, or Rancher) come with sane defaults that work well out of the box. They've already made sensible choices about which storage, runtime, and networking components to include.

This pluggability is what makes Kubernetes so powerful for those who need it. Need a specific storage solution? Plug in a CSI driver. Want a more secure container runtime? Swap in a different CRI implementation. But for everyone else, the defaults will serve you just fine.

The beauty of this approach is that it gives you room to grow. Start with the defaults, and when you have specific requirements, the extension points are there waiting for you. That's the real magic of Kubernetes – it works great out of the box but doesn't limit your options as your needs evolve.

A trick to manage frequently used prompts in Claude/ChatGPT

2025-02-27 08:00:00

So far, whenever I wanted to recycle a prompt from another context in Claude (though this also applies to ChatGPT and some other LLMs), I went back in my conversation history, copied the prompt, pasted it in a new chat and adjusted the context. But I recently discovered that Claude Projects can be misused as "prompt templates", which makes it way easier to handle repetitive tasks.

In Projects, you can set a system prompt that will be applied to all conversations in the project. I guess it's supposed to be used for relevant information about whatever you want to work on, but I like to think about a project more as a prompt template, rather than a project. For example, here's a project prompt that I use to brainstorm project ideas:

Ask me one question at a time so we can develop a thorough, step-by-step spec for this idea. Each question should build on my previous answers, and our end goal is to have a detailed specification I can hand off to a developer. Let’s do this iteratively and dig into every relevant detail. Remember, only one question at a time.

(Stolen from this great blog post)

While you could copy and paste this from a text file into every new conversation, Claude's projects make it super easy to save this as a template.

I guess this is an obvious feature for some people, but to me, it was a huge help once I found this out.

Got any other neat tricks for working with LLMs? I'd love to hear them!