Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions homeassistant-addon/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

If this add-on saves you time, you can [buy me a coffee](https://buymeacoffee.com/dougrathbone).

## [1.35.0] - 2026-09-29

### Fixed

- **A USB PC Interface stuck while opening is now closed and reopened automatically.** (#122)
- **Startup waits for the C-Bus interface before polling, avoiding repeated command failures.** (#122)
- **Discovery keeps retrying while the C-Bus interface is unavailable.** (#122)
- **Managed C-Gate file logs stay bounded between add-on restarts.** Set the maximum C-Gate log size option to change the 500 MiB default. (#122)

## [1.34.16] - 2026-09-29

### Fixed

- **USB PC Interface recovery and managed C-Gate log limits.** (#122)

## [1.34.15] - 2026-09-28

### Fixed
Expand Down
11 changes: 11 additions & 0 deletions homeassistant-addon/DOCS.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ These settings only apply when `cgate_mode` is set to `managed`.
| `cgate_download_url` | string | (empty) | Override the default download URL for C-Gate. Leave empty to use the official Clipsal URL. |
| `cgate_download_sha256` | string | (empty) | Optional SHA256 of the C-Gate zip. When set, download and upload installs fail on mismatch. Downloads from the built-in default URL are verified against a checksum pinned in the install script; setting this overrides that pin (the escape hatch if Clipsal re-releases the zip). Required for a custom `cgate_download_url`; uploads without it proceed with a log warning and no integrity check. |
| `cgate_force_reinstall` | boolean | `false` | Reinstall/upgrade C-Gate from the install source on the next start. Once C-Gate is installed it is normally kept as is across restarts; turn this on to replace it (for example to move to a newer C-Gate version). Your project DBs and config are preserved. Turn it back off after the upgrade, or C-Gate reinstalls on every boot. |
| `cgate_log_max_mb` | integer | `500` | Maximum total size in MiB for managed C-Gate file logs. The add-on checks every 15 minutes and removes the oldest closed log files first. |
| `cgate_serial_device` | device | (empty) | **BETA — opt-in, field-tested with 5500PC and 5500PCU.** Dropdown of the serial devices detected on the HA host. Prefer a `/dev/serial/by-id/...` alias over a bare `/dev/ttyUSB0`: it survives replugging into another USB port. Hidden optional field; leave empty to disable. See "USB-serial PCI support" below. |
| `cgate_external_clients` | list of objects | `[]` | Addresses allowed to connect to the managed C-Gate (for tools such as C-Bus Toolkit), each with an `address` and a `level` of `monitor`, `operate` or `program`. Empty means the add-on itself only. **C-Gate has no authentication on its ports** — see "Letting external clients reach managed C-Gate" below before using this. |

Expand Down Expand Up @@ -98,6 +99,16 @@ On every start the add-on:
- if log files still exceed 500 MiB total, deletes the oldest segments until
under that cap

While the add-on remains running, it repeats the size and age cleanup every
15 minutes. Set the optional `cgate_log_max_mb` value (50–10000, default 500)
to choose the maximum retained C-Gate file-log size. Files C-Gate is actively
writing are never deleted; its native rotation closes them before cleanup.

This is separate from the add-on log shown by Home Assistant. cgateweb writes
that log to stdout/stderr so Supervisor manages it. `/data` is already the
standard persistent add-on volume, so mapping another host folder does not
provide Home Assistant log rotation and is not required.

Your project databases and `config/` are never touched. The active `event.log`
is kept; only older rotated segments are eligible for pruning.

Expand Down
3 changes: 2 additions & 1 deletion homeassistant-addon/config.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: "C-Gate Web Bridge"
version: "1.34.15"
version: "1.35.0"
slug: cgateweb
description: "Bridge between Clipsal C-Bus systems and MQTT/Home Assistant"
url: "https://github.com/dougrathbone/cgateweb"
Expand Down Expand Up @@ -132,6 +132,7 @@ schema:
# ── C-Gate Managed Mode ──
cgate_install_source: "list(download|upload)?"
cgate_download_url: "str?"
cgate_log_max_mb: "int(50,10000)?"
cgate_download_sha256: "str?"
cgate_force_reinstall: "bool?"
# USB-serial PCI passthrough (beta, issue #28): opt-in. The device schema
Expand Down
59 changes: 50 additions & 9 deletions homeassistant-addon/rootfs/etc/cont-init.d/cgate-install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,13 @@ CGATEWEB_DEFAULT_PAYLOAD_SHA256="b135a367f435b63ec0a08e5cde0b96735da23c8a48f3f48

# Managed C-Gate log retention (#81). C-Gate writes unbounded files under
# /data/cgate/logs/ and rotated event-file segments; without a cap a busy
# network can fill the host SD card. Pruned on every boot before C-Gate starts.
# network can fill the host SD card. Pruned on every boot before C-Gate starts
# and periodically by the cgate-log-pruner service while it remains running.
CGATEWEB_LOG_MAX_BYTES="${CGATEWEB_LOG_MAX_BYTES:-524288000}" # 500 MiB
CGATEWEB_LOG_MAX_AGE_DAYS="${CGATEWEB_LOG_MAX_AGE_DAYS:-7}"
CGATEWEB_EVENT_FILE_SPLIT_SIZE="${CGATEWEB_EVENT_FILE_SPLIT_SIZE:-5000000}" # 5 MiB (C-Gate default)
CGATEWEB_EVENT_FILE_SPLIT_COUNT="${CGATEWEB_EVENT_FILE_SPLIT_COUNT:-50}" # ~250 MiB of event segments
CGATEWEB_PROC_ROOT="${CGATEWEB_PROC_ROOT:-/proc}"

# The identity-aware serial resolver (issue #28) and the file it publishes its
# answer to. Both are overridable so the unit tests can run the repo copy of
Expand Down Expand Up @@ -370,10 +372,23 @@ _cgateweb_stat_mtime() {
stat -c '%Y' "$1" 2>/dev/null || stat -f '%m' "$1" 2>/dev/null || echo 0
}

# Return 0 when a file under the managed C-Gate install may be pruned.
# Only log directories and rotated event-file segments are eligible — never
# Projects/, config/, or the live event.log C-Gate is writing.
_cgateweb_prune_cgate_logs_is_prunable() {
# Return 0 when a running process currently has the file open. Runtime pruning
# must not unlink an active C-Gate log: the JVM would keep writing to the
# unlinked inode, so the disk space would remain consumed but become invisible
# to later retention passes. Startup pruning normally finds no open files.
_cgateweb_log_file_is_open() {
local file="$1" fd target
for fd in "${CGATEWEB_PROC_ROOT}"/[0-9]*/fd/*; do
[[ -e "${fd}" || -L "${fd}" ]] || continue
target=$(readlink -f "${fd}" 2>/dev/null || true)
[[ "${target}" == "${file}" ]] && return 0
done
return 1
}

# Return 0 for files that count toward managed C-Gate's log budget. Only log
# directories and event-file segments are in scope — never Projects/ or config/.
_cgateweb_prune_cgate_logs_is_in_scope() {
local cgate_dir="$1" file="$2"
case "${file}" in
"${cgate_dir}/logs"/*|"${cgate_dir}/log"/*) return 0 ;;
Expand All @@ -387,9 +402,18 @@ _cgateweb_prune_cgate_logs_is_prunable() {
esac
}

# Return 0 when an in-scope file can safely be deleted. An open file still
# counts toward the cap, but remains until C-Gate closes or rotates it.
_cgateweb_prune_cgate_logs_is_prunable() {
local cgate_dir="$1" file="$2"
_cgateweb_prune_cgate_logs_is_in_scope "${cgate_dir}" "${file}" || return 1
_cgateweb_log_file_is_open "${file}" && return 1
return 0
}

# Prune C-Gate log files under the managed install directory. Runs on every
# boot before C-Gate starts so a reinstall is not the only way to reclaim
# space (#81). Echoes a one-line summary when anything was removed.
# boot and periodically while C-Gate is running so a long-lived add-on cannot
# grow unchecked (#81). Echoes a one-line summary when anything was removed.
_cgateweb_prune_cgate_logs() {
local cgate_dir="$1"
local max_bytes="${2:-${CGATEWEB_LOG_MAX_BYTES}}"
Expand All @@ -400,7 +424,7 @@ _cgateweb_prune_cgate_logs() {
[[ -d "${cgate_dir}" ]] || return 0

while IFS= read -r -d '' file; do
_cgateweb_prune_cgate_logs_is_prunable "${cgate_dir}" "${file}" || continue
_cgateweb_prune_cgate_logs_is_in_scope "${cgate_dir}" "${file}" || continue
candidates+=("${file}")
done < <(find "${cgate_dir}" \( -path "${cgate_dir}/logs/*" -o -path "${cgate_dir}/log/*" \
-o -name 'event.*.log' -o -name 'event-*' \) -type f -print0 2>/dev/null)
Expand All @@ -411,6 +435,7 @@ _cgateweb_prune_cgate_logs() {

for file in "${candidates[@]}"; do
[[ -f "${file}" ]] || continue
_cgateweb_prune_cgate_logs_is_prunable "${cgate_dir}" "${file}" || continue
if find "${file}" -mtime +"${max_age_days}" -print -quit 2>/dev/null | grep -q .; then
age_bytes=$(_cgateweb_stat_size "${file}")
rm -f "${file}" && deleted_age=$((deleted_age + 1)) && reclaimed=$((reclaimed + age_bytes))
Expand All @@ -420,7 +445,7 @@ _cgateweb_prune_cgate_logs() {
# Rebuild the candidate list after age pruning.
candidates=()
while IFS= read -r -d '' file; do
_cgateweb_prune_cgate_logs_is_prunable "${cgate_dir}" "${file}" || continue
_cgateweb_prune_cgate_logs_is_in_scope "${cgate_dir}" "${file}" || continue
candidates+=("${file}")
done < <(find "${cgate_dir}" \( -path "${cgate_dir}/logs/*" -o -path "${cgate_dir}/log/*" \
-o -name 'event.*.log' -o -name 'event-*' \) -type f -print0 2>/dev/null)
Expand All @@ -446,6 +471,7 @@ _cgateweb_prune_cgate_logs() {
for i in "${!candidates[@]}"; do
file="${candidates[$i]}"
[[ -f "${file}" ]] || continue
_cgateweb_prune_cgate_logs_is_prunable "${cgate_dir}" "${file}" || continue
mtime=$(_cgateweb_stat_mtime "${file}")
if [[ ${mtime} -lt ${oldest_mtime} ]]; then
oldest_mtime=${mtime}
Expand Down Expand Up @@ -1128,6 +1154,21 @@ if [[ "${CGATE_MODE}" != "managed" ]]; then
exit 0
fi

# Keep the existing 500 MiB default but let managed-mode users choose a lower
# or higher ceiling. The Supervisor schema validates this as an integer; the
# fallback also keeps direct/test execution safe.
CGATE_LOG_MAX_MB=$(bashio::config 'cgate_log_max_mb' '500')
[[ "${CGATE_LOG_MAX_MB}" =~ ^[0-9]+$ ]] || CGATE_LOG_MAX_MB=500
CGATEWEB_LOG_MAX_BYTES=$((CGATE_LOG_MAX_MB * 1048576))

# Reserve at most half of the overall budget for C-Gate's native event-file
# rotation. At the default this preserves the historical 50 x 5 MiB setting;
# smaller user caps reduce the native segment count so the periodic pruner does
# not spend its time fighting a larger built-in retention target.
CGATEWEB_EVENT_FILE_SPLIT_COUNT=$((CGATEWEB_LOG_MAX_BYTES / CGATEWEB_EVENT_FILE_SPLIT_SIZE / 2))
[[ ${CGATEWEB_EVENT_FILE_SPLIT_COUNT} -lt 2 ]] && CGATEWEB_EVENT_FILE_SPLIT_COUNT=2
[[ ${CGATEWEB_EVENT_FILE_SPLIT_COUNT} -gt 50 ]] && CGATEWEB_EVENT_FILE_SPLIT_COUNT=50

# Overridable so tests can point the whole install flow at a temp dir instead
# of the real /data/cgate, the same test-seam pattern used by
# CGATEWEB_SERIAL_DEVICE_FILE above. Unset in production, so this always
Expand Down
24 changes: 24 additions & 0 deletions homeassistant-addon/rootfs/etc/services.d/cgate-log-pruner/run
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/usr/bin/with-contenv bashio
# Keep managed C-Gate's private file logs bounded between add-on restarts.
set -euo pipefail

CGATE_MODE=$(bashio::config 'cgate_mode' 'remote')
if [[ "${CGATE_MODE}" != "managed" ]]; then
exec sleep infinity
fi

CGATE_DIR="${CGATE_DIR:-/data/cgate}"
CGATEWEB_LOG_PRUNE_INTERVAL_SECONDS="${CGATEWEB_LOG_PRUNE_INTERVAL_SECONDS:-900}"
CGATE_LOG_MAX_MB=$(bashio::config 'cgate_log_max_mb' '500')
[[ "${CGATE_LOG_MAX_MB}" =~ ^[0-9]+$ ]] || CGATE_LOG_MAX_MB=500
CGATEWEB_LOG_MAX_BYTES=$((CGATE_LOG_MAX_MB * 1048576))

# Source the install script in library mode so startup and runtime retention
# use exactly the same path allowlist and deletion rules.
export CGATEWEB_INSTALL_SOURCE_ONLY=1
# shellcheck disable=SC1090
source "${CGATEWEB_INSTALL_SCRIPT:-/etc/cont-init.d/cgate-install.sh}"

while sleep "${CGATEWEB_LOG_PRUNE_INTERVAL_SECONDS}"; do
_cgateweb_prune_cgate_logs "${CGATE_DIR}" "${CGATEWEB_LOG_MAX_BYTES}"
done
79 changes: 79 additions & 0 deletions homeassistant-addon/translations/catalog.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -892,6 +892,85 @@ configuration:
(бази даних проєкту та конфігурація зберігаються); у режимі завантаження
можна натомість покласти новіший zip у /share/cgate. Вимкніть його після
оновлення.
cgate_log_max_mb:
name:
en: Maximum C-Gate Log Size
de: Maximale C-Gate-Protokollgröße
es: Tamaño máximo del registro de C-Gate
fr: Taille maximale des journaux C-Gate
it: Dimensione massima dei log C-Gate
nl: Maximale grootte van C-Gate-logboeken
pt: Tamanho máximo dos registos do C-Gate
ru: Максимальный размер журналов C-Gate
zh: C-Gate 日志最大大小
ja: C-Gate ログの最大サイズ
ko: 최대 C-Gate 로그 크기
pl: Maksymalny rozmiar dzienników C-Gate
sv: Maximal storlek för C-Gate-loggar
no: Maksimal størrelse på C-Gate-logger
da: Maksimal størrelse på C-Gate-logfiler
cs: Maximální velikost protokolů C-Gate
uk: Максимальний розмір журналів C-Gate
description:
en: >-
Maximum total size in MiB for managed C-Gate file logs. The oldest
closed log files are removed every 15 minutes. Default: 500.
de: >-
Maximale Gesamtgröße in MiB für Protokolldateien des verwalteten C-Gate.
Die ältesten geschlossenen Dateien werden alle 15 Minuten entfernt.
Standard: 500.
es: >-
Tamaño total máximo en MiB de los archivos de registro de C-Gate
gestionado. Los archivos cerrados más antiguos se eliminan cada 15
minutos. Predeterminado: 500.
fr: >-
Taille totale maximale en Mio des fichiers journaux du C-Gate géré. Les
plus anciens fichiers fermés sont supprimés toutes les 15 minutes. Par
défaut : 500.
it: >-
Dimensione totale massima in MiB dei file di log di C-Gate gestito. I
file chiusi meno recenti vengono rimossi ogni 15 minuti. Predefinito:
500.
nl: >-
Maximale totale grootte in MiB voor logbestanden van beheerde C-Gate. De
oudste gesloten bestanden worden elke 15 minuten verwijderd. Standaard:
500.
pt: >-
Tamanho total máximo em MiB dos ficheiros de registo do C-Gate gerido.
Os ficheiros fechados mais antigos são removidos a cada 15 minutos.
Predefinição: 500.
ru: >-
Максимальный общий размер файлов журналов управляемого C-Gate в МиБ.
Самые старые закрытые файлы удаляются каждые 15 минут. По умолчанию:
500.
zh: >-
托管 C-Gate 文件日志的最大总大小(MiB)。每 15 分钟删除最旧的已关闭日志文件。默认值:500。
ja: >-
マネージド C-Gate のログファイルの合計上限(MiB)です。閉じられた古いログファイルを 15
分ごとに削除します。既定値:500。
ko: >-
관리형 C-Gate 파일 로그의 최대 총크기(MiB)입니다. 닫힌 로그 파일 중 가장 오래된 파일을 15분마다
삭제합니다. 기본값: 500.
pl: >-
Maksymalny łączny rozmiar plików dziennika zarządzanego C-Gate w MiB.
Najstarsze zamknięte pliki są usuwane co 15 minut. Domyślnie: 500.
sv: >-
Maximal total storlek i MiB för hanterade C-Gate-loggfiler. De äldsta
stängda filerna tas bort var 15:e minut. Standard: 500.
no: >-
Maksimal samlet størrelse i MiB for administrerte C-Gate-loggfiler. De
eldste lukkede filene fjernes hvert 15. minutt. Standard: 500.
da: >-
Maksimal samlet størrelse i MiB for administrerede C-Gate-logfiler. De
ældste lukkede filer fjernes hvert 15. minut. Standard: 500.
cs: >-
Maximální celková velikost souborů protokolu spravovaného C-Gate v MiB.
Nejstarší zavřené soubory se odstraňují každých 15 minut. Výchozí
hodnota: 500.
uk: >-
Максимальний загальний розмір файлів журналів керованого C-Gate у МіБ.
Найстаріші закриті файли видаляються кожні 15 хвилин. Типове значення:
500.
cgate_serial_device:
name:
en: Serial PCI Device (Beta)
Expand Down
6 changes: 6 additions & 0 deletions homeassistant-addon/translations/cs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ configuration:
přeinstalovat/aktualizovat C-Gate ze zdroje instalace (databáze projektu a
konfigurace zůstanou zachovány); v režimu nahrávání můžete místo toho
umístit novější zip do /share/cgate. Po aktualizaci ji opět vypněte.
cgate_log_max_mb:
name: Maximální velikost protokolů C-Gate
description: >-
Maximální celková velikost souborů protokolu spravovaného C-Gate v MiB.
Nejstarší zavřené soubory se odstraňují každých 15 minut. Výchozí hodnota:
500.
cgate_serial_device:
name: Sériové PCI zařízení (beta)
description: >-
Expand Down
5 changes: 5 additions & 0 deletions homeassistant-addon/translations/da.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ configuration:
(dine projektdatabaser og konfiguration bevares); i upload-tilstand kan du
i stedet lægge en nyere zip i /share/cgate. Slå det fra igen efter
opgraderingen.
cgate_log_max_mb:
name: Maksimal størrelse på C-Gate-logfiler
description: >-
Maksimal samlet størrelse i MiB for administrerede C-Gate-logfiler. De
ældste lukkede filer fjernes hvert 15. minut. Standard: 500.
cgate_serial_device:
name: Seriel PCI-enhed (beta)
description: >-
Expand Down
6 changes: 6 additions & 0 deletions homeassistant-addon/translations/de.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ configuration:
Projekt-Datenbanken und Konfiguration bleiben erhalten); im Upload-Modus
können Sie stattdessen eine neuere zip in /share/cgate ablegen. Schalten
Sie dies nach dem Upgrade wieder aus.
cgate_log_max_mb:
name: Maximale C-Gate-Protokollgröße
description: >-
Maximale Gesamtgröße in MiB für Protokolldateien des verwalteten C-Gate.
Die ältesten geschlossenen Dateien werden alle 15 Minuten entfernt.
Standard: 500.
cgate_serial_device:
name: Serielles PCI-Gerät (Beta)
description: >-
Expand Down
5 changes: 5 additions & 0 deletions homeassistant-addon/translations/en.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ configuration:
the next start (your project DBs and config are preserved). In upload mode
you can instead just drop a newer zip into /share/cgate and it upgrades
automatically. Turn this back off after the upgrade.
cgate_log_max_mb:
name: Maximum C-Gate Log Size
description: >-
Maximum total size in MiB for managed C-Gate file logs. The oldest closed
log files are removed every 15 minutes. Default: 500.
cgate_serial_device:
name: Serial PCI Device (Beta)
description: >-
Expand Down
6 changes: 6 additions & 0 deletions homeassistant-addon/translations/es.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ configuration:
fuente de instalación en el próximo inicio (sus bases de datos de proyecto
y la configuración se conservan); en modo de carga puede colocar un zip
más reciente en /share/cgate. Desactívelo después de la actualización.
cgate_log_max_mb:
name: Tamaño máximo del registro de C-Gate
description: >-
Tamaño total máximo en MiB de los archivos de registro de C-Gate
gestionado. Los archivos cerrados más antiguos se eliminan cada 15
minutos. Predeterminado: 500.
cgate_serial_device:
name: Dispositivo PCI serie (beta)
description: >-
Expand Down
6 changes: 6 additions & 0 deletions homeassistant-addon/translations/fr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ configuration:
données de projet et la configuration sont conservées) ; en mode upload,
vous pouvez aussi déposer un zip plus récent dans /share/cgate.
Désactivez-la après la mise à jour.
cgate_log_max_mb:
name: Taille maximale des journaux C-Gate
description: >-
Taille totale maximale en Mio des fichiers journaux du C-Gate géré. Les
plus anciens fichiers fermés sont supprimés toutes les 15 minutes. Par
défaut : 500.
cgate_serial_device:
name: Périphérique PCI série (bêta)
description: >-
Expand Down
Loading
Loading