diff --git a/flake.nix b/flake.nix index cc7565f..49e46e6 100644 --- a/flake.nix +++ b/flake.nix @@ -54,6 +54,7 @@ nixng/containers/matrix/heisenbridge nixng/containers/matrix/synapse nixng/containers/website + nixng/containers/home-assistant nixng/containers/email/getmail nixng/containers/email/dovecot.nix nixng/containers/email/postfix diff --git a/nixng/containers/home-assistant/configs/alarm_control_panel.nix b/nixng/containers/home-assistant/configs/alarm_control_panel.nix new file mode 100644 index 0000000..4db7258 --- /dev/null +++ b/nixng/containers/home-assistant/configs/alarm_control_panel.nix @@ -0,0 +1,17 @@ +{ + platform = "manual"; + name = "Home Alarm"; + code = "\${ALARM_CODE}"; + code_arm_required = false; # Don't need code to arm. + disarm_after_trigger = false; # Leave alarm armed after triggering. + arming_time = 0; # Time to leave the house after arming. + delay_time = 30; # Time to turn off after entering the house. + trigger_time = 3600; # How long the alarm goes off for. + disarmed = { + trigger_time = 0; # Can't be triggered when disarmed. + }; + armed_home = { + arming_time = 0; # Arm instantly when at home. + delay_time = 0; # Trigger instantly when at home. + }; +} diff --git a/nixng/containers/home-assistant/configs/automations/alarm/armed_away.nix b/nixng/containers/home-assistant/configs/automations/alarm/armed_away.nix new file mode 100644 index 0000000..f22bb4c --- /dev/null +++ b/nixng/containers/home-assistant/configs/automations/alarm/armed_away.nix @@ -0,0 +1,45 @@ +{ + alias = "Alarm - Armed Check Windows"; + id = "8748ce03-2910-468e-8764-8d43604a051d"; + + trigger = [ + { + entity_id = "alarm_control_panel.home_alarm"; + platform = "state"; + to = "armed"; + } + ]; + + condition = { + condition = "or"; + conditions = [ + { + condition = "state"; + entity_id = "binary_sensor.left_window_contact"; + state = "on"; + } + { + condition = "state"; + entity_id = "binary_sensor.right_window_contact"; + state = "on"; + } + ]; + }; + + action = [ + { + service = "alarm_control_panel.disarm"; + entity_id = "alarm_control_panel.home_alarm"; + } + { + data = { + message = "Alarm arming failed - Window Open!"; + data = { + channel = "Motion"; + importance = "high"; + }; + }; + service = "notify.modile_app_redrum"; + } + ]; +} diff --git a/nixng/containers/home-assistant/configs/automations/alarm/disarmed.nix b/nixng/containers/home-assistant/configs/automations/alarm/disarmed.nix new file mode 100644 index 0000000..1606d36 --- /dev/null +++ b/nixng/containers/home-assistant/configs/automations/alarm/disarmed.nix @@ -0,0 +1,24 @@ +{ + alias = "Alarm - Disarmed"; + id = "a8c26d1c-3a71-4a99-b0f3-dc799495a706"; + + trigger = [ + { + entity_id = "alarm_control_panel.home_alarm"; + platform = "state"; + to = "disarmed"; + } + ]; + action = [ + { + data = { + message = "Alarm Disarmed"; + data = { + channel = "Motion"; + importance = "high"; + }; + }; + service = "notify.mobile_app_redrum"; + } + ]; +} diff --git a/nixng/containers/home-assistant/configs/automations/alarm/motion-detected-away.nix b/nixng/containers/home-assistant/configs/automations/alarm/motion-detected-away.nix new file mode 100644 index 0000000..c67e514 --- /dev/null +++ b/nixng/containers/home-assistant/configs/automations/alarm/motion-detected-away.nix @@ -0,0 +1,43 @@ +{ + alias = "Alarm - Trigger when motion detected - Away"; + id = "b16e988f-2ee0-4596-a14d-0fda1a95ac2d"; + + trigger = [ + { + platform = "state"; + entity_id = "binary_sensor.motion_sensor_occupancy"; + to = "on"; + } + { + platform = "state"; + entity_id = "binary_sensor.sensor_entry_door_contact"; + to = "on"; + } + { + platform = "state"; + entity_id = "binary_sensor.left_window_contact"; + to = "on"; + } + { + platform = "state"; + entity_id = "binary_sensor.right_window_contact"; + to = "on"; + } + ]; + condition = { + condition = "or"; + conditions = [ + { + condition = "state"; + entity_id = "alarm_control_panel.home_alarm"; + state = "armed_away"; + } + ]; + }; + action = [ + { + service = "alarm_control_panel.alarm_trigger"; + entity_id = "alarm_control_panel.home_alarm"; + } + ]; +} diff --git a/nixng/containers/home-assistant/configs/automations/alarm/motion-detected.nix b/nixng/containers/home-assistant/configs/automations/alarm/motion-detected.nix new file mode 100644 index 0000000..f2a5f7f --- /dev/null +++ b/nixng/containers/home-assistant/configs/automations/alarm/motion-detected.nix @@ -0,0 +1,28 @@ +{ + alias = "Alarm - Trigger when motion detected - Home"; + id = "972bccd8-a68c-48fc-ac90-ebbeb87c64b9"; + + trigger = [ + { + platform = "state"; + entity_id = "binary_sensor.sensor_entry_door_contact"; + to = "on"; + } + ]; + condition = { + condition = "or"; + conditions = [ + { + condition = "state"; + entity_id = "alarm_control_panel.home_alarm"; + state = "armed_home"; + } + ]; + }; + action = [ + { + service = "alarm_control_panel.alarm_trigger"; + entity_id = "alarm_control_panel.home_alarm"; + } + ]; +} diff --git a/nixng/containers/home-assistant/configs/automations/alarm/pending.nix b/nixng/containers/home-assistant/configs/automations/alarm/pending.nix new file mode 100644 index 0000000..1d94588 --- /dev/null +++ b/nixng/containers/home-assistant/configs/automations/alarm/pending.nix @@ -0,0 +1,25 @@ +{ + alias = "Alarm - Pending"; + id = "e4771f5a-1205-4fbe-9142-6a56600d7bd4"; + + trigger = [ + { + entity_id = "alarm_control_panel.home_alarm"; + from = "armed_away"; + platform = "state"; + to = "pending"; + } + ]; + action = [ + { + data = { + message = "Alarm Pending - Intruder Detected!"; + data = { + channel = "Motion"; + importance = "high"; + }; + }; + service = "notify.mobile_app_redrum"; + } + ]; +} diff --git a/nixng/containers/home-assistant/configs/automations/alarm/retriggered.nix b/nixng/containers/home-assistant/configs/automations/alarm/retriggered.nix new file mode 100644 index 0000000..d26076e --- /dev/null +++ b/nixng/containers/home-assistant/configs/automations/alarm/retriggered.nix @@ -0,0 +1,35 @@ +{ + alias = "Alarm - ReTriggered"; + id = "2f4942bc-3538-4888-bc52-84171e0b040f"; + + trigger = [ + { + platform = "state"; + entity_id = "binary_sensor.motion_sensor_occupancy"; + to = "on"; + } + ]; + + condition = { + condition = "or"; + conditions = [ + { + condition = "state"; + entity_id = "alarm_control_panel.home_alarm"; + state = "triggered"; + } + ]; + }; + + action = [ + { + service = "camera.record"; + target.entity_id = "camera.mirror_camera"; + data = { + lookback = 30; + duration = 30; + filename = "/mnt/cctv/cctv-{{ states('sensor.date_time_iso') }}.mp4"; + }; + } + ]; +} diff --git a/nixng/containers/home-assistant/configs/automations/alarm/triggered.nix b/nixng/containers/home-assistant/configs/automations/alarm/triggered.nix new file mode 100644 index 0000000..452efd6 --- /dev/null +++ b/nixng/containers/home-assistant/configs/automations/alarm/triggered.nix @@ -0,0 +1,33 @@ +{ + alias = "Alarm - Triggered"; + id = "6b9c6dac-2c6b-4add-b499-8e2b134e5d9c"; + + trigger = [ + { + entity_id = "alarm_control_panel.home_alarm"; + platform = "state"; + to = "triggered"; + } + ]; + action = [ + { + service = "camera.record"; + target.entity_id = "camera.mirror_camera"; + data = { + lookback = 30; + duration = 30; + filename = "/mnt/cctv/cctv-{{ states('sensor.date_time_iso') }}.mp4"; + }; + } + { + data = { + message = "Alarm Triggered - Motion Detected!"; + data = { + channel = "Motion"; + importance = "high"; + }; + }; + service = "notify.mobile_app_redrum"; + } + ]; +} diff --git a/nixng/containers/home-assistant/configs/automations/bathroom_light/brighten.nix b/nixng/containers/home-assistant/configs/automations/bathroom_light/brighten.nix new file mode 100644 index 0000000..746beec --- /dev/null +++ b/nixng/containers/home-assistant/configs/automations/bathroom_light/brighten.nix @@ -0,0 +1,54 @@ +{ + alias = "Bathroom Light Brighten"; + description = ""; + id = "dff10ca7-a27c-469a-8015-bd6899458c8d"; + + action = [{ + "else" = [{ stop = ""; }]; + "if" = [{ + condition = "trigger"; + id = "brightness_down"; + }]; + "then" = [{ + repeat = { + sequence = [ + { + data = { brightness_step_pct = 10; }; + service = "light.turn_on"; + target = { entity_id = "light.bathroom_lights"; }; + } + { + delay = { + hours = 0; + milliseconds = 200; + minutes = 0; + seconds = 0; + }; + } + ]; + while = [{ + condition = "not"; + conditions = [ ]; + }]; + }; + }]; + }]; + + condition = [ ]; + + trigger = [ + { + entity_id = [ "sensor.0x540f57fffe3c601d_action" ]; + id = "brightness_down"; + platform = "state"; + to = "brightness_move_up"; + } + { + entity_id = [ "sensor.0x540f57fffe3c601d_action" ]; + platform = "state"; + to = "brightness_stop"; + } + ]; + + mode = "restart"; +} diff --git a/nixng/containers/home-assistant/configs/automations/bathroom_light/dim.nix b/nixng/containers/home-assistant/configs/automations/bathroom_light/dim.nix new file mode 100644 index 0000000..cf3c0f3 --- /dev/null +++ b/nixng/containers/home-assistant/configs/automations/bathroom_light/dim.nix @@ -0,0 +1,54 @@ +{ + alias = "Bathroom Light Dim"; + description = ""; + id = "c28cad3b-8435-48b4-9ced-bd5befaf11df"; + + trigger = [ + { + entity_id = [ "sensor.0x540f57fffe3c601d_action" ]; + id = "brightness_down"; + platform = "state"; + to = "brightness_move_down"; + } + { + entity_id = [ "sensor.0x540f57fffe3c601d_action" ]; + platform = "state"; + to = "brightness_stop"; + } + ]; + + condition = [ ]; + + action = [{ + "else" = [{ stop = ""; }]; + "if" = [{ + condition = "trigger"; + id = "brightness_down"; + }]; + "then" = [{ + repeat = { + sequence = [ + { + data = { brightness_step_pct = -10; }; + service = "light.turn_on"; + target = { entity_id = "light.bathroom_lights"; }; + } + { + delay = { + hours = 0; + milliseconds = 200; + minutes = 0; + seconds = 0; + }; + } + ]; + while = [{ + condition = "not"; + conditions = [ ]; + }]; + }; + }]; + }]; + + mode = "restart"; +} diff --git a/nixng/containers/home-assistant/configs/automations/bathroom_light/off.nix b/nixng/containers/home-assistant/configs/automations/bathroom_light/off.nix new file mode 100644 index 0000000..bea283d --- /dev/null +++ b/nixng/containers/home-assistant/configs/automations/bathroom_light/off.nix @@ -0,0 +1,24 @@ +{ + alias = "Light Off"; + description = ""; + id = "5f773a4d-5a52-4483-a49d-9c0944ea0b21"; + + trigger = [{ + device_id = "bf6aed0be7735065cddf5a0c11629661"; + discovery_id = "0x540f57fffe3c601d action_off"; + domain = "mqtt"; + platform = "device"; + subtype = "off"; + type = "action"; + }]; + + condition = [ ]; + + action = [{ + data = { }; + service = "light.turn_off"; + target = { entity_id = "light.bathroom_lights"; }; + }]; + + mode = "single"; +} diff --git a/nixng/containers/home-assistant/configs/automations/bathroom_light/on.nix b/nixng/containers/home-assistant/configs/automations/bathroom_light/on.nix new file mode 100644 index 0000000..9502320 --- /dev/null +++ b/nixng/containers/home-assistant/configs/automations/bathroom_light/on.nix @@ -0,0 +1,24 @@ +{ + alias = "Light On"; + description = ""; + id = "1330a1c7-3f3f-488e-8aba-aea8937236ce"; + + trigger = [{ + device_id = "bf6aed0be7735065cddf5a0c11629661"; + discovery_id = "0x540f57fffe3c601d action_on"; + domain = "mqtt"; + platform = "device"; + subtype = "on"; + type = "action"; + }]; + + condition = [ ]; + + action = [{ + data = { }; + service = "light.turn_on"; + target = { entity_id = "light.bathroom_lights"; }; + }]; + + mode = "single"; +} diff --git a/nixng/containers/home-assistant/default.nix b/nixng/containers/home-assistant/default.nix new file mode 100644 index 0000000..f75b15c --- /dev/null +++ b/nixng/containers/home-assistant/default.nix @@ -0,0 +1,14 @@ +{ inputs, lib, ... }: +let + callPackage = lib.callPackageWith { + inherit (inputs) + nixpkgs; + inherit (inputs.nixng.nglib) + makeSystem; + }; +in +{ + flake.nixngConfigurations.homeAssistant = callPackage ./home-assistant.nix {}; + flake.nixngConfigurations.zigbee2mqtt = callPackage ./zigbee2mqtt.nix {}; + flake.nixngConfigurations.mosquitto = callPackage ./mosquitto.nix {}; +} diff --git a/nixng/containers/home-assistant/home-assistant.nix b/nixng/containers/home-assistant/home-assistant.nix new file mode 100644 index 0000000..7e57cd7 --- /dev/null +++ b/nixng/containers/home-assistant/home-assistant.nix @@ -0,0 +1,133 @@ +{ makeSystem +, nixpkgs +}: +makeSystem { + system = "x86_64-linux"; + name = "nixng-home-assistant"; + inherit nixpkgs; + config = + { pkgs, lib, ... }: + let + inherit (lib) + singleton; + in + { + config = { + dumb-init = { + enable = true; + type.services = {}; + }; + + init.services.home-assistant = { + shutdownOnExit = true; + environment.PATH = with pkgs; pkgs.lib.makeBinPath [ + coreutils git ffmpeg bash runit + ]; + ensureSomething.create."media" = { + type = "directory"; + mode = "755"; + owner = "home-assistant"; + dst = "/media"; + persistent = true; + }; + }; + + services.home-assistant = { + enable = true; + envsubst = true; + customComponents = {}; + config = { + default_config = {}; + recorder = { + db_url = "postgresql://hass:\${PSQL_PASSWORD}@127.0.0.1/hass?client_encoding=utf8"; + db_max_retries = 5; + db_retry_wait = 30; + }; + stream = {}; + http = { + server_port = "8123"; + use_x_forwarded_for = true; + trusted_proxies = [ "127.0.0.1" ]; + }; + logger.default = "info"; + homeassistant = + { name = "Home"; + latitude = "\${LATITUDE}"; + longitude = "\${LONGTITUDE}"; + elevation = "\${ELEVATION}"; + # currency = "EUR"; + unit_system = "metric"; + time_zone = "\${TIME_ZONE}"; + country = "\${COUNTRY}"; + internal_url = "http://localhost:8123/"; + whitelist_external_dirs = [ + "/mnt/cctv" + ]; + media_dirs.local = "/mnt/cctv"; + }; + automation = "!include automations.yaml"; + "automation static" = [ + (import ./configs/automations/alarm/pending.nix) + (import ./configs/automations/alarm/disarmed.nix) + (import ./configs/automations/alarm/triggered.nix) + (import ./configs/automations/alarm/retriggered.nix) + (import ./configs/automations/alarm/motion-detected.nix) + (import ./configs/automations/alarm/motion-detected-away.nix) + + (import ./configs/automations/bathroom_light/dim.nix) + (import ./configs/automations/bathroom_light/brighten.nix) + (import ./configs/automations/bathroom_light/on.nix) + (import ./configs/automations/bathroom_light/off.nix) + ]; + alarm_control_panel = import ./configs/alarm_control_panel.nix; + frontend.themes = {}; + sensor = singleton { + platform = "time_date"; + display_options = [ + "time" + "date" + "date_time" + "date_time_utc" + "date_time_iso" + "time_date" + "time_utc" + "beat" + ]; + }; + }; + package = pkgs.home-assistant.override { + extraComponents = [ + "http" + "homeassistant" + # "image" + "person" + "cloud" + "camera" + "onboarding" + "frontend" + "safe_mode" + "met" + "zha" + "mobile_app" + "dhcp" + "logbook" + "history" + "ssdp" + "mqtt" + "stream" + "recorder" + ]; + + extraPackages = ps: with ps; [ + xmodem + psycopg2 + aiohttp-cors + bleak-retry-connector + psutil-home-assistant + bluetooth-auto-recovery + ]; + }; + }; + }; + }; +} diff --git a/nixng/containers/home-assistant/mosquitto.nix b/nixng/containers/home-assistant/mosquitto.nix new file mode 100644 index 0000000..f643626 --- /dev/null +++ b/nixng/containers/home-assistant/mosquitto.nix @@ -0,0 +1,34 @@ +{ makeSystem +, nixpkgs +}: +makeSystem { + system = "x86_64-linux"; + name = "nixng-mosquitto"; + inherit nixpkgs; + config = + { pkgs, ... }: + { + config = { + dumb-init = { + enable = true; + type.services = {}; + }; + + init.services.mosquitto = { + shutdownOnExit = true; + }; + + services.mosquitto = { + enable = true; + config = { + listener = [ + "1883 0.0.0.0" + { + password_file = "/secrets/mqtt_password"; + } + ]; + }; + }; + }; + }; +} diff --git a/nixng/containers/home-assistant/postresql.nix b/nixng/containers/home-assistant/postresql.nix new file mode 100644 index 0000000..288d6fb --- /dev/null +++ b/nixng/containers/home-assistant/postresql.nix @@ -0,0 +1,42 @@ +{ makeSystem +, nixpkgs +}: +makeSystem { + system = "x86_64-linux"; + name = "nixng-hass-postgresql"; + inherit nixpkgs; + config = + { pkgs, lib, ... }: + let + inherit (lib) + singleton; + in + { + config = { + dumb-init = { + enable = true; + type.services = {}; + }; + services.postgresql = { + enable = true; + package = pkgs.postgresql_12; + + initialScript = "/secrets/init.sql"; + enableTCPIP = true; + + authentication = "host all all all md5"; + + ensureDatabases = [ "hass" ]; + ensureExtensions = { + "pg_trgm" = [ "hass" ]; + }; + ensureUsers = singleton { + name = "hass"; + ensurePermissions = { + "DATABASE \"hass\"" = "ALL PRIVILEGES"; + }; + }; + }; + }; + }; +} diff --git a/nixng/containers/home-assistant/zigbee2mqtt.nix b/nixng/containers/home-assistant/zigbee2mqtt.nix new file mode 100644 index 0000000..6db12ec --- /dev/null +++ b/nixng/containers/home-assistant/zigbee2mqtt.nix @@ -0,0 +1,53 @@ +{ makeSystem +, nixpkgs +}: +makeSystem { + system = "x86_64-linux"; + name = "nixng-zigbee2mqtt"; + inherit nixpkgs; + config = + { pkgs, ... }: + { + config = { + dumb-init = { + enable = true; + type.services = { }; + }; + + init.services.zigbee2mqtt = { + shutdownOnExit = true; + }; + + services.zigbee2mqtt = { + enable = true; + user = "root"; + envsubst = true; + config = { + homeassistant = true; + permit_join = true; + mqtt = { + base_topic = "zigbee2mqtt"; + server = "mqtt://127.0.0.1:1883"; + user = "\${MQTT_USER}"; + password = "\${MQTT_PASSWORD}"; + }; + + frontend = { + port = 8456; + host = "0.0.0.0"; + }; + + advanced.pan_id = 15408; + + serial.port = "/dev/ttyUSB0"; + + devices = "devices.yaml"; + groups = "groups.yaml"; + log_level = "debug"; + }; + }; + }; + }; +} + +