From ffc3eb7c160c8bace077f0701ec6310ba0326fa0 Mon Sep 17 00:00:00 2001 From: Nathan Bijnens Date: Thu, 20 Jul 2023 19:43:04 +0200 Subject: [PATCH] ebusd: new module --- .../manual/release-notes/rl-2311.section.md | 2 + nixos/modules/module-list.nix | 1 + .../services/home-automation/ebusd.nix | 270 ++++++++++++++++++ 3 files changed, 273 insertions(+) create mode 100644 nixos/modules/services/home-automation/ebusd.nix diff --git a/nixos/doc/manual/release-notes/rl-2311.section.md b/nixos/doc/manual/release-notes/rl-2311.section.md index 38fd9a3ff03d..a3208013fce3 100644 --- a/nixos/doc/manual/release-notes/rl-2311.section.md +++ b/nixos/doc/manual/release-notes/rl-2311.section.md @@ -28,6 +28,8 @@ - [osquery](https://www.osquery.io/), a SQL powered operating system instrumentation, monitoring, and analytics. +- [ebusd](https://ebusd.eu), a daemon for handling communication with eBUS devices connected to a 2-wire bus system (“energy bus” used by numerous heating systems). Available as [services.ebusd](#opt-services.ebusd.enable). + ## Backward Incompatibilities {#sec-release-23.11-incompatibilities} - The `boot.loader.raspberryPi` options have been marked deprecated, with intent for removal for NixOS 24.11. They had a limited use-case, and do not work like people expect. They required either very old installs ([before mid-2019](https://github.com/NixOS/nixpkgs/pull/62462)) or customized builds out of scope of the standard and generic AArch64 support. That option set never supported the Raspberry Pi 4 family of devices. diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 1e1c42c26301..2889a6fb5cc5 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -536,6 +536,7 @@ ./services/hardware/usbrelayd.nix ./services/hardware/vdr.nix ./services/hardware/keyd.nix + ./services/home-automation/ebusd.nix ./services/home-automation/esphome.nix ./services/home-automation/evcc.nix ./services/home-automation/home-assistant.nix diff --git a/nixos/modules/services/home-automation/ebusd.nix b/nixos/modules/services/home-automation/ebusd.nix new file mode 100644 index 000000000000..519d116e0e55 --- /dev/null +++ b/nixos/modules/services/home-automation/ebusd.nix @@ -0,0 +1,270 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.ebusd; + + package = pkgs.ebusd; + + arguments = [ + "${package}/bin/ebusd" + "--foreground" + "--updatecheck=off" + "--device=${cfg.device}" + "--port=${toString cfg.port}" + "--configpath=${cfg.configpath}" + "--scanconfig=${cfg.scanconfig}" + "--log=main:${cfg.logs.main}" + "--log=network:${cfg.logs.network}" + "--log=bus:${cfg.logs.bus}" + "--log=update:${cfg.logs.update}" + "--log=other:${cfg.logs.other}" + "--log=all:${cfg.logs.all}" + ] ++ lib.optionals cfg.readonly [ + "--readonly" + ] ++ lib.optionals cfg.mqtt.enable [ + "--mqtthost=${cfg.mqtt.host}" + "--mqttport=${toString cfg.mqtt.port}" + "--mqttuser=${cfg.mqtt.user}" + "--mqttpass=${cfg.mqtt.password}" + ] ++ lib.optionals cfg.mqtt.home-assistant [ + "--mqttint=${package}/etc/ebusd/mqtt-hassio.cfg" + "--mqttjson" + ] ++ lib.optionals cfg.mqtt.retain [ + "--mqttretain" + ] ++ cfg.extraArguments; + + usesDev = hasPrefix "/" cfg.device; + + command = concatStringsSep " " arguments; + +in +{ + meta.maintainers = with maintainers; [ nathan-gs ]; + + options.services.ebusd = { + enable = mkEnableOption (lib.mdDoc "ebusd service"); + + device = mkOption { + type = types.str; + default = ""; + example = "IP:PORT"; + description = lib.mdDoc '' + Use DEV as eBUS device [/dev/ttyUSB0]. + This can be either: + enh:DEVICE or enh:IP:PORT for enhanced device (only adapter v3 and newer), + ens:DEVICE for enhanced high speed serial device (only adapter v3 and newer with firmware since 20220731), + DEVICE for serial device (normal speed, for all other serial adapters like adapter v2 as well as adapter v3 in non-enhanced mode), or + [udp:]IP:PORT for network device. + https://github.com/john30/ebusd/wiki/2.-Run#device-options + ''; + }; + + port = mkOption { + default = 8888; + type = types.port; + description = lib.mdDoc '' + The port on which to listen on + ''; + }; + + readonly = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Only read from device, never write to it + ''; + }; + + configpath = mkOption { + type = types.str; + default = "https://cfg.ebusd.eu/"; + description = lib.mdDoc '' + Read CSV config files from PATH (local folder or HTTPS URL) [https://cfg.ebusd.eu/] + ''; + }; + + scanconfig = mkOption { + type = types.str; + default = "full"; + description = lib.mdDoc '' + 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). + 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, + see [Automatic configuration](https://github.com/john30/ebusd/wiki/4.7.-Automatic-configuration). + ''; + }; + + logs = { + main = mkOption { + type = types.enum [ "error" "notice" "info" "debug"]; + default = "info"; + description = lib.mdDoc '' + Only write log for matching AREAs (main|network|bus|update|other|all) below or equal to LEVEL (error|notice|info|debug) [all:notice]. + ''; + }; + + network = mkOption { + type = types.enum [ "error" "notice" "info" "debug"]; + default = "info"; + description = lib.mdDoc '' + Only write log for matching AREAs (main|network|bus|update|other|all) below or equal to LEVEL (error|notice|info|debug) [all:notice]. + ''; + }; + + bus = mkOption { + type = types.enum [ "error" "notice" "info" "debug"]; + default = "info"; + description = lib.mdDoc '' + Only write log for matching AREAs (main|network|bus|update|other|all) below or equal to LEVEL (error|notice|info|debug) [all:notice]. + ''; + }; + + update = mkOption { + type = types.enum [ "error" "notice" "info" "debug"]; + default = "info"; + description = lib.mdDoc '' + Only write log for matching AREAs (main|network|bus|update|other|all) below or equal to LEVEL (error|notice|info|debug) [all:notice]. + ''; + }; + + other = mkOption { + type = types.enum [ "error" "notice" "info" "debug"]; + default = "info"; + description = lib.mdDoc '' + Only write log for matching AREAs (main|network|bus|update|other|all) below or equal to LEVEL (error|notice|info|debug) [all:notice]. + ''; + }; + + all = mkOption { + type = types.enum [ "error" "notice" "info" "debug"]; + default = "info"; + description = lib.mdDoc '' + Only write log for matching AREAs (main|network|bus|update|other|all) below or equal to LEVEL (error|notice|info|debug) [all:notice]. + ''; + }; + }; + + mqtt = { + + enable = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Adds support for MQTT + ''; + }; + + host = mkOption { + type = types.str; + default = "localhost"; + description = lib.mdDoc '' + Connect to MQTT broker on HOST. + ''; + }; + + port = mkOption { + default = 1883; + type = types.port; + description = lib.mdDoc '' + The port on which to connect to MQTT + ''; + }; + + home-assistant = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Adds the Home Assistant topics to MQTT, read more at [MQTT Integration](https://github.com/john30/ebusd/wiki/MQTT-integration) + ''; + }; + + retain = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Set the retain flag on all topics instead of only selected global ones + ''; + }; + + user = mkOption { + type = types.str; + description = lib.mdDoc '' + The MQTT user to use + ''; + }; + + password = mkOption { + type = types.str; + description = lib.mdDoc '' + The MQTT password. + ''; + }; + + }; + + extraArguments = mkOption { + type = types.listOf types.str; + default = []; + description = lib.mdDoc '' + Extra arguments to the ebus daemon + ''; + }; + + }; + + config = mkIf (cfg.enable) { + + systemd.services.ebusd = { + description = "EBUSd Service"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + serviceConfig = { + ExecStart = command; + DynamicUser = true; + Restart = "on-failure"; + + # Hardening + CapabilityBoundingSet = ""; + DeviceAllow = lib.optionals usesDev [ + cfg.device + ] ; + DevicePolicy = "closed"; + LockPersonality = true; + MemoryDenyWriteExecute = false; + NoNewPrivileges = true; + PrivateDevices = usesDev; + PrivateUsers = true; + PrivateTmp = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + ProcSubset = "pid"; + ProtectSystem = "strict"; + RemoveIPC = true; + RestrictAddressFamilies = [ + "AF_INET" + "AF_INET6" + ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SupplementaryGroups = [ + "dialout" + ]; + SystemCallArchitectures = "native"; + SystemCallFilter = [ + "@system-service @pkey" + "~@privileged @resources" + ]; + UMask = "0077"; + }; + }; + + }; +}