AudioSwitcher: Swap Audio Tracks Fast Without Re-encoding Your Video

AudioSwitcher lets you replace a video’s audio track quickly without re-encoding the video. It can combine or select MP3s automatically, match audio length to the video, and keep your workflow fast and simple.

If you publish long-form video content, you know the pain: you need to replace or update the soundtrack without touching the video quality. AudioSwitcher is a lightweight Python tool that does exactly that—using ffmpeg to replace the audio track of a single long video while copying the video stream as-is. The result: fast processing and zero video re-encoding.

Why AudioSwitcher?

  • Keep video quality intact by copying the video stream.
  • Automatically trim or loop audio to match the video length.
  • Simple folder-based workflow or explicit file paths.
  • Batch-friendly behavior with clear reporting.

What it does
AudioSwitcher replaces the audio track in your video with an MP3 from the audio/ folder. If that folder is empty, it automatically combines MP3s from audio-input/ into a single track and uses that instead. It even writes a tracklist file next to the combined MP3 with start times for each song—perfect for long mixes.

Core features from the README

  • Replace audio on a video without re-encoding the video stream.
  • List MP3 durations in audio/ or audio-input/.
  • Combine multiple MP3s into one, with optional shuffle.
  • Smart ordering: files with two-digit prefixes (e.g., “00 Intro.mp3”) stay in order; others are shuffled.
  • Auto-select newest audio file or pick latest/oldest/name.
  • Loop or trim audio to match the video length.
  • Auto-select audio codec based on container (.webm -> opus, .mp4/.mov/.m4v/.mkv -> aac, .avi -> mp3).

Examples (from the README)

# Combine automatically (when audio/ is empty) and replace audio
./switch_audio.py

# Show MP3 durations in audio/
./switch_audio.py --list-audio-lengths

# Only combine MP3s in audio-input/ into audio/
./switch_audio.py --combine-only

# Combine with shuffle, but keep two-digit prefixes first (00, 01, ...)
./switch_audio.py --combine --shuffle-audio-input

Quick start workflow
1) Drop your video into video/ (or pass --video-input /path/to/video.mp4).
2) Drop a single MP3 into audio/ OR multiple MP3s into audio-input/.
3) Run ./switch_audio.py and let it handle the rest.

Get it on GitHub
AudioSwitcher is open-source and ready to use today. Grab it here:
https://github.com/LinuxLinusDE/AudioSwitcher

Track2Text: Turning GPX/FIT Tracks into Draft Route Descriptions

If you ride, run, or hike with a GPS device, you probably end up with a GPX or FIT file that is great for maps but not so great for telling a story. Track2Text is a small Python tool that converts the latest GPX or FIT file in your inbox folder into a structured, bullet-style draft route description. It highlights road changes, places, and sections along the way, so you can quickly turn a raw track into a readable narrative later (for example, with ChatGPT).

What makes Track2Text especially useful is its support for FIT files. FIT data often includes additional metrics such as ascent, descent, temperature ranges, average power, speed summaries, and more. The tool includes a concise summary at the top of the output and keeps a full Debug section at the end for deeper inspection. This is perfect if you want to keep both a human-readable draft and the underlying data in one place.

The script is designed for transparency: it prints detailed progress while running, respects geocoding rate limits, and writes both a text output and a JSON summary. It is simple to configure via a config file or command-line flags, and it offers presets for quick testing, fast runs, and detailed output.

If you want to try it or contribute, check out the project here:
LinuxLinusDE/Track2Text: Creates a draft route description from the newest GPX file in inbox/. The output is intentionally bullet-style and meant to be rewritten later (e.g. with ChatGPT). For more detailed metrics, prefer .fit files.

Exampe:

Rohfassung Wegbeschreibung
=======================

Hinweis: Diese Liste ist eine Rohfassung. Bitte mit ChatGPT zu einer
gut lesenden Wegbeschreibung zusammenfassen.

Format: Stichpunkte mit Straßenwechseln und Ortsangaben.
Abschnitte: automatisch nach Distanz gegliedert.

Rohdaten: Trackpunkte=3934, Samples=313, Distanz≈28.62 km

Quelle: Fahrt_am_Nachmittag (1).fit

Kurzueberblick
--------------
Distanz: 28.62 km
Trackpunkte: 3934
Samples: 313
Gesamte Zeit: 01:05:04

FIT-Zusammenfassung
-------------------
Gesamte Zeit: 01:05:04
Distanz: 28.07 km

Strecken-Details
----------------
- Start: Hoffschlägerweg (Ort: Coesfeld)
- Straßenwechsel: Bahnhofstraße
- Straßenwechsel: Daruper Straße
- Straßenwechsel: Münsterstraße
- Straßenwechsel: Große Viehstraße
- Straßenwechsel: Friedrich-Ebert-Straße
- Straßenwechsel: Osterwicker Straße
- Abschnitt: ab km 2 (Ort: Coesfeld)
- Straßenwechsel: Blomenesch
- Straßenwechsel: Billerbecker Straße
- Abschnitt: ab km 4 (Ort: Coesfeld)
- Straßenwechsel: Westhellen (Ort: Billerbeck)
- Abschnitt: ab km 6 (Ort: Billerbeck)
- Straßenwechsel: Osthellen
- Abschnitt: ab km 8 (Ort: Billerbeck)
- Abschnitt: ab km 10 (Ort: Billerbeck)
- Straßenwechsel: Lutum
- Straßenwechsel: L 581
- Straßenwechsel: Annettestraße
- Straßenwechsel: Berkelaue
- Straßenwechsel: Karl-Wagenfeld-Straße
- Straßenwechsel: Hagen
- Straßenwechsel: Mühlenstraße
- Straßenwechsel: Lilienbeck
- Abschnitt: ab km 12 (Ort: Billerbeck)
- Straßenwechsel: Lange Straße
- Straßenwechsel: Markt
- Straßenwechsel: Schmiedestraße
- Straßenwechsel: Kirchstraße
- Straßenwechsel: Hörster Straße
- Straßenwechsel: Friedhofstraße
- Straßenwechsel: Hagen
- Straßenwechsel: Osterwicker Straße
- Abschnitt: ab km 14 (Ort: Billerbeck)
- Straßenwechsel: L 577
- Abschnitt: ab km 16 (Ort: Billerbeck)
- Straßenwechsel: Hamern
- Straßenwechsel: K 42
- Straßenwechsel: Lutum
- Abschnitt: ab km 18 (Ort: Billerbeck)
- Straßenwechsel: Gaupel (Ort: Coesfeld)
- Abschnitt: ab km 20 (Ort: Coesfeld)
- Abschnitt: ab km 22 (Ort: Coesfeld)
- Straßenwechsel: Billerbecker Straße
- Abschnitt: ab km 24 (Ort: Coesfeld)
- Straßenwechsel: Loddeallee
- Straßenwechsel: Mühlenstraße
- Straßenwechsel: Große Viehstraße
- Straßenwechsel: Kleine Viehstraße
- Abschnitt: ab km 26 (Ort: Coesfeld)
- Straßenwechsel: Marktplatz
- Straßenwechsel: Schüppenstraße
- Straßenwechsel: Bernhard-von-Galen-Straße
- Straßenwechsel: Münsterstraße
- Straßenwechsel: Daruper Straße
- Straßenwechsel: Hoffschlägerweg
- Straßenwechsel: Grimpingstraße
- Straßenwechsel: Grenzweg
- Ziel: Grenzweg (Ort: Coesfeld)

Example Result with ChatGPT:

📊 Tourdaten (Kurzüberblick)
Distanz: 28,6 km · Zeit: 1:05 h
Ø Geschwindigkeit: 26,4 km/h


Wir starten am Hoffschlägerweg in Coesfeld und rollen uns zunächst locker ein. Über die Bahnhofstraße und die Daruper Straße geht es Richtung Innenstadt, wo wir kurz die Münsterstraße queren und über Große Viehstraße und Friedrich-Ebert-Straße weiterfahren. Mit der Osterwicker Straße lassen wir das Zentrum hinter uns und nehmen Kurs auf den nördlichen Stadtrand.

Ab etwa Kilometer 2 führt uns der Weg über Blomenesch und die Billerbecker Straße hinaus aus Coesfeld. Der Verkehr nimmt spürbar ab, die Umgebung wird ruhiger und ländlicher. Ab Kilometer 4 nähern wir uns Billerbeck und erreichen über Westhellen das Stadtgebiet.

Zwischen Kilometer 6 und 10 bewegen wir uns durch den Bereich Osthellen und die angrenzenden Abschnitte. Die Strecke ist hier angenehm gleichmäßig, ideal für einen ruhigen Tritt. Mit Lutum und der L 581 wechseln wir wieder auf bekannte Verbindungsstraßen, bevor wir über Annettestraße und Berkelaue tiefer nach Billerbeck hineinrollen.

Ab Kilometer 12 geht es durch mehrere innerstädtische Straßen: Karl-Wagenfeld-Straße, Hagen, Mühlenstraße und Lilienbeck leiten uns Richtung Zentrum. Über Lange Straße, den Markt, Schmiedestraße und Kirchstraße erleben wir Billerbeck von seiner lebendigen Seite, bevor wir über Hörster Straße und Friedhofstraße wieder hinausfahren.

Nach Kilometer 14 nehmen wir erneut den Hagen und anschließend die Osterwicker Straße. Über die L 577 und später Hamern sowie die K 42 führt die Route zurück in Richtung Coesfeld. Mit Kilometer 18 erreichen wir wieder Lutum, kurz darauf Gaupel, und sind damit zurück im Coesfelder Stadtgebiet.

Ab Kilometer 20 verläuft die Strecke ruhig und vertraut durch Coesfeld. Über die Billerbecker Straße, Loddeallee und erneut Mühlenstraße geht es Richtung Innenstadt. Große Viehstraße und Kleine Viehstraße führen uns ab Kilometer 26 zum Marktplatz, bevor wir über Schüppenstraße und Bernhard-von-Galen-Straße noch einmal die Münsterstraße kreuzen.

Zum Abschluss rollen wir über Daruper Straße, Hoffschlägerweg, Grimpingstraße und schließlich den Grenzweg, wo diese abwechslungsreiche Nachmittagsrunde in Coesfeld endet.


Hashtags:
#Radtour #Coesfeld #Billerbeck #SpinningTour #VirtualRide #Fahrrad #Radfahren #Outdoor #NRW #Deutschland #Nachmittagsrunde

YouTube-Titelvorschlag:
Entspannte Nachmittagsrunde von Coesfeld nach Billerbeck | 28 km virtuelle Radtour


DSGVO-Pixeler — Privacy-First Video Anonymization for 4K Action Cam Footage

If you work with action-cam or cycling footage, you’ve probably faced the same dilemma: you want to share your 4K videos, but you can’t expose license plates or faces. DSGVO-Pixeler solves this locally, on your own machine, without sending anything to the cloud.

DSGVO-Pixeler is a Python tool built around YOLOv8 that automatically detects both license plates and faces and pixelates them with a true mosaic effect (not just blur). It’s designed for Apple Silicon and handles 4K video while preserving audio. The priority is privacy: the system is tuned to avoid missed detections, and you can add padding or larger pixel blocks to ensure sensitive details stay unreadable.

Key features:

  • Local processing only — nothing leaves your machine
  • 4K support with optional downscaled detection for speed
  • Apple Silicon acceleration with CPU fallback
  • Hardware encoding via ffmpeg VideoToolbox, or software fallback
  • Separate plate and face models, plus optional extra models
  • Configurable pixel strength, padding, and detection confidence
  • Optional no-pixel zones (HUD overlays) with pixel-accurate coordinates
  • Fast test runs (first N minutes) and visual debug overlays

Typical workflow:
1) Export your action-cam footage as a flat 16:9 video.
2) Place your models into models/plates/ and models/faces/.
3) Run DSGVO-Pixeler and get an anonymized MP4 with audio intact.

Example:

Python
python dsgvo-pixeler.py --input input.mp4 --weights models/plates/best.pt --preset balanced

DSGVO-Pixeler is practical for long rides and busy street scenes, where a single clip can contain hundreds of plates and faces. You stay in control: tune for accuracy with larger inference sizes, increase pixel strength for extra safety, or carve out no-pixel zones so your on-screen HUD stays crisp.

If you need a privacy-first pipeline for publishing public footage, DSGVO-Pixeler is a strong foundation that keeps everything local, fast, and configurable.

You find DSGVO-Pixeler on Github! – for free!

Examples:

Pixelate License Plates in 4K on Apple Silicon — Fast, Local, Privacy-First

EDIT: You will find a new version here: DSGVO-Pixeler

If you work with action-cam footage, sooner or later you face a tough challenge: how to blur license plates reliably without sending videos to the cloud. This project solves that problem locally, in 4K, on Apple Silicon.

Plater is a lightweight Python tool that detects license plates with YOLOv8 and pixelates them (true mosaic blocks, not blur). The pipeline is designed for privacy: it prefers false positives over missed plates and adds optional padding to every detection. That makes it practical for public road footage where privacy must come first.

What makes it useful:

  • 4K-capable detection with optional downscaled inference for speed
  • Apple Silicon acceleration (MPS) with CPU fallback
  • Hardware-encoded output via ffmpeg (VideoToolbox) with audio preserved
  • Configurable pixel strength, confidence threshold, padding, and no-pixel zones

Typical workflow:
1) Export your Insta360 footage as a reframed 16:9 flat video.
2) Run the CLI with your YOLOv8 plate model (.pt).
3) Receive an MP4 with plates pixelated and audio intact.

Example command:

Python
python blur_plates_m4.py --input input.mp4 --output output.mp4 --weights best.pt

If you work with long rides and hundreds of plates, the tool stays robust and flexible. You can trade speed for accuracy using work_w, or enforce a safety margin with pad. For overlays (speed/elevation HUD), you can define no-pixel zones so UI elements remain untouched.

Plater is fully local, scriptable, and ready for GitHub. If you need a privacy-first pipeline for public footage, this is a great starting point.

Integrate Sofar HYD KTL-3PH inverters into the Home Assistant

I’m writing this article because I was worried for a long time whether I would be able to integrate my PV system into Home Assistant. However, integrating the Sofar system works perfectly.

To integrate a HYD KTL-3PH inverter into Home Assistant, I use the ‘Solarman Stick Logger’ (https://github.com/davidrapan/ha-solarman) integration.

This can be easily installed via HACS. There are already enough instructions for this.

The integration is now configured as follows via Settings, Integrations:

A total of 139 entities are currently read out, so presumably all the sensors relevant to you.

I have created two sensors in configuration.yaml:

Today’s profit from the feed-in

YAML
  - sensor:
      - name: "sofar-export-today-gains"
        unique_id: sofar.complete.gains.day
        unit_of_measurement: ""
        state_class: measurement
        device_class: monetary
        state: >
          {% set export = states('sensor.sofar_today_energy_export') | float(0) %}
          {{ (export * 0.0794) | round(2) }}

The complete profit from the PV system, i.e. including feed-in tariff and self-consumption.

YAML
 - sensor:
      - name: "sofar-today-gains"
        unique_id: sofar.today.gains
        unit_of_measurement: ""
        state: >
          {% set produktion = states('sensor.sofar_today_production') | float(0) %}
          {% set einspeisung = states('sensor.sofar_today_energy_export') | float(0) %}
          {% set netzbezug = states('sensor.sofar_today_energy_import') | float(0) %}
          {% set eigenverbrauch = produktion - einspeisung %}
          {% set eigenwert = eigenverbrauch * 0.29 %}
          {% set einspeisewert = einspeisung * 0.079 %}
          {% set netzkosten = netzbezug * 0.29 %}
          {{ (eigenwert + einspeisewert - netzkosten) | round(2) }}

ESP32 DIY Energy Display with ESPHome

In this guide, you’ll learn how to set up an ESP32 board to display sensor data from Home Assistant and connected I²C sensors on a 20×4 LCD display. This setup is perfect for monitoring solar power production, indoor temperatures, CO₂ levels, and more — all in real-time!

Continue reading “ESP32 DIY Energy Display with ESPHome”

Display values from Home Assistant on a display (1602A) using ESP32

I wanted to create a simple way to quickly display the current power consumption of the household and the power generation of a PV system.
Since I still had an ESP32 and a 1602A display, I created the display using the following code.

After the current power consumption, a simple bar is created using the # symbol, which roughly visualises the consumption.

Continue reading “Display values from Home Assistant on a display (1602A) using ESP32”

Home Assistant: Convert a negative value to a positive value

My PV system delivers a negative watt value as production at the Shelly. As I didn’t want to change it in the Shelly, I converted this value in the Home Assistant as follows:

Bash
template:
  - sensor:
      - name: "bkw1.watt"
        unique_id: bkw1.watt
        unit_of_measurement: "W"
        state: >
          {% set wert = states('sensor.sensor_shelly_bkw1_power') | float %}
          {{ -wert if wert < 0 else wert }}

InfluxDB Backup Script

A simple script to back up an InfluxDB

Bash
#!/bin/bash

# Configuration
INFLUXDB_HOST="http://localhost:8086"
INFLUXDB_ORG="ORG"
INFLUXDB_BUCKET="BUCKET"
INFLUXDB_TOKEN="my-token"  # Insert the actual token here
BACKUP_DIR="/opt/influxbu"
ROTATION_WEEKS=4

# Date for the backup
TIMESTAMP=$(date +"%Y-%m-%d")
BACKUP_FILE="$BACKUP_DIR/backup-$TIMESTAMP.tar.gz"

# Ensure the backup directory exists
mkdir -p "$BACKUP_DIR"

# Perform the backup
influx backup --host "$INFLUXDB_HOST" --token "$INFLUXDB_TOKEN" --org "$INFLUXDB_ORG" --bucket "$INFLUXDB_BUCKET" "$BACKUP_DIR/backup-$TIMESTAMP"

# Archive the backup
tar -czf "$BACKUP_FILE" -C "$BACKUP_DIR" "backup-$TIMESTAMP"
rm -rf "$BACKUP_DIR/backup-$TIMESTAMP"

# Delete old backups
find "$BACKUP_DIR" -type f -name "backup-*.tar.gz" -mtime +$((ROTATION_WEEKS * 7)) -exec rm {} \;

# Done
echo "Backup completed: $BACKUP_FILE"

Run via cron, eg.

Bash
0 20 * * * /root/influxbu.sh >> /var/log/influxdb_backup.log 2>&1

Home Assistant & InfluxDB & Grafana: Show device positions on map

To display device positions on a map with history, I first have the positions written to an InfluxDB. I display the data from the InfluxDB with Grafana.

The pivot function must be used for this: The pivot() function in InfluxDB (Flux query language) is used to reshape data by converting a long, narrow table into a wide table. It moves values from a column into multiple columns, making it easier to analyse and visualize time series data.

Example in line 6
from(bucket: "hass")
  |> range(start: v.timeRangeStart, stop: v.timeRangeStop)
  |> filter(fn: (r) => r["_measurement"] == "device_tracker.iphone")
  |> filter(fn: (r) => r["_field"] == "longitude" or r["_field"] == "latitude")
  |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)
  |> pivot(rowKey: ["_time"], columnKey: ["_field"], valueColumn: "_value")

Result: