diff --git a/ai/docker-compose.yml b/ai/docker-compose.yml index 3ba07e9..051e3a9 100644 --- a/ai/docker-compose.yml +++ b/ai/docker-compose.yml @@ -1,6 +1,6 @@ services: openwebui: - image: ghcr.io/open-webui/open-webui:0.6.23 + image: ghcr.io/open-webui/open-webui:0.6.40 container_name: open-webui restart: always environment: diff --git a/authentik/.env b/authentik/.env index fa4fb56..639c78c 100644 --- a/authentik/.env +++ b/authentik/.env @@ -13,4 +13,4 @@ AUTHENTIK_EMAIL__USE_SSL=false AUTHENTIK_EMAIL__TIMEOUT=10 # Email address authentik will send from, should have a correct @domain AUTHENTIK_EMAIL__FROM=home@thoster.net - +AUTHENTIK_TAG=2025.10.2 diff --git a/backup.sh b/backup.sh index 405d7d8..fb0944f 100755 --- a/backup.sh +++ b/backup.sh @@ -3,70 +3,90 @@ set -Eeuo pipefail shopt -s inherit_errexit nullglob # ----------------------------------------------------------------------------- -# CONFIGURATION — adjust to taste +# CONFIGURATION # ----------------------------------------------------------------------------- -readonly RETENTION_DAYS=7 readonly BACKUP_BASE=/media/extension/backup/ubuntu readonly MOUNT_POINT=/media/extension - -# timestamp format — no spaces -#readonly NOW=$(date +'%Y-%m-%d_%H-%M-%S') readonly NOW=$(date +'%Y-%m-%d') -readonly THIS_RUN=${BACKUP_BASE}/${NOW} -readonly LATEST=${BACKUP_BASE}/latest +readonly THIS_RUN="${BACKUP_BASE}/${NOW}" +readonly LATEST="${BACKUP_BASE}/latest" -# directories (or files) to back up: +# Directories (or files) to back up (without DB volumes) declare -a SOURCE_ITEMS=( "/home/oster/server" - "/home/oster/photo-lib" +# "/home/oster/photo-lib" ) -# rsync exclusions — add your own +# Rsync exclusions — exclude DB-related dirs that are dumped separately + common junk declare -a EXCLUDES=( --exclude='.cache' --exclude='node_modules' --exclude='*.tmp' + --exclude='**/postgresql' + --exclude='**/mysql' + --exclude='**/db' + --exclude='**/database' ) -# rsync niceties +# Rsync options RSYNC_OPTS=( - -aAXHv # archive, preserve ACLs/xattrs, human-readable, verbose + -aAXHv # preserve everything, human-friendly, verbose --delete # mirror deletions --partial # keep partials if interrupted - --stats # give stats at end + --stats # summary "${EXCLUDES[@]}" ) -# Dry-run? set to "--dry-run" to test without writing -DRY_RUN="" +# ----------------------------------------------------------------------------- +# LOGGING +# ----------------------------------------------------------------------------- +log() { echo "[$(date +'%F %T')] $*"; } +die() { log "FATAL: $*"; exit 1; } -# ----------------------------------------------------------------------------- -# LOGGING & LOCKING / CLEANUP -# ----------------------------------------------------------------------------- -log() { echo "[$(date +'%F %T')] $*"; } -die() { log "FATAL: $*"; exit 1; } +# Locking lockfile="${BACKUP_BASE}/.backup.lock" - -#trap 'rm -rf "$lockfile"' EXIT -#if ! touch "$lockfile" 2>/dev/null; then -# die "Another backup is running (lockfile exists)." -#fi +cleanup() { rm -f "$lockfile"; } +trap cleanup EXIT +if ! ( set -o noclobber; echo "$$" > "$lockfile" ) 2>/dev/null; then + die "Another backup is already running (lockfile exists at $lockfile)." +fi # ----------------------------------------------------------------------------- # FUNCTIONS # ----------------------------------------------------------------------------- check_mount() { if ! mountpoint -q "$MOUNT_POINT"; then - die "Backup mountpoint $MOUNT_POINT is not mounted." + die "Backup mountpoint $MOUNT_POINT is NOT mounted." fi } +# Rotation: keep 7 daily and 4 weekly rotate_old() { - log "Rotating backups older than ${RETENTION_DAYS} days." - find "$BACKUP_BASE" -maxdepth 1 -mindepth 1 -type d \ - ! -name 'latest' ! -name "$(basename "$THIS_RUN")" \ - -mtime +${RETENTION_DAYS} \ - -print -exec rm -rf {} \; + log "Applying retention policy (7 daily, 4 weekly)." + cd "$BACKUP_BASE" || die "Cannot cd into $BACKUP_BASE" + + # find snapshot dirs YYYY-MM-DD only + for dir in $(ls -1 | grep -E '^[0-9]{4}-[0-9]{2}-[0-9]{2}$'); do + keep=0 + + # Keep last 7 daily + for i in $(seq 0 6); do + [ "$dir" = "$(date -d "-$i day" +%F)" ] && keep=1 + done + + # Keep last 4 Sundays (weeklies) + for i in $(seq 0 27); do + d=$(date -d "last sunday -$i week" +%F) + [ "$dir" = "$d" ] && keep=1 + done + + if [ $keep -eq 0 ]; then + log " Deleting expired snapshot $dir" + rm -rf "$dir" + else + log " Keeping snapshot $dir" + fi + done } # ----------------------------------------------------------------------------- @@ -75,33 +95,33 @@ rotate_old() { log "=== Starting backup run: $NOW ===" check_mount -mkdir -p "$BACKUP_BASE" -mkdir -p "$THIS_RUN" +mkdir -p "$BACKUP_BASE" "$THIS_RUN" -# Determine if we can do a hardlink-incremental snapshot +# Incremental snapshot? if [ -d "$LATEST" ]; then - LINK_DEST="--link-dest=$(readlink -f $LATEST)" - log "Using link‐dest for incremental snapshot → $LATEST" + LINK_DEST="--link-dest=$(readlink -f "$LATEST")" + log "Using link-dest for incremental snapshot → $LATEST" else LINK_DEST="" - log "No previous backup; doing full snapshot." + log "No previous backup; creating full snapshot." fi +# Run backups for src in "${SOURCE_ITEMS[@]}"; do base=$(basename "$src") dst="${THIS_RUN}/${base}" + mkdir -p "$dst" log "Backing up $src → $dst" - nice -n 10 ionice -c2 -n7 \ - rsync "${RSYNC_OPTS[@]}" $LINK_DEST $DRY_RUN \ + rsync "${RSYNC_OPTS[@]}" $LINK_DEST \ "$src"/ "$dst"/ done -# update the "latest" symlink atomically -tmp_latest="${BACKUP_BASE}/.latest.tmp" -rm -f "$tmp_latest" -ln -sfn "$(basename "$THIS_RUN")" "$tmp_latest" -mv -T "$tmp_latest" "$LATEST" +# Update latest symlink atomically +ln -sfn "$(basename "$THIS_RUN")" "$BACKUP_BASE/.latest.tmp" +mv -T "$BACKUP_BASE/.latest.tmp" "$LATEST" +log "Snapshots done. Running rotation policy..." rotate_old + log "=== Backup completed successfully ===" diff --git a/hass/homeassistant/automations.yaml b/hass/homeassistant/automations.yaml index 744faf0..1c0dc1d 100644 --- a/hass/homeassistant/automations.yaml +++ b/hass/homeassistant/automations.yaml @@ -141,7 +141,7 @@ - at: 01:00:00 trigger: time - trigger: time - at: 08:00:00 + at: 08:30:00 conditions: [] actions: - action: light.turn_off @@ -194,37 +194,7 @@ kelvin: 2627 target: device_id: 4a993938cbf7289987eb684f71b00b92 -- id: '1732736495026' - alias: Lichter aus bei Sonnenaufgang - description: '' - trigger: - - platform: time - at: 01:00:00 - condition: [] - action: - - action: light.turn_off - metadata: {} - data: {} - target: - device_id: 03477708398bb2e1ceb3f4a776770a29 - mode: single -- id: '1732736623945' - alias: Licht an bei Sonnenuntergang - description: '' - trigger: - - platform: sun - event: sunset - offset: -00:30:00 - condition: [] - action: - - action: light.turn_on - metadata: {} - data: - kelvin: 2490 - brightness_pct: 85 - target: - device_id: 03477708398bb2e1ceb3f4a776770a29 - mode: single + controller_device: 180e52c2b4e6bfd02de1a022cd77eda0 - id: '1733523441756' alias: Küche Theke Schalter description: '' @@ -297,11 +267,6 @@ device_id: a09f77bde7ba5098df0c3ec599ca7377 entity_id: 2a315e13dac7a4d0e3a5331216e79a5b domain: switch - - action: light.turn_off - metadata: {} - data: {} - target: - device_id: aa992894922dfd99d4de351691fddbeb mode: single - id: '1733558486213' alias: Küche Theke und Tisch aus @@ -569,3 +534,54 @@ target: entity_id: counter.bewegung_kueche mode: single +- id: '1761475605456' + alias: Bewegungsmelder Licht Treppe oben + description: '' + triggers: + - type: occupied + device_id: 4629e222cda722b7ad07de427acb56bd + entity_id: aae8135070bc723603a2477818b484a9 + domain: binary_sensor + trigger: device + - type: occupied + device_id: d0addbba1dd8a92697554ff8a015345e + entity_id: 42c91c337a8f76b1559776a7daec6e84 + domain: binary_sensor + trigger: device + conditions: [] + actions: + - type: turn_on + device_id: 68bb158ff829c34611860fedff328e32 + entity_id: 073c2fbfb9a0796461063e922049587e + domain: switch + - delay: + hours: 0 + minutes: 10 + seconds: 0 + milliseconds: 0 + - type: turn_off + device_id: 68bb158ff829c34611860fedff328e32 + entity_id: 073c2fbfb9a0796461063e922049587e + domain: switch + mode: single +- id: '1761484676480' + alias: Treppenlicht aus verzögert + description: '' + triggers: + - type: turned_on + device_id: 68bb158ff829c34611860fedff328e32 + entity_id: 073c2fbfb9a0796461063e922049587e + domain: switch + trigger: device + conditions: [] + actions: + - delay: + hours: 0 + minutes: 10 + seconds: 0 + milliseconds: 0 + - type: turn_off + device_id: 68bb158ff829c34611860fedff328e32 + entity_id: 073c2fbfb9a0796461063e922049587e + domain: switch + mode: single diff --git a/hass/homeassistant/configuration.yaml b/hass/homeassistant/configuration.yaml index 4a15e72..f64c730 100644 --- a/hass/homeassistant/configuration.yaml +++ b/hass/homeassistant/configuration.yaml @@ -14,8 +14,8 @@ shell_command: !include shell_commands.yaml switch: - platform: wake_on_lan name: "mini PC" - host: "192.168.178.87" - mac: "58:47:CA:78:4E:59" + host: "192.168.178.138" + mac: "9c:6b:00:8b:3c:20" turn_off: service: shell_command.turn_off_remote_pc - platform: template diff --git a/hass/zb2mqttdata/configuration.yaml b/hass/zb2mqttdata/configuration.yaml index 394367b..d7dee26 100644 --- a/hass/zb2mqttdata/configuration.yaml +++ b/hass/zb2mqttdata/configuration.yaml @@ -51,8 +51,6 @@ devices: friendly_name: Flurlicht '0x7cb03eaa0a0176bd': friendly_name: Küchentisch - '0x000d6f001417c142': - friendly_name: Fernbedienung 2 '0xa4c138e68023f4c6': friendly_name: Wassersensor Keller '0xa4c1389d42e94844': @@ -65,6 +63,12 @@ devices: friendly_name: Wasser Sensor Bad '0x000d6f00109eb214': friendly_name: Fernbedienung + '0xa4c138f8d17c3daa': + friendly_name: Bewegungsmelder Treppe oben + '0xa4c138b00278ea51': + friendly_name: Bewegungsmelder Treppe oben 2 + '0x000d6f001417c142': + friendly_name: Fernbedienung Kühlschrank homeassistant: enabled: true version: 4 diff --git a/hass/zb2mqttdata/state.json b/hass/zb2mqttdata/state.json index 6bb8ecb..e434089 100644 --- a/hass/zb2mqttdata/state.json +++ b/hass/zb2mqttdata/state.json @@ -6,40 +6,40 @@ "installed_version": 51999504, "latest_version": 51999504 }, - "brightness": 105, - "state": "OFF", + "brightness": 254, + "state": "ON", "color": { "x": 0.4599, "y": 0.4106 }, "color_temp": 370, - "linkquality": 90, + "linkquality": 72, "update_available": false }, "0x00212effff0cb8b6": { "color_mode": "xy", "state": "OFF", "state_rgb": "OFF", - "brightness_white": 76, - "state_white": "OFF", + "brightness_white": 254, + "state_white": "ON", "brightness_rgb": 26, "color_mode_rgb": "color_temp", "color_temp_rgb": 199, "color_mode_white": "color_temp", - "color_temp_white": 284, + "color_temp_white": 365, "color": { "x": 0.6942, "y": 0.2963 }, "color_white": { - "x": 0.4042, - "y": 0.3902 + "x": 0.4566, + "y": 0.4098 }, "color_rgb": { "x": 0.3445, "y": 0.3512 }, - "linkquality": 75, + "linkquality": 129, "state_10": "OFF" }, "0xa4c1384187f0960d": { @@ -51,20 +51,20 @@ "linkquality": 78 }, "0xa4c13864e6c8c400": { - "battery": 93, + "battery": 89, "voltage": 2900, "occupancy": false, "tamper": false, "battery_low": false, - "linkquality": 111 + "linkquality": 60 }, "0xa4c138ef60e2fdbe": { "contact": true, "tamper": false, "battery_low": false, - "voltage": 2800, - "battery": 100, - "linkquality": 150 + "voltage": 2700, + "battery": 98, + "linkquality": 156 }, "0xf0d1b800001388f0": { "update": { @@ -72,9 +72,9 @@ "installed_version": 17130496, "latest_version": 17130496 }, - "state": "OFF", + "state": "ON", "brightness": 229, - "linkquality": 12, + "linkquality": 0, "update_available": false }, "0x7cb03eaa0a0176bd": { @@ -85,14 +85,9 @@ }, "brightness": 152, "state": "ON", - "linkquality": 147, + "linkquality": 117, "update_available": false }, - "0x000d6f001417c142": { - "voltage": 3000, - "battery": 100, - "linkquality": 66 - }, "0xa4c138e68023f4c6": { "battery": 59, "water_leak": false, @@ -101,23 +96,23 @@ }, "0xa4c13838ba59a1b2": { "battery": 100, - "temperature": 30.3, - "humidity": 39, + "temperature": 28.9, + "humidity": 42, "temperature_unit": "celsius", "temperature_calibration": 0, "humidity_calibration": 0, - "linkquality": 144 + "linkquality": 120 }, "0x000d6f001417c16e": { "voltage": 3000, "battery": 100, - "linkquality": 138 + "linkquality": 117 }, "0xa4c1383ec7aa7d95": { - "temperature": 24.58, - "humidity": 52.71, - "battery": 59, - "linkquality": 150 + "temperature": 23.45, + "humidity": 60.7, + "battery": 14, + "linkquality": 117 }, "0xa4c138066bf1f4c7": { "battery": 100, @@ -134,12 +129,12 @@ "installed_version": 33574183, "latest_version": 33574183 }, - "linkquality": 90 + "linkquality": 12 }, "0x001788010d1c884e": { "brightness": 254, "state": "ON", - "linkquality": 0, + "linkquality": 15, "update": { "state": "available", "installed_version": 16786434, @@ -149,10 +144,10 @@ "color_mode": "color_temp" }, "0xa4c1383db5531833": { - "temperature": 23.8, - "humidity": 43.3, - "battery": 29, - "linkquality": 147, + "temperature": 21.5, + "humidity": 84.2, + "battery": 25, + "linkquality": 15, "update": { "state": "idle", "installed_version": 268513281, @@ -160,23 +155,45 @@ } }, "0xa4c1389d42e94844": { - "temperature": 22.4, - "linkquality": 0, + "temperature": 21, + "linkquality": 126, "battery": 100, - "humidity": 61, + "humidity": 53, "temperature_unit": "fahrenheit", "temperature_calibration": 0, "humidity_calibration": 0 }, "0xa4c138570b1956d9": { - "temperature": 24.95, - "humidity": 51.97, - "battery": 46, - "linkquality": 72 + "temperature": 18.63, + "humidity": 66.52, + "battery": 0, + "linkquality": 24 }, "0x000d6f00109eb214": { + "voltage": 3000, + "battery": 100, + "battery_low": false, + "linkquality": 138 + }, + "0xa4c138f8d17c3daa": { + "battery": 96, + "voltage": 2900, + "occupancy": false, + "tamper": false, + "battery_low": false, + "linkquality": 54 + }, + "0xa4c138b00278ea51": { + "battery": 86, + "occupancy": false, + "tamper": false, + "battery_low": false, + "voltage": 2900, + "linkquality": 24 + }, + "0x000d6f001417c142": { "voltage": 3100, "battery": 100, - "battery_low": false + "linkquality": 123 } } \ No newline at end of file diff --git a/jellyfin/config/config/migrations.xml b/jellyfin/config/config/migrations.xml index 1ad6c1c..9521dfe 100644 --- a/jellyfin/config/config/migrations.xml +++ b/jellyfin/config/config/migrations.xml @@ -149,5 +149,133 @@ 96c156a2-7a13-4b3b-a8b8-fb80c94d20c0 RemoveDuplicatePlaylistChildren + + 9b354818-94d5-4b68-ac49-e35cb85f9d84 + CreateNetworkConfiguration + + + a6dcacf4-c057-4ef9-80d3-61cef9ddb4f0 + MigrateMusicBrainzTimeout + + + 4fb5c950-1991-11ee-9b4b-0800200c9a66 + MigrateNetworkConfiguration + + + a8e61960-7726-4450-8f3d-82c12daabbcb + MigrateEncodingOptions + + + 4124c2cd-e939-4ffb-9be9-9b311c413638 + DisableTranscodingThrottling + + + ef103419-8451-40d8-9f34-d1a8e93a1679 + CreateLoggingConfigHeirarchy + + + 3793eb59-bc8c-456c-8b9f-bd5a62a42978 + MigrateActivityLogDatabase + + + acbe17b7-8435-4a83-8b64-6fcf162cb9bd + RemoveDuplicateExtras + + + 5c4b82a2-f053-4009-bd05-b6fcad82f14c + MigrateUserDatabase + + + 06387815-c3cc-421f-a888-fb5f9992bea8 + MigrateDisplayPreferencesDatabase + + + a81f75e0-8f43-416f-a5e8-516ccab4d8cc + RemoveDownloadImagesInAdvance + + + 5bd72f41-e6f3-4f60-90aa-09869abe0e22 + MigrateAuthenticationDatabase + + + 615dfa9e-2497-4dbb-a472-61938b752c5b + FixPlaylistOwner + + + d34bfc33-5d2e-4790-8085-069ef6eecb4e + MigrateRatingLevels + + + cf6fabc2-9fbe-4933-84a5-ffe52ef22a58 + FixAudioData + + + 96c156a2-7a13-4b3b-a8b8-fb80c94d20c0 + RemoveDuplicatePlaylistChildren + + + 9b354818-94d5-4b68-ac49-e35cb85f9d84 + CreateNetworkConfiguration + + + a6dcacf4-c057-4ef9-80d3-61cef9ddb4f0 + MigrateMusicBrainzTimeout + + + 4fb5c950-1991-11ee-9b4b-0800200c9a66 + MigrateNetworkConfiguration + + + a8e61960-7726-4450-8f3d-82c12daabbcb + MigrateEncodingOptions + + + 4124c2cd-e939-4ffb-9be9-9b311c413638 + DisableTranscodingThrottling + + + ef103419-8451-40d8-9f34-d1a8e93a1679 + CreateLoggingConfigHeirarchy + + + 3793eb59-bc8c-456c-8b9f-bd5a62a42978 + MigrateActivityLogDatabase + + + acbe17b7-8435-4a83-8b64-6fcf162cb9bd + RemoveDuplicateExtras + + + 5c4b82a2-f053-4009-bd05-b6fcad82f14c + MigrateUserDatabase + + + 06387815-c3cc-421f-a888-fb5f9992bea8 + MigrateDisplayPreferencesDatabase + + + a81f75e0-8f43-416f-a5e8-516ccab4d8cc + RemoveDownloadImagesInAdvance + + + 5bd72f41-e6f3-4f60-90aa-09869abe0e22 + MigrateAuthenticationDatabase + + + 615dfa9e-2497-4dbb-a472-61938b752c5b + FixPlaylistOwner + + + d34bfc33-5d2e-4790-8085-069ef6eecb4e + MigrateRatingLevels + + + cf6fabc2-9fbe-4933-84a5-ffe52ef22a58 + FixAudioData + + + 96c156a2-7a13-4b3b-a8b8-fb80c94d20c0 + RemoveDuplicatePlaylistChildren + \ No newline at end of file diff --git a/jellyfin/docker-compose.yml b/jellyfin/docker-compose.yml index d96f941..dac7660 100644 --- a/jellyfin/docker-compose.yml +++ b/jellyfin/docker-compose.yml @@ -1,6 +1,6 @@ services: jellyfin: - image: jellyfin/jellyfin:10.10.6 + image: jellyfin/jellyfin:10.10.7 container_name: jellyfin user: 1000:1000 # network_mode: 'host' @@ -30,7 +30,7 @@ services: restart: 'unless-stopped' # Optional - alternative address used for autodiscovery environment: - - JELLYFIN_PublishedServerUrl=http://example.com + - JELLYFIN_PublishedServerUrl=https://media.home.thoster.net # Optional - may be necessary for docker healthcheck to pass if running in host network mode extra_hosts: - 'host.docker.internal:host-gateway' diff --git a/wikimd/docker-compose.yml b/wikimd/docker-compose.yml index f6c950a..9baa7fa 100644 --- a/wikimd/docker-compose.yml +++ b/wikimd/docker-compose.yml @@ -1,4 +1,3 @@ -version: "2.1" services: wikmd: image: linbreux/wikmd:latest