diff --git a/configs/pepe/home-assistant/zigbee2mqtt.nix b/configs/pepe/home-assistant/zigbee2mqtt.nix index 7bdf334..8073cce 100644 --- a/configs/pepe/home-assistant/zigbee2mqtt.nix +++ b/configs/pepe/home-assistant/zigbee2mqtt.nix @@ -1,61 +1,7 @@ { pkgs, lib, config, ... }: let - # allow new devices to join - enablePairing = true; - - device = "/dev/ttyACM0"; - dataFolder = "/srv/zigbee/data"; - - lights = { - "light_1" = { - id = "0x7cb03eaa0a0347b5"; - room = "floor_room_essential"; - }; - "light_2" = { - id = "0x7cb03eaa0a0387b9"; - room = "floor_room"; - }; - "light_3" = { - id = "0x7cb03eaa0a033a86"; - room = "living_room"; - }; - "light_4" = { - id = "0x7cb03eaa0a04aabf"; - room = "bath_room_essential"; - }; - "light_5" = { - id = "0x7cb03eaa0a0346e4"; - room = "living_room"; - }; - "light_6" = { id = "0x7cb03eaa0a034b46"; }; - "light_7" = { id = "0x7cb03eaa0a033b4f"; }; - "light_8" = { - id = "0x7cb03eaa0a0384d3"; - room = "bed_room"; - }; - - }; sensors = { - buttons = { - "button_1" = { - id = "0x00158d0002b04f65"; - # room = "living_room"; # is where it actually is placed (optional) (default "unknown") - # presents = "living_room_present" # presents group to append (optional) (default "unknown") - # states = { click = "input_boolean.living_room_button"; } # input state to hold (optional) (default "input_boolean.single_${name}") - room = "living_room_present"; - }; - "button_2" = { - id = "0x00158d0002b04f09"; - room = "bed_room_present"; - states.single = "input_boolean.bed_room_buttons"; - }; - "button_3" = { - id = "0x00158d0002b00e04"; - room = "bed_room_present"; - states.single = "input_boolean.bed_room_buttons"; - }; - }; temperature = { "temperature_sensor_1" = { id = "0x00158d0002d79220"; @@ -66,40 +12,6 @@ let 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 = "bath_room_essential_present"; - }; - "motion_sensor_6" = { - id = "0x00158d0002f9a5cb"; - room = "floor_room_present"; - }; - "motion_sensor_7" = { - id = "0x00158d0002f9a6aa"; - room = "bed_room_essential_present"; - }; - "motion_sensor_8" = { - id = "0x00158d0002f04637"; - room = "bath_room_essential_present"; - }; - }; door = { "door_sensor_1" = { id = "0x00158d000312dc52"; }; "door_sensor_2" = { @@ -116,103 +28,34 @@ let }; # todo : generate automatically - allSensors = with sensors; buttons // temperature // motion // door; - allLights = lights; + allSensors = with sensors; temperature // 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 // allLights); - }; - - configurationYaml = - pkgs.writeText "configuration.yml" (builtins.toJSON zigBee2MqttConfig); in { - imports = [ ./mqtt.nix ]; + imports = [ + ./mqtt.nix + ./zigbee2mqtt/service.nix + ./zigbee2mqtt/lights.nix + ./zigbee2mqtt/motion.nix + ./zigbee2mqtt/buttons.nix + ]; + + services.zigbee2mqtt.enable = true; + services.zigbee2mqtt.password = lib.fileContents ; + services.zigbee2mqtt.devices = lib.mapAttrs' (name: + { id, ... }: { + name = id; + value = { + retain = false; + friendly_name = name; + }; + }) (allSensors); services.homeAssistantConfig = { - # define input_boolean - # -------------------- - # which get toggled by the buttons - input_boolean = let stripEmpty = lib.filter (a: a != { }); - in builtins.listToAttrs (stripEmpty (lib.flatten (lib.mapAttrsToList (name: - { states ? { }, ... }: [ - (lib.optionalAttrs (!lib.hasAttr "single" states) { - name = "single_${name}"; - value = { icon = "mdi:toggle-switch"; }; - }) - (lib.optionalAttrs (!lib.hasAttr "double" states) { - name = "double_${name}"; - value = { icon = "mdi:toggle-switch"; }; - }) - (lib.optionalAttrs (!lib.hasAttr "hold" states) { - name = "hold_${name}"; - value = { icon = "mdi:toggle-switch"; }; - }) - ]) sensors.buttons))); - - light = lib.mapAttrsToList (name: - { ... }: { - platform = "mqtt"; - name = name; - #icon = "mdi:toggle-switch"; - state_topic = "zigbee2mqtt/${name}"; - availability_topic = "zigbee2mqtt/bridge/state"; - command_topic = "zigbee2mqtt/${name}/set"; - value_template = "{{ value_json.click }}"; - brightness = true; - color_temp = true; - schema = "json"; - }) allLights; - # 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: @@ -267,28 +110,14 @@ in { unit_of_measurement = "lqi"; value_template = "{{ value_json.linkquality }}"; } - ]) (allSensors // allLights); + ]) allSensors; - in lib.flatten (buttons ++ temperature ++ informations); + in lib.flatten (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: @@ -303,7 +132,7 @@ in { value_template = "{{ value_json.contact}}"; }) sensors.door; - in lib.flatten (motion ++ door); + in lib.flatten (door); # create groups # ------------- @@ -312,34 +141,6 @@ in { information = name: [ "sensor.battery_${name}" "sensor.link_${name}" ]; - lightGroups = lib.mapAttrs' (name: - { ... }: { - name = name; - value = { - control = "hidden"; - entities = [ "light.${name}" ] ++ (information name); - }; - }) allLights; - - sensorButtons = lib.mapAttrs' (name: - { states ? { }, ... }: - let - entityIds = { single ? "input_boolean.single_${name}" - , double ? "input_boolean.double_${name}" - , hold ? "input_boolean.hold_${name}", ... }: [ - single - double - hold - ]; - in { - name = name; - value = { - control = "hidden"; - entities = [ "sensor.${name}" ] ++ (entityIds states) - ++ (information name); - }; - }) (sensors.buttons); - sensorTemperature = lib.mapAttrs' (name: { ... }: { name = name; @@ -353,15 +154,6 @@ in { }; }) (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; @@ -395,158 +187,24 @@ in { lib.mapAttrs (_: devices: map entityNameing devices) allRooms'; in allRooms; groupEntries = { - motionGroups = createGroupEntities { - sensors = sensors.motion; - entityNameing = ({ name, ... }: "binary_sensor.${name}"); - }; - buttonGroups = createGroupEntities { - sensors = sensors.buttons; - entityNameing = ({ name - , states ? { single = "input_boolean.single_${name}"; }, ... }: - states.single); - }; doorGroups = createGroupEntities { sensors = sensors.door; entityNameing = ({ name, ... }: "binary_sensor.${name}"); }; - lightGroups = createGroupEntities { - sensors = allLights; - entityNameing = ({ name, ... }: "light.${name}"); - }; }; # merge all group entries in lib.mapAttrs (_: entities: { entities = lib.flatten entities; }) (lib.zipAttrs (builtins.attrValues groupEntries)); - in lightGroups // views // allRoomsGroups // sensorButtons // sensorMotions - // sensorTemperature // sensorDoors // { + in views // allRoomsGroups // sensorTemperature // sensorDoors // { all_sensors.entities = (lib.mapAttrsToList (name: { ... }: "sensor.${name}") - (sensors.buttons // sensors.temperature)) + (sensors.temperature)) ++ (lib.mapAttrsToList (name: { ... }: "binary_sensor.${name}") - (sensors.motion // sensors.door)); - all_lights.entities = - lib.mapAttrsToList (name: { ... }: "light.${name}") allLights; + sensors.door); }; - # create automation - # ----------------- - automation = let - - # single click - toggle_single_button_input = lib.mapAttrsToList (name: - { states ? { }, ... }: - let - entityId = if (lib.hasAttr "single" states) then - states.single - else - "input_boolean.single_${name}"; - in { - alias = "toggle single click ${name}"; - trigger = { - platform = "mqtt"; - topic = "zigbee2mqtt/${name}"; - }; - condition = { - condition = "template"; - value_template = ''{{ "single" == trigger.payload_json.click}}''; - }; - action = { - service = "input_boolean.toggle"; - data.entity_id = entityId; - }; - }) sensors.buttons; - - # double click - toggle_double_button_input = lib.mapAttrsToList (name: - { states ? { }, ... }: - let - entityId = if (lib.hasAttr "double" states) then - states.double - else - "input_boolean.double_${name}"; - in { - alias = "toggle double click ${name}"; - trigger = { - platform = "mqtt"; - topic = "zigbee2mqtt/${name}"; - }; - condition = { - condition = "template"; - value_template = ''{{ "double" == trigger.payload_json.click}}''; - }; - action = { - service = "input_boolean.toggle"; - data.entity_id = entityId; - }; - }) sensors.buttons; - - # hold - toggle_hold_button_input = lib.mapAttrsToList (name: - { states ? { }, ... }: - let - entityId = if (lib.hasAttr "hold" states) then - states.hold - else - "input_boolean.hold_${name}"; - in { - alias = "toggle hold ${name}"; - trigger = { - platform = "mqtt"; - topic = "zigbee2mqtt/${name}"; - }; - condition = { - condition = "template"; - value_template = ''{{ "hold" == trigger.payload_json.action}}''; - }; - action = { - service = "input_boolean.toggle"; - data.entity_id = entityId; - }; - }) 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 - ''; }; } diff --git a/configs/pepe/home-assistant/zigbee2mqtt/buttons.nix b/configs/pepe/home-assistant/zigbee2mqtt/buttons.nix new file mode 100644 index 0000000..f7c81ff --- /dev/null +++ b/configs/pepe/home-assistant/zigbee2mqtt/buttons.nix @@ -0,0 +1,219 @@ +{ pkgs, lib, ... }: +let + + # https://www.zigbee2mqtt.io/devices/WXKG12LM.html + allDevices = { + "button_1" = { + id = "0x00158d0002b04f65"; + # states = { click = "input_boolean.living_room_button"; } # input state to hold (optional) (default "input_boolean.single_${name}") + groups = [ "living_room_present" ]; + }; + "button_2" = { + id = "0x00158d0002b04f09"; + groups = [ "bed_room_present" ]; + states.single = "input_boolean.bed_room_buttons"; + }; + "button_3" = { + id = "0x00158d0002b00e04"; + groups = [ "bed_room_present" ]; + states.single = "input_boolean.bed_room_buttons"; + }; + }; + +in { + + services.zigbee2mqtt.devices = lib.mapAttrs' (name: + { id, ... }: { + name = id; + value = { + retain = false; + friendly_name = name; + }; + }) allDevices; + + services.homeAssistantConfig = { + + # define input_boolean + # -------------------- + # which get toggled by the buttons + input_boolean = let stripEmpty = lib.filter (a: a != { }); + in builtins.listToAttrs (stripEmpty (lib.flatten (lib.mapAttrsToList (name: + { states ? { }, ... }: [ + (lib.optionalAttrs (!lib.hasAttr "single" states) { + name = "single_${name}"; + value = { icon = "mdi:toggle-switch"; }; + }) + (lib.optionalAttrs (!lib.hasAttr "double" states) { + name = "double_${name}"; + value = { icon = "mdi:toggle-switch"; }; + }) + (lib.optionalAttrs (!lib.hasAttr "hold" states) { + name = "hold_${name}"; + value = { icon = "mdi:toggle-switch"; }; + }) + ]) allDevices))); + + # define meta information sensors + sensor = lib.flatten (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 }}"; + } + { + 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 }}"; + } + ]) allDevices); + + binary_sensor = 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 }}"; + }) allDevices; + + # create groups + # ------------- + group = let + + # to have nice panels for every device + sensorGroups = lib.mapAttrs (name: + { states ? { }, ... }: + let + entityIds = { single ? "input_boolean.single_${name}" + , double ? "input_boolean.double_${name}" + , hold ? "input_boolean.hold_${name}", ... }: [ + single + double + hold + ]; + in { + control = "hidden"; + entities = [ "sensor.${name}" ] ++ (entityIds states) + ++ [ "sensor.battery_${name}" "sensor.link_${name}" ]; + }) allDevices; + + # sort lights into given groups. + sortedInGroups = let + groupEntries = lib.zipAttrs (lib.flatten (lib.mapAttrsToList (name: + { groups ? [ ], states ? { }, ... }: + map (groupName: { + "${groupName}" = if (lib.hasAttr "single" states) then + states.single + else + "input_boolean.single_${name}"; + }) groups) allDevices)); + in lib.mapAttrs (name: entities: { inherit entities; }) groupEntries; + + in sortedInGroups // sensorGroups // { + + all_sensors.entities = + lib.mapAttrsToList (name: { ... }: "binary_sensor.${name}") allDevices; + }; + + # create automation + # ----------------- + automation = let + + # single click + toggle_single_button_input = lib.mapAttrsToList (name: + { states ? { }, ... }: + let + entityId = if (lib.hasAttr "single" states) then + states.single + else + "input_boolean.single_${name}"; + in { + alias = "toggle single click ${name}"; + trigger = { + platform = "mqtt"; + topic = "zigbee2mqtt/${name}"; + }; + condition = { + condition = "template"; + value_template = ''{{ "single" == trigger.payload_json.click}}''; + }; + action = { + service = "input_boolean.toggle"; + data.entity_id = entityId; + }; + }) allDevices; + + # double click + toggle_double_button_input = lib.mapAttrsToList (name: + { states ? { }, ... }: + let + entityId = if (lib.hasAttr "double" states) then + states.double + else + "input_boolean.double_${name}"; + in { + alias = "toggle double click ${name}"; + trigger = { + platform = "mqtt"; + topic = "zigbee2mqtt/${name}"; + }; + condition = { + condition = "template"; + value_template = ''{{ "double" == trigger.payload_json.click}}''; + }; + action = { + service = "input_boolean.toggle"; + data.entity_id = entityId; + }; + }) allDevices; + + # hold + toggle_hold_button_input = lib.mapAttrsToList (name: + { states ? { }, ... }: + let + entityId = if (lib.hasAttr "hold" states) then + states.hold + else + "input_boolean.hold_${name}"; + in { + alias = "toggle hold ${name}"; + trigger = { + platform = "mqtt"; + topic = "zigbee2mqtt/${name}"; + }; + condition = { + condition = "template"; + value_template = ''{{ "hold" == trigger.payload_json.action}}''; + }; + action = { + service = "input_boolean.toggle"; + data.entity_id = entityId; + }; + }) allDevices; + + in lib.flatten (toggle_single_button_input ++ toggle_double_button_input + ++ toggle_hold_button_input); + + }; + +} diff --git a/configs/pepe/home-assistant/zigbee2mqtt/lights.nix b/configs/pepe/home-assistant/zigbee2mqtt/lights.nix new file mode 100644 index 0000000..e608d5c --- /dev/null +++ b/configs/pepe/home-assistant/zigbee2mqtt/lights.nix @@ -0,0 +1,109 @@ +{ pkgs, lib, ... }: +let + + # https://www.zigbee2mqtt.io/devices/AC10787.html + allDevices = { + "light_1" = { + id = "0x7cb03eaa0a0347b5"; + groups = [ "floor_room_essential" ]; + }; + "light_2" = { + id = "0x7cb03eaa0a0387b9"; + groups = [ "floor_room" ]; + }; + "light_3" = { + id = "0x7cb03eaa0a033a86"; + groups = [ "living_room" ]; + }; + "light_4" = { + id = "0x7cb03eaa0a04aabf"; + groups = [ "bath_room_essential" ]; + }; + "light_5" = { + id = "0x7cb03eaa0a0346e4"; + groups = [ "living_room" ]; + }; + "light_6" = { id = "0x7cb03eaa0a034b46"; }; + "light_7" = { id = "0x7cb03eaa0a033b4f"; }; + "light_8" = { + id = "0x7cb03eaa0a0384d3"; + groups = [ "bed_room" ]; + }; + }; + +in { + + services.zigbee2mqtt.devices = lib.mapAttrs' (name: + { id, ... }: { + name = id; + value = { + retain = false; + friendly_name = name; + osram_set_transition = 2; # time in seconds (integer or float) + }; + }) allDevices; + + services.homeAssistantConfig = { + + light = lib.mapAttrsToList (name: + { ... }: { + platform = "mqtt"; + name = name; + state_topic = "zigbee2mqtt/${name}"; + availability_topic = "zigbee2mqtt/bridge/state"; + command_topic = "zigbee2mqtt/${name}/set"; + value_template = "{{ value_json.click }}"; + brightness = true; + color_temp = true; + schema = "json"; + }) allDevices; + + sensor = with lib; + mapAttrsToList (name: + { ... }: { + name = name; + platform = "mqtt"; + state_topic = "zigbee2mqtt/${name}"; + availability_topic = "zigbee2mqtt/bridge/state"; + icon = "mdi:signal"; + value_template = "{{ value_json.linkquality}}"; + }) allDevices; + + binary_sensor = lib.mapAttrsToList (name: + { ... }: { + name = name; + platform = "mqtt"; + state_topic = "zigbee2mqtt/${name}"; + availability_topic = "zigbee2mqtt/bridge/state"; + payload_on = true; + payload_off = false; + value_template = "{{ value_json.update_available }}"; + }) allDevices; + + # create groups + # ------------- + group = let + + # to have nice panels for every device + lightGroups = lib.mapAttrs (name: + { ... }: { + control = "hidden"; + entities = + [ "light.${name}" "sensor.${name}" "binary_sensor.${name}" ]; + }) allDevices; + + # sort lights into given groups. + sortedInGroups = let + groupEntries = lib.zipAttrs (lib.flatten (lib.mapAttrsToList (name: + { groups ? [ ], ... }: + map (groupName: { "${groupName}" = "light.${name}"; }) groups) + allDevices)); + in lib.mapAttrs (name: entities: { inherit entities; }) groupEntries; + + in sortedInGroups // lightGroups // { + all_lights.entities = + lib.mapAttrsToList (name: { ... }: "light.${name}") allDevices; + }; + }; + +} diff --git a/configs/pepe/home-assistant/zigbee2mqtt/motion.nix b/configs/pepe/home-assistant/zigbee2mqtt/motion.nix new file mode 100644 index 0000000..685318d --- /dev/null +++ b/configs/pepe/home-assistant/zigbee2mqtt/motion.nix @@ -0,0 +1,119 @@ +{ pkgs, lib, ... }: +let + + # https://www.zigbee2mqtt.io/devices/RTCGQ01LM.html + allDevices = { + + "motion_sensor_1" = { + id = "0x00158d0002fbd451"; + groups = [ "kitchen_room_present" ]; + }; + "motion_sensor_2" = { + id = "0x00158d0002f9a6b8"; + groups = [ "kitchen_room_present" ]; + }; + "motion_sensor_3" = { + id = "0x00158d0002f04522"; + groups = [ "living_room_present" ]; + }; + "motion_sensor_4" = { + id = "0x00158d0002f9a558"; + groups = [ "living_room_present" ]; + }; + "motion_sensor_5" = { + id = "0x00158d0002f9a56f"; + groups = [ "bath_room_essential_present" ]; + }; + "motion_sensor_6" = { + id = "0x00158d0002f9a5cb"; + groups = [ "floor_room_present" ]; + }; + "motion_sensor_7" = { + id = "0x00158d0002f9a6aa"; + groups = [ "bed_room_essential_present" ]; + }; + "motion_sensor_8" = { + id = "0x00158d0002f04637"; + groups = [ "bath_room_essential_present" ]; + }; + }; + +in { + + services.zigbee2mqtt.devices = lib.mapAttrs' (name: + { id, ... }: { + name = id; + value = { + retain = false; + friendly_name = name; + }; + }) allDevices; + + services.homeAssistantConfig = { + + # define meta information sensors + sensor = lib.flatten (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 }}"; + } + ]) allDevices); + + binary_sensor = 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 }}"; + }) allDevices; + + # create groups + # ------------- + group = let + + # to have nice panels for every device + sensorGroups = lib.mapAttrs (name: + { ... }: { + control = "hidden"; + entities = [ + "binary_sensor.${name}" + "sensor.battery_${name}" + "sensor.link_${name}" + ]; + }) allDevices; + + # sort lights into given groups. + sortedInGroups = let + groupEntries = lib.zipAttrs (lib.flatten (lib.mapAttrsToList (name: + { groups ? [ ], ... }: + map (groupName: { "${groupName}" = "binary_sensor.${name}"; }) groups) + allDevices)); + in lib.mapAttrs (name: entities: { inherit entities; }) groupEntries; + + in sortedInGroups // sensorGroups // { + + all_sensors.entities = + lib.mapAttrsToList (name: { ... }: "binary_sensor.${name}") allDevices; + }; + }; + +} diff --git a/configs/pepe/home-assistant/zigbee2mqtt/service.nix b/configs/pepe/home-assistant/zigbee2mqtt/service.nix new file mode 100644 index 0000000..d7cc72d --- /dev/null +++ b/configs/pepe/home-assistant/zigbee2mqtt/service.nix @@ -0,0 +1,99 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.zigbee2mqtt; + + device = "/dev/ttyACM0"; + dataFolder = "/srv/zigbee/data"; + configurationYaml = pkgs.writeText "configuration.yml" (builtins.toJSON { + + # Home Assistant integration (MQTT discovery) + homeassistant = false; + # homeassistant = true; + + # allow new devices to join + permit_join = cfg.discover; + + # 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 = cfg.password; + }; + + # Serial settings + serial = { + port = device; + # disable LED of CC2531 USB sniffer + disable_led = true; + }; + + devices = cfg.devices; + }); + +in { + + options.services.zigbee2mqtt = { + enable = mkEnableOption "enable services.zigbee2mqtt"; + discover = mkEnableOption "discover new devices"; + password = mkOption { + type = with types; str; + description = '' + password of the mqtt server. + ''; + }; + devices = mkOption { + type = with types; attrs; + description = '' + device definitions. + ''; + }; + }; + + config = mkIf cfg.enable { + + # 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 + ''; + }; + + }; +}