diff --git a/README.md b/README.md index 43333ab..4015f7c 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ - get age key from SSH ```console curl https://raw.githubusercontent.com/elitak/nixos-infect/master/nixos-infect | PROVIDER=hetznercloud NIX_CHANNEL=nixos-24.05 bash 2>&1 | tee /tmp/infect.log -nix-shell -p ssh-to-age --run 'ssh-keyscan example.com | ssh-to-age' +nix-shell -p ssh-to-age --run 'ssh-keyscan install.cloonar.com | ssh-to-age' ``` - fix secrets files ```console diff --git a/esphome/hallway-light-switch.yaml b/esphome/hallway-light-switch.yaml new file mode 100644 index 0000000..3db6ac7 --- /dev/null +++ b/esphome/hallway-light-switch.yaml @@ -0,0 +1,340 @@ +substitutions: + # Default name + name: "hallway-light-switch" + # Default friendly name + friendly_name: "Hallway Light Switch" + # Allows ESP device to be automatically linked to an 'Area' in Home Assistant. Typically used for areas such as 'Lounge Room', 'Kitchen' etc + room: "Hallway" + # Description as appears in ESPHome & top of webserver page + device_description: "Hallway Light Switch" + # Project Name + project_name: "Athom Technology.Mini Relay V2" + # Projection version denotes the release version of the yaml file, allowing checking of deployed vs latest version + project_version: "v2.0.4" + # Restore the relay (GPO switch) upon reboot to state: + light_restore_mode: RESTORE_DEFAULT_OFF + # Set the update interval for sensors + sensor_update_interval: 10s + # Current Limit in Amps. + current_limit : "10" + # Define a domain for this device to use. i.e. iot.home.lan (so device will appear as athom-smart-plug-v2.iot.home.lan in DNS/DHCP logs) + dns_domain: ".cloonar.smart" + # Set timezone of the smart plug. Useful if the plug is in a location different to the HA server. Can be entered in unix Country/Area format (i.e. "Australia/Sydney") + timezone: "" + # Set the duration between the sntp service polling ntp.org servers for an update + sntp_update_interval: 6h + # Network time servers for your region, enter from lowest to highest priority. To use local servers update as per zones or countries at: https://www.ntppool.org/zone/@ + sntp_server_1: "0.pool.ntp.org" + sntp_server_2: "1.pool.ntp.org" + sntp_server_3: "2.pool.ntp.org" + # Enables faster network connections, with last connected SSID being connected to and no full scan for SSID being undertaken + wifi_fast_connect: "false" + # Define logging level: NONE, ERROR, WARN, INFO, DEBUG (Default), VERBOSE, VERY_VERBOSE + log_level: "WARN" + # Hide the ENERGY sensor that shows kWh consumed, but with no time period associated with it. Resets when device restarted and reflashed. + hide_energy_sensor: "true" + # Enable or disable the use of IPv6 networking on the device + ipv6_enable: "false" + +esphome: + name: "${name}" + friendly_name: "${friendly_name}" + comment: "${device_description}" + area: "${room}" + name_add_mac_suffix: false + min_version: 2024.5.0 + project: + name: "${project_name}" + version: "${project_version}" + platformio_options: + board_build.mcu: esp32c3 + board_build.variant: esp32c3 + board_build.flash_mode: dio + +esp32: + board: esp32-c3-devkitm-1 + flash_size: 4MB + variant: ESP32C3 + framework: + type: arduino + version: recommended + +preferences: + flash_write_interval: 5min + +api: + +ota: + - platform: esphome + +mdns: + disabled: false + +web_server: + port: 80 + +network: + enable_ipv6: ${ipv6_enable} + +wifi: + ssid: !secret wifi_ssid + password: !secret wifi_password + fast_connect: True + domain: ${dns_domain} + +esp32_improv: + authorizer: none + +uart: + rx_pin: GPIO20 + baud_rate: 4800 + data_bits: 8 + stop_bits: 1 + parity: EVEN + +globals: + - id: total_energy + type: float + restore_value: yes + initial_value: '0.0' + +binary_sensor: + - platform: status + name: "Status" + entity_category: diagnostic + + - platform: gpio + pin: + number: GPIO3 + mode: INPUT_PULLUP + inverted: true + name: "Power Button" + disabled_by_default: true + on_multi_click: + - timing: + - ON for at most 1s + - OFF for at least 0.2s + then: + - light.toggle: mini_relay + - timing: + - ON for at least 4s + then: + - button.press: Reset + + - platform: gpio + id: the_switch + name: "Power Switch" + pin: + number: GPIO4 + mode: INPUT_PULLUP + inverted: true + on_multi_click: + - timing: + - ON for at most 1s + then: + - light.toggle: mini_relay + +sensor: + - platform: uptime + name: "Uptime Sensor" + id: uptime_sensor + entity_category: diagnostic + internal: true + + - platform: wifi_signal + name: "WiFi Signal dB" + id: wifi_signal_db + update_interval: 60s + entity_category: "diagnostic" + + - platform: copy + source_id: wifi_signal_db + name: "WiFi Signal Percent" + filters: + - lambda: return min(max(2 * (x + 100.0), 0.0), 100.0); + unit_of_measurement: "Signal %" + entity_category: "diagnostic" + device_class: "" + + - platform: cse7766 + id: athom_cse7766 + current: + name: "Current" + filters: + - throttle_average: ${sensor_update_interval} + - lambda: if (x < 0.060) return 0.0; else return x; #For the chip will report less than 3w power when no load is connected + on_value_range: + - above: ${current_limit} + then: + - light.turn_off: mini_relay + + voltage: + name: "Voltage" + filters: + - throttle_average: ${sensor_update_interval} + + power: + name: "Power" + id: power_sensor + filters: + - throttle_average: ${sensor_update_interval} + - lambda: if (x < 3.0) return 0.0; else return x; #For the chip will report less than 3w power when no load is connected + + energy: + name: "Energy" + id: energy + unit_of_measurement: kWh + filters: + - throttle: ${sensor_update_interval} + # Multiplication factor from W to kW is 0.001 + - multiply: 0.001 + on_value: + then: + - lambda: |- + static float previous_energy_value = 0.0; + float current_energy_value = id(energy).state; + id(total_energy) += current_energy_value - previous_energy_value; + previous_energy_value = current_energy_value; + id(total_energy_sensor).update(); + + apparent_power: + name: "Apparent Power" + filters: + - throttle_average: ${sensor_update_interval} + reactive_power: + name: "Reactive Power" + filters: + - throttle_average: ${sensor_update_interval} + power_factor: + name: "Power Factor" + filters: + - throttle_average: ${sensor_update_interval} + + - platform: template + name: "Total Energy" + id: total_energy_sensor + unit_of_measurement: kWh + device_class: "energy" + state_class: "total_increasing" + icon: "mdi:lightning-bolt" + accuracy_decimals: 3 + lambda: |- + return id(total_energy); + update_interval: ${sensor_update_interval} + + - platform: total_daily_energy + name: "Total Energy Today" + restore: true + power_id: power_sensor + unit_of_measurement: kWh + accuracy_decimals: 3 + filters: + - multiply: 0.001 + +button: + - platform: restart + name: "Restart" + entity_category: config + + - platform: factory_reset + name: "Factory Reset" + id: Reset + entity_category: config + + - platform: safe_mode + name: "Safe Mode" + internal: false + entity_category: config + +output: + - platform: gpio + id: relay_output + pin: GPIO6 + +light: + - platform: status_led + id: led + name: "Blue LED" + disabled_by_default: true + pin: + number: GPIO7 + inverted: true + + - platform: binary + id: mini_relay + output: relay_output + name: "Mini Switch" + restore_mode: ${light_restore_mode} + on_turn_on: + - light.turn_on: led + on_turn_off: + - light.turn_off: led + +text_sensor: + - platform: wifi_info + ip_address: + name: "IP Address" + entity_category: diagnostic + ssid: + name: "Connected SSID" + entity_category: diagnostic + mac_address: + name: "Mac Address" + entity_category: diagnostic + + # Creates a sensor showing when the device was last restarted + - platform: template + name: 'Last Restart' + id: device_last_restart + icon: mdi:clock + entity_category: diagnostic +# device_class: timestamp + + # Creates a sensor of the uptime of the device, in formatted days, hours, minutes and seconds + - platform: template + name: "Uptime" + entity_category: diagnostic + lambda: |- + int seconds = (id(uptime_sensor).state); + int days = seconds / (24 * 3600); + seconds = seconds % (24 * 3600); + int hours = seconds / 3600; + seconds = seconds % 3600; + int minutes = seconds / 60; + seconds = seconds % 60; + if ( days > 3650 ) { + return { "Starting up" }; + } else if ( days ) { + return { (String(days) +"d " + String(hours) +"h " + String(minutes) +"m "+ String(seconds) +"s").c_str() }; + } else if ( hours ) { + return { (String(hours) +"h " + String(minutes) +"m "+ String(seconds) +"s").c_str() }; + } else if ( minutes ) { + return { (String(minutes) +"m "+ String(seconds) +"s").c_str() }; + } else { + return { (String(seconds) +"s").c_str() }; + } + icon: mdi:clock-start + +time: + - platform: sntp + id: sntp_time + # Define the timezone of the device + timezone: "${timezone}" + # Change sync interval from default 5min to 6 hours (or as set in substitutions) + update_interval: ${sntp_update_interval} + # Set specific sntp servers to use + servers: + - "${sntp_server_1}" + - "${sntp_server_2}" + - "${sntp_server_3}" + # Publish the time the device was last restarted + on_time_sync: + then: + # Update last restart time, but only once. + - if: + condition: + lambda: 'return id(device_last_restart).state == "";' + then: + - text_sensor.template.publish: + id: device_last_restart + state: !lambda 'return id(sntp_time).now().strftime("%a %d %b %Y - %I:%M:%S %p");' diff --git a/esphome/livingroom-bulb-1.yaml b/esphome/livingroom-bulb-1.yaml index 8de423e..7f848c9 100644 --- a/esphome/livingroom-bulb-1.yaml +++ b/esphome/livingroom-bulb-1.yaml @@ -10,7 +10,6 @@ substitutions: sntp_server_1: "0.pool.ntp.org" sntp_server_2: "1.pool.ntp.org" sntp_server_3: "2.pool.ntp.org" - log_level: "WARN" esphome: name: "${name}" @@ -23,27 +22,27 @@ esphome: name: "${project_name}" version: "${project_version}" on_boot: - then: - - light.turn_on: - id: rgbww_light - - delay: 100ms - - light.turn_on: - id: rgbww_light - brightness: 20% - - delay: 100ms - - light.turn_on: - id: rgbww_light - red: 100% - green: 50% - blue: 0% - white: 100% + then: + - light.turn_on: + id: rgbww_light + - delay: 100ms + - light.turn_on: + id: rgbww_light + brightness: 20% + - delay: 100ms + - light.turn_on: + id: rgbww_light + red: 100% + green: 50% + blue: 0% + white: 100% interval: - interval: 15s then: - if: condition: - api.connected: # check if api connected + api.connected: else: - light.turn_on: id: rgbww_light @@ -61,21 +60,31 @@ api: ota: - platform: esphome -logger: - -mdns: - disabled: false - wifi: - ssid: !secret wifi_ssid - password: !secret wifi_password - fast_connect: True - domain: .cloonar.smart + # Disable fast_connect so we do a full scan (required for hidden SSIDs) + fast_connect: false + domain: "${dns_domain}" -captive_portal: + # Your hidden network + networks: + - ssid: !secret wifi_ssid + password: !secret wifi_password + channel: 1 + hidden: true -dashboard_import: - package_import_url: github://athom-tech/athom-configs/athom-rgbww-light.yaml + manual_ip: + static_ip: 10.42.100.11 + gateway: 10.42.100.1 + subnet: 255.255.255.0 + dns1: 8.8.8.8 + dns2: 1.1.1.1 + + # Fallback access point if Wi-Fi fails + ap: + ssid: "${name}_AP" + password: "bulb_fallback_pw" + ap_timeout: 2min # after 2 min of failed join, enable AP + reboot_timeout: 5min # if still not joined after 5 min, reboot and retry binary_sensor: - platform: status @@ -93,7 +102,7 @@ sensor: name: "WiFi Signal dB" id: wifi_signal_db update_interval: 60s - entity_category: "diagnostic" + entity_category: diagnostic - platform: copy source_id: wifi_signal_db @@ -101,23 +110,7 @@ sensor: filters: - lambda: return min(max(2 * (x + 100.0), 0.0), 100.0); unit_of_measurement: "Signal %" - entity_category: "diagnostic" - device_class: "" - -button: - - platform: restart - name: "Restart" - entity_category: config - - - platform: factory_reset - name: "Factory Reset" - id: Reset - entity_category: config - - - platform: safe_mode - name: "Safe Mode" - internal: false - entity_category: config + entity_category: diagnostic output: - platform: esp8266_pwm @@ -171,59 +164,51 @@ text_sensor: name: "Mac Address" entity_category: diagnostic - # Creates a sensor showing when the device was last restarted - platform: template name: 'Last Restart' id: device_last_restart icon: mdi:clock entity_category: diagnostic -# device_class: timestamp - # Creates a sensor of the uptime of the device, in formatted days, hours, minutes and seconds - platform: template name: "Uptime" entity_category: diagnostic lambda: |- int seconds = (id(uptime_sensor).state); int days = seconds / (24 * 3600); - seconds = seconds % (24 * 3600); + seconds %= (24 * 3600); int hours = seconds / 3600; - seconds = seconds % 3600; - int minutes = seconds / 60; - seconds = seconds % 60; - if ( days > 3650 ) { + seconds %= 3600; + int minutes = seconds / 60; + seconds %= 60; + if (days > 3650) { return { "Starting up" }; - } else if ( days ) { - return { (String(days) +"d " + String(hours) +"h " + String(minutes) +"m "+ String(seconds) +"s").c_str() }; - } else if ( hours ) { - return { (String(hours) +"h " + String(minutes) +"m "+ String(seconds) +"s").c_str() }; - } else if ( minutes ) { - return { (String(minutes) +"m "+ String(seconds) +"s").c_str() }; + } else if (days) { + return { (String(days) + "d " + String(hours) + "h " + String(minutes) + "m " + String(seconds) + "s").c_str() }; + } else if (hours) { + return { (String(hours) + "h " + String(minutes) + "m " + String(seconds) + "s").c_str() }; + } else if (minutes) { + return { (String(minutes) + "m " + String(seconds) + "s").c_str() }; } else { - return { (String(seconds) +"s").c_str() }; + return { (String(seconds) + "s").c_str() }; } icon: mdi:clock-start time: - platform: sntp id: sntp_time - # Define the timezone of the device timezone: "${timezone}" - # Change sync interval from default 5min to 6 hours (or as set in substitutions) update_interval: ${sntp_update_interval} - # Set specific sntp servers to use servers: - "${sntp_server_1}" - "${sntp_server_2}" - "${sntp_server_3}" - # Publish the time the device was last restarted on_time_sync: then: - # Update last restart time, but only once. - if: condition: lambda: 'return id(device_last_restart).state == "";' then: - text_sensor.template.publish: id: device_last_restart - state: !lambda 'return id(sntp_time).now().strftime("%a %d %b %Y - %I:%M:%S %p");' \ No newline at end of file + state: !lambda 'return id(sntp_time).now().strftime("%a %d %b %Y - %I:%M:%S %p");' diff --git a/esphome/livingroom-bulb-2.yaml b/esphome/livingroom-bulb-2.yaml index 9699e3e..b005f58 100644 --- a/esphome/livingroom-bulb-2.yaml +++ b/esphome/livingroom-bulb-2.yaml @@ -61,21 +61,18 @@ api: ota: - platform: esphome -logger: - -mdns: - disabled: false - wifi: - ssid: !secret wifi_ssid - password: !secret wifi_password - fast_connect: True domain: .cloonar.smart - -captive_portal: - -dashboard_import: - package_import_url: github://athom-tech/athom-configs/athom-rgbww-light.yaml + fast_connect: False + networks: + - ssid: !secret wifi_ssid + password: !secret wifi_password + channel: 1 + hidden: True + manual_ip: + static_ip: 10.42.100.12 + gateway: 10.42.100.1 + subnet: 255.255.255.0 binary_sensor: - platform: status @@ -104,21 +101,6 @@ sensor: entity_category: "diagnostic" device_class: "" -button: - - platform: restart - name: "Restart" - entity_category: config - - - platform: factory_reset - name: "Factory Reset" - id: Reset - entity_category: config - - - platform: safe_mode - name: "Safe Mode" - internal: false - entity_category: config - output: - platform: esp8266_pwm id: red_output diff --git a/esphome/livingroom-bulb-3.yaml b/esphome/livingroom-bulb-3.yaml index a0ad908..d7bc72c 100644 --- a/esphome/livingroom-bulb-3.yaml +++ b/esphome/livingroom-bulb-3.yaml @@ -72,21 +72,18 @@ api: ota: - platform: esphome -logger: - -mdns: - disabled: false - wifi: - ssid: !secret wifi_ssid - password: !secret wifi_password - fast_connect: True domain: .cloonar.smart - -captive_portal: - -dashboard_import: - package_import_url: github://athom-tech/athom-configs/athom-rgbww-light.yaml + fast_connect: False + networks: + - ssid: !secret wifi_ssid + password: !secret wifi_password + hidden: True + channel: 1 + manual_ip: + static_ip: 10.42.100.13 + gateway: 10.42.100.1 + subnet: 255.255.255.0 binary_sensor: - platform: status @@ -115,21 +112,6 @@ sensor: entity_category: "diagnostic" device_class: "" -button: - - platform: restart - name: "Restart" - entity_category: config - - - platform: factory_reset - name: "Factory Reset" - id: Reset - entity_category: config - - - platform: safe_mode - name: "Safe Mode" - internal: false - entity_category: config - output: - platform: esp8266_pwm id: red_output diff --git a/esphome/livingroom-bulb-4.yaml b/esphome/livingroom-bulb-4.yaml index 68c8cb5..84e0086 100644 --- a/esphome/livingroom-bulb-4.yaml +++ b/esphome/livingroom-bulb-4.yaml @@ -72,21 +72,17 @@ api: ota: - platform: esphome -logger: - -mdns: - disabled: false - wifi: - ssid: !secret wifi_ssid - password: !secret wifi_password - fast_connect: True domain: .cloonar.smart - -captive_portal: - -dashboard_import: - package_import_url: github://athom-tech/athom-configs/athom-rgbww-light.yaml + fast_connect: False + networks: + - ssid: !secret wifi_ssid + password: !secret wifi_password + hidden: True + manual_ip: + static_ip: 10.42.100.14 + gateway: 10.42.100.1 + subnet: 255.255.255.0 binary_sensor: - platform: status @@ -115,21 +111,6 @@ sensor: entity_category: "diagnostic" device_class: "" -button: - - platform: restart - name: "Restart" - entity_category: config - - - platform: factory_reset - name: "Factory Reset" - id: Reset - entity_category: config - - - platform: safe_mode - name: "Safe Mode" - internal: false - entity_category: config - output: - platform: esp8266_pwm id: red_output diff --git a/esphome/livingroom-bulb-5.yaml b/esphome/livingroom-bulb-5.yaml index 4aec363..295e56a 100644 --- a/esphome/livingroom-bulb-5.yaml +++ b/esphome/livingroom-bulb-5.yaml @@ -72,21 +72,17 @@ api: ota: - platform: esphome -logger: - -mdns: - disabled: false - wifi: - ssid: !secret wifi_ssid - password: !secret wifi_password - fast_connect: True domain: .cloonar.smart - -captive_portal: - -dashboard_import: - package_import_url: github://athom-tech/athom-configs/athom-rgbww-light.yaml + fast_connect: False + networks: + - ssid: !secret wifi_ssid + password: !secret wifi_password + hidden: True + manual_ip: + static_ip: 10.42.100.15 + gateway: 10.42.100.1 + subnet: 255.255.255.0 binary_sensor: - platform: status @@ -115,21 +111,6 @@ sensor: entity_category: "diagnostic" device_class: "" -button: - - platform: restart - name: "Restart" - entity_category: config - - - platform: factory_reset - name: "Factory Reset" - id: Reset - entity_category: config - - - platform: safe_mode - name: "Safe Mode" - internal: false - entity_category: config - output: - platform: esp8266_pwm id: red_output diff --git a/esphome/livingroom-bulb-6.yaml b/esphome/livingroom-bulb-6.yaml index 1e86304..a06f5e1 100644 --- a/esphome/livingroom-bulb-6.yaml +++ b/esphome/livingroom-bulb-6.yaml @@ -72,21 +72,18 @@ api: ota: - platform: esphome -logger: - -mdns: - disabled: false - wifi: - ssid: !secret wifi_ssid - password: !secret wifi_password - fast_connect: True domain: .cloonar.smart - -captive_portal: - -dashboard_import: - package_import_url: github://athom-tech/athom-configs/athom-rgbww-light.yaml + fast_connect: False + networks: + - ssid: !secret wifi_ssid + password: !secret wifi_password + hidden: True + channel: 1 + manual_ip: + static_ip: 10.42.100.16 + gateway: 10.42.100.1 + subnet: 255.255.255.0 binary_sensor: - platform: status @@ -115,21 +112,6 @@ sensor: entity_category: "diagnostic" device_class: "" -button: - - platform: restart - name: "Restart" - entity_category: config - - - platform: factory_reset - name: "Factory Reset" - id: Reset - entity_category: config - - - platform: safe_mode - name: "Safe Mode" - internal: false - entity_category: config - output: - platform: esp8266_pwm id: red_output diff --git a/hosts/fw/configuration.nix b/hosts/fw/configuration.nix index f1fa1f7..0401028 100644 --- a/hosts/fw/configuration.nix +++ b/hosts/fw/configuration.nix @@ -16,8 +16,10 @@ ./modules/networking.nix ./modules/setupnetwork.nix ./modules/firewall.nix - ./modules/dhcp4.nix - ./modules/unbound.nix + # ./modules/dhcp4.nix + # ./modules/unbound.nix + + ./modules/dnsmasq.nix ./modules/avahi.nix ./modules/openconnect.nix ./modules/wireguard.nix @@ -33,7 +35,7 @@ # ./modules/vscode-server.nix # Add VS Code Server microvm ./modules/ai-mailer.nix - ./modules/wazuh.nix + # ./modules/wazuh.nix # web ./modules/web @@ -52,7 +54,8 @@ ./modules/deconz.nix # ./modules/mopidy.nix # ./modules/mosquitto.nix - ./modules/snapserver.nix + # ./modules/snapserver.nix + ./modules/lms.nix # gaming # ./modules/palworld.nix diff --git a/hosts/fw/modules/dnsmasq.nix b/hosts/fw/modules/dnsmasq.nix new file mode 100644 index 0000000..35a1d7e --- /dev/null +++ b/hosts/fw/modules/dnsmasq.nix @@ -0,0 +1,160 @@ +{ config, ... }: { + services.resolved.enable = false; + + services.dnsmasq = { + enable = true; + settings = { + port = "53"; + bind-interfaces = true; # force dnsmasq to bind immediately + expand-hosts = true; + + log-dhcp = true; + + server = [ + "/epicenter.works/10.50.60.1" + "/akvorrat.at/10.50.60.1" + "9.9.9.9" + "149.112.112.11" + ]; + + interface = [ + "lan" + "server" + "infrastructure" + "multimedia" + "guest" + "smart" + ]; + + domain = [ + "cloonar.com,lan" + "cloonar.com,server" + "cloonar.com,infrastructure" + "cloonar.multimedia,multimedia" + "cloonar.smart,smart" + "cloonar.guest,guest" + ]; + + dhcp-option = [ + "lan,15,cloonar.com" # domain name + "lan,3,${config.networkPrefix}.96.1" # Gateway + "lan,6,${config.networkPrefix}.96.1" # DNS + "server,15,cloonar.com" + "server,3,${config.networkPrefix}.97.1" + "server,6,${config.networkPrefix}.97.1" + "infrastructure,15,cloonar.com" + "infrastructure,3,${config.networkPrefix}.101.1" + "infrastructure,6,${config.networkPrefix}.101.1" + "multimedia,15,cloonar.multimedia" + "multimedia,3,${config.networkPrefix}.99.1" + "multimedia,6,${config.networkPrefix}.99.1" + "smart,15,cloonar.smart" + "smart,3,${config.networkPrefix}.100.1" + "smart,6,${config.networkPrefix}.100.1" + "guest,15,cloonar.guest" + "guest,3,${config.networkPrefix}.254.1" + "guest,6,9.9.9.9" + ]; + + dhcp-range = [ + "lan,${config.networkPrefix}.96.100,${config.networkPrefix}.96.200,24h" + "server,${config.networkPrefix}.97.100,${config.networkPrefix}.97.200,24h" + "infrastructure,${config.networkPrefix}.101.100,${config.networkPrefix}.101.200,24h" + "multimedia,${config.networkPrefix}.99.100,${config.networkPrefix}.99.200,24h" + "smart,${config.networkPrefix}.100.100,${config.networkPrefix}.100.200,24h" + "guest,${config.networkPrefix}.254.100,${config.networkPrefix}.254.200,24h" + ]; + + dhcp-host = [ + "30:05:5c:56:62:37,${config.networkPrefix}.96.100,brn30055c566237" + "24:df:a7:b1:1b:74,${config.networkPrefix}.96.101,rmproplus-b1-1b-74" + + "1a:c4:04:6e:29:bd,${config.networkPrefix}.97.2,omada" + "02:00:00:00:00:03,${config.networkPrefix}.97.5,web-02" + "02:00:00:00:00:04,${config.networkPrefix}.97.6,matrix" + "ea:db:d4:c1:18:ba,${config.networkPrefix}.97.50,git" + "c2:4f:64:dd:13:0c,${config.networkPrefix}.97.20,home-assistant" + "1a:c4:04:6e:29:02,${config.networkPrefix}.101.25,deconz" + + "c4:a7:2b:c7:ea:30,${config.networkPrefix}.99.10,metz" + "f0:2f:9e:d4:3b:21,${config.networkPrefix}.99.11,firetv-living" + "e4:2a:ac:32:3f:79,${config.networkPrefix}.99.13,xbox" + "f0:2f:9e:c1:74:72,${config.networkPrefix}.99.21,firetv-bedroom" + "30:05:5c:56:62:37,${config.networkPrefix}.99.100,brn30055c566237" + + "fc:ee:28:03:63:e9,${config.networkPrefix}.100.148,k1c" + "cc:50:e3:bc:27:64,${config.networkPrefix}.100.112,Nuki_Bridge_1A753F72" + "34:6f:24:f3:af:ad,${config.networkPrefix}.100.137,daikin86604" + "34:6f:24:c1:f8:54,${config.networkPrefix}.100.139,daikin53800" + ]; + + address = [ + "/fw.cloonar.com/${config.networkPrefix}.97.1" + "/omada.cloonar.com/${config.networkPrefix}.97.2" + "/pc.cloonar.com/${config.networkPrefix}.96.5" + "/home-assistant.cloonar.com/${config.networkPrefix}.97.20" + "/mopidy.cloonar.com/${config.networkPrefix}.97.21" + "/snapcast.cloonar.com/${config.networkPrefix}.97.21" + "/git.cloonar.com/${config.networkPrefix}.97.50" + "/feeds.cloonar.com/188.34.191.144" + + "/stage.wsw.at/10.254.235.22" + "/prod.wsw.at/10.254.217.23" + "/piwik.wohnservice-wien.at/10.254.240.109" + "/wohnberatung-wien.at/10.254.240.109" + "/wohnpartner-wien.at/10.254.240.109" + "/wohnservice-wien.at/10.254.240.109" + "/mieterhilfe.at/10.254.240.109" + "/wienbautvor.at/10.254.240.109" + "/wienwohntbesser.at/10.254.240.109" + "/a.stage.wohnberatung-wien.at/10.254.240.110" + "/a.stage.wohnpartner-wien.at/10.254.240.110" + "/a.stage.wohnservice-wien.at/10.254.240.110" + "/a.stage.mieterhilfe.at/10.254.240.110" + "/a.stage.wienbautvor.at/10.254.240.110" + "/a.stage.wienwohntbesser.at/10.254.240.110" + "/b.stage.wohnberatung-wien.at/10.254.240.110" + "/b.stage.wohnpartner-wien.at/10.254.240.110" + "/b.stage.wohnservice-wien.at/10.254.240.110" + "/b.stage.mieterhilfe.at/10.254.240.110" + "/b.stage.wienbautvor.at/10.254.240.110" + "/b.stage.wienwohntbesser.at/10.254.240.110" + + "/web.hilgenberg-gmbh.de/91.107.197.169" + # gaming + "/foundry-vtt.cloonar.com/${config.networkPrefix}.97.5" + + "/deconz.cloonar.multimedia/${config.networkPrefix}.97.22" + + "/ddl-warez.to/172.67.184.30" + "/cdnjs.cloudflare.com/104.17.24.14" + + # esphome devices + "/livingroom-bulb-1.cloonar.smart/${config.networkPrefix}.100.11" + "/livingroom-bulb-2.cloonar.smart/${config.networkPrefix}.100.12" + "/livingroom-bulb-3.cloonar.smart/${config.networkPrefix}.100.13" + "/livingroom-bulb-4.cloonar.smart/${config.networkPrefix}.100.14" + "/livingroom-bulb-5.cloonar.smart/${config.networkPrefix}.100.15" + "/livingroom-bulb-6.cloonar.smart/${config.networkPrefix}.100.16" + + "/bedroom-bulb-0.cloonar.smart/${config.networkPrefix}.100.21" + "/bedroom-bulb-0.cloonar.smart/${config.networkPrefix}.100.22" + "/bedroom-bulb-0.cloonar.smart/${config.networkPrefix}.100.23" + "/bedroom-bulb-0.cloonar.smart/${config.networkPrefix}.100.24" + + "/hallway-bulb-0.cloonar.smart/${config.networkPrefix}.100.31" + "/hallway-bulb-0.cloonar.smart/${config.networkPrefix}.100.32" + + "/bath-bulb-0.cloonar.smart/${config.networkPrefix}.100.41" + "/bath-bulb-0.cloonar.smart/${config.networkPrefix}.100.42" + ]; + }; + }; + + systemd.services.dnsmasq = { + requires = [ "network-online.target" ]; + after = [ "network-online.target" ]; + }; + + networking.firewall.allowedUDPPorts = [ 53 67 ]; +} diff --git a/hosts/fw/modules/firewall.nix b/hosts/fw/modules/firewall.nix index 07c2255..22bc6a1 100644 --- a/hosts/fw/modules/firewall.nix +++ b/hosts/fw/modules/firewall.nix @@ -8,6 +8,18 @@ "cloonar-fw" = { family = "inet"; content = '' + chain snap-qos-raw { + type filter hook prerouting priority raw; policy accept; + tcp dport 1704 counter mark set 10 comment "Mark Snapcast traffic" + tcp dport 3483 counter mark set 10 comment "Mark Squezelite traffic" + udp dport 3483 counter mark set 10 comment "Mark Squezelite traffic" + } + + chain snap-qos-mangle { + type filter hook postrouting priority mangle + 10; policy accept; + mark 10 counter ip dscp set cs3 comment "Tag Snapcast with CS3" + } + chain output { type filter hook output priority 100; policy accept; } @@ -22,6 +34,7 @@ type filter hook input priority filter; policy drop; iifname "lo" accept comment "trusted interfaces" iifname "lan" counter accept comment "Spice" + iifname { "server", "vserver", "vm-*", "lan", "wg_cloonar" } counter accept comment "allow trusted to router" ct state vmap { invalid : drop, established : accept, related : accept, new : jump input-allow, untracked : jump input-allow } tcp flags syn / fin,syn,rst,ack log prefix "refused connection: " level info } @@ -34,11 +47,14 @@ iifname "lan" tcp dport 5931 counter accept comment "Spice" iifname { "server", "vserver", "vm-*", "lan", "wg_cloonar" } counter accept comment "allow trusted to router" iifname { "multimedia", "smart", "infrastructure", "podman0", "setup" } udp dport { 53, 5353 } counter accept comment "DNS" + iifname { "multimedia", "smart", "infrastructure", "server", "lan", "guest" } udp dport { 67 } counter accept comment "DHCP" iifname { "wan", "multimedia" } icmp type { echo-request, destination-unreachable, time-exceeded } counter accept comment "Allow select ICMP" # Accept mDNS for avahi reflection iifname "server" ip saddr ${config.networkPrefix}.97.20/32 tcp dport { llmnr } counter accept iifname "server" ip saddr ${config.networkPrefix}.97.20/32 udp dport { mdns, llmnr } counter accept + iifname "server" udp dport 5353 ip daddr 224.0.0.251 counter accept comment "Avahi mDNS" + iifname "lan" udp dport 5353 ip daddr 224.0.0.251 counter accept comment "Avahi mDNS" # Allow all returning traffic ct state { established, related } counter accept @@ -79,10 +95,24 @@ # multimedia airplay iifname "multimedia" oifname { "lan" } counter accept iifname "multimedia" oifname "server" tcp dport { 1704, 1705 } counter accept + iifname "multimedia" oifname "server" tcp dport { 3483, 9000 } counter accept + iifname "multimedia" oifname "server" udp dport { 3483 } counter accept + iifname "multimedia" oifname "server" icmp type { echo-request, destination-unreachable, time-exceeded } counter accept comment "Allow select ICMP" iifname "lan" oifname "server" udp dport { 5000, 5353, 6001 - 6011 } counter accept # avahi iifname "server" ip saddr ${config.networkPrefix}.97.20/32 oifname { "lan" } counter accept + # Allow Chromecast + iifname "lan" oifname "server" udp dport 5353 ip daddr 224.0.0.251 counter accept comment "mDNS query LAN→Server" + iifname "server" oifname "lan" udp sport 5353 ip saddr 224.0.0.251 counter accept comment "mDNS response Server→LAN" + iifname "lan" oifname "server" tcp dport 9881 counter accept comment "chromecast" + + # SSDP / UPnP discovery if needed + iifname { "lan", "server" } oifname { "server", "lan" } \ + udp dport 1900 ip daddr 239.255.255.250 counter accept comment "SSDP query" + iifname { "lan", "server" } oifname { "server", "lan" } \ + udp sport 1900 ip saddr 239.255.255.250 counter accept comment "SSDP response" + # smart home coap iifname "smart" oifname "server" ip daddr ${config.networkPrefix}.97.20/32 udp dport { 5683 } counter accept iifname "smart" oifname "server" ip daddr ${config.networkPrefix}.97.20/32 tcp dport { 1883 } counter accept diff --git a/hosts/fw/modules/home-assistant/light.nix b/hosts/fw/modules/home-assistant/light.nix index dc9e944..cb5b718 100644 --- a/hosts/fw/modules/home-assistant/light.nix +++ b/hosts/fw/modules/home-assistant/light.nix @@ -421,21 +421,62 @@ "light.bathroom_bulb_2" ]; } - { - platform = "switch"; - name = "Hallway Switch"; - entity_id = "switch.hallway"; - } { platform = "group"; name = "Hallway Lights"; all = true; entities = [ - "light.hallway_switch" + "light.hallway_light_switch_mini_switch" "light.hallway_bulb_1" "light.hallway_bulb_2" ]; } + { + platform = "template"; + lights = { + hallway_group_proxy = { + friendly_name = "Hallway Lights (Proxy)"; + # follow the real group’s on/off state + value_template = "{{ is_state('light.hallway_lights','on') }}"; + turn_on = { + service = "light.turn_on"; + data = { entity_id = "light.hallway_lights"; }; + }; + turn_off = { + service = "light.turn_off"; + data = { entity_id = "light.hallway_lights"; }; + }; + # brightness support + set_level = { + service = "light.turn_on"; + data_template = { + entity_id = "light.hallway_lights"; + brightness = "{{ brightness }}"; + }; + }; + # color temperature support (if you have CT-capable bulbs) + set_temperature = { + service = "light.turn_on"; + data_template = { + entity_id = "light.hallway_lights"; + color_temp = "{{ color_temp }}"; + }; + }; + # RGB color support + set_color = { + service = "light.turn_on"; + data_template = { + entity_id = "light.hallway_lights"; + rgb_color = [ "{{ red }}" "{{ green }}" "{{ blue }}" ]; + }; + }; + # always report as “available” + availability_template = "true"; + # declare which color modes you need + supported_color_modes = [ "brightness" "color_temp" "rgb" ]; + }; + }; + } { platform = "switch"; name = "Toilet Switch"; diff --git a/hosts/fw/modules/home-assistant/locks.nix b/hosts/fw/modules/home-assistant/locks.nix index 2378d51..aa14f1d 100644 --- a/hosts/fw/modules/home-assistant/locks.nix +++ b/hosts/fw/modules/home-assistant/locks.nix @@ -1,4 +1,9 @@ -{ +let + devices = [ + "device_tracker.dominiks_iphone" + "device_tracker.dominiks_mp01" + ]; +in { services.home-assistant.extraComponents = [ "nuki" ]; @@ -9,9 +14,7 @@ mode = "restart"; trigger = { platform = "state"; - entity_id = [ - "device_tracker.dominiks_iphone" - ]; + entity_id = devices; from = "not_home"; to = "home"; }; diff --git a/hosts/fw/modules/home-assistant/multimedia.nix b/hosts/fw/modules/home-assistant/multimedia.nix index 5bac9b6..4c932e1 100644 --- a/hosts/fw/modules/home-assistant/multimedia.nix +++ b/hosts/fw/modules/home-assistant/multimedia.nix @@ -494,6 +494,12 @@ entity_id = "script.turn_on_tv"; }; } + { + service = "media_player.turn_off"; + target = { + entity_id = "media_player.marantz_sr6015"; + }; + } ]; } ]; diff --git a/hosts/fw/modules/home-assistant/music.nix b/hosts/fw/modules/home-assistant/music.nix index ddc5586..2736e61 100644 --- a/hosts/fw/modules/home-assistant/music.nix +++ b/hosts/fw/modules/home-assistant/music.nix @@ -1,10 +1,11 @@ { + services.home-assistant.extraComponents = [ "squeezebox" ]; services.home-assistant.config = { "automation toilet music" = { alias = "toilet music"; trigger = { platform = "state"; - entity_id = "light.toilett_lights"; + entity_id = "light.toilet_switch"; }; action = [ { @@ -13,10 +14,52 @@ entity_id = "media_player.music_toilet_snapcast_client"; }; data = { - is_volume_muted = "{{ trigger.to_state.state == 'off' }}"; + is_volume_muted = "{{ trigger.to_state.state != 'on' }}"; }; } ]; }; + "automation bathroom music" = { + alias = "bathroom music"; + trigger = { + platform = "state"; + entity_id = "light.bathroom_switch"; + }; + action = [ + { + service = "media_player.volume_mute"; + target = { + entity_id = "media_player.music_bathroom_snapcast_client"; + }; + data = { + is_volume_muted = "{{ trigger.to_state.state != 'on' }}"; + }; + } + ]; + }; + "automation piano" = { + alias = "piano"; + trigger = { + platform = "state"; + entity_id = "media_player.music_piano_snapcast_client"; + attribute = "is_volume_muted"; + }; + condition = [ + { + condition = "template"; + value_template = "{{ trigger.from_state.state != 'unavailable' }}"; + } + { + condition = "template"; + value_template = "{{ state_attr('media_player.music_piano_snapcast_client', 'is_volume_muted') == true or state_attr('media_player.music_piano_snapcast_client', 'is_volume_muted') == false }}"; + } + ]; + action = { + service = "switch.turn_on"; + target = { + entity_id = "switch.piano_switch_power"; + }; + }; + }; }; } diff --git a/hosts/fw/modules/home-assistant/snapcast.nix b/hosts/fw/modules/home-assistant/snapcast.nix index 9fc54d6..e745ed5 100644 --- a/hosts/fw/modules/home-assistant/snapcast.nix +++ b/hosts/fw/modules/home-assistant/snapcast.nix @@ -1,31 +1,5 @@ { services.home-assistant = { extraComponents = [ "snapcast" ]; - config = { - "automation piano" = { - alias = "piano"; - trigger = { - platform = "state"; - entity_id = "media_player.music_piano_snapcast_client"; - attribute = "is_volume_muted"; - }; - condition = [ - { - condition = "template"; - value_template = "{{ trigger.from_state.state != 'unavailable' }}"; - } - { - condition = "template"; - value_template = "{{ state_attr('media_player.music_piano_snapcast_client', 'is_volume_muted') == true or state_attr('media_player.music_piano_snapcast_client', 'is_volume_muted') == false }}"; - } - ]; - action = { - service = "switch.turn_on"; - target = { - entity_id = "switch.piano_switch_power"; - }; - }; - }; - }; }; } diff --git a/hosts/fw/modules/lms.nix b/hosts/fw/modules/lms.nix new file mode 100644 index 0000000..2de58cc --- /dev/null +++ b/hosts/fw/modules/lms.nix @@ -0,0 +1,88 @@ +{ pkgs, config, lib, python3Packages, ... }: + +let + lmsDomain = "lms.cloonar.com"; + networkPrefix = config.networkPrefix; +in +{ + security.acme.certs."${lmsDomain}" = { + group = "nginx"; + }; + + sops.secrets.lms-spotify = { }; + + containers.lms = { + autoStart = true; + ephemeral = false; + privateNetwork = true; + hostBridge = "server"; + + hostAddress = "${networkPrefix}.97.2"; + localAddress = "${networkPrefix}.97.21/24"; + + extraFlags = [ "--capability=CAP_NET_ADMIN" ]; + + bindMounts = { + "/var/lib/acme/lms/" = { + hostPath = config.security.acme.certs.${lmsDomain}.directory; + isReadOnly = true; + }; + "/run/secrets/lms-spotify" = { + hostPath = config.sops.secrets.lms-spotify.path; + }; + }; + + config = { pkgs, lib, config, ... }: + let + in + { + networking = { + hostName = "lms"; + useHostResolvConf = false; + defaultGateway = { + address = "${networkPrefix}.97.1"; + interface = "eth0"; + }; + nameservers = [ "${networkPrefix}.97.1" ]; + firewall.enable = false; + }; + + environment.systemPackages = with pkgs; [ + slimserver # Logitech/Lyrion Media Server + ]; + + services.slimserver = { + enable = true; + package = pkgs.slimserver; + }; + + # make LMS discoverable via mDNS/Avahi + services.avahi = { + enable = true; + publish.enable = true; + publish.userServices = true; + }; + + services.nginx.enable = true; + services.nginx.virtualHosts."${lmsDomain}" = { + sslCertificate = "/var/lib/acme/lms/fullchain.pem"; + sslCertificateKey = "/var/lib/acme/lms/key.pem"; + sslTrustedCertificate = "/var/lib/acme/lms/chain.pem"; + forceSSL = true; + extraConfig = "proxy_buffering off;"; + + locations."/".extraConfig = '' + proxy_pass http://127.0.0.1:9000/; + proxy_set_header Host $host; + proxy_redirect http:// https://; + proxy_http_version 1.1; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + ''; + }; + + system.stateVersion = "23.05"; + }; + }; +} diff --git a/hosts/fw/modules/unbound.nix b/hosts/fw/modules/unbound.nix index d4f9012..c5f21c5 100644 --- a/hosts/fw/modules/unbound.nix +++ b/hosts/fw/modules/unbound.nix @@ -261,6 +261,10 @@ in { enable = true; path = with pkgs; [ unbound inotify-tools ]; script = '' + #!/usr/bin/env bash + set -euo pipefail + + # readFile and readFileUnique as before… function readFile() { if [[ "''\$2" == "A" ]] ; then cat "''\$1" | tail -n +2 | while IFS=, read -r address hwaddr client_id valid_lifetime expire subnet_id fqdn_fwd fqdn_rev hostname state user_context @@ -273,8 +277,8 @@ in { echo "''\${address},''\${hostname}" done fi - } - + } + function readFileUnique() { readFile "''\$1" ''\$2 | uniq | while IFS=, read -r address hostname do @@ -313,19 +317,27 @@ in { fi fi done - } - - function syncFile() { - # readFileUnique "''\$1" "''\$2" - while true; do - readFileUnique "''\$1" "''\$2" - sleep 10 - done } - syncFile "/var/lib/kea/dhcp4.leases" A & - # syncFile "/var/lib/kea/dhcp6.leases" AAAA & - wait + function syncLeases() { + # 1) nuke all of our old lease records from unbound + unbound-control list_local_data \ + | grep -E 'cloonar\.(com|multimedia|smart)|ip4\.arpa|in-addr\.arpa' \ + | while read -r name type data; do + unbound-control local_data_remove "$name" "$type" "$data" \ + > /dev/null 2>&1 + done + + # 2) re-push every current lease + readFileUnique "/var/lib/kea/dhcp4.leases" A + # if you need IPv6: + # readFileUnique "/var/lib/kea/dhcp6.leases" AAAA + } + + while true; do + syncLeases + sleep 10 + done ''; wants = [ "network-online.target" "unbound.service" ]; after = [ "network-online.target" "unbound.service" ]; diff --git a/hosts/fw/secrets.yaml b/hosts/fw/secrets.yaml index 9f5a619..7019145 100644 --- a/hosts/fw/secrets.yaml +++ b/hosts/fw/secrets.yaml @@ -17,6 +17,7 @@ matrix-shared-secret: ENC[AES256_GCM,data:67imd3m6WBeGP/5Msmjy8B6sP983jMyWzRIzWg palworld: ENC[AES256_GCM,data:rdqChPt4gSJHS1D60+HJ+4m5mg35JbC+pOmevK21Y95QyAIeyBLVGhRYlOaUcqdZM2e4atyTTSf6z4nHsm539ddCbW7J2DCdF5PQkrAGDmmdTVq+jyJAT8gTrbXXCglT1wvFYY5dbf2NKA4ASJIA8bdVNuwRZU0CtFiishzLuc9m8ZcGCNwQ/+xkMZgkUAHYRlEJAZyMpXR6KkFftiR05JRAFczD4N7GXPPe+vyvgXg7QBGtf20Qd4SGBUw0zI/SNTRmifHUuc4Z6+Fe9JHgvTc3uFcTMVnty0fEuL+a29liaVdAFq8BnqJfc5CNV401ZSUeMbG41lCn1cegP/WChs9J6HXNrhWDgiXa6ln++NoKcfOHIfZVbYOCoOxFR6+YWeBU2+sHmdwI9j5XQf5Ly2hmg12j0Ds2Cn8k4PG5aQP+HT2bedqyxwSt6fi97A0Osnh4ig7+DzYAjSNLewbYLzVdK39VdvB9hqLto+yFS3gAaeYOHwPwtqa+COI85c55lHiyKHlSwPhBqYaaiDu00lQTUzq9R5vz6F/l+T3bUjuna5RryUu8yhnk5DyK834KycTOg4ETcZTqro6prfiEBxc+Utsc9JvEtZgwFv6fsVLOu7nHxuiYuvseZ4YA8LlYdwPJboMPO2XsuhwWtT1uz/rh2orH7/vsXvzA/kF8NFemWBEMVLYA8byC5ze8doiGDYp4T5AAf10nJB1ceQ==,iv:gs78fxhvo9KlTaR5nzs12/LdgPChSFPHD2k4VQp3ARo=,tag:lpWBOi9xh2cWkS+71KD/UQ==,type:str] ark: ENC[AES256_GCM,data:YYGyzoVIKI9Ac1zGOr0BEpd3fgBsvp1hSwAvfO07/EQdg8ufMWUkNvqNHDKN62ZK5A1NnY3JTA1p4gyZ4ryQeAOsbwqU1GSk2YKHFyPeEnpLz/Ml82KMsv7XPGXuKRXZ4v3UcLu0R8k1Q0gQsMWo4FjCs3FF5mVtJG/YWxxbCYHoBLJ/di5p0DgjuFgJBQknYBpuLzr+yIoeqEyN7XcGYAJO53trEJuOOxLILULifkqISHjZ66i5F1fHW0iUdRbmeWV4aOAeOrsQqXYv,iv:gJwV5ip84zHqpU0l0uESfWWOtcgihMvEEdLaeI+twcU=,tag:sy8udVQsKxV/jOqwhJmWAg==,type:str] firefox-sync: ENC[AES256_GCM,data:uAJAdyKAuXRuqCFl8742vIejU5RnAPpUxUFCC0s0QeXZR5oH2YOrDh+3vKUmckW4V1cIhSHoe+4+I4HuU5E73DDrJThfIzBEw+spo4HXwZf5KBtu3ujgX6/fSTlPWV7pEsDDsZ0y6ziKPADBDym8yEk0bU9nRedvTBUhVryo3aolzF/c+gJvdeDvKUYa8+8=,iv:yuvE4KG7z7Rp9ZNlLiJ2rh0keed3DuvrELzsfJu4+bs=,tag:HFo1A53Eva31NJ8fRE7TlA==,type:str] +knot-tsig-key: ENC[AES256_GCM,data:H2jEkRSVSIJl1dSolAXj9uUmzD6eEh9zPpoajZLxfuuFt7/LJF8aCEHyk+Q=,iv:9aqywuaILYtejuZGd+Cy8oErrHIoL2XhL1g9HtcUn/o=,tag:K3SnVEXGC/NhlchU7OyA6Q==,type:str] sops: kms: [] gcp_kms: [] @@ -59,8 +60,8 @@ sops: WXJpUUxadERyYUExRFMzNzBXaUVET3cKG9ZwWy5YvTr/BAw/i+ZJos5trwRvaW5j eV/SHiEteZZtCuCVFAp3iolE/mJyu97nA2yFwWaLN86h+/xkOJsdqA== -----END AGE ENCRYPTED FILE----- - lastmodified: "2025-03-01T22:14:10Z" - mac: ENC[AES256_GCM,data:UWwjvi8jLNgu4l7ldMYtkAATm3y5+BSxbCuPN/e1OC4/3ULYJndqFLfTOMpqQbj2+uHo3onelK4f0MAJuSH0oUx58CclkNBBLE0RXafxbowa7kJtTNDfTboJNqH7rFmhGhqCtHAOOpKBuowqoOUHP5BtzZfucra0Q/pIJt5lma0=,iv:iJEW/mTbizioPSN8G+WqHSipx8P6VCDrVG/Cmk+MBUc=,tag:L4OkeKec5AZdCrpUrnqcOA==,type:str] + lastmodified: "2025-05-01T20:36:09Z" + mac: ENC[AES256_GCM,data:ZtXJcuwDpDlBl2xdRtMF1PwwqbW00Eps2ZZG5x4C2djAq+meXJCxKS9sNazQhMYFOqphQXe3JEhChykLxnJyWivY/Er1ig2sU6Ke1uVcfSP85B1/rpzhe/7QI+GBDWrkCk1O0xGKKj8fWt+Yv2MV8gw2XctdtJ9Md4imUhcK7zo=,iv:5NFH+7Z0alBiq/b94T40XJSCar2+BGaFB20z0Kc59fU=,tag:18n0tt17RNMyyE0eECH2kQ==,type:str] pgp: [] unencrypted_suffix: _unencrypted version: 3.9.4 diff --git a/hosts/nb/configuration.nix b/hosts/nb/configuration.nix index 6302db1..483a45c 100644 --- a/hosts/nb/configuration.nix +++ b/hosts/nb/configuration.nix @@ -25,7 +25,7 @@ in { ./utils/modules/nur.nix ./modules/appimage.nix ./modules/sway/sway.nix - ./modules/printer.nix + # ./modules/printer.nix # ./modules/cyberghost.nix ./utils/modules/autoupgrade.nix ./modules/puppeteer.nix diff --git a/hosts/nb/users/dominik.nix b/hosts/nb/users/dominik.nix index 83f2275..8cf7864 100644 --- a/hosts/nb/users/dominik.nix +++ b/hosts/nb/users/dominik.nix @@ -78,6 +78,7 @@ let "media.ffmpeg.vaapi.enabled" = true; "media.ffmpeg.vaapi-drm-display.enabled" = true; "gfx.webrender.all" = true; + "xpinstall.signatures.required" = false; }; firefoxUserChrome = '' @@ -531,7 +532,7 @@ in programs.firefox = { enable = true; - package = pkgs.firefox.overrideAttrs (a: { + package = pkgs.firefox-devedition.overrideAttrs (a: { postInstall = a.postInstall or "" + '' wrapProgram "$out/bin/firefox" \ export MOZ_ENABLE_WAYLAND=1 @@ -558,6 +559,11 @@ in icon = "fingerprint"; id = 1; }; + "dating" = { + color = "pink"; + icon = "circle"; + id = 5; + }; "cloonar technologies" = { color = "red"; icon = "briefcase"; diff --git a/hosts/web-arm/modules/collabora.nix b/hosts/web-arm/modules/collabora.nix index 0d83556..01800f3 100644 --- a/hosts/web-arm/modules/collabora.nix +++ b/hosts/web-arm/modules/collabora.nix @@ -8,7 +8,10 @@ server_name = "code.cloonar.com"; aliasgroup1 = "https://nextcloud.cloonar.com:443"; aliasgroup2 = "https://cloud.cloonar.com:443"; - dictionaries = "en_US"; + # Hannes + aliasgroup3 = "https://nx67898.your-storageshare.de:443"; + aliasgroup4 = "https://hs-cloud.cloonar.com:443"; + dictionaries = "en_GB en_US de_DE"; extra_params = "--o:ssl.enable=false --o:ssl.termination=true"; }; extraOptions = [ diff --git a/hosts/web-arm/modules/nextcloud/default.nix b/hosts/web-arm/modules/nextcloud/default.nix index 28e2489..882d768 100644 --- a/hosts/web-arm/modules/nextcloud/default.nix +++ b/hosts/web-arm/modules/nextcloud/default.nix @@ -16,7 +16,7 @@ in enable = true; hostName = "nextcloud.cloonar.com"; https = true; - package = nextcloud30; + package = pkgs.nextcloud31; # Instead of using pkgs.nextcloud27Packages.apps, # we'll reference the package version specified above extraApps = { diff --git a/hosts/web-arm/modules/postfix.nix b/hosts/web-arm/modules/postfix.nix new file mode 100644 index 0000000..22d450d --- /dev/null +++ b/hosts/web-arm/modules/postfix.nix @@ -0,0 +1,7 @@ +{ pkgs, ... }: { + services.postfix = { + enable = true; + domain = "cloonar.com"; + hostname = "web-arm.cloonar.com"; + }; +} diff --git a/raspberry/picoreplayer.sh b/raspberry/picoreplayer.sh new file mode 100755 index 0000000..3687876 --- /dev/null +++ b/raspberry/picoreplayer.sh @@ -0,0 +1,122 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Must be run as root +if [[ $EUID -ne 0 ]]; then + echo "This script must be run as root. Try sudo $0 ..." >&2 + exit 1 +fi + +# Default env file +ENV_FILE="./.env" + +# PiCorePlayer version to use +PCP_VERSION="10.0.0" +BASE_URL="https://repo.pcplayer.org/insitu/piCorePlayer${PCP_VERSION}" +IMG_ZIP="piCorePlayer${PCP_VERSION}.img.xz" +DOWNLOAD_URL="${BASE_URL}/${IMG_ZIP}" + +# Print usage +usage() { + cat < \ + --sdcard \ + [--env-file /path/to/.env] + + --device Target board (rpi-4, rpi-zero, rpi-zero-2) + --sdcard Device node of the SD card (e.g. /dev/sdb) + --env-file File containing WIFI_SSID and WIFI_PSK (defaults to ./.env) +EOF + exit 1 +} + +# Parse args +DEVICE="" SDCARD="" +while [[ $# -gt 0 ]]; do + case "$1" in + --device) DEVICE="$2"; shift 2;; + --sdcard) SDCARD="$2"; shift 2;; + --env-file) ENV_FILE="$2"; shift 2;; + -h|--help) usage;; + *) echo "Unknown option: $1" >&2; usage;; + esac +done + +# Validate +if [[ -z "$DEVICE" || -z "$SDCARD" ]]; then + usage +fi +if [[ ! -b "$SDCARD" ]]; then + echo "Error: $SDCARD is not a block device." >&2 + exit 1 +fi +if [[ ! -f "$ENV_FILE" ]]; then + echo "Error: env file '$ENV_FILE' not found." >&2 + exit 1 +fi + +# Load Wi-Fi credentials +source "$ENV_FILE" +if [[ -z "${WIFI_SSID:-}" || -z "${WIFI_PSK:-}" ]]; then + echo "Error: WIFI_SSID and WIFI_PSK must be set in $ENV_FILE" >&2 + exit 1 +fi + +# Download if needed +if [[ ! -f "$IMG_ZIP" ]]; then + echo "Downloading $IMG_ZIP ..." + wget -q --show-progress "$DOWNLOAD_URL" +fi + +# Flash image +echo "Flashing $IMG_ZIP to $SDCARD (will overwrite everything!)" +sync +xzcat "$IMG_ZIP" | dd of="$SDCARD" bs=4M status=progress +sync + +# Determine partition suffixes (/dev/sdX1 vs /dev/mmcblk0p1) +if [[ "$SDCARD" =~ [0-9]$ ]]; then + PART1="${SDCARD}p1"; PART2="${SDCARD}p2" +else + PART1="${SDCARD}1"; PART2="${SDCARD}2" +fi + +# Mount partitions +BOOT_MNT=$(mktemp -d) +ROOT_MNT=$(mktemp -d) +mount "$PART1" "$BOOT_MNT" +mount "$PART2" "$ROOT_MNT" + +# Create hidden Wi-Fi config on boot partition +cat > "$BOOT_MNT/wpa_supplicant.conf" <> "$BOOT_MNT/config.txt" <