{ pkgs, lib, config, ... }: let # allow new devices to join enablePairing = false; device = "/dev/ttyACM0"; dataFolder = "/srv/zigbee/data"; sensors = { buttons = { "button_1" = { id = "0x00158d0002b04f65"; room = "living_room_present"; }; "button_2" = { id = "0x00158d0002b04f09"; room = "bed_room_present"; }; "button_3" = { id = "0x00158d0002b00e04"; room = "bed_room_present"; }; }; temperature = { "temperature_sensor_1" = { id = "0x00158d0002d79220"; room = "living_room_present"; }; "temperature_sensor_2" = { id = "0x00158d0002d7913d"; room = "living_room_present"; }; }; motion = { "motion_sensor_1" = { id = "0x00158d0002fbd451"; room = "kitchen_room_present"; }; "motion_sensor_2" = { id = "0x00158d0002f9a6b8"; room = "kitchen_room_present"; }; "motion_sensor_3" = { id = "0x00158d0002f04522"; room = "living_room_present"; }; "motion_sensor_4" = { id = "0x00158d0002f9a558"; room = "living_room_present"; }; "motion_sensor_5" = { id = "0x00158d0002f9a56f"; room = "floor_room_present"; }; "motion_sensor_6" = { id = "0x00158d0002f9a5cb"; room = "floor_room_present"; }; "motion_sensor_7" = { id = "0x00158d0002f9a6aa"; }; "motion_sensor_8" = { id = "0x00158d0002f04637"; }; }; door = { "door_sensor_1" = { id = "0x00158d000312dc52"; }; "door_sensor_2" = { id = "0x00158d000316d5bf"; }; "door_sensor_3" = { id = "0x00158d0002f9516f"; }; "door_sensor_4" = { id = "0x00158d00031383b9"; }; "door_sensor_5" = { id = "0x00158d0003120d3e"; }; }; }; # todo : generate automatically allSensors = with sensors; buttons // temperature // motion // door; zigBee2MqttConfig = { # Home Assistant integration (MQTT discovery) homeassistant = false; # homeassistant = true; # allow new devices to join permit_join = enablePairing; # MQTT settings mqtt = { # MQTT base topic for zigbee2mqtt MQTT messages base_topic = "zigbee2mqtt"; # MQTT server URL server = "mqtt://127.0.0.1:1883"; # MQTT server authentication, uncomment if required: user = "zigbee"; password = lib.fileContents ; }; # Serial settings serial = { port = "/dev/ttyACM0"; # Optional: disable LED of CC2531 USB sniffer disable_led = true; }; devices = lib.mapAttrs' (name: { id, ... }: { name = id; value = { retain = false; friendly_name = name; }; }) allSensors; }; configurationYaml = pkgs.writeText "configuration.yml" (builtins.toJSON zigBee2MqttConfig); in { imports = [ ./mqtt.nix ]; services.homeAssistantConfig = { # define input_boolean # -------------------- # which get toggled by the buttons input_boolean = builtins.listToAttrs (lib.flatten (lib.mapAttrsToList (name: { ... }: [ { name = "single_${name}"; value = { icon = "mdi:radiobox-blank"; }; } { name = "double_${name}"; value = { icon = "mdi:radiobox-blank"; }; } { name = "hold_${name}"; value = { icon = "mdi:radiobox-blank"; }; } ]) sensors.buttons)); # define sensors # -------------- sensor = let # define button sensors buttons = with lib; mapAttrsToList (name: { ... }: [{ platform = "mqtt"; name = name; icon = "mdi:toggle-switch"; state_topic = "zigbee2mqtt/${name}"; availability_topic = "zigbee2mqtt/bridge/state"; value_template = "{{ value_json.click }}"; }]) sensors.buttons; # define temperature sensors temperature = with lib; mapAttrsToList (name: { ... }: [ { platform = "mqtt"; name = name; state_topic = "zigbee2mqtt/${name}"; availability_topic = "zigbee2mqtt/bridge/state"; unit_of_measurement = "°C"; device_class = "temperature"; value_template = "{{ value_json.temperature }}"; } { platform = "mqtt"; name = "humidity_${name}"; state_topic = "zigbee2mqtt/${name}"; availability_topic = "zigbee2mqtt/bridge/state"; unit_of_measurement = "%"; device_class = "humidity"; value_template = "{{ value_json.humidity }}"; } { platform = "mqtt"; name = "pressure_${name}"; state_topic = "zigbee2mqtt/${name}"; availability_topic = "zigbee2mqtt/bridge/state"; unit_of_measurement = "hPa"; device_class = "pressure"; value_template = "{{ value_json.pressure }}"; } ]) sensors.temperature; # define meta information sensors informations = lib.mapAttrsToList (name: { ... }: [ { name = "battery_${name}"; platform = "mqtt"; state_topic = "zigbee2mqtt/${name}"; availability_topic = "zigbee2mqtt/bridge/state"; unit_of_measurement = "%"; icon = "mdi:battery-10"; value_template = "{{ value_json.battery }}"; } { name = "link_${name}"; platform = "mqtt"; state_topic = "zigbee2mqtt/${name}"; availability_topic = "zigbee2mqtt/bridge/state"; icon = "mdi:signal"; unit_of_measurement = "lqi"; value_template = "{{ value_json.linkquality }}"; } ]) allSensors; in lib.flatten (buttons ++ temperature ++ informations); # define binary sensors # --------------------- binary_sensor = let # define motion sensors # --------------------- motion = lib.mapAttrsToList (name: { ... }: { name = name; platform = "mqtt"; device_class = "motion"; state_topic = "zigbee2mqtt/${name}"; availability_topic = "zigbee2mqtt/bridge/state"; payload_on = true; payload_off = false; value_template = "{{ value_json.occupancy }}"; }) sensors.motion; # define door sensors # ------------------- door = lib.mapAttrsToList (name: { ... }: { name = name; platform = "mqtt"; device_class = "door"; state_topic = "zigbee2mqtt/${name}"; availability_topic = "zigbee2mqtt/bridge/state"; payload_on = false; payload_off = true; value_template = "{{ value_json.contact}}"; }) sensors.door; in lib.flatten (motion ++ door); # create groups # ------------- # to have nice panels for every device group = let information = name: [ "sensor.battery_${name}" "sensor.link_${name}" ]; sensorButtons = lib.mapAttrs' (name: { ... }: { name = name; value = { control = "hidden"; entities = [ "sensor.${name}" "input_boolean.single_${name}" "input_boolean.double_${name}" "input_boolean.hold_${name}" ] ++ (information name); }; }) (sensors.buttons); sensorTemperature = lib.mapAttrs' (name: { ... }: { name = name; value = { control = "hidden"; entities = [ "sensor.${name}" "sensor.humidity_${name}" "sensor.pressure_${name}" ] ++ (information name); }; }) (sensors.temperature); sensorMotions = lib.mapAttrs' (name: { ... }: { name = name; value = { control = "hidden"; entities = [ "binary_sensor.${name}" ] ++ (information name); }; }) (sensors.motion); sensorDoors = lib.mapAttrs' (name: { ... }: { name = name; value = { control = "hidden"; entities = [ "binary_sensor.${name}" ] ++ (information name); }; }) (sensors.door); views = { view_sensors = { name = "Sensoren"; control = "hidden"; view = true; entities = lib.mapAttrsToList (name: { ... }: "group.${name}") allSensors; }; }; # todo : make this more generic # append to *_room_present group flatMotionSensors = lib.mapAttrsToList (name: values: { name = name; } // values) sensors.motion; motionAllRooms' = lib.groupBy ({ room ? "unknown", ... }: room) flatMotionSensors; motionAllRooms = lib.mapAttrs (_: devices: map ({ name, ... }: "binary_sensor.${name}") devices) motionAllRooms'; # todo : make this more generic # append to *_room_present group flatButtonSensors = lib.mapAttrsToList (name: values: { name = name; } // values) sensors.buttons; buttonAllRooms' = lib.groupBy ({ room ? "unknown", ... }: room) flatButtonSensors; buttonAllRooms = lib.mapAttrs (_: devices: map ({ name, ... }: "input_boolean.single_${name}") devices) buttonAllRooms'; presentsAllRooms = lib.mapAttrs (_: entities: { entities = lib.flatten entities; }) (lib.zipAttrs [ buttonAllRooms motionAllRooms ]); in views // presentsAllRooms // sensorButtons // sensorMotions // sensorTemperature // sensorDoors // { all_sensors.entities = (lib.mapAttrsToList (name: { ... }: "sensor.${name}") (sensors.buttons // sensors.temperature)) ++ (lib.mapAttrsToList (name: { ... }: "binary_sensor.${name}") (sensors.motion // sensors.door)); }; # create automation # ----------------- automation = let # single click toggle_single_button_input = map (button: { alias = "toggle hold ${button}"; trigger = { platform = "mqtt"; topic = "zigbee2mqtt/${button}"; }; condition = { condition = "template"; value_template = ''{{ "single" == trigger.payload_json.click}}''; }; action = { service = "input_boolean.toggle"; data.entity_id = "input_boolean.single_${button}"; }; }) (builtins.attrNames sensors.buttons); # double click toggle_double_button_input = map (button: { alias = "toggle hold ${button}"; trigger = { platform = "mqtt"; topic = "zigbee2mqtt/${button}"; }; condition = { condition = "template"; value_template = ''{{ "double" == trigger.payload_json.click}}''; }; action = { service = "input_boolean.toggle"; data.entity_id = "input_boolean.double_${button}"; }; }) (builtins.attrNames sensors.buttons); # hold toggle_hold_button_input = map (button: { alias = "toggle hold ${button}"; trigger = { platform = "mqtt"; topic = "zigbee2mqtt/${button}"; }; condition = { condition = "template"; value_template = ''{{ "hold" == trigger.payload_json.action }}''; }; action = { service = "input_boolean.toggle"; data.entity_id = "input_boolean.hold_${button}"; }; }) (builtins.attrNames sensors.buttons); in lib.flatten (toggle_single_button_input ++ toggle_double_button_input ++ toggle_hold_button_input); }; # manage zigbee2mqtt # ------------------ # todo : einen eigenen container bauen mit dockerTool : https://nixos.wiki/wiki/Docker virtualisation.docker.enable = true; systemd.services."zigbee2mqtt" = { enable = true; description = "Allows you to use your Zigbee devices without the vendors bridge/gateway."; after = [ "docker.service" ]; requires = [ "docker.service" ]; # todo : udev rule erstellen, die diesen service erst startet, dieses wanted by ist labil wantedBy = [ "home-assistant.service" ]; preStart = '' if [ -f ${dataFolder}/configuration.yaml ] then rm ${dataFolder}/configuration.yaml fi mkdir -p ${dataFolder} cat ${configurationYaml} | ${pkgs.yq}/bin/yq --yaml-output '.' > ${dataFolder}/configuration.yaml ''; restartTriggers = [ configurationYaml ]; script = '' # delete old instance to ensure update ${pkgs.docker}/bin/docker stop zigbee2mqtt || true && ${pkgs.docker}/bin/docker rm -f zigbee2mqtt || true # start instance ${pkgs.docker}/bin/docker run \ --network="host" \ --name zigbee2mqtt \ -v ${dataFolder}:/app/data \ --device=${device} \ koenkk/zigbee2mqtt ''; }; }