Renting an electric Van

My holidays in recent years have been bikepacking holidays with hotels. Either with a longer route staying one day at a hotel or one location and cycling around there.

This summer I waited a lot at train stations. Deutsche Bahn obviously needs to improve and they plan on fixing their network over the next years. So I wanted to experiment with a solution I normally detest: using a car. I looked around what is available and rented a Citroen e-Spacetourer.

Some experiences from my week using an electic van

Driving electric is clearly the future and feels better than driving a combustion based car. I don't have a lot of electric cars used in the past, but compared to them the e-Spacetourer feels heavy and starts slowly.

The van had a camping setup, so there is not that much space for other luggage. Of course I wanted to transport my folding bike but it didn't fit in the rest of the space in the trunk. So I had to store it behind the front seats while driving and on the passenger seat while sleeping:

/images/van-brompton-backseat.png/images/van-brompton-frontseat.png

I didn't sleep that well in the van. Of course, it would have helped to go to bed early and not after midnight, but being woken up by the sun shortened the night considerably. Converting the van from drive mode to sleep mode doesn't take that long, but can be annoying in the middle of the night. One of the other things that annoyed me the whole week is the entrance height of the van. I couldn't get used to the two steps to get down from the drivers seat when leaving the car.

The whole trip is of course more expensive than using the Deutschlandticket and hotels. Renting the van costed more than a week of (cheap) hotels. And even compared to (pre booked) fast train tickets, charging the car was more expensive. For the nerd in me the whole trip was an experiment, so I tested different chargers from different companies without optimizing for the price. The pricing is quite different: between 0.49 €/kwh and 0.89 €/kwh (EnBW!). I only used adhoc charging and the app for Ionity. Because I only charged the week I had the van, I didn't subscribe to any better optimized tariffs. In hindsight, for Ionity a subscription would have saved me money.

For the 1905 km I paid 275€ overall. The overall is not absolutely correct, because the car was not full when I returned it. The final charging before returning was about 80 km before I returned the car. The 1905km and 275€ results in about 14€/100 km -- quite expensive. In detail I spent for each charging (always stopped below 85%): 20.72€, 10.05€ (EnBW), 15.06€, 18.24€, 33.05€, 29.99€, 25.53€ (Ionity), 18.31€, 20.99€ (Stadtwerke Stuttgart), 19.66€ (Stadtwerke Lindau), 24.51€, 9.62€ (E.ON), 29.69€ (Aral Pulse).

Charging Stations near the highways (at Rastplätzen) was not only with faster chargers, but a lot easier to use, because the charging spaces are not as small as the ones in the cities. The 5 meter long van should fit into all parking spaces, but it felt too risky to squeeze into the small charging parking lots in the cities. Finding a parking space in general is another annoying point why I don't miss owning a car. The best charging stations I used were from Aral Pulse, because it is drive through and wide enough for the van. The best app and user experience had Ionity -- so I used them the most (and even returned to some chargers days later).

Conclusion

Is the freedom to move around worth more money and sitting on the drivers seat? In a train I would have listened to podcasts the same as I did in the car. But driving a car is more tiring than sitting in a train. And for me driving doesn't spark joy.

So as a result: for me this wasn't worth it. But I can totally see why others like it. The experience on the other hand helped me to judge that I don't like it enough to buy a car in the near future. Maybe I will try this again with a bigger van (no conversion of the car for sleeping; enough space for my bicycle). But this kind of van is not electric yet.

Scripting local Library Website

My local library sends me an email when a book is due three days in advance. But I want this information in Homeassistant, of course without manually inserting the date there.

By using playwright I login to their website and get the first entry of the books I borrowed. The secrets (username and password) are stored in a json file and loaded at the beginning. When successfully retrieved the next return date, an upload function is called with the date. The code for this function is at the end of this post.

Code to get the next return date for my local library:

import asyncio
import datetime
import json

from playwright.async_api import async_playwright

async def get_return_dates():
    async with async_playwright() as p:
        # load username / password from a json file
        # format: {"username": "0123456", "password": "your_password"}
        secrets = json.load(open(".secrets.json"))

        browser = await p.chromium.launch()
        page = await browser.new_page()
        await page.goto("https://stadtbibliothek-stuttgart.de")

        # go to login mask
        await page.get_by_role("link", name="Mein Konto").click()
        # fill login form
        await page.locator("#IDENT_1").fill(secrets.get("username"))
        await page.locator("#LPASSW_1").fill(secrets.get("password"))
        # click on Anmelden button
        await page.get_by_role("button", name="Anmelden").click()
        # goto list of rentals
        await page.get_by_role("link", name="Ausleihen").click()
        # there is only one table; this could be more granular - currently this is good enough
        lines = await page.query_selector_all("tr")
        for item in lines:
            columns = await item.query_selector_all("td")
            # filter empty columns
            if len(columns):
                # second column is the date
                dt = datetime.datetime.strptime(
                    (await columns[1].inner_html()).strip(), "%d.%m.%Y"
                )
                date = str(dt.date())
                # print result
                print("next_return_date:", date))
                # upload to homeassistant
                upload(date)
                # first line with a date is all we need
                break
        else:
            # nothing found; untested - because I have no empty list here atm
            print("no return date found.")

        await browser.close()

asyncio.run(get_return_dates())

Add this trigger to your Homeassistant configuration.yaml:

trigger:

  - trigger:
      - platform: webhook
        webhook_id: !secret stadtbib
        allowed_methods:
          - POST
    unique_id: "stadtbib"
    sensor:
      - name: "next return date"
        state: "{{ trigger.json.stadtbib_next_return }}"
        device_class: date
        unique_id: "stadtbib_next_return"

And add a (unique) secret to use in the upload function.

Function references above upload the date to Homeassistant. The code needs httpx (or requests by replacing every httpx with requests).

def upload(date: datetime.date):
    import httpx

    r = httpx.post(
        f"http://IP_OF_HOMEASSISTANT:8123/api/webhook/your_unique_secret",
        json={"stadtbib_next_return": date},
    )
    assert r.status_code == 200

Calling this once a day is enough. The next return date could only change when I return the book with the earliest return date.

Of course, this pattern can be used for any other website to get the desired information into Homeassistant. Some time ago I scraped the water levels of rivers in Baden Württemberg with playwright (code). I didn't use the data over years so I stopped the Github Action that downloaded the data. But scraping the river near your home could be an interesting datapoint to upload to Homeassistant.

Experimentation install of Homeassistiant on a Zero 2W

I have a few more things I want to experiment with on Homeassistant and some with ESPHome. Having unused stuff on my primary Homeassistant starts to annoy me, so I will install Homeassistant on a Raspberry PI Zero 2W for short term experimentation.

I will not use the installation instructions for Raspberry PIs on the Homeassistant site. Mainly because I generate my Raspberry PI images with Ansible and want to use one of these images as starting point.

Because Homeassistant requires Python 3.12 and the default on the current Raspberry PI OS is Python 3.11, I chose to use Docker and not run Homeassistant via systemd. So my starting point is Install Home Assistant Container.

I am aware that the Zero 2W has not enough RAM, so a swapfile is probably a good idea. Raspberry PI seems to have a default way to handle swap (dphys-swapfile), but I want to use the same way I use on other systems, so I ignore that there is already an existing swap with 200M as default.

All commands for future me:

# get system up to date
sudo apt-get update && sudo apt-get upgrade -y

# add 1GB of swap
sudo fallocate -l 1G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
sudo swapon --show
# register in fstab to persist for next boot
echo "/swapfile none swap sw 0 0" | sudo tee -a /etc/fstab

# install docker
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
# sudo every time is fine for me
sudo docker ps

This is the compose file from the Homeassistant instructions with only the config line modified:

services:
  homeassistant:
    container_name: homeassistant
    image: "ghcr.io/home-assistant/home-assistant:stable"
    volumes:
      - /home/pi/.homeassistant:/config
      - /etc/localtime:/etc/localtime:ro
      - /run/dbus:/run/dbus:ro
    restart: unless-stopped
    privileged: true
    network_mode: host

Finally use the docker-compose.yml to start Homeassistant:

# pull image
sudo docker compose pull
# run it (in the background)
sudo docker compose up -d
# to view logs
sudo docker compose logs -f

The container runs in host mode, so it is visible in the local network. A http://<ip-address-of-pi>:8123 works for me. Then follow the onboarding. For me my ESPHome devices were found at the end of the onboarding and I installed some of them to have some data in.

My dashboard looks like this after the setup:

img1

This feels like a good starting point for further Homeassistant experiments.