diff --git a/broadlinkac2mqtt/.gitignore b/broadlinkac2mqtt/.gitignore new file mode 100644 index 0000000..8a49481 --- /dev/null +++ b/broadlinkac2mqtt/.gitignore @@ -0,0 +1,2 @@ +test-values.yaml +*.tgz diff --git a/broadlinkac2mqtt/Chart.yaml b/broadlinkac2mqtt/Chart.yaml new file mode 100644 index 0000000..f673224 --- /dev/null +++ b/broadlinkac2mqtt/Chart.yaml @@ -0,0 +1,9 @@ +apiVersion: v2 +name: broadlinkac2mqtt +description: Broadlink AC to MQTT bridge - control Broadlink AC units via MQTT +type: application +version: 0.1.0 +appVersion: "1.5.5" +annotations: + version-source: github-release:ArtemVladimirov/broadlinkac2mqtt + version-pattern: "s|^v||" diff --git a/broadlinkac2mqtt/README.md b/broadlinkac2mqtt/README.md new file mode 100644 index 0000000..a81e410 --- /dev/null +++ b/broadlinkac2mqtt/README.md @@ -0,0 +1,220 @@ +# Broadlink AC2MQTT Helm Chart + +Bridge between Broadlink-based air conditioners and MQTT. Enables control of Broadlink AC units through MQTT, with Home Assistant auto-discovery support. + +## Overview + +- **Application**: [ArtemVladimirov/broadlinkac2mqtt](https://github.com/ArtemVladimirov/broadlinkac2mqtt) +- **Type**: MQTT bridge (no HTTP server) +- **Platform Support**: Multi-arch (amd64, arm64) +- **Image**: `ghcr.io/artemvladimirov/broadlinkac2mqtt` + +## Features + +- Control Broadlink AC units via MQTT +- Home Assistant MQTT Discovery support +- Multi-device support +- Configurable update intervals +- Automatic reconnection handling +- Health checks for container reliability + +## Prerequisites + +- Kubernetes cluster +- MQTT broker (e.g., Mosquitto) +- Broadlink AC units on your network +- Device IP addresses and MAC addresses + +## Installation + +### Basic Installation + +```bash +helm install broadlinkac2mqtt ./broadlinkac2mqtt \ + --set config.mqtt.server="tcp://mqtt-broker:1883" \ + --set config.mqtt.user="myuser" \ + --set config.mqtt.password="mypassword" +``` + +### With Device Configuration + +Create a `values-custom.yaml` file: + +```yaml +config: + mqtt: + server: "tcp://mqtt.home.lan:1883" + user: "broadlink" + password: "secret" + discovery: true + discovery_prefix: "homeassistant" + + devices: + - ip: "192.168.1.100" + mac: "AA:BB:CC:DD:EE:FF" + name: "Living Room AC" + port: 80 + temp_unit: "C" + + - ip: "192.168.1.101" + mac: "11:22:33:44:55:66" + name: "Bedroom AC" + port: 80 + temp_unit: "F" +``` + +Install with custom values: + +```bash +helm install broadlinkac2mqtt ./broadlinkac2mqtt -f values-custom.yaml +``` + +### Using Existing Secret for MQTT Credentials + +Create a secret: + +```bash +kubectl create secret generic broadlink-mqtt-creds \ + --from-literal=MQTT_USER=myuser \ + --from-literal=MQTT_PASSWORD=mypassword +``` + +Install referencing the secret: + +```yaml +existingSecret: "broadlink-mqtt-creds" +secretKeys: + mqttUser: "MQTT_USER" + mqttPassword: "MQTT_PASSWORD" + +config: + mqtt: + server: "tcp://mqtt.home.lan:1883" + # user and password will be read from secret + devices: + - ip: "192.168.1.100" + mac: "AA:BB:CC:DD:EE:FF" + name: "Living Room AC" +``` + +## Configuration + +### Service Settings + +| Parameter | Description | Default | +|-----------|-------------|---------| +| `config.service.update_interval` | Seconds between device polls | `10` | +| `config.service.log_level` | Log level (debug/info/error/disabled) | `info` | + +### MQTT Settings + +| Parameter | Description | Default | +|-----------|-------------|---------| +| `config.mqtt.server` | MQTT broker URL | `tcp://mqtt-broker:1883` | +| `config.mqtt.user` | MQTT username | `""` | +| `config.mqtt.password` | MQTT password | `""` | +| `config.mqtt.client_id` | MQTT client ID | `broadlinkac2mqtt` | +| `config.mqtt.discovery` | Enable Home Assistant discovery | `true` | +| `config.mqtt.discovery_prefix` | HA discovery prefix | `homeassistant` | +| `config.mqtt.topic_prefix` | Base topic prefix | `broadlinkac2mqtt` | + +### Device Configuration + +Each device requires: + +- `ip`: Device IP address (required) +- `mac`: Device MAC address in format AA:BB:CC:DD:EE:FF (required) +- `name`: Friendly name for the device (required) +- `port`: Device port (optional, default: 80) +- `temp_unit`: Temperature unit "C" or "F" (optional, default: C) + +### Resource Configuration + +| Parameter | Description | Default | +|-----------|-------------|---------| +| `resources.requests.cpu` | CPU request | `50m` | +| `resources.requests.memory` | Memory request | `64Mi` | +| `resources.limits.cpu` | CPU limit | `200m` | +| `resources.limits.memory` | Memory limit | `256Mi` | + +## Health Checks + +The chart includes comprehensive health checks: + +- **Startup Probe**: Allows 60 seconds for MQTT connection and device authentication +- **Liveness Probe**: Verifies the main process is running every 30 seconds +- **Readiness Probe**: Checks config file presence and process health every 15 seconds + +All probes use process-based checks since the application doesn't expose HTTP endpoints. + +## Home Assistant Integration + +When `config.mqtt.discovery` is enabled, devices automatically appear in Home Assistant as climate entities. The integration supports: + +- Power on/off +- Mode selection (cool, heat, auto, fan, dry) +- Temperature setpoint +- Fan speed control +- Swing mode control + +## Troubleshooting + +### Check pod status + +```bash +kubectl get pods -l app=broadlinkac2mqtt +kubectl logs -l app=broadlinkac2mqtt +``` + +### Verify configuration + +```bash +kubectl get configmap broadlinkac2mqtt-config -o yaml +``` + +### Common Issues + +1. **Pod won't start**: Check MQTT broker connectivity +2. **Devices not discovered**: Verify IP addresses and MAC addresses are correct +3. **Connection timeouts**: Ensure devices are on the same network segment +4. **MQTT authentication failed**: Verify credentials in secret or config + +## Version Management + +The chart includes version tracking annotations compatible with automated version checking tools: + +```yaml +annotations: + version-source: github-release:ArtemVladimirov/broadlinkac2mqtt + version-pattern: "s|^v||" +``` + +Check for updates: +```bash +# Using just (if in k3s repo) +just app check broadlinkac2mqtt + +# Or manually check releases +curl -s https://api.github.com/repos/ArtemVladimirov/broadlinkac2mqtt/releases/latest | jq -r .tag_name +``` + +## Upgrading + +Update the chart version: + +```bash +helm upgrade broadlinkac2mqtt ./broadlinkac2mqtt -f values-custom.yaml +``` + +## Uninstalling + +```bash +helm uninstall broadlinkac2mqtt +``` + +## Notes + +- This is an MQTT-only application with no HTTP interface +- Only one replica should run to avoid conflicting device commands +- The application uses environment variable substitution for secrets in config.yml +- Config changes require pod restart to take effect diff --git a/broadlinkac2mqtt/templates/configmap.yaml b/broadlinkac2mqtt/templates/configmap.yaml new file mode 100644 index 0000000..a2e5168 --- /dev/null +++ b/broadlinkac2mqtt/templates/configmap.yaml @@ -0,0 +1,51 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ .Release.Name }}-config + labels: + app.kubernetes.io/name: {{ .Chart.Name }} + app.kubernetes.io/instance: {{ .Release.Name }} +data: + config.yml: | + service: + update_interval: {{ .Values.config.service.update_interval }} + log_level: {{ .Values.config.service.log_level }} + + mqtt: + server: {{ .Values.config.mqtt.server | quote }} + {{- if .Values.existingSecret }} + user: ${MQTT_USER} + password: ${MQTT_PASSWORD} + {{- else }} + user: {{ .Values.config.mqtt.user | quote }} + password: {{ .Values.config.mqtt.password | quote }} + {{- end }} + client_id: {{ .Values.config.mqtt.client_id | quote }} + discovery: {{ .Values.config.mqtt.discovery }} + discovery_prefix: {{ .Values.config.mqtt.discovery_prefix | quote }} + topic_prefix: {{ .Values.config.mqtt.topic_prefix | quote }} + state_topic: {{ .Values.config.mqtt.state_topic | quote }} + command_topic: {{ .Values.config.mqtt.command_topic | quote }} + availability_topic: {{ .Values.config.mqtt.availability_topic | quote }} + {{- with .Values.config.mqtt.ca_cert }} + ca_cert: {{ . | quote }} + {{- end }} + {{- with .Values.config.mqtt.client_cert }} + client_cert: {{ . | quote }} + {{- end }} + {{- with .Values.config.mqtt.client_key }} + client_key: {{ . | quote }} + {{- end }} + + devices: + {{- range .Values.config.devices }} + - ip: {{ .ip | quote }} + mac: {{ .mac | quote }} + name: {{ .name | quote }} + {{- with .port }} + port: {{ . }} + {{- end }} + {{- with .temp_unit }} + temp_unit: {{ . | quote }} + {{- end }} + {{- end }} diff --git a/broadlinkac2mqtt/templates/deployment.yaml b/broadlinkac2mqtt/templates/deployment.yaml new file mode 100644 index 0000000..3b1367b --- /dev/null +++ b/broadlinkac2mqtt/templates/deployment.yaml @@ -0,0 +1,97 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Release.Name }} + labels: + app.kubernetes.io/name: {{ .Chart.Name }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} + app.kubernetes.io/component: mqtt-bridge + app.kubernetes.io/part-of: home-automation + annotations: + version-source: {{ index .Chart.Annotations "version-source" }} + version-pattern: {{ index .Chart.Annotations "version-pattern" | quote }} +spec: + replicas: 1 + strategy: + type: Recreate # Only one instance should control the AC units + selector: + matchLabels: + app: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ .Release.Name }} + annotations: + checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} + spec: + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + env: + - name: TZ + value: {{ .Values.timezone | quote }} + {{- if .Values.existingSecret }} + - name: MQTT_USER + valueFrom: + secretKeyRef: + name: {{ .Values.existingSecret }} + key: {{ .Values.secretKeys.mqttUser }} + - name: MQTT_PASSWORD + valueFrom: + secretKeyRef: + name: {{ .Values.existingSecret }} + key: {{ .Values.secretKeys.mqttPassword }} + {{- end }} + volumeMounts: + - name: config + mountPath: /app/config + readOnly: true + resources: + {{- toYaml .Values.resources | nindent 12 }} + # Startup probe: give MQTT connection and device auth time to complete + startupProbe: + exec: + command: + - /bin/sh + - -c + - 'pgrep -f /main > /dev/null' + initialDelaySeconds: 5 + periodSeconds: 5 + failureThreshold: 12 # 60 seconds total startup time + # Liveness probe: verify process is still running + livenessProbe: + exec: + command: + - /bin/sh + - -c + - 'pgrep -f /main > /dev/null' + initialDelaySeconds: 30 + periodSeconds: 30 + failureThreshold: 3 + # Readiness probe: check config file is present and process is running + readinessProbe: + exec: + command: + - /bin/sh + - -c + - 'test -f /app/config/config.yml && pgrep -f /main > /dev/null' + initialDelaySeconds: 10 + periodSeconds: 15 + volumes: + - name: config + configMap: + name: {{ .Release.Name }}-config diff --git a/broadlinkac2mqtt/templates/ingress.yaml b/broadlinkac2mqtt/templates/ingress.yaml new file mode 100644 index 0000000..ae2ec55 --- /dev/null +++ b/broadlinkac2mqtt/templates/ingress.yaml @@ -0,0 +1,30 @@ +# Ingress is not applicable for broadlinkac2mqtt (MQTT-only application) +# This template is included for Helm chart consistency only + +{{- range $name, $ingress := .Values.ingresses }} +# --- +# apiVersion: traefik.io/v1alpha1 +# kind: IngressRoute +# metadata: +# name: {{ $.Release.Name }}-{{ $name }} +# labels: +# app.kubernetes.io/name: {{ $.Chart.Name }} +# app.kubernetes.io/instance: {{ $.Release.Name }} +# {{- with $ingress.annotations }} +# annotations: +# {{- toYaml . | nindent 4 }} +# {{- end }} +# spec: +# entryPoints: +# - websecure +# routes: +# - kind: Rule +# match: Host(`{{ $ingress.host }}`) +# services: +# - name: {{ $.Release.Name }} +# port: 80 +# {{- with $ingress.tls }} +# tls: +# {{- toYaml . | nindent 4 }} +# {{- end }} +{{- end }} diff --git a/broadlinkac2mqtt/templates/service.yaml b/broadlinkac2mqtt/templates/service.yaml new file mode 100644 index 0000000..064984c --- /dev/null +++ b/broadlinkac2mqtt/templates/service.yaml @@ -0,0 +1,17 @@ +# This service is not used by broadlinkac2mqtt (MQTT-only application) +# Included for Helm chart consistency only +# apiVersion: v1 +# kind: Service +# metadata: +# name: {{ .Release.Name }} +# labels: +# app.kubernetes.io/name: {{ .Chart.Name }} +# app.kubernetes.io/instance: {{ .Release.Name }} +# spec: +# type: ClusterIP +# selector: +# app: {{ .Release.Name }} +# ports: +# - protocol: TCP +# port: 80 +# targetPort: 80 diff --git a/broadlinkac2mqtt/values.yaml b/broadlinkac2mqtt/values.yaml new file mode 100644 index 0000000..ab14763 --- /dev/null +++ b/broadlinkac2mqtt/values.yaml @@ -0,0 +1,79 @@ +# Default values for broadlinkac2mqtt Helm chart +# Bridge between Broadlink AC units and MQTT + +image: + repository: ghcr.io/artemvladimirov/broadlinkac2mqtt + tag: "" # defaults to Chart.appVersion if empty + pullPolicy: IfNotPresent + +# Timezone for the container +timezone: "Europe/Kyiv" + +resources: + requests: + cpu: "50m" + memory: "64Mi" + limits: + cpu: "200m" + memory: "256Mi" + +# Configuration for the application +# This will be mounted as /app/config/config.yml inside the container +config: + # Service configuration + service: + update_interval: 10 # seconds between device polls + log_level: "info" # debug, info, error, disabled + + # MQTT broker configuration + mqtt: + server: "tcp://mqtt-broker:1883" + user: "" + password: "" + client_id: "broadlinkac2mqtt" + + # Home Assistant MQTT Discovery + discovery: true + discovery_prefix: "homeassistant" + + # Topic structure + topic_prefix: "broadlinkac2mqtt" + state_topic: "tele" + command_topic: "cmnd" + availability_topic: "avty" + + # Optional: TLS/SSL configuration + # ca_cert: "" + # client_cert: "" + # client_key: "" + + # Broadlink AC devices + # Each device needs: ip, mac, name, and optionally port and temp_unit + devices: [] + # - ip: "192.168.1.100" + # mac: "AA:BB:CC:DD:EE:FF" + # name: "Living Room AC" + # port: 80 + # temp_unit: "C" # C or F + # - ip: "192.168.1.101" + # mac: "11:22:33:44:55:66" + # name: "Bedroom AC" + # port: 80 + # temp_unit: "C" + +# Alternative: use existing secret for MQTT credentials +# If set, mqtt.user and mqtt.password from config above will be ignored +existingSecret: "" +secretKeys: + mqttUser: "MQTT_USER" + mqttPassword: "MQTT_PASSWORD" + +# Node scheduling +nodeSelector: {} + +tolerations: [] + +affinity: {} + +# Ingress configuration (not used by this MQTT-only app, but kept for consistency) +ingresses: {}