Compare commits

...

12 Commits

26 changed files with 1003 additions and 271 deletions

View File

@@ -3,7 +3,7 @@
- get age key from SSH - get age key from SSH
```console ```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 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 - fix secrets files
```console ```console

View File

@@ -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");'

View File

@@ -10,7 +10,6 @@ substitutions:
sntp_server_1: "0.pool.ntp.org" sntp_server_1: "0.pool.ntp.org"
sntp_server_2: "1.pool.ntp.org" sntp_server_2: "1.pool.ntp.org"
sntp_server_3: "2.pool.ntp.org" sntp_server_3: "2.pool.ntp.org"
log_level: "WARN"
esphome: esphome:
name: "${name}" name: "${name}"
@@ -23,27 +22,27 @@ esphome:
name: "${project_name}" name: "${project_name}"
version: "${project_version}" version: "${project_version}"
on_boot: on_boot:
then: then:
- light.turn_on: - light.turn_on:
id: rgbww_light id: rgbww_light
- delay: 100ms - delay: 100ms
- light.turn_on: - light.turn_on:
id: rgbww_light id: rgbww_light
brightness: 20% brightness: 20%
- delay: 100ms - delay: 100ms
- light.turn_on: - light.turn_on:
id: rgbww_light id: rgbww_light
red: 100% red: 100%
green: 50% green: 50%
blue: 0% blue: 0%
white: 100% white: 100%
interval: interval:
- interval: 15s - interval: 15s
then: then:
- if: - if:
condition: condition:
api.connected: # check if api connected api.connected:
else: else:
- light.turn_on: - light.turn_on:
id: rgbww_light id: rgbww_light
@@ -61,21 +60,31 @@ api:
ota: ota:
- platform: esphome - platform: esphome
logger:
mdns:
disabled: false
wifi: wifi:
ssid: !secret wifi_ssid # Disable fast_connect so we do a full scan (required for hidden SSIDs)
password: !secret wifi_password fast_connect: false
fast_connect: True domain: "${dns_domain}"
domain: .cloonar.smart
captive_portal: # Your hidden network
networks:
- ssid: !secret wifi_ssid
password: !secret wifi_password
channel: 1
hidden: true
dashboard_import: manual_ip:
package_import_url: github://athom-tech/athom-configs/athom-rgbww-light.yaml 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: binary_sensor:
- platform: status - platform: status
@@ -93,7 +102,7 @@ sensor:
name: "WiFi Signal dB" name: "WiFi Signal dB"
id: wifi_signal_db id: wifi_signal_db
update_interval: 60s update_interval: 60s
entity_category: "diagnostic" entity_category: diagnostic
- platform: copy - platform: copy
source_id: wifi_signal_db source_id: wifi_signal_db
@@ -101,23 +110,7 @@ sensor:
filters: filters:
- lambda: return min(max(2 * (x + 100.0), 0.0), 100.0); - lambda: return min(max(2 * (x + 100.0), 0.0), 100.0);
unit_of_measurement: "Signal %" unit_of_measurement: "Signal %"
entity_category: "diagnostic" 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: output:
- platform: esp8266_pwm - platform: esp8266_pwm
@@ -171,55 +164,47 @@ text_sensor:
name: "Mac Address" name: "Mac Address"
entity_category: diagnostic entity_category: diagnostic
# Creates a sensor showing when the device was last restarted
- platform: template - platform: template
name: 'Last Restart' name: 'Last Restart'
id: device_last_restart id: device_last_restart
icon: mdi:clock icon: mdi:clock
entity_category: diagnostic entity_category: diagnostic
# device_class: timestamp
# Creates a sensor of the uptime of the device, in formatted days, hours, minutes and seconds
- platform: template - platform: template
name: "Uptime" name: "Uptime"
entity_category: diagnostic entity_category: diagnostic
lambda: |- lambda: |-
int seconds = (id(uptime_sensor).state); int seconds = (id(uptime_sensor).state);
int days = seconds / (24 * 3600); int days = seconds / (24 * 3600);
seconds = seconds % (24 * 3600); seconds %= (24 * 3600);
int hours = seconds / 3600; int hours = seconds / 3600;
seconds = seconds % 3600; seconds %= 3600;
int minutes = seconds / 60; int minutes = seconds / 60;
seconds = seconds % 60; seconds %= 60;
if ( days > 3650 ) { if (days > 3650) {
return { "Starting up" }; return { "Starting up" };
} else if ( days ) { } else if (days) {
return { (String(days) +"d " + String(hours) +"h " + String(minutes) +"m "+ String(seconds) +"s").c_str() }; return { (String(days) + "d " + String(hours) + "h " + String(minutes) + "m " + String(seconds) + "s").c_str() };
} else if ( hours ) { } else if (hours) {
return { (String(hours) +"h " + String(minutes) +"m "+ String(seconds) +"s").c_str() }; return { (String(hours) + "h " + String(minutes) + "m " + String(seconds) + "s").c_str() };
} else if ( minutes ) { } else if (minutes) {
return { (String(minutes) +"m "+ String(seconds) +"s").c_str() }; return { (String(minutes) + "m " + String(seconds) + "s").c_str() };
} else { } else {
return { (String(seconds) +"s").c_str() }; return { (String(seconds) + "s").c_str() };
} }
icon: mdi:clock-start icon: mdi:clock-start
time: time:
- platform: sntp - platform: sntp
id: sntp_time id: sntp_time
# Define the timezone of the device
timezone: "${timezone}" timezone: "${timezone}"
# Change sync interval from default 5min to 6 hours (or as set in substitutions)
update_interval: ${sntp_update_interval} update_interval: ${sntp_update_interval}
# Set specific sntp servers to use
servers: servers:
- "${sntp_server_1}" - "${sntp_server_1}"
- "${sntp_server_2}" - "${sntp_server_2}"
- "${sntp_server_3}" - "${sntp_server_3}"
# Publish the time the device was last restarted
on_time_sync: on_time_sync:
then: then:
# Update last restart time, but only once.
- if: - if:
condition: condition:
lambda: 'return id(device_last_restart).state == "";' lambda: 'return id(device_last_restart).state == "";'

View File

@@ -61,21 +61,18 @@ api:
ota: ota:
- platform: esphome - platform: esphome
logger:
mdns:
disabled: false
wifi: wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
fast_connect: True
domain: .cloonar.smart domain: .cloonar.smart
fast_connect: False
captive_portal: networks:
- ssid: !secret wifi_ssid
dashboard_import: password: !secret wifi_password
package_import_url: github://athom-tech/athom-configs/athom-rgbww-light.yaml channel: 1
hidden: True
manual_ip:
static_ip: 10.42.100.12
gateway: 10.42.100.1
subnet: 255.255.255.0
binary_sensor: binary_sensor:
- platform: status - platform: status
@@ -104,21 +101,6 @@ sensor:
entity_category: "diagnostic" entity_category: "diagnostic"
device_class: "" 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: output:
- platform: esp8266_pwm - platform: esp8266_pwm
id: red_output id: red_output

View File

@@ -72,21 +72,18 @@ api:
ota: ota:
- platform: esphome - platform: esphome
logger:
mdns:
disabled: false
wifi: wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
fast_connect: True
domain: .cloonar.smart domain: .cloonar.smart
fast_connect: False
captive_portal: networks:
- ssid: !secret wifi_ssid
dashboard_import: password: !secret wifi_password
package_import_url: github://athom-tech/athom-configs/athom-rgbww-light.yaml hidden: True
channel: 1
manual_ip:
static_ip: 10.42.100.13
gateway: 10.42.100.1
subnet: 255.255.255.0
binary_sensor: binary_sensor:
- platform: status - platform: status
@@ -115,21 +112,6 @@ sensor:
entity_category: "diagnostic" entity_category: "diagnostic"
device_class: "" 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: output:
- platform: esp8266_pwm - platform: esp8266_pwm
id: red_output id: red_output

View File

@@ -72,21 +72,17 @@ api:
ota: ota:
- platform: esphome - platform: esphome
logger:
mdns:
disabled: false
wifi: wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
fast_connect: True
domain: .cloonar.smart domain: .cloonar.smart
fast_connect: False
captive_portal: networks:
- ssid: !secret wifi_ssid
dashboard_import: password: !secret wifi_password
package_import_url: github://athom-tech/athom-configs/athom-rgbww-light.yaml hidden: True
manual_ip:
static_ip: 10.42.100.14
gateway: 10.42.100.1
subnet: 255.255.255.0
binary_sensor: binary_sensor:
- platform: status - platform: status
@@ -115,21 +111,6 @@ sensor:
entity_category: "diagnostic" entity_category: "diagnostic"
device_class: "" 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: output:
- platform: esp8266_pwm - platform: esp8266_pwm
id: red_output id: red_output

View File

@@ -72,21 +72,17 @@ api:
ota: ota:
- platform: esphome - platform: esphome
logger:
mdns:
disabled: false
wifi: wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
fast_connect: True
domain: .cloonar.smart domain: .cloonar.smart
fast_connect: False
captive_portal: networks:
- ssid: !secret wifi_ssid
dashboard_import: password: !secret wifi_password
package_import_url: github://athom-tech/athom-configs/athom-rgbww-light.yaml hidden: True
manual_ip:
static_ip: 10.42.100.15
gateway: 10.42.100.1
subnet: 255.255.255.0
binary_sensor: binary_sensor:
- platform: status - platform: status
@@ -115,21 +111,6 @@ sensor:
entity_category: "diagnostic" entity_category: "diagnostic"
device_class: "" 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: output:
- platform: esp8266_pwm - platform: esp8266_pwm
id: red_output id: red_output

View File

@@ -72,21 +72,18 @@ api:
ota: ota:
- platform: esphome - platform: esphome
logger:
mdns:
disabled: false
wifi: wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
fast_connect: True
domain: .cloonar.smart domain: .cloonar.smart
fast_connect: False
captive_portal: networks:
- ssid: !secret wifi_ssid
dashboard_import: password: !secret wifi_password
package_import_url: github://athom-tech/athom-configs/athom-rgbww-light.yaml hidden: True
channel: 1
manual_ip:
static_ip: 10.42.100.16
gateway: 10.42.100.1
subnet: 255.255.255.0
binary_sensor: binary_sensor:
- platform: status - platform: status
@@ -115,21 +112,6 @@ sensor:
entity_category: "diagnostic" entity_category: "diagnostic"
device_class: "" 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: output:
- platform: esp8266_pwm - platform: esp8266_pwm
id: red_output id: red_output

View File

@@ -16,8 +16,10 @@
./modules/networking.nix ./modules/networking.nix
./modules/setupnetwork.nix ./modules/setupnetwork.nix
./modules/firewall.nix ./modules/firewall.nix
./modules/dhcp4.nix # ./modules/dhcp4.nix
./modules/unbound.nix # ./modules/unbound.nix
./modules/dnsmasq.nix
./modules/avahi.nix ./modules/avahi.nix
./modules/openconnect.nix ./modules/openconnect.nix
./modules/wireguard.nix ./modules/wireguard.nix
@@ -33,7 +35,7 @@
# ./modules/vscode-server.nix # Add VS Code Server microvm # ./modules/vscode-server.nix # Add VS Code Server microvm
./modules/ai-mailer.nix ./modules/ai-mailer.nix
./modules/wazuh.nix # ./modules/wazuh.nix
# web # web
./modules/web ./modules/web
@@ -52,7 +54,8 @@
./modules/deconz.nix ./modules/deconz.nix
# ./modules/mopidy.nix # ./modules/mopidy.nix
# ./modules/mosquitto.nix # ./modules/mosquitto.nix
./modules/snapserver.nix # ./modules/snapserver.nix
./modules/lms.nix
# gaming # gaming
# ./modules/palworld.nix # ./modules/palworld.nix

View File

@@ -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 ];
}

View File

@@ -8,6 +8,18 @@
"cloonar-fw" = { "cloonar-fw" = {
family = "inet"; family = "inet";
content = '' 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 { chain output {
type filter hook output priority 100; policy accept; type filter hook output priority 100; policy accept;
} }
@@ -22,6 +34,7 @@
type filter hook input priority filter; policy drop; type filter hook input priority filter; policy drop;
iifname "lo" accept comment "trusted interfaces" iifname "lo" accept comment "trusted interfaces"
iifname "lan" counter accept comment "Spice" 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 } 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 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 "lan" tcp dport 5931 counter accept comment "Spice"
iifname { "server", "vserver", "vm-*", "lan", "wg_cloonar" } counter accept comment "allow trusted to router" 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", "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" iifname { "wan", "multimedia" } icmp type { echo-request, destination-unreachable, time-exceeded } counter accept comment "Allow select ICMP"
# Accept mDNS for avahi reflection # 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 tcp dport { llmnr } counter accept
iifname "server" ip saddr ${config.networkPrefix}.97.20/32 udp dport { mdns, 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 # Allow all returning traffic
ct state { established, related } counter accept ct state { established, related } counter accept
@@ -79,10 +95,24 @@
# multimedia airplay # multimedia airplay
iifname "multimedia" oifname { "lan" } counter accept iifname "multimedia" oifname { "lan" } counter accept
iifname "multimedia" oifname "server" tcp dport { 1704, 1705 } 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 iifname "lan" oifname "server" udp dport { 5000, 5353, 6001 - 6011 } counter accept
# avahi # avahi
iifname "server" ip saddr ${config.networkPrefix}.97.20/32 oifname { "lan" } counter accept 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 LANServer"
iifname "server" oifname "lan" udp sport 5353 ip saddr 224.0.0.251 counter accept comment "mDNS response ServerLAN"
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 # 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 udp dport { 5683 } counter accept
iifname "smart" oifname "server" ip daddr ${config.networkPrefix}.97.20/32 tcp dport { 1883 } counter accept iifname "smart" oifname "server" ip daddr ${config.networkPrefix}.97.20/32 tcp dport { 1883 } counter accept

View File

@@ -421,21 +421,62 @@
"light.bathroom_bulb_2" "light.bathroom_bulb_2"
]; ];
} }
{
platform = "switch";
name = "Hallway Switch";
entity_id = "switch.hallway";
}
{ {
platform = "group"; platform = "group";
name = "Hallway Lights"; name = "Hallway Lights";
all = true; all = true;
entities = [ entities = [
"light.hallway_switch" "light.hallway_light_switch_mini_switch"
"light.hallway_bulb_1" "light.hallway_bulb_1"
"light.hallway_bulb_2" "light.hallway_bulb_2"
]; ];
} }
{
platform = "template";
lights = {
hallway_group_proxy = {
friendly_name = "Hallway Lights (Proxy)";
# follow the real groups 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"; platform = "switch";
name = "Toilet Switch"; name = "Toilet Switch";

View File

@@ -1,4 +1,9 @@
{ let
devices = [
"device_tracker.dominiks_iphone"
"device_tracker.dominiks_mp01"
];
in {
services.home-assistant.extraComponents = [ services.home-assistant.extraComponents = [
"nuki" "nuki"
]; ];
@@ -9,9 +14,7 @@
mode = "restart"; mode = "restart";
trigger = { trigger = {
platform = "state"; platform = "state";
entity_id = [ entity_id = devices;
"device_tracker.dominiks_iphone"
];
from = "not_home"; from = "not_home";
to = "home"; to = "home";
}; };

View File

@@ -494,6 +494,12 @@
entity_id = "script.turn_on_tv"; entity_id = "script.turn_on_tv";
}; };
} }
{
service = "media_player.turn_off";
target = {
entity_id = "media_player.marantz_sr6015";
};
}
]; ];
} }
]; ];

View File

@@ -1,10 +1,11 @@
{ {
services.home-assistant.extraComponents = [ "squeezebox" ];
services.home-assistant.config = { services.home-assistant.config = {
"automation toilet music" = { "automation toilet music" = {
alias = "toilet music"; alias = "toilet music";
trigger = { trigger = {
platform = "state"; platform = "state";
entity_id = "light.toilett_lights"; entity_id = "light.toilet_switch";
}; };
action = [ action = [
{ {
@@ -13,10 +14,52 @@
entity_id = "media_player.music_toilet_snapcast_client"; entity_id = "media_player.music_toilet_snapcast_client";
}; };
data = { 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";
};
};
};
}; };
} }

View File

@@ -1,31 +1,5 @@
{ {
services.home-assistant = { services.home-assistant = {
extraComponents = [ "snapcast" ]; 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";
};
};
};
};
}; };
} }

88
hosts/fw/modules/lms.nix Normal file
View File

@@ -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";
};
};
}

View File

@@ -261,6 +261,10 @@ in {
enable = true; enable = true;
path = with pkgs; [ unbound inotify-tools ]; path = with pkgs; [ unbound inotify-tools ];
script = '' script = ''
#!/usr/bin/env bash
set -euo pipefail
# readFile and readFileUnique as before
function readFile() { function readFile() {
if [[ "''\$2" == "A" ]] ; then 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 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
@@ -315,17 +319,25 @@ in {
done done
} }
function syncFile() { function syncLeases() {
# readFileUnique "''\$1" "''\$2" # 1) nuke all of our old lease records from unbound
while true; do unbound-control list_local_data \
readFileUnique "''\$1" "''\$2" | grep -E 'cloonar\.(com|multimedia|smart)|ip4\.arpa|in-addr\.arpa' \
sleep 10 | while read -r name type data; do
done 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
} }
syncFile "/var/lib/kea/dhcp4.leases" A & while true; do
# syncFile "/var/lib/kea/dhcp6.leases" AAAA & syncLeases
wait sleep 10
done
''; '';
wants = [ "network-online.target" "unbound.service" ]; wants = [ "network-online.target" "unbound.service" ];
after = [ "network-online.target" "unbound.service" ]; after = [ "network-online.target" "unbound.service" ];

View File

@@ -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] 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] 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] 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: sops:
kms: [] kms: []
gcp_kms: [] gcp_kms: []
@@ -59,8 +60,8 @@ sops:
WXJpUUxadERyYUExRFMzNzBXaUVET3cKG9ZwWy5YvTr/BAw/i+ZJos5trwRvaW5j WXJpUUxadERyYUExRFMzNzBXaUVET3cKG9ZwWy5YvTr/BAw/i+ZJos5trwRvaW5j
eV/SHiEteZZtCuCVFAp3iolE/mJyu97nA2yFwWaLN86h+/xkOJsdqA== eV/SHiEteZZtCuCVFAp3iolE/mJyu97nA2yFwWaLN86h+/xkOJsdqA==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
lastmodified: "2025-03-01T22:14:10Z" lastmodified: "2025-05-01T20:36:09Z"
mac: ENC[AES256_GCM,data:UWwjvi8jLNgu4l7ldMYtkAATm3y5+BSxbCuPN/e1OC4/3ULYJndqFLfTOMpqQbj2+uHo3onelK4f0MAJuSH0oUx58CclkNBBLE0RXafxbowa7kJtTNDfTboJNqH7rFmhGhqCtHAOOpKBuowqoOUHP5BtzZfucra0Q/pIJt5lma0=,iv:iJEW/mTbizioPSN8G+WqHSipx8P6VCDrVG/Cmk+MBUc=,tag:L4OkeKec5AZdCrpUrnqcOA==,type:str] mac: ENC[AES256_GCM,data:ZtXJcuwDpDlBl2xdRtMF1PwwqbW00Eps2ZZG5x4C2djAq+meXJCxKS9sNazQhMYFOqphQXe3JEhChykLxnJyWivY/Er1ig2sU6Ke1uVcfSP85B1/rpzhe/7QI+GBDWrkCk1O0xGKKj8fWt+Yv2MV8gw2XctdtJ9Md4imUhcK7zo=,iv:5NFH+7Z0alBiq/b94T40XJSCar2+BGaFB20z0Kc59fU=,tag:18n0tt17RNMyyE0eECH2kQ==,type:str]
pgp: [] pgp: []
unencrypted_suffix: _unencrypted unencrypted_suffix: _unencrypted
version: 3.9.4 version: 3.9.4

View File

@@ -25,7 +25,7 @@ in {
./utils/modules/nur.nix ./utils/modules/nur.nix
./modules/appimage.nix ./modules/appimage.nix
./modules/sway/sway.nix ./modules/sway/sway.nix
./modules/printer.nix # ./modules/printer.nix
# ./modules/cyberghost.nix # ./modules/cyberghost.nix
./utils/modules/autoupgrade.nix ./utils/modules/autoupgrade.nix
./modules/puppeteer.nix ./modules/puppeteer.nix

View File

@@ -78,6 +78,7 @@ let
"media.ffmpeg.vaapi.enabled" = true; "media.ffmpeg.vaapi.enabled" = true;
"media.ffmpeg.vaapi-drm-display.enabled" = true; "media.ffmpeg.vaapi-drm-display.enabled" = true;
"gfx.webrender.all" = true; "gfx.webrender.all" = true;
"xpinstall.signatures.required" = false;
}; };
firefoxUserChrome = '' firefoxUserChrome = ''
@@ -531,7 +532,7 @@ in
programs.firefox = { programs.firefox = {
enable = true; enable = true;
package = pkgs.firefox.overrideAttrs (a: { package = pkgs.firefox-devedition.overrideAttrs (a: {
postInstall = a.postInstall or "" + '' postInstall = a.postInstall or "" + ''
wrapProgram "$out/bin/firefox" \ wrapProgram "$out/bin/firefox" \
export MOZ_ENABLE_WAYLAND=1 export MOZ_ENABLE_WAYLAND=1
@@ -558,6 +559,11 @@ in
icon = "fingerprint"; icon = "fingerprint";
id = 1; id = 1;
}; };
"dating" = {
color = "pink";
icon = "circle";
id = 5;
};
"cloonar technologies" = { "cloonar technologies" = {
color = "red"; color = "red";
icon = "briefcase"; icon = "briefcase";

View File

@@ -8,7 +8,10 @@
server_name = "code.cloonar.com"; server_name = "code.cloonar.com";
aliasgroup1 = "https://nextcloud.cloonar.com:443"; aliasgroup1 = "https://nextcloud.cloonar.com:443";
aliasgroup2 = "https://cloud.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"; extra_params = "--o:ssl.enable=false --o:ssl.termination=true";
}; };
extraOptions = [ extraOptions = [

View File

@@ -16,7 +16,7 @@ in
enable = true; enable = true;
hostName = "nextcloud.cloonar.com"; hostName = "nextcloud.cloonar.com";
https = true; https = true;
package = nextcloud30; package = pkgs.nextcloud31;
# Instead of using pkgs.nextcloud27Packages.apps, # Instead of using pkgs.nextcloud27Packages.apps,
# we'll reference the package version specified above # we'll reference the package version specified above
extraApps = { extraApps = {

View File

@@ -0,0 +1,7 @@
{ pkgs, ... }: {
services.postfix = {
enable = true;
domain = "cloonar.com";
hostname = "web-arm.cloonar.com";
};
}

122
raspberry/picoreplayer.sh Executable file
View File

@@ -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 <<EOF
Usage: sudo $0 --device <rpi-4|rpi-zero|rpi-zero-2> \
--sdcard </dev/sdX> \
[--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" <<EOF
ctrl_interface=/var/run/wpa_supplicant
ctrl_interface_group=staff
country=AT
update_config=1
network={
ssid="$WIFI_SSID"
psk="$WIFI_PSK"
scan_ssid=1
key_mgmt=WPA-PSK
auth_alg=OPEN
}
EOF
# If a Zero device, enable Hifiberry DAC+ and disable onboard audio
if [[ "$DEVICE" == "rpi-zero" || "$DEVICE" == "rpi-zero-2" ]]; then
cat >> "$BOOT_MNT/config.txt" <<EOF
# Hifiberry DAC+ overlay for Zero models
dtoverlay=hifiberry-dacplus-std
EOF
fi
# Cleanup
umount "$BOOT_MNT" "$ROOT_MNT"
rmdir "$BOOT_MNT" "$ROOT_MNT"
echo "Done! SD card is ready with PiCorePlayer ${PCP_VERSION} on $DEVICE,"
echo "hidden Wi-Fi '$WIFI_SSID'."
echo "Insert the SD card into the Pi and power it on."
echo "You can now connect to the PiCorePlayer web interface at http://pcp.local"

View File

@@ -6,8 +6,8 @@ stdenv.mkDerivation rec {
src = fetchgit { src = fetchgit {
url = "https://github.com/dpolakovics/bento.git"; url = "https://github.com/dpolakovics/bento.git";
rev = "8d911a02dc9af222ffb5892bbddd4a3895893959"; rev = "73092673b194fd734d782f5b7e83dfbb5d169372";
sha256 = "sha256-9R3glZcjc+t8LKvo5HOAo+HzXFQ6GOtzehJpb7GjmYM="; sha256 = "sha256-/37RJTjo+FJa1Flt59LrQbaJIcBid/z+Hy1xfhYGXNA=";
}; };
buildInputs = [ ]; buildInputs = [ ];