1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 cfg = config.services.ebusd; 7 8 package = pkgs.ebusd; 9 10 arguments = [ 11 "${package}/bin/ebusd" 12 "--foreground" 13 "--updatecheck=off" 14 "--device=${cfg.device}" 15 "--port=${toString cfg.port}" 16 "--configpath=${cfg.configpath}" 17 "--scanconfig=${cfg.scanconfig}" 18 "--log=main:${cfg.logs.main}" 19 "--log=network:${cfg.logs.network}" 20 "--log=bus:${cfg.logs.bus}" 21 "--log=update:${cfg.logs.update}" 22 "--log=other:${cfg.logs.other}" 23 "--log=all:${cfg.logs.all}" 24 ] ++ lib.optionals cfg.readonly [ 25 "--readonly" 26 ] ++ lib.optionals cfg.mqtt.enable [ 27 "--mqtthost=${cfg.mqtt.host}" 28 "--mqttport=${toString cfg.mqtt.port}" 29 "--mqttuser=${cfg.mqtt.user}" 30 "--mqttpass=${cfg.mqtt.password}" 31 ] ++ lib.optionals cfg.mqtt.home-assistant [ 32 "--mqttint=${package}/etc/ebusd/mqtt-hassio.cfg" 33 "--mqttjson" 34 ] ++ lib.optionals cfg.mqtt.retain [ 35 "--mqttretain" 36 ] ++ cfg.extraArguments; 37 38 usesDev = hasPrefix "/" cfg.device; 39 40 command = concatStringsSep " " arguments; 41 42in 43{ 44 meta.maintainers = with maintainers; [ nathan-gs ]; 45 46 options.services.ebusd = { 47 enable = mkEnableOption (lib.mdDoc "ebusd service"); 48 49 device = mkOption { 50 type = types.str; 51 default = ""; 52 example = "IP:PORT"; 53 description = lib.mdDoc '' 54 Use DEV as eBUS device [/dev/ttyUSB0]. 55 This can be either: 56 enh:DEVICE or enh:IP:PORT for enhanced device (only adapter v3 and newer), 57 ens:DEVICE for enhanced high speed serial device (only adapter v3 and newer with firmware since 20220731), 58 DEVICE for serial device (normal speed, for all other serial adapters like adapter v2 as well as adapter v3 in non-enhanced mode), or 59 [udp:]IP:PORT for network device. 60 https://github.com/john30/ebusd/wiki/2.-Run#device-options 61 ''; 62 }; 63 64 port = mkOption { 65 default = 8888; 66 type = types.port; 67 description = lib.mdDoc '' 68 The port on which to listen on 69 ''; 70 }; 71 72 readonly = mkOption { 73 type = types.bool; 74 default = false; 75 description = lib.mdDoc '' 76 Only read from device, never write to it 77 ''; 78 }; 79 80 configpath = mkOption { 81 type = types.str; 82 default = "https://cfg.ebusd.eu/"; 83 description = lib.mdDoc '' 84 Read CSV config files from PATH (local folder or HTTPS URL) [https://cfg.ebusd.eu/] 85 ''; 86 }; 87 88 scanconfig = mkOption { 89 type = types.str; 90 default = "full"; 91 description = lib.mdDoc '' 92 Pick CSV config files matching initial scan ("none" or empty for no initial scan message, "full" for full scan, or a single hex address to scan, default is to send a broadcast ident message). 93 If combined with --checkconfig, you can add scan message data as arguments for checking a particular scan configuration, e.g. "FF08070400/0AB5454850303003277201". For further details on this option, 94 see [Automatic configuration](https://github.com/john30/ebusd/wiki/4.7.-Automatic-configuration). 95 ''; 96 }; 97 98 logs = { 99 main = mkOption { 100 type = types.enum [ "error" "notice" "info" "debug"]; 101 default = "info"; 102 description = lib.mdDoc '' 103 Only write log for matching AREAs (main|network|bus|update|other|all) below or equal to LEVEL (error|notice|info|debug) [all:notice]. 104 ''; 105 }; 106 107 network = mkOption { 108 type = types.enum [ "error" "notice" "info" "debug"]; 109 default = "info"; 110 description = lib.mdDoc '' 111 Only write log for matching AREAs (main|network|bus|update|other|all) below or equal to LEVEL (error|notice|info|debug) [all:notice]. 112 ''; 113 }; 114 115 bus = mkOption { 116 type = types.enum [ "error" "notice" "info" "debug"]; 117 default = "info"; 118 description = lib.mdDoc '' 119 Only write log for matching AREAs (main|network|bus|update|other|all) below or equal to LEVEL (error|notice|info|debug) [all:notice]. 120 ''; 121 }; 122 123 update = mkOption { 124 type = types.enum [ "error" "notice" "info" "debug"]; 125 default = "info"; 126 description = lib.mdDoc '' 127 Only write log for matching AREAs (main|network|bus|update|other|all) below or equal to LEVEL (error|notice|info|debug) [all:notice]. 128 ''; 129 }; 130 131 other = mkOption { 132 type = types.enum [ "error" "notice" "info" "debug"]; 133 default = "info"; 134 description = lib.mdDoc '' 135 Only write log for matching AREAs (main|network|bus|update|other|all) below or equal to LEVEL (error|notice|info|debug) [all:notice]. 136 ''; 137 }; 138 139 all = mkOption { 140 type = types.enum [ "error" "notice" "info" "debug"]; 141 default = "info"; 142 description = lib.mdDoc '' 143 Only write log for matching AREAs (main|network|bus|update|other|all) below or equal to LEVEL (error|notice|info|debug) [all:notice]. 144 ''; 145 }; 146 }; 147 148 mqtt = { 149 150 enable = mkOption { 151 type = types.bool; 152 default = false; 153 description = lib.mdDoc '' 154 Adds support for MQTT 155 ''; 156 }; 157 158 host = mkOption { 159 type = types.str; 160 default = "localhost"; 161 description = lib.mdDoc '' 162 Connect to MQTT broker on HOST. 163 ''; 164 }; 165 166 port = mkOption { 167 default = 1883; 168 type = types.port; 169 description = lib.mdDoc '' 170 The port on which to connect to MQTT 171 ''; 172 }; 173 174 home-assistant = mkOption { 175 type = types.bool; 176 default = false; 177 description = lib.mdDoc '' 178 Adds the Home Assistant topics to MQTT, read more at [MQTT Integration](https://github.com/john30/ebusd/wiki/MQTT-integration) 179 ''; 180 }; 181 182 retain = mkOption { 183 type = types.bool; 184 default = false; 185 description = lib.mdDoc '' 186 Set the retain flag on all topics instead of only selected global ones 187 ''; 188 }; 189 190 user = mkOption { 191 type = types.str; 192 description = lib.mdDoc '' 193 The MQTT user to use 194 ''; 195 }; 196 197 password = mkOption { 198 type = types.str; 199 description = lib.mdDoc '' 200 The MQTT password. 201 ''; 202 }; 203 204 }; 205 206 extraArguments = mkOption { 207 type = types.listOf types.str; 208 default = []; 209 description = lib.mdDoc '' 210 Extra arguments to the ebus daemon 211 ''; 212 }; 213 214 }; 215 216 config = mkIf (cfg.enable) { 217 218 systemd.services.ebusd = { 219 description = "EBUSd Service"; 220 wantedBy = [ "multi-user.target" ]; 221 after = [ "network.target" ]; 222 serviceConfig = { 223 ExecStart = command; 224 DynamicUser = true; 225 Restart = "on-failure"; 226 227 # Hardening 228 CapabilityBoundingSet = ""; 229 DeviceAllow = lib.optionals usesDev [ 230 cfg.device 231 ] ; 232 DevicePolicy = "closed"; 233 LockPersonality = true; 234 MemoryDenyWriteExecute = false; 235 NoNewPrivileges = true; 236 PrivateDevices = usesDev; 237 PrivateUsers = true; 238 PrivateTmp = true; 239 ProtectClock = true; 240 ProtectControlGroups = true; 241 ProtectHome = true; 242 ProtectHostname = true; 243 ProtectKernelLogs = true; 244 ProtectKernelModules = true; 245 ProtectKernelTunables = true; 246 ProtectProc = "invisible"; 247 ProcSubset = "pid"; 248 ProtectSystem = "strict"; 249 RemoveIPC = true; 250 RestrictAddressFamilies = [ 251 "AF_INET" 252 "AF_INET6" 253 ]; 254 RestrictNamespaces = true; 255 RestrictRealtime = true; 256 RestrictSUIDSGID = true; 257 SupplementaryGroups = [ 258 "dialout" 259 ]; 260 SystemCallArchitectures = "native"; 261 SystemCallFilter = [ 262 "@system-service @pkey" 263 "~@privileged @resources" 264 ]; 265 UMask = "0077"; 266 }; 267 }; 268 269 }; 270}