From bf5c7a74cbd3c58f398ee1d4d5cd6965ee84abbb Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Mon, 29 Sep 2025 15:59:12 +0200 Subject: [PATCH 1/6] feat: esphome updates --- esphome/README.md | 2 +- esphome/archive/install.yaml | 19 +++++++ esphome/bathroom-bulb-1.yaml | 6 +- esphome/bathroom-bulb-2.yaml | 6 +- esphome/bedroom-bulb-1.yaml | 6 +- esphome/bedroom-bulb-2.yaml | 6 +- esphome/bedroom-bulb-3.yaml | 6 +- esphome/bedroom-bulb-4.yaml | 6 +- esphome/hallway-bulb-1.yaml | 6 +- esphome/hallway-bulb-2.yaml | 6 +- esphome/livingroom-bulb-1.yaml | 4 +- esphome/livingroom-bulb-2.yaml | 89 +++++++++++++++-------------- esphome/livingroom-bulb-3.yaml | 100 +++++++++++++++----------------- esphome/livingroom-bulb-4.yaml | 101 +++++++++++++++------------------ esphome/livingroom-bulb-5.yaml | 101 +++++++++++++++------------------ esphome/livingroom-bulb-6.yaml | 100 +++++++++++++++----------------- esphome/toilet-bulb.yaml | 6 +- hosts/nb/users/codex-cli.nix | 49 ++++++++++++++++ hosts/nb/users/dominik.nix | 3 + 19 files changed, 341 insertions(+), 281 deletions(-) create mode 100644 esphome/archive/install.yaml create mode 100644 hosts/nb/users/codex-cli.nix diff --git a/esphome/README.md b/esphome/README.md index 7151fdf..130e14c 100644 --- a/esphome/README.md +++ b/esphome/README.md @@ -6,4 +6,4 @@ https://github.com/tasmota/mgos32-to-tasmota32/releases In Tasmota make OTA Update to minimal: http://ota.tasmota.com/tasmota/release/tasmota-minimal.bin.gz Make ESPHome Configuration in Dashboard: -docker run --rm -p 6052:6052 -e ESPHOME_DASHBOARD_USE_PING=true -v "${PWD}":/config -it ghcr.io/esphome/esphome +docker run --rm -p 6052:6052 -e ESPHOME_DASHBOARD_USE_PING=true -v "${PWD}":/config -it ghcr.io/esphome/esphome:latest diff --git a/esphome/archive/install.yaml b/esphome/archive/install.yaml new file mode 100644 index 0000000..ba3eb8d --- /dev/null +++ b/esphome/archive/install.yaml @@ -0,0 +1,19 @@ +substitutions: + device_name: "install" + friendly_name: "Esphome Install" + +esphome: + name: ${device_name} + comment: ${friendly_name} + platform: ESP8266 + board: esp01_1m + +web_server: + port: 80 + +ota: + platform: esphome + +wifi: + ssid: Cloonar-Smart + password: 0m6sY7Ue3G31 \ No newline at end of file diff --git a/esphome/bathroom-bulb-1.yaml b/esphome/bathroom-bulb-1.yaml index f7c85a5..cc95a58 100644 --- a/esphome/bathroom-bulb-1.yaml +++ b/esphome/bathroom-bulb-1.yaml @@ -5,8 +5,6 @@ substitutions: esphome: name: ${device_name} comment: ${friendly_name} - platform: ESP8266 - board: esp01_1m on_boot: priority: 300 then: @@ -23,6 +21,9 @@ esphome: green: 50% blue: 0% white: 100% + +esp8266: + board: esp01_1m interval: - interval: 15s @@ -37,6 +38,7 @@ interval: # Enable Home Assistant API api: + batch_delay: 0ms ota: platform: esphome diff --git a/esphome/bathroom-bulb-2.yaml b/esphome/bathroom-bulb-2.yaml index c6da5b0..75694e5 100644 --- a/esphome/bathroom-bulb-2.yaml +++ b/esphome/bathroom-bulb-2.yaml @@ -5,8 +5,6 @@ substitutions: esphome: name: ${device_name} comment: ${friendly_name} - platform: ESP8266 - board: esp01_1m on_boot: priority: 300 then: @@ -23,6 +21,9 @@ esphome: green: 50% blue: 0% white: 100% + +esp8266: + board: esp01_1m interval: - interval: 15s @@ -37,6 +38,7 @@ interval: # Enable Home Assistant API api: + batch_delay: 0ms ota: platform: esphome diff --git a/esphome/bedroom-bulb-1.yaml b/esphome/bedroom-bulb-1.yaml index c7f5859..ba8cc2e 100644 --- a/esphome/bedroom-bulb-1.yaml +++ b/esphome/bedroom-bulb-1.yaml @@ -5,8 +5,6 @@ substitutions: esphome: name: ${device_name} comment: ${friendly_name} - platform: ESP8266 - board: esp01_1m on_boot: priority: 300 then: @@ -23,6 +21,9 @@ esphome: green: 50% blue: 0% white: 100% + +esp8266: + board: esp01_1m interval: - interval: 15s @@ -37,6 +38,7 @@ interval: # Enable Home Assistant API api: + batch_delay: 0ms ota: platform: esphome diff --git a/esphome/bedroom-bulb-2.yaml b/esphome/bedroom-bulb-2.yaml index 48db408..6e84803 100644 --- a/esphome/bedroom-bulb-2.yaml +++ b/esphome/bedroom-bulb-2.yaml @@ -5,8 +5,6 @@ substitutions: esphome: name: ${device_name} comment: ${friendly_name} - platform: ESP8266 - board: esp01_1m on_boot: priority: 300 then: @@ -23,6 +21,9 @@ esphome: green: 50% blue: 0% white: 100% + +esp8266: + board: esp01_1m interval: - interval: 15s @@ -37,6 +38,7 @@ interval: # Enable Home Assistant API api: + batch_delay: 0ms ota: platform: esphome diff --git a/esphome/bedroom-bulb-3.yaml b/esphome/bedroom-bulb-3.yaml index b9d90c0..9b7cb89 100644 --- a/esphome/bedroom-bulb-3.yaml +++ b/esphome/bedroom-bulb-3.yaml @@ -5,8 +5,6 @@ substitutions: esphome: name: ${device_name} comment: ${friendly_name} - platform: ESP8266 - board: esp01_1m on_boot: priority: 300 then: @@ -23,6 +21,9 @@ esphome: green: 50% blue: 0% white: 100% + +esp8266: + board: esp01_1m interval: - interval: 15s @@ -37,6 +38,7 @@ interval: # Enable Home Assistant API api: + batch_delay: 0ms ota: platform: esphome diff --git a/esphome/bedroom-bulb-4.yaml b/esphome/bedroom-bulb-4.yaml index d6ecb3d..5894ebe 100644 --- a/esphome/bedroom-bulb-4.yaml +++ b/esphome/bedroom-bulb-4.yaml @@ -5,8 +5,6 @@ substitutions: esphome: name: ${device_name} comment: ${friendly_name} - platform: ESP8266 - board: esp01_1m on_boot: priority: 300 then: @@ -23,6 +21,9 @@ esphome: green: 50% blue: 0% white: 100% + +esp8266: + board: esp01_1m interval: - interval: 15s @@ -37,6 +38,7 @@ interval: # Enable Home Assistant API api: + batch_delay: 0ms ota: platform: esphome diff --git a/esphome/hallway-bulb-1.yaml b/esphome/hallway-bulb-1.yaml index c3b36ec..4a2b368 100644 --- a/esphome/hallway-bulb-1.yaml +++ b/esphome/hallway-bulb-1.yaml @@ -5,8 +5,6 @@ substitutions: esphome: name: ${device_name} comment: ${friendly_name} - platform: ESP8266 - board: esp01_1m on_boot: priority: 300 then: @@ -23,6 +21,9 @@ esphome: green: 50% blue: 0% white: 100% + +esp8266: + board: esp01_1m interval: - interval: 15s @@ -37,6 +38,7 @@ interval: # Enable Home Assistant API api: + batch_delay: 0ms ota: platform: esphome diff --git a/esphome/hallway-bulb-2.yaml b/esphome/hallway-bulb-2.yaml index d779e17..0f0aa84 100644 --- a/esphome/hallway-bulb-2.yaml +++ b/esphome/hallway-bulb-2.yaml @@ -5,8 +5,6 @@ substitutions: esphome: name: ${device_name} comment: ${friendly_name} - platform: ESP8266 - board: esp01_1m on_boot: priority: 300 then: @@ -23,6 +21,9 @@ esphome: green: 50% blue: 0% white: 100% + +esp8266: + board: esp01_1m interval: - interval: 15s @@ -37,6 +38,7 @@ interval: # Enable Home Assistant API api: + batch_delay: 0ms ota: platform: esphome diff --git a/esphome/livingroom-bulb-1.yaml b/esphome/livingroom-bulb-1.yaml index 7f848c9..0afe6b2 100644 --- a/esphome/livingroom-bulb-1.yaml +++ b/esphome/livingroom-bulb-1.yaml @@ -56,13 +56,14 @@ preferences: flash_write_interval: 1min api: + batch_delay: 0ms ota: - platform: esphome wifi: # Disable fast_connect so we do a full scan (required for hidden SSIDs) - fast_connect: false + fast_connect: True domain: "${dns_domain}" # Your hidden network @@ -84,7 +85,6 @@ wifi: 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 diff --git a/esphome/livingroom-bulb-2.yaml b/esphome/livingroom-bulb-2.yaml index b005f58..f36bcc9 100644 --- a/esphome/livingroom-bulb-2.yaml +++ b/esphome/livingroom-bulb-2.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 @@ -57,22 +56,35 @@ preferences: flash_write_interval: 1min api: + batch_delay: 0ms ota: - platform: esphome wifi: - domain: .cloonar.smart - fast_connect: False + # Disable fast_connect so we do a full scan (required for hidden SSIDs) + fast_connect: True + domain: "${dns_domain}" + + # Your hidden network networks: - ssid: !secret wifi_ssid password: !secret wifi_password channel: 1 - hidden: True + hidden: true + manual_ip: static_ip: 10.42.100.12 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 binary_sensor: - platform: status @@ -90,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 @@ -98,8 +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: "" + entity_category: diagnostic output: - platform: esp8266_pwm @@ -153,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-3.yaml b/esphome/livingroom-bulb-3.yaml index d7bc72c..3e2509f 100644 --- a/esphome/livingroom-bulb-3.yaml +++ b/esphome/livingroom-bulb-3.yaml @@ -10,18 +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" - -globals: - - id: fast_boot - type: int - restore_value: yes - initial_value: '0' - - - id: restore_mode - type: int - restore_value: yes - initial_value: "1" esphome: name: "${name}" @@ -34,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 @@ -68,22 +56,35 @@ preferences: flash_write_interval: 1min api: + batch_delay: 0ms ota: - platform: esphome wifi: - domain: .cloonar.smart - fast_connect: False + # Disable fast_connect so we do a full scan (required for hidden SSIDs) + fast_connect: True + domain: "${dns_domain}" + + # Your hidden network networks: - ssid: !secret wifi_ssid password: !secret wifi_password - hidden: True channel: 1 + hidden: true + manual_ip: static_ip: 10.42.100.13 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 binary_sensor: - platform: status @@ -101,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 @@ -109,8 +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: "" + entity_category: diagnostic output: - platform: esp8266_pwm @@ -164,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-4.yaml b/esphome/livingroom-bulb-4.yaml index 84e0086..13d3478 100644 --- a/esphome/livingroom-bulb-4.yaml +++ b/esphome/livingroom-bulb-4.yaml @@ -10,18 +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" - -globals: - - id: fast_boot - type: int - restore_value: yes - initial_value: '0' - - - id: restore_mode - type: int - restore_value: yes - initial_value: "1" esphome: name: "${name}" @@ -34,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 @@ -68,21 +56,35 @@ preferences: flash_write_interval: 1min api: + batch_delay: 0ms ota: - platform: esphome wifi: - domain: .cloonar.smart - fast_connect: False + # Disable fast_connect so we do a full scan (required for hidden SSIDs) + fast_connect: True + domain: "${dns_domain}" + + # Your hidden network networks: - ssid: !secret wifi_ssid password: !secret wifi_password - hidden: True + channel: 1 + hidden: true + manual_ip: static_ip: 10.42.100.14 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 binary_sensor: - platform: status @@ -100,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 @@ -108,8 +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: "" + entity_category: diagnostic output: - platform: esp8266_pwm @@ -163,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-5.yaml b/esphome/livingroom-bulb-5.yaml index 295e56a..daae20a 100644 --- a/esphome/livingroom-bulb-5.yaml +++ b/esphome/livingroom-bulb-5.yaml @@ -10,18 +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" - -globals: - - id: fast_boot - type: int - restore_value: yes - initial_value: '0' - - - id: restore_mode - type: int - restore_value: yes - initial_value: "1" esphome: name: "${name}" @@ -34,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 @@ -68,21 +56,35 @@ preferences: flash_write_interval: 1min api: + batch_delay: 0ms ota: - platform: esphome wifi: - domain: .cloonar.smart - fast_connect: False + # Disable fast_connect so we do a full scan (required for hidden SSIDs) + fast_connect: True + domain: "${dns_domain}" + + # Your hidden network networks: - ssid: !secret wifi_ssid password: !secret wifi_password - hidden: True + channel: 1 + hidden: true + manual_ip: static_ip: 10.42.100.15 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 binary_sensor: - platform: status @@ -100,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 @@ -108,8 +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: "" + entity_category: diagnostic output: - platform: esp8266_pwm @@ -163,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-6.yaml b/esphome/livingroom-bulb-6.yaml index a06f5e1..97617a9 100644 --- a/esphome/livingroom-bulb-6.yaml +++ b/esphome/livingroom-bulb-6.yaml @@ -10,18 +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" - -globals: - - id: fast_boot - type: int - restore_value: yes - initial_value: '0' - - - id: restore_mode - type: int - restore_value: yes - initial_value: "1" esphome: name: "${name}" @@ -34,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 @@ -68,22 +56,35 @@ preferences: flash_write_interval: 1min api: + batch_delay: 0ms ota: - platform: esphome wifi: - domain: .cloonar.smart - fast_connect: False + # Disable fast_connect so we do a full scan (required for hidden SSIDs) + fast_connect: True + domain: "${dns_domain}" + + # Your hidden network networks: - ssid: !secret wifi_ssid password: !secret wifi_password - hidden: True channel: 1 + hidden: true + manual_ip: static_ip: 10.42.100.16 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 binary_sensor: - platform: status @@ -101,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 @@ -109,8 +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: "" + entity_category: diagnostic output: - platform: esp8266_pwm @@ -164,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/toilet-bulb.yaml b/esphome/toilet-bulb.yaml index 78dc9ad..50ecc01 100644 --- a/esphome/toilet-bulb.yaml +++ b/esphome/toilet-bulb.yaml @@ -5,8 +5,6 @@ substitutions: esphome: name: ${device_name} comment: ${friendly_name} - platform: ESP8266 - board: esp01_1m on_boot: then: - light.turn_on: @@ -20,7 +18,8 @@ esphome: id: my_light color_temperature: 2700 K - +esp8266: + board: esp01_1m interval: - interval: 15s @@ -40,6 +39,7 @@ interval: # Enable Home Assistant API api: + batch_delay: 0ms ota: platform: esphome diff --git a/hosts/nb/users/codex-cli.nix b/hosts/nb/users/codex-cli.nix new file mode 100644 index 0000000..c093110 --- /dev/null +++ b/hosts/nb/users/codex-cli.nix @@ -0,0 +1,49 @@ +{ config, lib, pkgs, ... }: +let + user = "dominik"; + home = "/home/${user}"; + npmPrefix = "${home}/.npm-global"; + node = pkgs.nodejs; # or pkgs.nodejs_20 +in { + home-manager.users.dominik = { lib, pkgs, ... }: { + home.packages = with pkgs; [ + node + gnutar # provides `tar` + gzip # provides `gzip` used by tar + unzip + python314 # useful for codex model use + jq # useful for JSON processing + ]; + + # Ensure ~/.npmrc with a user prefix (no sudo needed) + home.file.".npmrc"= { + text = '' + prefix=${npmPrefix} + ''; + force = true; + }; + + + # Ensure the npm bin dir is on PATH for all shells + home.sessionPath = [ + "${npmPrefix}/bin" + ]; + + # Nice-to-have: visible variables in the shell + home.sessionVariables = { + NPM_CONFIG_PREFIX = npmPrefix; + }; + + # Auto-install @openai/codex if it's not already there + # (idempotent on each `home-manager switch`) + home.activation.installCodexCli = lib.hm.dag.entryAfter [ "writeBoundary" ] '' + export PATH=${node}/bin:${pkgs.gnutar}/bin:${pkgs.gzip}/bin:${pkgs.unzip}/bin:${pkgs.curl}/bin:$PATH + mkdir -p ${npmPrefix} + if ! command -v codex >/dev/null 2>&1; then + echo "Installing @openai/codex globally..." + # --global uses prefix from ~/.npmrc; PATH has node for postinstall + ${node}/bin/npm install -g @openai/codex + fi + ''; + }; +} diff --git a/hosts/nb/users/dominik.nix b/hosts/nb/users/dominik.nix index bcb39f5..657a17b 100644 --- a/hosts/nb/users/dominik.nix +++ b/hosts/nb/users/dominik.nix @@ -147,6 +147,9 @@ let persistHome = "/home/dominik"; in { + imports = [ + ./codex-cli.nix + ]; sops.secrets.openai_api_key = { owner = "dominik"; From 8ab1c91b38805f654f28faefebcc32e7c2308b4a Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Mon, 29 Sep 2025 15:59:48 +0200 Subject: [PATCH 2/6] feat: scana11y changes --- hosts/web-arm/modules/sa-core.nix | 2 +- hosts/web-arm/pkgs/sa-core.nix | 4 ++-- hosts/web-arm/sites/scana11y.com.nix | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hosts/web-arm/modules/sa-core.nix b/hosts/web-arm/modules/sa-core.nix index bac004f..c00b4d7 100644 --- a/hosts/web-arm/modules/sa-core.nix +++ b/hosts/web-arm/modules/sa-core.nix @@ -23,7 +23,7 @@ -depth=3 \ -max-pages=100 \ -image=cloonar/sa-core:v0.1.3 \ - -output=/var/www/scana11y.com/reports \ + -output=/var/www/scana11y.com/public/reports \ -wkhtmltopdf=${pkgs.wkhtmltopdf}/bin/wkhtmltopdf \ -email-server=imap.cloonar.com \ -email-username=office@scana11y.com \ diff --git a/hosts/web-arm/pkgs/sa-core.nix b/hosts/web-arm/pkgs/sa-core.nix index f8b9bc5..0c3933a 100644 --- a/hosts/web-arm/pkgs/sa-core.nix +++ b/hosts/web-arm/pkgs/sa-core.nix @@ -2,13 +2,13 @@ let saRepoSrc = builtins.fetchGit { url = "ssh://gitea@git.cloonar.com/ScanA11y/sa-core.git"; - rev = "daf955c2dffb5d27593746274c33e6e3043c8227"; + rev = "844c5a6b95ae28ab17da127011d1f027680d1a9a"; }; in buildGoModule rec { pname = "sa-core"; - version = "0.1.0"; + version = "0.2.0"; src = saRepoSrc; diff --git a/hosts/web-arm/sites/scana11y.com.nix b/hosts/web-arm/sites/scana11y.com.nix index 0b056d9..abb8757 100644 --- a/hosts/web-arm/sites/scana11y.com.nix +++ b/hosts/web-arm/sites/scana11y.com.nix @@ -9,7 +9,7 @@ in { enableACME = true; acmeRoot = "/var/lib/acme/acme-challenge"; - root = "${dataDir}"; + root = "${dataDir}/public"; locations."/favicon.ico".extraConfig = '' log_not_found off; From 305ce21e41a1fae4ff2ce541c6af6fe2899946da Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Thu, 2 Oct 2025 19:45:08 +0200 Subject: [PATCH 3/6] feat: add modularity to scana11y --- hosts/web-arm/configuration.nix | 1 + hosts/web-arm/modules/sa-core.nix | 161 +++++++++++++++++++++-------- hosts/web-arm/modules/scana11y.nix | 8 ++ hosts/web-arm/pkgs/sa-core.nix | 4 +- 4 files changed, 129 insertions(+), 45 deletions(-) create mode 100644 hosts/web-arm/modules/scana11y.nix diff --git a/hosts/web-arm/configuration.nix b/hosts/web-arm/configuration.nix index acabb98..f0c0201 100644 --- a/hosts/web-arm/configuration.nix +++ b/hosts/web-arm/configuration.nix @@ -38,6 +38,7 @@ # comment out for first build, so ssh key and config is present # otherwise the build will fail ./modules/sa-core.nix + ./modules/scana11y.nix ]; diff --git a/hosts/web-arm/modules/sa-core.nix b/hosts/web-arm/modules/sa-core.nix index c00b4d7..d2adced 100644 --- a/hosts/web-arm/modules/sa-core.nix +++ b/hosts/web-arm/modules/sa-core.nix @@ -1,48 +1,123 @@ -{ config, pkgs, ... }: +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.sa-core; + + instanceOpts = { name, ... }: + { + options = { + domain = mkOption { + type = types.nullOr types.str; + default = null; + description = lib.mdDoc '' + Domain of the website to scan. Defaults to attribute name in instances. + ''; + example = "example.org"; + }; + + email = mkOption { + type = types.str; + default = "office@scana11y.com"; + description = lib.mdDoc '' + Email where to send scan reports. + ''; + example = "example.org"; + }; + + maxPages = mkOption { + type = types.int; + default = 100; + description = lib.mdDoc '' + Maximum number of pages to scan. + ''; + example = "100"; + }; + + timerConfig = mkOption { + type = types.attrs; + default = { + OnCalendar = "monthly"; + Persistent = true; + RandomizedDelaySec = "1h"; # spread load a bit + }; + description = lib.mdDoc '' + Configuration for the systemd timer. + ''; + }; + }; + }; +in { - virtualisation = { - docker.enable = true; - }; - - users.users.scana11y_com.extraGroups = [ "docker" ]; - - systemd.services."scana11y-wohnservice-wien.at" = { - description = "ScanA11y wohnservice-wien.at"; - wants = [ "docker.service" ]; - after = [ "docker.service" "network-online.target" ]; - serviceConfig = { - Type = "oneshot"; - User = "scana11y_com"; - }; - - path = [ pkgs.docker pkgs.wkhtmltopdf ]; - - script = '' - exec ${pkgs.sa-core}/bin/sa-core scan \ - -domain=wohnservice-wien.at \ - -depth=3 \ - -max-pages=100 \ - -image=cloonar/sa-core:v0.1.3 \ - -output=/var/www/scana11y.com/public/reports \ - -wkhtmltopdf=${pkgs.wkhtmltopdf}/bin/wkhtmltopdf \ - -email-server=imap.cloonar.com \ - -email-username=office@scana11y.com \ - -email-password-file=${config.sops.secrets.sa-core-mailpw.path} \ - -email-sender=office@scana11y.com \ - -email-recipient=dominik.polakovics@cloonar.com \ - -keep 6 \ - -webroot=https://scana11y.com/reports - ''; - }; - - systemd.timers."scana11y-wohnservice-wien.at" = { - wantedBy = [ "timers.target" ]; - timerConfig = { - OnCalendar = "monthly"; - Persistent = true; - RandomizedDelaySec = "1h"; # spread load a bit + options.services.sa-core = { + instances = mkOption { + type = types.attrsOf (types.submodule instanceOpts); + default = {}; + description = lib.mdDoc "Define ScanA11y instances here."; + example = literalExpression '' + { + "example.org" = { + domain = "example.org"; + email = "test@example.org"; + }; + } + ''; }; }; - sops.secrets.sa-core-mailpw.owner = "scana11y_com"; # written as root at activation + config = { + virtualisation = { + docker.enable = true; + }; + + systemd.timers = mapAttrs' (instance: instanceOpts: + let + domain = if instanceOpts.domain != null then instanceOpts.domain else instance; + in + nameValuePair ("sa-core-" + domain) { + wantedBy = [ "timers.target" ]; + timerConfig = instanceOpts.timerConfig; + } + ) cfg.instances; + + systemd.services = mapAttrs' (instance: instanceOpts: + let + domain = if instanceOpts.domain != null then instanceOpts.domain else instance; + maxPages = if instanceOpts.maxPages != null then toString instanceOpts.maxPages else "100"; + in + nameValuePair ("sa-core-" + domain) { + description = "ScanA11y ${domain}"; + wants = [ "docker.service" ]; + after = [ "docker.service" "network-online.target" ]; + serviceConfig = { + Type = "oneshot"; + User = "scana11y_com"; + Group= "nginx"; + SupplementaryGroups = "docker"; + }; + + path = [ pkgs.docker pkgs.wkhtmltopdf ]; + + script = '' + exec ${pkgs.sa-core}/bin/sa-core scan \ + -domain=${domain} \ + -depth=3 \ + -max-pages=${maxPages} \ + -image=cloonar/sa-core:v0.1.3 \ + -output=/var/www/scana11y.com/public/reports \ + -wkhtmltopdf=${pkgs.wkhtmltopdf}/bin/wkhtmltopdf \ + -email-server=imap.cloonar.com \ + -email-username=office@scana11y.com \ + -email-password-file=${config.sops.secrets.sa-core-mailpw.path} \ + -email-sender=office@scana11y.com \ + -email-recipient=${instanceOpts.email} \ + -keep 6 \ + -webroot=https://scana11y.com/reports + ''; + } + ) cfg.instances; + + sops.secrets.sa-core-mailpw.owner = "scana11y_com"; + }; } diff --git a/hosts/web-arm/modules/scana11y.nix b/hosts/web-arm/modules/scana11y.nix new file mode 100644 index 0000000..f685644 --- /dev/null +++ b/hosts/web-arm/modules/scana11y.nix @@ -0,0 +1,8 @@ +{ config, pkgs, ... }: +{ + services.sa-core.instances = { + "wohnservice-wien.at" = { + email = "dominik.polakovics@cloonar.com"; + }; + }; +} diff --git a/hosts/web-arm/pkgs/sa-core.nix b/hosts/web-arm/pkgs/sa-core.nix index 0c3933a..d09c48d 100644 --- a/hosts/web-arm/pkgs/sa-core.nix +++ b/hosts/web-arm/pkgs/sa-core.nix @@ -2,13 +2,13 @@ let saRepoSrc = builtins.fetchGit { url = "ssh://gitea@git.cloonar.com/ScanA11y/sa-core.git"; - rev = "844c5a6b95ae28ab17da127011d1f027680d1a9a"; + rev = "0d33fdb79b62c270d230ad307341ae107be96600"; }; in buildGoModule rec { pname = "sa-core"; - version = "0.2.0"; + version = "0.3.2"; src = saRepoSrc; From 6339b733c419835ae21de2c4b224a2e0015154a1 Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Thu, 2 Oct 2025 19:45:37 +0200 Subject: [PATCH 4/6] feat: nb update coding config --- hosts/nb/modules/desktop/default.nix | 4 ---- hosts/nb/modules/development/coding.nix | 10 ++++++---- hosts/nb/modules/development/default.nix | 2 ++ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/hosts/nb/modules/desktop/default.nix b/hosts/nb/modules/desktop/default.nix index 0605a20..9403117 100644 --- a/hosts/nb/modules/desktop/default.nix +++ b/hosts/nb/modules/desktop/default.nix @@ -37,10 +37,6 @@ in { variants = ["qt5"]; }) - vscode - code-cursor - - dracula-theme foot fractal diff --git a/hosts/nb/modules/development/coding.nix b/hosts/nb/modules/development/coding.nix index af37c38..c45ac94 100644 --- a/hosts/nb/modules/development/coding.nix +++ b/hosts/nb/modules/development/coding.nix @@ -1,10 +1,12 @@ { config, pkgs, ... }: let - # unstable = import (fetchTarball https://nixos.org/channels/nixos-unstable/nixexprs.tar.xz) { - # config = { allowUnfree = true; }; - # }; + unstable = import (fetchTarball https://nixos.org/channels/nixos-unstable/nixexprs.tar.xz) { + config = { allowUnfree = true; }; + }; in { environment.systemPackages = with pkgs; [ - claude-code + unstable.claude-code + unstable.code-cursor + unstable.vscode ]; } diff --git a/hosts/nb/modules/development/default.nix b/hosts/nb/modules/development/default.nix index 9ac16a1..75afc24 100644 --- a/hosts/nb/modules/development/default.nix +++ b/hosts/nb/modules/development/default.nix @@ -10,6 +10,7 @@ in { imports = [ # ./mcp.nix ./coding.nix + ./android.nix ./nvim/default.nix ]; environment.systemPackages = with pkgs; [ @@ -37,6 +38,7 @@ in { vim wget wireguard-tools + wkhtmltopdf wol ]; From 15f6b2edd05b4a94d0e9a958d39ae9f4d9621e57 Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Thu, 2 Oct 2025 19:45:58 +0200 Subject: [PATCH 5/6] feat: nb change sway config to nautilus --- hosts/nb/modules/sway/sway.conf | 7 ++++-- hosts/nb/modules/sway/sway.nix | 39 +++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/hosts/nb/modules/sway/sway.conf b/hosts/nb/modules/sway/sway.conf index 28c95af..7995b52 100644 --- a/hosts/nb/modules/sway/sway.conf +++ b/hosts/nb/modules/sway/sway.conf @@ -70,7 +70,7 @@ bindsym $mod+d exec foot -a launcher -e env TERMINAL_COMMAND="foot -c /etc/xdg/f #bindsym $mod+d exec --no-startup-id bemenu --dmenu="dmenu -i -fn 'Source Sans Pro-10' -nb '#212121' -sb '#2b83a6' -sf '#ffffff' # start an explorer -bindsym $mod+e exec --no-startup-id thunar +bindsym $mod+e exec --no-startup-id nautilus # switching window with win+tab bindsym $mod+Tab exec --no-startup-id wofi -show window @@ -260,7 +260,7 @@ assign [app_id="foot" title="^(?!terminal-sway)$"] → $ws2 assign [app_id="chromium"] → $ws3 assign [app_id="firefox"] → $ws3 assign [app_id="librewolf"] → $ws3 -assign [app_id="pcmanfm"] → $ws4 +assign [app_id="nautilus"] → $ws4 assign [app_id="libreoffice-calc"] → $ws5 assign [app_id="libreoffice-writer"] → $ws5 assign [class="vlc"] → $ws6 @@ -310,6 +310,9 @@ exec dunst exec 'sleep 2; swaymsg workspace "$ws8"; swaymsg layout tabbed' exec mako --default-timeout=5000 +# Battery low warning every minute +exec_always sh -c 'while :; do battery-alert-swaynag; sleep 60; done' & + # wallpaper # output eDP-1 scale 2 scale_filter linear output eDP-1 mode 2880x1920 scale 2 scale_filter linear diff --git a/hosts/nb/modules/sway/sway.nix b/hosts/nb/modules/sway/sway.nix index 7f5fc2a..873598e 100644 --- a/hosts/nb/modules/sway/sway.nix +++ b/hosts/nb/modules/sway/sway.nix @@ -17,6 +17,44 @@ let ''; }; + battery-alert-swaynag = pkgs.writeShellScriptBin "battery-alert-swaynag" '' + #!/usr/bin/env bash + set -euo pipefail + + # Detect a battery + cap_file="" + status_file="" + for d in /sys/class/power_supply/BAT*; do + if [[ -f "$d/capacity" ]]; then + cap_file="$d/capacity" + status_file="$d/status" + break + fi + done + + if [[ -z "$cap_file" ]]; then + exit 0 + fi + + capacity=$(cat "$cap_file") + status=$(cat "$status_file" 2>/dev/null || echo "Unknown") + + if [[ "$capacity" -lt 20 && "$status" != "Charging" && "$status" != "Full" ]]; then + stamp="/run/user/$(id -u)/.battery_swaynag_stamp" + now=$(date +%s) + last=0 + if [[ -f "$stamp" ]]; then + last=$(cat "$stamp" 2>/dev/null || echo 0) + fi + # Avoid spamming: at most once every 5 minutes + if (( now - last >= 300 )); then + echo "$now" > "$stamp" + swaynag -t warning -m "Battery low: ''${capacity}% - plug in the charger." -b "Dismiss" "true" & + disown || true + fi + fi + ''; + sway-conf = builtins.readFile ./sway.conf + '' exec swaybg -m center -c 252525 -i ~/.wallpaper.png @@ -32,6 +70,7 @@ in { waybar wayland wofi + battery-alert-swaynag ]; programs.sway = { From c3f260370201169b39beaafc5499d53e29d14660 Mon Sep 17 00:00:00 2001 From: Dominik Polakovics Date: Thu, 2 Oct 2025 19:46:19 +0200 Subject: [PATCH 6/6] feat: nb add scana11y address and change firefox config --- hosts/nb/users/dominik.nix | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/hosts/nb/users/dominik.nix b/hosts/nb/users/dominik.nix index 657a17b..803e33c 100644 --- a/hosts/nb/users/dominik.nix +++ b/hosts/nb/users/dominik.nix @@ -79,6 +79,10 @@ let "media.ffmpeg.vaapi-drm-display.enabled" = true; "gfx.webrender.all" = true; "xpinstall.signatures.required" = false; + "layers.acceleration.disabled" = false; + "gfx.webrender.force-disabled" = false; + "gfx.webrender.software" = false; + "gfx.webrender.compositor" = true; }; # firefoxUserChrome = '' @@ -445,6 +449,32 @@ in }; }; }; + "office@scana11y.com" = { + realName = "Dominik Polakovics"; + address = "office@scana11y.com"; + userName = "office@scana11y.com"; + imap = { + host = "imap.cloonar.com"; + port = 993; + }; + smtp = { + host = "mail.cloonar.com"; + port = 587; + tls = { + enable = true; + useStartTls = true; + }; + }; + thunderbird = { + enable = true; + profiles = [ "cloonar" "work" ]; + settings = id: { + "mail.identity.id_${id}.reply_on_top" = 1; + "mail.identity.id_${id}.sig_bottom" = false; + "mail.identity.id_${id}.htmlSigFormat" = true; + }; + }; + }; "dominik.polakovics@epicenter.works" = { realName = "Dominik Polakovics"; address = "dominik.polakovics@epicenter.works";