Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
101e2a4
inital add a first blog post
clowa Dec 17, 2025
c493a99
add support to render markdown alerts with github style
clowa Dec 17, 2025
c56cacd
chore: add gh-cli to devcontainer
clowa Dec 17, 2025
d2e9e39
refactor: streamline Card and CardGrid components for improved readab…
clowa Dec 17, 2025
02e71e5
integrate default page colors
clowa Dec 17, 2025
1b5877d
fix weird shadow of article tags
clowa Dec 17, 2025
b498450
chore: update description and keyword for SEO
clowa Dec 17, 2025
dc0e52c
chore: setup robots.txt and prepare sitemap
clowa Dec 17, 2025
074e001
fix: css gradient color variables
clowa Dec 17, 2025
ac37f2c
chore: update canonical link to use dynamic site URL
clowa Dec 18, 2025
287424e
chore: update devcontainer repo of feature trivy
clowa Dec 18, 2025
6303f89
chore: add devcontainer feature github-cli
clowa Dec 18, 2025
6b8b0ef
Chore: Update devcontainer node js and install azure-function-core-to…
clowa Dec 18, 2025
863830f
fix: Update background color to use rgb() for contrast-dark
clowa Dec 18, 2025
9da9d71
move profile picutre to src/ folder to allow processing by astro
clowa Dec 18, 2025
41989c9
chore: reorder import statements in index.astro
clowa Dec 18, 2025
55e1bd5
add blog posts to landingpage
clowa Dec 19, 2025
b234f68
fix: styling issues where importing pico.css globally was causing
clowa Dec 19, 2025
a3b0efa
chore: delete pico.css since it's imported from CDN to only apply to …
clowa Dec 19, 2025
49807ff
fix: remove weird shadow inherinted from pico.css from blog article s…
clowa Dec 19, 2025
b7c535f
feat: sort blog posts by publish date to appear by newest first
clowa Dec 19, 2025
5ebbd79
fix: reorder CardGrid components to display recent blog posts for git…
clowa Dec 19, 2025
ad59ab5
Merge branch 'main' into feature/blog
clowa Dec 19, 2025
2c52383
fix: update title and description for clarity in Home Assistant SSH a…
clowa Dec 19, 2025
87965a5
feat: display article tags of blog post
clowa Dec 19, 2025
a38f32b
chore: remove unused frontmatter of blog post image
clowa Dec 19, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions swa/astro.config.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { defineConfig } from 'astro/config';
import { remarkAlert } from "remark-github-blockquote-alert";

// https://astro.build/config
export default defineConfig({
markdown: {
remarkPlugins: [remarkAlert],
},
site: 'https://clowa.dev',
});
16 changes: 16 additions & 0 deletions swa/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions swa/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"@fortawesome/free-brands-svg-icons": "^6.5.1",
"@fortawesome/free-solid-svg-icons": "^6.5.1",
"astro": "^5.16.2",
"remark-github-blockquote-alert": "^2.0.1",
"typescript": "^5.3.3"
}
}
21 changes: 11 additions & 10 deletions swa/src/components/Card.astro
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,12 @@ interface Props {
const { href, title, body } = Astro.props;
---

<li class="link-card">
<a href={href}>
{title && <h2>{title}</h2>}
<p class={!title ? 'full-space' : ''}>
{body}
</p>
</a>
</li>
<style>
.link-card {
list-style: none;
display: flex;
padding: 1px;
background-color: var(--contrast-dark);
background-color: rgb(var(--contrast-dark));
background-image: none;
background-size: 400%;
border-radius: 7px;
Expand All @@ -36,7 +28,7 @@ const { href, title, body } = Astro.props;
padding: calc(1.5rem - 1px);
border-radius: 8px;
color: white;
background-color: var(--contrast-dark);
background-color: rgb(var(--contrast-dark));
opacity: 0.8;
}
h2 {
Expand All @@ -59,3 +51,12 @@ const { href, title, body } = Astro.props;
color: rgb(var(--accent-light));
}
</style>

<li class="link-card">
<a href={href}>
{title && <h2>{title}</h2>}
<p class={!title ? 'full-space' : ''}>
{body}
</p>
</a>
</li>
24 changes: 12 additions & 12 deletions swa/src/components/CardGrid.astro
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,6 @@ interface Props {
const { title, cards } = Astro.props;
---

<section class="section-container">
<div class="content-wrapper">
<h2 class="section-title">{title}</h2>
<div class="repositories-grid">
{cards.map((card) => (
<Card {...card} />
))}
</div>
</div>
</section>

<style>
/* Container for the entire section */
.section-container {
Expand Down Expand Up @@ -59,4 +48,15 @@ const { title, cards } = Astro.props;
grid-template-columns: repeat(2, 1fr);
}
}
</style>
</style>

<section class="section-container">
<div class="content-wrapper">
<h2 class="section-title">{title}</h2>
<div class="repositories-grid">
{cards.map((card) => (
<Card {...card} />
))}
</div>
</div>
</section>
100 changes: 100 additions & 0 deletions swa/src/layouts/BlogPost.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
---
import "../styles/base.css";
import "remark-github-blockquote-alert/alert.css";
// 1. The frontmatter prop gives access to frontmatter and other data
const { frontmatter } = Astro.props;
---
<html lang="en">
<head>
<title>{frontmatter.title}</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="light dark">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css">
<style>
main.container {
max-width: 1000px;
margin: 2rem auto;
padding: 2rem;
background-color: rgb(var(--contrast-dark));
border-radius: 1.25rem;
box-shadow: 0 4px 24px 0 rgb(var(--contrast-light));
}
header.blog-header {
margin-bottom: 1.5rem;
text-align: center;
}
.meta {
color: rgb(var(--accent-light));
display: flex;
justify-content: center;
align-items: center;
gap: 1rem;
flex-wrap: wrap;
}
.meta > span:first-child {
flex-basis: 100%;
}
.tags {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
justify-content: center;
}
.tag {
padding: 0.375rem 0.75rem;
background-color: rgb(var(--accent-light));
color: rgb(var(--contrast));
border-radius: 0.375rem;
font-size: 0.75rem;
font-weight: 600;
text-transform: capitalize;
white-space: nowrap;
}
article.blog-content {
padding: 1.6rem;
margin-bottom: 2rem;
background-color: rgb(var(--contrast));
border-radius: 1.25rem;
overflow-wrap: break-word;
font-size: 0.9rem;
/* explicitly remove box-shadow to overwrite pico.css */
box-shadow: none !important;
}
article.blog-content>h2 {
--pico-font-size: 0.6rem !important;
font-size: 0.6rem !important;
}
footer.blog-footer {
border-top: 1px solid rgb(var(--accent-light, #eee));
padding-top: 1.5rem;
text-align: center;
color: rgb(var(--accent-light));
/* font-size: 0.95rem; */
}
</style>
</head>
<body>
<main class="container">
<header class="blog-header">
<h1>{frontmatter.title}</h1>
<div class="meta">
<span>By {frontmatter.author} &middot; {new Date(frontmatter.pubDate).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' })}</span>
{frontmatter.tags && frontmatter.tags.length > 0 && (
<div class="tags">
{frontmatter.tags.map((tag: string) => (
<span class="tag">{tag}</span>
))}
</div>
)}
</div>
</header>
<article class="blog-content">
<slot /> <!-- Markdown content is injected here -->
</article>
<footer class="blog-footer">
© {new Date().getFullYear()} {frontmatter.author} — All rights reserved.
</footer>
</main>
</body>
</html>
2 changes: 1 addition & 1 deletion swa/src/pages/404.astro
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,4 @@ import Layout from '../layouts/Layout.astro';
.text {
font-size: 1.5rem;
}
</style>
</style>
31 changes: 26 additions & 5 deletions swa/src/pages/index.astro
Original file line number Diff line number Diff line change
@@ -1,28 +1,38 @@
---
import Layout from '../layouts/Layout.astro';
import CardGrid from '../components/CardGrid.astro';
import SocialBubble from '../components/SocialBubble.astro';
// add font awesome icons
import { faGithub, faPaypal, faStackOverflow } from '@fortawesome/free-brands-svg-icons'
import { faGithub, faPaypal, faStackOverflow } from '@fortawesome/free-brands-svg-icons';
// import the Image component and the image
import { Image } from 'astro:assets';
import CardGrid from '../components/CardGrid.astro'
import profilePicture from '../images/profile-picture.jpg';
// import custom scripts
import loadRepositories from '../scripts/loadRepositories.js';
import loadGistsOfPastYear from '../scripts/loadGistsOfPastYear.js';

const repos = await loadRepositories(['clowa.dev', 'my-setup', 'docker-terraform', 'docker-powershell-core', 'arduino-plant-watering', 'golang-custom-rpi-exporter']);
const gists = await loadGistsOfPastYear();

// Load blog posts dynamically from the posts directory
const postFiles = import.meta.glob('./posts/*.md', { eager: true });
const allPosts = Object.values(postFiles);
// Filter out draft posts and sort by pubDate (newest first)
const publishedPosts = allPosts
.filter((post: any) => post.frontmatter.draft !== true)
.sort((a: any, b: any) => new Date(b.frontmatter.pubDate).valueOf() - new Date(a.frontmatter.pubDate).valueOf());
---

<Layout title="Cedric Ahlers">
<main>
<div class="profile-picture">
<Image
src="/images/profile-picture.jpg"
src={profilePicture}
alt="Cedric Ahlers"
width="1659"
height="2034"
loading="eager"
priority
quality="max"
/>
</div>
<h1>Nice to meet you 👋🏻 </br>I'm <span class="text-gradient">Cedric</span></h1>
Expand Down Expand Up @@ -51,7 +61,18 @@ const gists = await loadGistsOfPastYear();
</p>
<hr class="divider" />
<CardGrid
title="My recent GitHub repositories"
title="My recent Blog Posts"
cards={publishedPosts
.filter((post: any) => post.url)
.map((post: any) => ({
title: post.frontmatter.title,
body: post.frontmatter.description,
href: post.url
}))}
/>
<hr class="divider" />
<CardGrid
title="My GitHub repositories"
cards={repos.map((repo: { name: any; description: any; html_url: any }) => ({
title: repo.name,
body: repo.description,
Expand Down
84 changes: 84 additions & 0 deletions swa/src/pages/posts/home-assistant-host-ssh-access.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
---
layout: '../../layouts/BlogPost.astro'
title: 'Accessing Home Assistant via SSH'
draft: false
pubDate: 2025-12-17
description: 'A guide on how to access the node of your Home Assistant system using SSH.'
author: 'Clowa'
tags: ["home assistant", "SSH", "tutorial"]
---

If you're looking to access the Host of your Home Assistant via SSH for development or troubleshooting purposes, this guide will walk you through the necessary steps.

The home assistant team itself published a guide on how to do this in their [developer documentation](https://developers.home-assistant.io/docs/operating-system/debugging/) and only recommend this for development and debugging purposes **not for regular use**.

> [!WARNING]
> Please note that accessing the host system is not intended for regular use. This method will grant you access to almost everything on your Home Assistant host system including the docker containers of Home Assistant and it's add-ons. Be very careful with what you do as you can easily break your Home Assistant installation to an unrecoverable state.

## Prerequisites

- Home Assistant installation
- USB drive _(for storing your ssh key)_
- A computer with an ssh client installed

## Step 1: Generate SSH Keys

First, you'll need to generate an SSH key pair on your local machine if you don't already have one. You can do this using the following command:

```bash
ssh-keygen -t rsa -b 4096
```

This command will create a public and private key pair. By default, the keys will be named `id_rsa` _(private key)_and `id_rsa.pub` _(public key)_ and stored in the `~/.ssh/` directory, but you will be prompted to specify a different location if desired.

## Step 2: Prepare the USB Drive

Next, format a USB drive as `FAT32`, `ext4` or `NTFS` and name this partition `CONFIG`. At the root of the partition, create a text file named `authorized_keys` (without any file extension) on the USB drive and copy the contents of your public key (the contents of `id_rsa.pub`) into this file. If you have multiple keys, you can add them on separate lines.

> [!NOTE]
> Ensure the `authorized_keys` file is saved with ASCII encoding and Unix-style line endings (LF) to avoid any issues. Windows uses CRLF by default, which will not work.

## Step 3: Insert the USB into Home Assistant

Now, safely eject the USB drive from your local machine and insert it into the Home Assistant host system. The Home Assistant OS will automatically detect the `authorized_keys` file on the USB drive during reboot and configure SSH access accordingly.

In case your Home Assistant host is already running, you must reboot it to load the keys.

## Step 4: Connect via SSH

Once the Home Assistant host has rebooted, you can connect to it via SSH using the following command:

```bash
ssh [email protected] -p 22222 -i /path/to/your/private/key/id_rsa
```

Replace `/path/to/your/private/key/id_rsa` with the actual path to your private key file. If you are using a different hostname or IP address for your Home Assistant host, replace `homeassistant.local` with that address or hostname.

You should now be connected to the Home Assistant host system via SSH. 🎉

## Step 5: Make things comfortable

Optionally, you can enhance your SSH experience by creating or updating the SSH configuration file at `~/.ssh/config` on your local machine. Add the following configuration:

```plaintext
Host homeassistant
HostName homeassistant.local
Port 22222
User root
IdentityFile /path/to/your/private/key/id_rsa
```

>[!Tip]
> If you use a password manager with SSH key support (such as 1Password or Bitwarden), or if your SSH key is loaded in an SSH agent via some other method, you can omit the `IdentityFile` line. For detailed steps, consult your password manager’s documentation.

With this configuration, you can simply connect to your Home Assistant host by running:

```bash
ssh homeassistant
```

## Conclusion

You have successfully set up SSH access to your Home Assistant host system. Remember to use this access responsibly and avoid making changes that could compromise the stability or security of your Home Assistant installation.

Happy home automating! 🚀
Loading