Home Automation – Kubernetes Style

I’ve been using Smartthings as my smart home hub for a while. And I’d like to migrate to something that’s open source (or more importantly, self-hosted). There’s been a few horror stories out there that make me worry about the potential “hostage” situation. (e.g. manufacturers charging a monthly price for the service, or flat-out discontinuing the service).

Hardware

Image 31 - Dell Wyse N03D Thin Client Celeron CPU N2807 1.58GHz 16GB SSD, 4GB DDR3 (NEW)
Dell Wyse N03D
Z-Wave + Zigbee Dongle

I bought a Dell Wyse N03D Thin Client off eBay at $35 each, complete with power supply, keyboard and mouse. The thin client is definitely not a powerhouse, but it’s dual core x86-64 processor and 4GB of RAM should be plenty sufficient for my use case. To let it connect to Z-Wave and ZigBee devices, I also paired it with a dongle.

Software

OS

The thin client came with Windows 7 Embedded pre-installed, which I absolutely don’t need. I replaced it with a fresh install of Ubuntu Server 20.04 LTS. As the thin client only has 16G of onboard storage, I modified the default partitioning scheme to get more space to the rootfs.

Kubernetes

I chose k3s for this project. It’s small size is a perfect fit. For the installation, running the following snippet is sufficient.

curl -sfL https://get.k3s.io | sh -

Running rancher is always a good idea. To have rancher installed on k3s, add the following manifest to /var/lib/rancher/k3s/server/manifests

---
apiVersion: v1
kind: Namespace
metadata:
  name: cattle-system
---
apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
  name: rancher
  namespace: kube-system
spec:
  chart: https://releases.rancher.com/server-charts/stable/rancher-2.5.8.tgz
  targetNamespace: cattle-system
  valuesContent: |-
    hostname: [REDACTED]
    tls: external
    replicas: 1

This works because k3s comes with a helm CRD.

Following that, apply the following manifest to get home-assistant and its friends installed.

---
apiVersion: v1
kind: Namespace
metadata:
  name: home-assistant
---
apiVersion: v1
kind: Service
metadata:
  namespace: home-assistant
  name: home-assistant
spec:
  selector:
    app: home-assistant
  ports:
  - name: http
    protocol: TCP
    port: 80
    targetPort: 8123
---
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: home-assistant
  name: home-assistant
  labels:
    app: home-assistant
spec:
  replicas: 1
  selector:
    matchLabels:
      app: home-assistant
  template:
    metadata:
      labels:
        app: home-assistant
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: kubernetes.io/hostname
                operator: In
                values:
                - homelab-us-east-1-nano-1
      containers:
      - name: home-assistant
        image: homeassistant/home-assistant:stable
        ports:
        - containerPort: 8123
        volumeMounts:
        - mountPath: /config
          name: config
        - mountPath: /dev/ttyUSB1
          name: zigbee
        securityContext:
          privileged: true
      volumes:
      - name: config
        hostPath:
          path: /etc/home-assistant
          type: DirectoryOrCreate
      - name: zigbee
        hostPath:
          path: /dev/ttyUSB1
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  namespace: home-assistant
  name: home-assistant
spec:
  rules:
  - host: [REDACTED]
    http:
      paths:
      - pathType: Prefix
        path: /
        backend:
          service:
            name: home-assistant
            port:
              number: 80
---
apiVersion: v1
kind: Service
metadata:
  namespace: home-assistant
  name: zwavejs2mqtt
spec:
  selector:
    app: zwavejs2mqtt
  type: NodePort
  ports:
  - name: http
    protocol: TCP
    port: 8091
    targetPort: 8091
    nodePort: 30091
  - name: ws
    protocol: TCP
    port: 3000
    targetPort: 3000
---
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: home-assistant
  name: zwavejs2mqtt
  labels:
    app: zwavejs2mqtt
spec:
  replicas: 1
  selector:
    matchLabels:
      app: zwavejs2mqtt
  template:
    metadata:
      labels:
        app: zwavejs2mqtt
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: kubernetes.io/hostname
                operator: In
                values:
                - homelab-us-east-1-nano-1
      containers:
      - name: zwavejs2mqtt
        image: zwavejs/zwavejs2mqtt:4.5.1
        ports:
        - containerPort: 8091
        - containerPort: 3000
        volumeMounts:
        - mountPath: /usr/src/app/store
          name: config
        - mountPath: /dev/ttyUSB0
          name: zwave
        securityContext:
          privileged: true
      volumes:
      - name: config
        hostPath:
          path: /etc/zwavejs2mqtt
          type: DirectoryOrCreate
      - name: zwave
        hostPath:
          path: /dev/ttyUSB0

Done

I also used Cloudflare Argo tunnel to allow me to control my home automation outside of my house.

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.