Files
nix-config/modules/nixos/homeassistant/services/homeassistant/default.nix
2026-03-23 14:07:48 -05:00

1089 lines
35 KiB
Nix

{
config,
lib,
pkgs,
namespace,
...
}:
with lib;
let
cfg = config.${namespace}.services.home-assistant;
in
{
# disabledModules = [
# "services/home-automation/home-assistant.nix"
# ];
# imports = [
# "${inputs.nixpkgs-unstable}/nixos/modules/services/home-automation/home-assistant.nix"
# ];
config = mkIf cfg.enable {
sops = {
secrets = {
"home-assistant/auth-client-id" = {
sopsFile = (lib.snowfall.fs.get-file "secrets/nuc-secrets.yaml");
owner = config.users.users.hass.name;
group = config.users.users.hass.group;
restartUnits = [ "home-assistant.service" ];
};
"home-assistant/auth-client-secret" = {
sopsFile = (lib.snowfall.fs.get-file "secrets/nuc-secrets.yaml");
owner = config.users.users.hass.name;
group = config.users.users.hass.group;
restartUnits = [ "home-assistant.service" ];
};
};
templates = {
"auth.yaml" = {
content = ''
client_id: "${config.sops.placeholder."home-assistant/auth-client-id"}"
client_secret: "${config.sops.placeholder."home-assistant/auth-client-secret"}"
discovery_url: "https://authentik.mjallen.dev/application/o/home-assistant/.well-known/openid-configuration"
roles:
user: "non_existent"
admin: "hass-admin"
display_name: "Authentik"
'';
owner = config.users.users.hass.name;
group = config.users.users.hass.group;
restartUnits = [ "home-assistant.service" ];
};
};
};
services.home-assistant = {
enable = true;
package = pkgs.home-assistant;
openFirewall = true;
configDir = "/var/lib/homeassistant";
configWritable = true; # todo
# All components
# extraComponents = config.services.home-assistant.package.availableComponents;
extraComponents = [
"accuweather"
"adguard"
"air_quality"
"apcupsd"
"apple_tv"
"analytics"
"assist_pipeline"
"assist_satellite"
"backup"
"bluetooth"
"bluetooth_adapters"
"bluetooth_le_tracker"
"brother"
"browser"
"button"
"caldav"
"calendar"
"camera"
"cast"
"climate"
"cloud"
"cloudflare"
"co2signal"
"coinbase"
"color_extractor"
"config"
"configurator"
"conversation"
"counter"
"cpuspeed"
"date"
"datetime"
"deluge"
"device_automation"
"device_sun_light_trigger"
"device_tracker"
"dhcp"
"diagnostics"
"discord"
"dlna_dmr"
"dlna_dms"
"downloader"
"ecobee"
"energy"
"epic_games_store"
"esphome"
"event"
"fan"
"fastdotcom"
"ffmpeg"
"ffmpeg_motion"
"ffmpeg_noise"
"file"
"file_upload"
"filesize"
"filter"
"folder"
"fully_kiosk"
"generic"
"generic_thermostat"
# "geo_json_events"
# "geo_location"
# "geo_rss_events"
"github"
"gitlab_ci"
"glances"
"google"
"google_cloud"
"google_drive"
"google_mail"
"google_maps"
"google_photos"
"google_translate"
"govee_ble"
"gpsd"
"gpslogger"
"hardware"
"hddtemp"
"hdmi_cec"
"history"
"history_stats"
"holiday"
"homeassistant"
"homeassistant_alerts"
"homeassistant_sky_connect"
"homekit"
"homekit_controller"
"http"
"humidifier"
"ibeacon"
"image"
"image_processing"
"image_upload"
"immich"
"input_boolean"
"input_button"
"input_datetime"
"input_number"
"input_select"
"input_text"
"ios"
"ipp"
"isal"
"jellyfin"
"lidarr"
"light"
"litterrobot"
"local_calendar"
"local_file"
"local_ip"
"local_todo"
"logbook"
"logentries"
"logger"
"lovelace"
"manual_mqtt"
"matter"
"met"
"microsoft"
"min_max"
"mobile_app"
"mqtt"
"music_assistant"
"ntfy"
"nut"
"nextcloud"
"notify"
"nws"
"ollama"
"onedrive"
"onkyo"
"openhardwaremonitor"
"opensky"
"openweathermap"
"open_router"
"paperless_ngx"
"persistent_notification"
"pinecil"
"ping"
"piper"
"playstation_network"
"radarr"
"radio_browser"
"raspberry_pi"
"recorder"
"reddit"
"remote"
"remote_calendar"
"remote_rpi_gpio"
"roborock" # currently broken
"samsungtv"
"schedule"
"script"
"search"
"season"
"select"
"sense"
"sensor"
"shopping_list"
"simplefin"
"smartthings"
"sonarr"
"speedtestdotnet"
"sql"
"ssdp"
"statistics"
"stream"
"subaru"
"sun"
"switch"
"system_health"
"system_log"
"systemmonitor"
"tailscale"
"tasmota"
"tcp"
"template"
"text"
"thread"
"time"
"time_date"
"timer"
"torque"
"tplink"
"tuya"
"upnp"
"uptime"
"usb"
"vacuum"
"vlc"
"vesync"
"wake_on_lan"
"waze_travel_time"
"weather"
"weatherkit"
"webdav"
"webhook"
"whisper"
"whois"
"workday"
"wyoming"
"youtube"
"zeroconf"
"zha"
"zone"
];
customComponents =
with pkgs.home-assistant-custom-components;
[
# nixpkgs
auth_oidc
better_thermostat
localtuya
luxer_one
prometheus_sensor
roborock_custom_map
smartir
versatile_thermostat
]
++ (with pkgs.${namespace}; [
ha-anycubic
ha-bambulab
ha-bedjet
ha-gehome
# ha-govee
ha-icloud3
ha-local-llm
ha-mail-and-packages
ha-nanokvm
ha-openhasp
ha-overseerr
ha-petlibro
ha-wyzeapi
]);
customLovelaceModules = with pkgs.home-assistant-custom-lovelace-modules; [
atomic-calendar-revive
bubble-card
button-card
hourly-weather
mini-graph-card
mini-media-player
multiple-entity-row
mushroom
vacuum-card
weather-chart-card
zigbee2mqtt-networkmap
];
# use postgresql instead of sqlite
extraPackages =
ps:
with ps;
[
dateparser
psycopg2
]
++ (with pkgs.${namespace}; [
gehomesdk
magicattr
pyoverseerr
python-roborock
wyzeapy
]);
config = {
# Includes dependencies for a basic setup
# https://www.home-assistant.io/integrations/default_config/
default_config = { };
cloud = false;
frontend = {
themes = "!include_dir_merge_named themes";
};
"automation manual" = "!include_dir_merge_list /etc/hass";
"automation ui" = "!include automations.yaml";
"scene ui" = "!include scenes.yaml";
"script ui" = "!include scripts.yaml";
auth_oidc = "!include ${config.sops.templates."auth.yaml".path}";
http = {
use_x_forwarded_for = true;
trusted_proxies = [
"172.30.33.0/24"
"10.0.1.4"
"10.0.1.3"
"10.0.1.18"
"10.0.1.0/24"
];
};
recorder = {
db_url = "postgresql://homeassistant@10.0.1.3/homeassistant";
purge_keep_days = 180;
};
# https://www.home-assistant.io/integrations/ota_updater/
zha.zigpy_config.ota.z2m_remote_index = "https://raw.githubusercontent.com/Koenkk/zigbee-OTA/master/index.json";
openhasp = {
plate = {
# nodes = [
# "plate"
# ];
# pages_jsonl = [
# "pages.jsonl"
# "page1.jsonl"
# "page2.jsonl"
# ];
objects = [
# -----------------------------------------------------------------------
# PAGE 1 LIGHTS
# -----------------------------------------------------------------------
# -- Living Room Lights ---
{
obj = "p1b11"; # Toggle button — short press toggles, long press opens color picker
properties = {
val = ''{{ 1 if is_state("light.living_room_lights", "on") else 0 }}'';
text = ''{{ "\uE6E8" if is_state("light.living_room_lights", "on") else "\uE335" | e }}'';
text_color = ''{{ "#FFD700" if is_state("light.living_room_lights", "on") else "#666666" }}'';
};
event = {
down = {
action = "homeassistant.toggle";
target = {
entity_id = "light.living_room_lights";
};
};
long = {
action = "openhasp.command";
data = {
node = "plate";
command = "page";
parameters = "4";
};
};
};
}
{
obj = "p1b13"; # Brightness slider
properties = {
val = ''
{{ state_attr('light.living_room_lights', 'brightness') | int(default=0)
if state_attr('light.living_room_lights', 'brightness') != None else 0 }}'';
click = "{{ 'false' if is_state('light.living_room_lights', 'off') else 'true' }}";
};
event = {
changed = {
action = "light.turn_on";
data = {
brightness = "{{ val }}";
};
target = {
entity_id = "light.living_room_lights";
};
};
up = {
action = "light.turn_on";
data = {
brightness = "{{ val }}";
};
target = {
entity_id = "light.living_room_lights";
};
};
};
}
# -- Bedroom Lights ---
{
obj = "p1b21"; # Toggle button — short press toggles, long press opens color picker
properties = {
val = ''{{ 1 if is_state("light.bedroom_lights", "on") else 0 }}'';
text = ''{{ "\uE6E8" if is_state("light.bedroom_lights", "on") else "\uE335" | e }}'';
text_color = ''{{ "#FFD700" if is_state("light.bedroom_lights", "on") else "#666666" }}'';
};
event = {
down = {
action = "homeassistant.toggle";
target = {
entity_id = "light.bedroom_lights";
};
};
long = {
action = "openhasp.command";
data = {
node = "plate";
command = "page";
parameters = "5";
};
};
};
}
{
obj = "p1b23"; # Brightness slider
properties = {
val = ''
{{ state_attr('light.bedroom_lights', 'brightness') | int(default=0)
if state_attr('light.bedroom_lights', 'brightness') != None else 0 }}'';
click = "{{ 'false' if is_state('light.bedroom_lights', 'off') else 'true' }}";
};
event = {
changed = {
action = "light.turn_on";
data = {
brightness = "{{ val }}";
};
target = {
entity_id = "light.bedroom_lights";
};
};
up = {
action = "light.turn_on";
data = {
brightness = "{{ val }}";
};
target = {
entity_id = "light.bedroom_lights";
};
};
};
}
# -- Front Closet Lights ---
{
obj = "p1b31"; # Toggle button — short press toggles, long press opens color picker
properties = {
val = ''{{ 1 if is_state("light.front_closet_lights", "on") else 0 }}'';
text = ''{{ "\uE6E8" if is_state("light.front_closet_lights", "on") else "\uE335" | e }}'';
text_color = ''{{ "#FFD700" if is_state("light.front_closet_lights", "on") else "#666666" }}'';
};
event = {
down = {
action = "homeassistant.toggle";
target = {
entity_id = "light.front_closet_lights";
};
};
long = {
action = "openhasp.command";
data = {
node = "plate";
command = "page";
parameters = "6";
};
};
};
}
# -----------------------------------------------------------------------
# PAGE 3 LIGHT COLOR TEMPERATURE
# -----------------------------------------------------------------------
# -- Living Room color temp --
{
obj = "p3b11"; # Toggle button (mirrors p1b11)
properties = {
val = ''{{ 1 if is_state("light.living_room_lights", "on") else 0 }}'';
text = ''{{ "\uE6E8" if is_state("light.living_room_lights", "on") else "\uE335" }}'';
text_color = ''{{ "#FFD700" if is_state("light.living_room_lights", "on") else "#666666" }}'';
};
event = {
down = {
action = "homeassistant.toggle";
target = {
entity_id = "light.living_room_lights";
};
};
};
}
{
obj = "p3b15"; # Color temp slider (153=cool, 500=warm mireds)
properties = {
val = ''
{{ state_attr('light.living_room_lights', 'color_temp') | int(default=250)
if state_attr('light.living_room_lights', 'color_temp') != None else 250 }}'';
click = "{{ 'false' if is_state('light.living_room_lights', 'off') else 'true' }}";
};
event = {
changed = {
action = "light.turn_on";
data = {
color_temp = "{{ val }}";
};
target = {
entity_id = "light.living_room_lights";
};
};
up = {
action = "light.turn_on";
data = {
color_temp = "{{ val }}";
};
target = {
entity_id = "light.living_room_lights";
};
};
};
}
# -- Bedroom color temp --
{
obj = "p3b21"; # Toggle button (mirrors p1b21)
properties = {
val = ''{{ 1 if is_state("light.bedroom_lights", "on") else 0 }}'';
text = ''{{ "\uE6E8" if is_state("light.bedroom_lights", "on") else "\uE335" }}'';
text_color = ''{{ "#FFD700" if is_state("light.bedroom_lights", "on") else "#666666" }}'';
};
event = {
down = {
action = "homeassistant.toggle";
target = {
entity_id = "light.bedroom_lights";
};
};
};
}
{
obj = "p3b25"; # Color temp slider
properties = {
val = ''
{{ state_attr('light.bedroom_lights', 'color_temp') | int(default=250)
if state_attr('light.bedroom_lights', 'color_temp') != None else 250 }}'';
click = "{{ 'false' if is_state('light.bedroom_lights', 'off') else 'true' }}";
};
event = {
changed = {
action = "light.turn_on";
data = {
color_temp = "{{ val }}";
};
target = {
entity_id = "light.bedroom_lights";
};
};
up = {
action = "light.turn_on";
data = {
color_temp = "{{ val }}";
};
target = {
entity_id = "light.bedroom_lights";
};
};
};
}
# -- Front Closet color temp --
{
obj = "p3b31"; # Toggle button (mirrors p1b31)
properties = {
val = ''{{ 1 if is_state("light.front_closet_lights", "on") else 0 }}'';
text = ''{{ "\uE6E8" if is_state("light.front_closet_lights", "on") else "\uE335" }}'';
text_color = ''{{ "#FFD700" if is_state("light.front_closet_lights", "on") else "#666666" }}'';
};
event = {
down = {
action = "homeassistant.toggle";
target = {
entity_id = "light.front_closet_lights";
};
};
};
}
{
obj = "p3b35"; # Color temp slider
properties = {
val = ''
{{ state_attr('light.front_closet_lights', 'color_temp') | int(default=250)
if state_attr('light.front_closet_lights', 'color_temp') != None else 250 }}'';
click = "{{ 'false' if is_state('light.front_closet_lights', 'off') else 'true' }}";
};
event = {
changed = {
action = "light.turn_on";
data = {
color_temp = "{{ val }}";
};
target = {
entity_id = "light.front_closet_lights";
};
};
up = {
action = "light.turn_on";
data = {
color_temp = "{{ val }}";
};
target = {
entity_id = "light.front_closet_lights";
};
};
};
}
# -----------------------------------------------------------------------
# PAGES 4/5/6 RGB COLOR PICKERS (long-press from page 1 buttons)
# -----------------------------------------------------------------------
# -- Page 4: Living Room RGB --
{
obj = "p4b11"; # Brightness slider (vertical)
properties = {
val = ''
{{ state_attr('light.living_room_lights', 'brightness') | int(default=128)
if state_attr('light.living_room_lights', 'brightness') != None else 128 }}'';
};
event = {
changed = {
action = "light.turn_on";
data = {
brightness = "{{ val }}";
};
target = {
entity_id = "light.living_room_lights";
};
};
up = {
action = "light.turn_on";
data = {
brightness = "{{ val }}";
};
target = {
entity_id = "light.living_room_lights";
};
};
};
}
{
obj = "p4b12"; # Color picker
properties = {
color = ''
{% if is_state('light.living_room_lights', 'on') %}
{% set rgb = state_attr('light.living_room_lights', 'rgb_color') %}
{% if rgb %}{{ "#%02x%02x%02x" | format(rgb[0], rgb[1], rgb[2]) }}{% endif %}
{% endif %}'';
};
event = {
up = {
action = "light.turn_on";
data = {
rgb_color = "[{{ r }},{{ g }},{{ b }}]";
};
target = {
entity_id = "light.living_room_lights";
};
};
};
}
{
obj = "p4b13"; # Color temp slider
properties = {
val = ''
{{ state_attr('light.living_room_lights', 'color_temp') | int(default=250)
if state_attr('light.living_room_lights', 'color_temp') != None else 250 }}'';
};
event = {
changed = {
action = "light.turn_on";
data = {
color_temp = "{{ val }}";
};
target = {
entity_id = "light.living_room_lights";
};
};
up = {
action = "light.turn_on";
data = {
color_temp = "{{ val }}";
};
target = {
entity_id = "light.living_room_lights";
};
};
};
}
# -- Page 5: Bedroom RGB --
{
obj = "p5b11"; # Brightness slider (vertical)
properties = {
val = ''
{{ state_attr('light.bedroom_lights', 'brightness') | int(default=128)
if state_attr('light.bedroom_lights', 'brightness') != None else 128 }}'';
};
event = {
changed = {
action = "light.turn_on";
data = {
brightness = "{{ val }}";
};
target = {
entity_id = "light.bedroom_lights";
};
};
up = {
action = "light.turn_on";
data = {
brightness = "{{ val }}";
};
target = {
entity_id = "light.bedroom_lights";
};
};
};
}
{
obj = "p5b12"; # Color picker
properties = {
color = ''
{% if is_state('light.bedroom_lights', 'on') %}
{% set rgb = state_attr('light.bedroom_lights', 'rgb_color') %}
{% if rgb %}{{ "#%02x%02x%02x" | format(rgb[0], rgb[1], rgb[2]) }}{% endif %}
{% endif %}'';
};
event = {
up = {
action = "light.turn_on";
data = {
rgb_color = "[{{ r }},{{ g }},{{ b }}]";
};
target = {
entity_id = "light.bedroom_lights";
};
};
};
}
{
obj = "p5b13"; # Color temp slider
properties = {
val = ''
{{ state_attr('light.bedroom_lights', 'color_temp') | int(default=250)
if state_attr('light.bedroom_lights', 'color_temp') != None else 250 }}'';
};
event = {
changed = {
action = "light.turn_on";
data = {
color_temp = "{{ val }}";
};
target = {
entity_id = "light.bedroom_lights";
};
};
up = {
action = "light.turn_on";
data = {
color_temp = "{{ val }}";
};
target = {
entity_id = "light.bedroom_lights";
};
};
};
}
# -- Page 6: Front Closet RGB --
{
obj = "p6b11"; # Brightness slider (vertical)
properties = {
val = ''
{{ state_attr('light.front_closet_lights', 'brightness') | int(default=128)
if state_attr('light.front_closet_lights', 'brightness') != None else 128 }}'';
};
event = {
changed = {
action = "light.turn_on";
data = {
brightness = "{{ val }}";
};
target = {
entity_id = "light.front_closet_lights";
};
};
up = {
action = "light.turn_on";
data = {
brightness = "{{ val }}";
};
target = {
entity_id = "light.front_closet_lights";
};
};
};
}
{
obj = "p6b12"; # Color picker
properties = {
color = ''
{% if is_state('light.front_closet_lights', 'on') %}
{% set rgb = state_attr('light.front_closet_lights', 'rgb_color') %}
{% if rgb %}{{ "#%02x%02x%02x" | format(rgb[0], rgb[1], rgb[2]) }}{% endif %}
{% endif %}'';
};
event = {
up = {
action = "light.turn_on";
data = {
rgb_color = "[{{ r }},{{ g }},{{ b }}]";
};
target = {
entity_id = "light.front_closet_lights";
};
};
};
}
{
obj = "p6b13"; # Color temp slider
properties = {
val = ''
{{ state_attr('light.front_closet_lights', 'color_temp') | int(default=250)
if state_attr('light.front_closet_lights', 'color_temp') != None else 250 }}'';
};
event = {
changed = {
action = "light.turn_on";
data = {
color_temp = "{{ val }}";
};
target = {
entity_id = "light.front_closet_lights";
};
};
up = {
action = "light.turn_on";
data = {
color_temp = "{{ val }}";
};
target = {
entity_id = "light.front_closet_lights";
};
};
};
}
# -----------------------------------------------------------------------
# PAGE 2 FANS
# -----------------------------------------------------------------------
# -- Bedroom Fan (preset mode = auto/low/medium/high) ---
{
obj = "p2b11"; # Toggle button
properties = {
val = ''{{ 1 if is_state("fan.bedroom_fan", "on") else 0 }}'';
text = ''{{ "\uE210" if is_state("fan.bedroom_fan", "on") else "\uE210" | e }}'';
text_color = ''{{ "#4FC3F7" if is_state("fan.bedroom_fan", "on") else "#666666" }}'';
};
event = {
down = {
action = "fan.toggle";
target = {
entity_id = "fan.bedroom_fan";
};
};
};
}
{
obj = "p2b13"; # Preset mode dropdown (auto/low/medium/high)
properties = {
val = ''
{% set modes = ['auto', 'low', 'medium', 'high'] %}
{% set current = state_attr('fan.bedroom_fan', 'preset_mode') %}
{{ modes.index(current) if current in modes else 0 }}'';
click = "{{ 'false' if is_state('fan.bedroom_fan', 'off') else 'true' }}";
};
event = {
changed = {
action = "fan.set_preset_mode";
data = {
preset_mode = "{{ text }}";
};
target = {
entity_id = "fan.bedroom_fan";
};
};
};
}
# -- Bedroom Air Purifier ---
{
obj = "p2b21"; # Toggle button
properties = {
val = ''{{ 1 if is_state("fan.bedroom_air_purifier", "on") else 0 }}'';
text = ''{{ "\uE210" | e }}'';
text_color = ''{{ "#4FC3F7" if is_state("fan.bedroom_air_purifier", "on") else "#666666" }}'';
};
event = {
down = {
action = "fan.toggle";
target = {
entity_id = "fan.bedroom_air_purifier";
};
};
};
}
{
obj = "p2b23"; # Speed slider (0-100%)
properties = {
val = ''
{{ state_attr('fan.bedroom_air_purifier', 'percentage') | int(default=0)
if state_attr('fan.bedroom_air_purifier', 'percentage') != None else 0 }}'';
click = "{{ 'false' if is_state('fan.bedroom_air_purifier', 'off') else 'true' }}";
};
event = {
changed = {
action = "fan.set_percentage";
data = {
percentage = "{{ val }}";
};
target = {
entity_id = "fan.bedroom_air_purifier";
};
};
up = {
action = "fan.set_percentage";
data = {
percentage = "{{ val }}";
};
target = {
entity_id = "fan.bedroom_air_purifier";
};
};
};
}
# -- Living Room Air Purifier ---
{
obj = "p2b31"; # Toggle button
properties = {
val = ''{{ 1 if is_state("fan.living_room_air_purifier", "on") else 0 }}'';
text = ''{{ "\uE210" | e }}'';
text_color = ''{{ "#4FC3F7" if is_state("fan.living_room_air_purifier", "on") else "#666666" }}'';
};
event = {
down = {
action = "fan.toggle";
target = {
entity_id = "fan.living_room_air_purifier";
};
};
};
}
{
obj = "p2b33"; # Speed slider (0-100%)
properties = {
val = ''
{{ state_attr('fan.living_room_air_purifier', 'percentage') | int(default=0)
if state_attr('fan.living_room_air_purifier', 'percentage') != None else 0 }}'';
click = "{{ 'false' if is_state('fan.living_room_air_purifier', 'off') else 'true' }}";
};
event = {
changed = {
action = "fan.set_percentage";
data = {
percentage = "{{ val }}";
};
target = {
entity_id = "fan.living_room_air_purifier";
};
};
up = {
action = "fan.set_percentage";
data = {
percentage = "{{ val }}";
};
target = {
entity_id = "fan.living_room_air_purifier";
};
};
};
}
];
};
};
conversation = {
intents = {
GetStatus = [
"What's the status of {entity}"
"Is {entity} {state}"
"How is the {entity}"
"What is the {sensor_type} in {location}"
];
ControlDevice = [
"Turn {state} {entity}"
"Set {entity} to {value}"
"Adjust {entity} to {value}"
"Make {entity} {value}"
"{state} {entity}"
];
ActivateScene = [
"Activate {scene}"
"Set scene to {scene}"
"I'm {activity}"
"We're {activity}"
"Time for {activity}"
];
RunAutomation = [
"Run {automation}"
"Start {automation}"
"Trigger {automation}"
"Execute {automation}"
];
HomeQuery = [
"When did {entity} last change"
"How long has {entity} been {state}"
"Which {domain} are {state}"
"Where is {person}"
];
SystemCommand = [
"Restart Home Assistant"
"Reload {component}"
"What version are you running"
"Are there any errors"
"System status"
];
};
};
};
};
};
}