From e56526600dec68faa8c46c3fefac56624d8824d0 Mon Sep 17 00:00:00 2001 From: "Dustin C. Hatch" Date: Wed, 27 Dec 2023 14:11:51 -0600 Subject: [PATCH] home-assistant: Manage YAML files with ConfigMap Editing `configuration.yaml` et al. using `vi` via `kubectl exec` is rather tedious, since the version of `vi` in the *home-assistant* container image is very rudimentary. Thus, I think it would be better to use a ConfigMap to store the manually-edited YAML files, so I can edit them with my regular editor on my desktop. For this to work, the ConfigMap has to be mounted as a directory rather than as individual files (using `subPath`), as otherwise the pod would have to be restarted every time one of the files is updated. --- home-assistant/README.md | 16 ++ home-assistant/configuration.yaml | 289 ++++++++++++++++++++++++++++++ home-assistant/groups.yaml | 15 ++ home-assistant/kustomization.yaml | 28 +++ home-assistant/secrets.yaml | 18 ++ home-assistant/shell-command.yaml | 2 + 6 files changed, 368 insertions(+) create mode 100644 home-assistant/configuration.yaml create mode 100644 home-assistant/groups.yaml create mode 100644 home-assistant/shell-command.yaml diff --git a/home-assistant/README.md b/home-assistant/README.md index 53ef53e..26246c0 100644 --- a/home-assistant/README.md +++ b/home-assistant/README.md @@ -50,6 +50,22 @@ exposed by a Service resource, which in turn is proxied by an Ingress resource. [Home Assistant]: https://www.home-assistant.io/ +#### ConfigMaps + +Although most Home Assistant configuration is managed by its web UI, some +settings and integrations are read from manually-managed YAML files. Some +notable examples include the [Shell Command] and [Group] integrations. To make +it easier to edit these files, they are stored in a ConfigMap which is mounted +into the Home Assistant container. Since the Kublet will not automatically +update mounted ConfigMaps when files are mounted individually, the entire +ConfigMap has to be mounted as a directory. Files that must exist within the +configuration directory (i.e. `/config`) need symbolic links pointing to the +respective files in the ConfigMap mount point. + +[Shell Command]: https://www.home-assistant.io/integrations/shell_command +[Group]: https://www.home-assistant.io/integrations/group + + ### PostgreSQL Although Home Assistant stores all of its internal state in JSON files on the diff --git a/home-assistant/configuration.yaml b/home-assistant/configuration.yaml new file mode 100644 index 0000000..18bc67c --- /dev/null +++ b/home-assistant/configuration.yaml @@ -0,0 +1,289 @@ +assist_pipeline: +config: +counter: +energy: +frontend: +history: +image: +input_boolean: +input_button: +input_datetime: +input_number: +input_select: +input_text: +logbook: +map: +media_source: +mobile_app: +person: +schedule: +ssdp: +stream: +sun: +system_health: +tag: +timer: +webhook: +zeroconf: +zone: + +http: + trusted_proxies: + - 172.30.0.160/28 + use_x_forwarded_for: true + +recorder: + db_url: !env_var RECORDER_DB_URL + db_max_retries: 100 + purge_keep_days: 366 + commit_interval: 0 + +homeassistant: + whitelist_external_dirs: + - /config + - /tmp + +logger: + default: info + +tts: + - platform: picotts + +group: !include groups.yaml +automation: !include automations.yaml +script: !include scripts.yaml +scene: !include scenes.yaml +shell_command: !include /run/config/shell-command.yaml + +lovelace: + mode: storage + #dashboards: !include dashboards.yaml + +#mqtt: !include mqtt.yaml + +light: +- platform: group + name: Basement Lights + entities: + - light.desk_lamp_1 + - light.desk_lamp_2 + - light.desk_lamp_3 + - light.light_2 + - light.light_3 + - light.light_4 + - light.light_5 + - light.light_6 + - light.light_7 + +matrix: + homeserver: https://hatch.chat + username: '@homeassistant:hatch.chat' + password: !secret matrix_password + rooms: + - '!DdgnpVhlRqeTeNqSEM:hatch.chat' + - '!oyDXJxjUeJkEFshmAn:hatch.chat' + commands: + - word: snapshot + name: snapshot + - word: bunnies + name: bunnies + - expression: 'lights (?P.*)' + name: lights + +notify: +- platform: matrix + name: matrix + default_room: '!DdgnpVhlRqeTeNqSEM:hatch.chat' +- platform: group + name: mobile_apps_group + services: + - service: mobile_app_pixel_5 + - service: mobile_app_pixel_6a +- name: ntfy + platform: rest + method: POST_JSON + target_param_name: topic + title_param_name: title + message_param_name: message + resource: https://ntfy.pyrocufflink.net +- name: ntfy2 + platform: ntfy + url: https://ntfy.pyrocufflink.net + default_topic: homeassistant + +sensor: +- name: Rain last 24 hours + platform: statistics + entity_id: sensor.rain_gauge + unique_id: sensor.rain_last_24_hours + state_characteristic: change + max_age: + hours: 24 + +template: +- sensor: + - name: 'Thermostat Temperature' + device_class: temperature + unit_of_measurement: °C + state: >- + {% if is_state('sensor.season', 'winter') %} + {{ states('sensor.living_room_temperature') }} + {% else %} + {{ states('sensor.bedroom_temperature') }} + {% endif %} + + - name: "Tonight's Forecast" + device_class: temperature + unit_of_measurement: °C + state: >- + {{ state_attr('weather.kojc_daynight', 'forecast') + | rejectattr('is_daytime') + | map(attribute='temperature') + | first }} + + - name: Cost per Mow + device_class: monetary + unit_of_measurement: USD + state: >- + {{ 3072.21 / states('counter.mow_count')|int }} + + - name: Apc1500 Load + device_class: power + unit_of_measurement: W + state: >- + {{ "%.1f" | format( + states('sensor.apc1500_load') | float / 100 + * states('sensor.apc1500_nominal_real_power') | float + ) + }} + + - name: Apc1300 Load + device_class: power + unit_of_measurement: W + state: >- + {{ "%.1f" | format( + states('sensor.apc1300_load') | float / 100 + * states('sensor.apc1300_nominal_real_power') | float + ) + }} + +cover: +- platform: template + covers: + garage_door_1: + device_class: garage + friendly_name: "Garage Door 1" + value_template: >- + {% if is_state('binary_sensor.garage_door_1_window_door_is_open', 'on') %} + open + {% else %} + closed + {% endif %} + open_cover: + service: switch.turn_on + target: + entity_id: switch.garage_door_relay_3 + close_cover: + service: switch.turn_on + target: + entity_id: switch.garage_door_relay_3 + icon_template: >- + {% if is_state('binary_sensor.garage_door_1_window_door_is_open', 'on') %} + mdi:garage-open + {% else %} + mdi:garage + {% endif %} + +- platform: template + covers: + garage_door_2: + device_class: garage + friendly_name: "Garage Door 2" + value_template: >- + {% if is_state('binary_sensor.garage_door_2_window_door_is_open', 'on') %} + open + {% else %} + closed + {% endif %} + open_cover: + service: switch.turn_on + target: + entity_id: switch.garage_door_relay_2_2 + close_cover: + service: switch.turn_on + target: + entity_id: switch.garage_door_relay_2_2 + icon_template: >- + {% if is_state('binary_sensor.garage_door_2_window_door_is_open', 'on') %} + mdi:garage-open + {% else %} + mdi:garage + {% endif %} + +- platform: template + covers: + garage_door_3: + device_class: garage + friendly_name: "Garage Door 3" + value_template: >- + {% if is_state('binary_sensor.garage_door_3_window_door_is_open', 'on') %} + open + {% else %} + closed + {% endif %} + open_cover: + service: switch.turn_on + target: + entity_id: switch.garage_door_relay_2 + close_cover: + service: switch.turn_on + target: + entity_id: switch.garage_door_relay_2 + icon_template: >- + {% if is_state('binary_sensor.garage_door_3_window_door_is_open', 'on') %} + mdi:garage-open + {% else %} + mdi:garage + {% endif %} + +switch: +- platform: wake_on_lan + name: Rosalina + host: rosalina.pyrocufflink.blue + mac: 0c:9d:92:0e:3a:41 +- platform: wake_on_lan + name: vmhost0 + host: vmhost0.pyrocufflink.blue + mac: e0:d5:5e:a2:2e:1a +- platform: wake_on_lan + name: vmhost1 + host: vmhost1.pyrocufflink.blue + mac: 18:c0:4d:90:4d:00 +- platform: wake_on_lan + name: Toad + host: toad.pyrocufflink.blue + mac: 1c:1b:0d:0f:ad:df +- platform: wake_on_lan + name: Sonic + host: sonic.pyrocufflink.blue + mac: e0:d5:5e:6e:ad:ac + broadcast_address: 172.30.0.63 + +binary_sensor: +- platform: template + sensors: + roomba_is_downstairs: + friendly_name: Roomba is Downstairs + value_template: >- + {% if is_state('binary_sensor.roomba_ibeacon_ble_presence', 'on') and + states('sensor.roomba_ibeacon_ble_rssi') | float > -70 %} + on + {% else %} + off + {% endif %} + +prometheus: + filter: + exclude_entity_globs: + - binary_sensor.node_14* + - binary_sensor.node_15* diff --git a/home-assistant/groups.yaml b/home-assistant/groups.yaml new file mode 100644 index 0000000..cc4dee7 --- /dev/null +++ b/home-assistant/groups.yaml @@ -0,0 +1,15 @@ +family: + entities: + - person.dustin + - person.tabitha + name: Family +watch_view: + entities: + - cover.garage_door_3 + - cover.garage_door_2 + - cover.garage_door_1 + - light.front_porch_light + - light.back_porch_light + - light.back_porch_flood_light + - light.garage_lights + name: Watch View diff --git a/home-assistant/kustomization.yaml b/home-assistant/kustomization.yaml index 7c04681..cfda1bd 100644 --- a/home-assistant/kustomization.yaml +++ b/home-assistant/kustomization.yaml @@ -20,6 +20,18 @@ resources: - ingress.yaml configMapGenerator: +- name: home-assistant + files: + - configuration.yaml + - groups.yaml + - shell-command.yaml + options: + disableNameSuffixHash: true + labels: + app.kubernetes.io/name: home-assistant + app.kubernetes.io/component: home-assistant + app.kubernetes.io/part-of: home-assistant + - name: mosquitto files: - mosquitto.conf @@ -48,3 +60,19 @@ patches: key: username - name: RECORDER_DB_URL value: postgresql://$(RECORDER_DB_USERNAME):$(RECORDER_DB_PASSWORD)@default.postgresql/homeassistant + volumeMounts: + - mountPath: /run/config + name: home-assistant-config + readOnly: true + - mountPath: /run/secrets/home-assistant + name: home-assistant-secrets + readOnly: true + volumes: + - name: home-assistant-config + configMap: + name: home-assistant + defaultMode: 0600 + - name: home-assistant-secrets + secret: + secretName: home-assistant + defaultMode: 0640 diff --git a/home-assistant/secrets.yaml b/home-assistant/secrets.yaml index 6dcb492..b8627ba 100644 --- a/home-assistant/secrets.yaml +++ b/home-assistant/secrets.yaml @@ -13,3 +13,21 @@ spec: creationTimestamp: null name: mosquitto namespace: home-assistant + +--- +apiVersion: bitnami.com/v1alpha1 +kind: SealedSecret +metadata: + name: home-assistant + namespace: home-assistant + labels: + app.kubernetes.io/name: home-assistant + app.kubernetes.io/component: home-assistant + app.kubernetes.io/part-of: home-assistant +spec: + encryptedData: + secrets.yaml: AgAcQGBxXTW6s/4lcGoyKYn9NL7h4L8rbFeC87GbXQunVD4h37kiET55c5AgSj95X0Y8Vn9EYgd85xY5vDjxkAQ8qmcBL0SlrHuAxlRONoVDIgUmGIk91p4boVXp4Db+DzvtezjCruraIh4MgVQS9nS30MG3PWb/IfsHeaGNAyIL6OCdJYWTIUD9ceCdCNrTCeK45qow+ZHYhwpw6puXLQEq45j1UX1fgCsnNfYxDXRoW/vOwFLnOfN6JTjSyu28Qo4jIqu0iF26hMViU+ok/eYzC9J0fpfwGHhDdGMI29cWYyTS+L0BBbV37mMPx7UKYShweL6kK6Ar2ewB6tFQzr4eHcX6Qb8EuNYKBHz9kLrgZp54diUuiX1thjwbSLkv+HJa4voKyvsab3Nug4FJPlvg6R6J852dpmFK1ElK5ejkGaqfvT0iqGA1AvQe51FOSOARjC9/g29i6T5S5+5Cc3igJdfiadd3vTWTKpR0kf7mvoL4MT4OGPvir2O1bjXB5Mv9zDt2Cm+ZD5ve3n4Dq5WqOvkJN9FJMRe04rM1a1yP8lSzuil20OVDc1N4wrCtPJ71bqsNpLAbsYy2E/t2gU1UVM+HkkA9FucAQs5fs8Fe5fYsYLg6zUtM6i9fB+Y1JbAY9GWYMAA+enkrkAHLqxxFF1g5Dc9cpmyP+1fu5jy2BF0GfmIPuyuUTch/20pngnoFLM9JdkRP0SkgJ4/wtAnWKY1twbUPjb/L53rEpZWICPbJjR19Y1FycDwAauZmTmg8ZQ0ro1uh77rbTsCmViUKwXsrSUTY7mGmD5q4LCtRZqWRBDt3Hl5jHvhzvUSGeso+YnnaLr6GXYdRkibxWA5X6SG1iBHoQzKqUPO28Ybsnv6Au4RloDU2TAAUNnl32L0Kq4yjIwlAq9r2ASWwgUJUqU12i3wnC4iHrE6jeLQSQ6yrkJOrUyizaPJWfr/4JFeLVOcBM1SjR0y/dtuWnPvgItsTHwDJyw== + template: + metadata: + name: home-assistant + namespace: home-assistant diff --git a/home-assistant/shell-command.yaml b/home-assistant/shell-command.yaml new file mode 100644 index 0000000..3c2b5c7 --- /dev/null +++ b/home-assistant/shell-command.yaml @@ -0,0 +1,2 @@ +restart_diddy_mopidy: >- + ssh -i /config/homeassistant-ssh.pem -oUserKnownHostsFile=/config/ssh_known_hosts -oBatchMode=yes pi@diddy.pyrocufflink.red restart-mopidy