about summary refs log tree commit diff
path: root/nixpkgs/nixos/modules/services/networking/deconz.nix
blob: 05b7247087771c2edeea22386dcdc652944f7915 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
{ config, lib, pkgs, ... }:

let
  cfg = config.services.deconz;
  name = "deconz";
  stateDir = "/var/lib/${name}";
  # ref. upstream deconz.service
  capabilities =
    lib.optionals (cfg.httpPort < 1024 || cfg.wsPort < 1024) [ "CAP_NET_BIND_SERVICE" ]
    ++ lib.optionals (cfg.allowRebootSystem) [ "CAP_SYS_BOOT" ]
    ++ lib.optionals (cfg.allowRestartService) [ "CAP_KILL" ]
    ++ lib.optionals (cfg.allowSetSystemTime) [ "CAP_SYS_TIME" ];
in
{
  options.services.deconz = {

    enable = lib.mkEnableOption "deCONZ, a Zigbee gateway for use with ConBee hardware (https://phoscon.de/en/conbee2)";

    package = lib.mkOption {
      type = lib.types.package;
      default = pkgs.deconz;
      defaultText = lib.literalExpression "pkgs.deconz";
      description = "Which deCONZ package to use.";
    };

    device = lib.mkOption {
      type = lib.types.nullOr lib.types.str;
      default = null;
      description = ''
        Force deCONZ to use a specific USB device (e.g. /dev/ttyACM0). By
        default it does a search.
      '';
    };

    listenAddress = lib.mkOption {
      type = lib.types.str;
      default = "127.0.0.1";
      description = ''
        Pin deCONZ to the network interface specified through the provided IP
        address. This applies for the webserver as well as the websocket
        notifications.
      '';
    };

    httpPort = lib.mkOption {
      type = lib.types.port;
      default = 80;
      description = "TCP port for the web server.";
    };

    wsPort = lib.mkOption {
      type = lib.types.port;
      default = 443;
      description = "TCP port for the WebSocket.";
    };

    openFirewall = lib.mkEnableOption "opening up the service ports in the firewall";

    allowRebootSystem = lib.mkEnableOption "rebooting the system";

    allowRestartService = lib.mkEnableOption "killing/restarting processes";

    allowSetSystemTime = lib.mkEnableOption "setting the system time";

    extraArgs = lib.mkOption {
      type = lib.types.listOf lib.types.str;
      default = [ ];
      example = [
        "--dbg-info=1"
        "--dbg-err=2"
      ];
      description = ''
        Extra command line arguments for deCONZ, see
        https://github.com/dresden-elektronik/deconz-rest-plugin/wiki/deCONZ-command-line-parameters.
      '';
    };
  };

  config = lib.mkIf cfg.enable {

    networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall [
      cfg.httpPort
      cfg.wsPort
    ];

    services.udev.packages = [ cfg.package ];

    systemd.services.deconz = {
      description = "deCONZ Zigbee gateway";
      wantedBy = [ "multi-user.target" ];
      preStart = ''
        # The service puts a nix store path reference in here, and that path can
        # be garbage collected. Ensure the file gets "refreshed" on every start.
        rm -f ${stateDir}/.local/share/dresden-elektronik/deCONZ/zcldb.txt
      '';
      environment = {
        HOME = stateDir;
        XDG_RUNTIME_DIR = "/run/${name}";
      };
      serviceConfig = {
        ExecStart =
          "${lib.getExe cfg.package}"
          + " -platform minimal"
          + " --http-listen=${cfg.listenAddress}"
          + " --http-port=${toString cfg.httpPort}"
          + " --ws-port=${toString cfg.wsPort}"
          + " --auto-connect=1"
          + (lib.optionalString (cfg.device != null) " --dev=${cfg.device}")
          + " " + (lib.escapeShellArgs cfg.extraArgs);
        Restart = "on-failure";
        AmbientCapabilities = capabilities;
        CapabilityBoundingSet = capabilities;
        UMask = "0027";
        DynamicUser = true;
        RuntimeDirectory = name;
        RuntimeDirectoryMode = "0700";
        StateDirectory = name;
        WorkingDirectory = stateDir;
        # For access to /dev/ttyACM0 (ConBee).
        SupplementaryGroups = [ "dialout" ];
        ProtectHome = true;
      };
    };
  };
}