diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d0318670..01c5f87c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -50,6 +50,8 @@ jobs: run: LD_LIBRARY_PATH="$HOME/hostroot/lib" tests/test-power.sh "$HOME/hostroot/bin" - name: Printers (shareos-printer with a fake /sys, ipp-usb and p910nd) run: LD_LIBRARY_PATH="$HOME/hostroot/lib" tests/test-printer.sh "$HOME/hostroot/bin" + - name: UPS (shareos-ups with fake NUT programs) + run: LD_LIBRARY_PATH="$HOME/hostroot/lib" tests/test-ups.sh "$HOME/hostroot/bin" - name: Dynamic DNS (shareos-ddns, all providers with a fake curl) run: LD_LIBRARY_PATH="$HOME/hostroot/lib" tests/test-ddns.sh "$HOME/hostroot/bin" - name: Zabbix agent controller (settings, disk/RAID items) diff --git a/CHANGELOG.md b/CHANGELOG.md index b4583a20..536aca64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,11 @@ pre-releases for test devices only. - *Current printers and multifunction devices* that speak IPP over USB (most since about 2015, e.g. HP Smart Tank, OfficeJet, Canon PIXMA, Epson EcoTank) print **without a driver**: Windows 10/11, macOS, iPhone/iPad and Android find them by themselves (IPP Everywhere, AirPrint, Mopria) – and their **scanner** works over the network as well (eSCL). ipp-usb (OpenPrinting) does the work; ShareOS's avahi-daemon has no D-Bus, so a patch makes ipp-usb announce the devices through static avahi service files. The port of each device stays the same (kept on the flash). - *Older printers* (usblp, `/dev/usb/lpN`): p910nd on port 9100 + N, announced as `_pdl-datastream`; the computer uses the maker's driver ("Standard TCP/IP port" in Windows, AppSocket/JetDirect). *Testseite* sends a plain-text/PCL page. - Printers are followed when plugged in or pulled (mdev rule for `lp*`, ipp-usb watches USB itself). Mode *Immer Port 9100* for printers whose driverless printing misbehaves. `shareos-printer`, rpcd `shareos.printer`, `tests/test-printer.sh` (14 checks with a fake /sys). +- **UPS over USB** (Dienste → USV, new system app **"nut"** in the store: Network UPS Tools 2.8.5 with the drivers usbhid-ups, nutdrv_qx and blazer_usb – APC, Eaton, CyberPower, most Megatec/Q1 boxes; 0.7 MB, kept on the flash so it works when a disk fails). + - Charge, runtime, load and model of the UPS; a power failure, the power coming back, a low or worn battery and a lost connection become messages (e-mail/push as set up) and are listed under *Ereignisse*. + - The box shuts down cleanly while on battery – when the UPS reports a low battery, below a charge of your choice or after some minutes on battery – and only then; if the power comes back first, nothing happens. Afterwards the UPS switches its output off and on again when the power is back, so the box starts by itself (can be switched off). + - Other computers can read the UPS over the network (NUT, port 3493, user `monitor` with a password) and shut down before the box. + - NUT without OpenSSL (`external.mk`: 4.4 MB of libraries less). `shareos-ups`, rpcd `shareos.ups`, the last step of the shutdown in `S05board`, `tests/test-ups.sh` (20 checks with fake NUT programs). - **DynDNS, own certificate authority and Let's Encrypt** (System → Netzwerk). - *DynDNS*: DuckDNS, dynv6, Cloudflare, No-IP or any dyndns2 address (Strato, IONOS, …), IPv4 and/or IPv6 (for lines without their own IPv4 address: DS-Lite, fibre, mobile). The box reports its public address every 5 minutes when it changed, otherwise once a day; a failure becomes a message. Token and password go to curl through stdin, not the command line, and are never sent back to the browser. `shareos-ddns`, `tests/test-ddns.sh` (24 checks with a fake curl). - *Own CA*: a certificate authority on the box (EC P-256, 10 years) issues certificates for the box and other devices in the home network (825 days, server authentication, wildcards like `*.home.lan`, IP addresses); its root certificate is installed once on PCs and phones, after that there are no certificate warnings. The box's own certificate follows name and IP address and is renewed 30 days before it runs out. The CA is part of the settings backup. `shareos-ca` (mbedtls of the firmware), `tests/test-ca.sh` (18 checks, in CI with the firmware's programs in qemu). diff --git a/CLAUDE.md b/CLAUDE.md index 52910308..d72a1784 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -6,7 +6,7 @@ - Tags/releases are created by Jan; prepare the release text and the `CHANGELOG.md` section. - **Every release updates the docs** – see `docs/RELEASE.md` (CHANGELOG, README status/features, TESTEN/TESTING, ENTWICKLUNG/DEVELOPMENT). - Photos for the docs: strip EXIF/GPS and blur serial-number stickers before committing. -- Tests: `tests/` (host: test-update, test-net, test-flash-bin, test-boards, test-raid-wait, test-smart, test-bootlog, test-power, test-notify, test-zabbix-items, test-printer, test-ddns, test-ca (mbedtls/uci of the firmware through qemu-arm-static, see build.yml), check-acl.py, check-i18n.py; ARM emulator with sudo: run-stack-qemu --check, test-users, test-storage, test-cloud-paperless, test-apps, test-firstinstall, test-transmission, test-media, test-dlna, test-vpn, test-tailscale, test-autobrr, test-zabbix – these seven need `make sysapps-pack`, test-snapshots, test-sysperms). UI work: `tests/mock-ubus.py`; app SDK in Chromium: `node tests/sdk-storage.js` (Playwright). ucode plugin: `tests/check-ucode-order.py` (no hoisting in ucode). +- Tests: `tests/` (host: test-update, test-net, test-flash-bin, test-boards, test-raid-wait, test-smart, test-bootlog, test-power, test-notify, test-zabbix-items, test-printer, test-ups, test-ddns, test-ca (mbedtls/uci of the firmware through qemu-arm-static, see build.yml), check-acl.py, check-i18n.py; ARM emulator with sudo: run-stack-qemu --check, test-users, test-storage, test-cloud-paperless, test-apps, test-firstinstall, test-transmission, test-media, test-dlna, test-vpn, test-tailscale, test-autobrr, test-zabbix – these seven need `make sysapps-pack`, test-snapshots, test-sysperms). UI work: `tests/mock-ubus.py`; app SDK in Chromium: `node tests/sdk-storage.js` (Playwright). ucode plugin: `tests/check-ucode-order.py` (no hoisting in ucode). - Never `rm -rf` a chroot that still has mounts below it (it would delete the host's /dev). - Apps live in the separate repository UniqueDroid/ShareOS-Apps. - New UI texts: add them to `i18n/en.json` and to every `tools/i18n/.tsv` (new numbers at the end), then `tools/i18n/build-lang.py`; `tests/check-i18n.py` must pass. diff --git a/README.md b/README.md index 1f2d7cea..38a311a9 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,7 @@ Version in brackets = first release with the feature. **Shares, users, files** - USB printers in the network: current printers without a driver (IPP over USB: AirPrint, Mopria, Windows), scanning over the network (eSCL), older printers over port 9100 (system app "printer", 0.2.0-beta.18) +- UPS over USB: charge and runtime, messages on a power failure, clean shutdown on battery (low battery, a percentage or minutes), other computers over the network (system app "nut", 0.2.0-beta.18) - SMB 2/3 (ksmbd) and NFS v4 shares with read/write permissions per user and group, guest access optional; the crypto engine of the SoC computes the SMB 3 signatures (about three times as fast, 0.2.0-beta.17); compatibility mode up to SMB 2.1 for difficult clients (0.2.0-beta.17) - Users and groups; one account for shares, web interface and apps; storage limit per user (ext4); import of Alt-F users with their passwords - "My ShareOS": users with web access manage their own password, look, shares, storage, cloud and Paperless diff --git a/board/dlink/dns320l/rootfs-overlay/etc/init.d/S05board b/board/dlink/dns320l/rootfs-overlay/etc/init.d/S05board index fbf7b715..985cdf82 100755 --- a/board/dlink/dns320l/rootfs-overlay/etc/init.d/S05board +++ b/board/dlink/dns320l/rootfs-overlay/etc/init.d/S05board @@ -94,6 +94,9 @@ stop() { rm -f "$BUTTONDDPID" start-stop-daemon -K -q -p "$PIDFILE" 2>/dev/null sleep 1 + # shut down on UPS battery: the UPS switches its output off now and on + # again when the power is back – and the box starts with it + [ -f /tmp/.poweroff ] && [ -x /usr/sbin/shareos-ups ] && /usr/sbin/shareos-ups killpower if [ -f /tmp/.poweroff ] && [ "$BOARD_CONTROL" = mcu ]; then # ask the MCU to cut power 3 seconds from now (other boxes: gpio-poweroff in the kernel) sync diff --git a/configs/shareos_sysapps_defconfig b/configs/shareos_sysapps_defconfig index c46d6ed8..8fdeafd6 100644 --- a/configs/shareos_sysapps_defconfig +++ b/configs/shareos_sysapps_defconfig @@ -54,6 +54,11 @@ BR2_PACKAGE_SHAREOS_ZABBIX_AGENT=y # package/shareos-ipp-usb BR2_PACKAGE_SHAREOS_IPP_USB=y BR2_PACKAGE_P910ND=y +# UPS (system app "nut"): Network UPS Tools with the USB drivers of most +# UPSs (usbhid-ups: APC, Eaton, CyberPower …; nutdrv_qx/blazer_usb: Megatec) +BR2_PACKAGE_LIBUSB=y +BR2_PACKAGE_NUT=y +BR2_PACKAGE_NUT_DRIVERS="usbhid-ups,nutdrv_qx,blazer_usb" BR2_PACKAGE_HTOP=y BR2_PACKAGE_STRACE=y BR2_PACKAGE_IPERF3=y diff --git a/external.mk b/external.mk index e8f6651f..17472f75 100644 --- a/external.mk +++ b/external.mk @@ -20,3 +20,8 @@ LIBEVENT_CONF_OPTS += --disable-openssl # (post-build.sh); rsync only uses them for faster checksums RSYNC_CONF_OPTS += --disable-openssl RSYNC_DEPENDENCIES := $(filter-out openssl,$(RSYNC_DEPENDENCIES)) + +# Network UPS Tools (system app "nut") without OpenSSL: upsd only talks to +# upsmon on the box and plain NUT clients – 4.4 MB of libraries less +NUT_CONF_OPTS += --without-ssl +NUT_DEPENDENCIES := $(filter-out openssl,$(NUT_DEPENDENCIES)) diff --git a/package/shareos-core/shareos-core.mk b/package/shareos-core/shareos-core.mk index 62659e9d..ade24f1b 100644 --- a/package/shareos-core/shareos-core.mk +++ b/package/shareos-core/shareos-core.mk @@ -78,6 +78,7 @@ define SHAREOS_CORE_INSTALL_TARGET_CMDS $(INSTALL) -D -m 0755 $(@D)/usr/sbin/shareos-dlna $(TARGET_DIR)/usr/sbin/shareos-dlna $(INSTALL) -D -m 0644 $(@D)/etc/config/dlna $(TARGET_DIR)/etc/config/dlna $(INSTALL) -D -m 0644 $(@D)/etc/config/printer $(TARGET_DIR)/etc/config/printer + $(INSTALL) -D -m 0644 $(@D)/etc/config/ups $(TARGET_DIR)/etc/config/ups $(INSTALL) -D -m 0755 $(@D)/usr/sbin/shareos-zabbix $(TARGET_DIR)/usr/sbin/shareos-zabbix $(INSTALL) -D -m 0644 $(@D)/etc/config/zabbix $(TARGET_DIR)/etc/config/zabbix $(INSTALL) -D -m 0755 $(@D)/usr/sbin/shareos-reset-access $(TARGET_DIR)/usr/sbin/shareos-reset-access @@ -91,6 +92,7 @@ define SHAREOS_CORE_INSTALL_TARGET_CMDS $(INSTALL) -D -m 0755 $(@D)/usr/sbin/shareos-bootlog $(TARGET_DIR)/usr/sbin/shareos-bootlog $(INSTALL) -D -m 0755 $(@D)/usr/sbin/shareos-power $(TARGET_DIR)/usr/sbin/shareos-power $(INSTALL) -D -m 0755 $(@D)/usr/sbin/shareos-printer $(TARGET_DIR)/usr/sbin/shareos-printer + $(INSTALL) -D -m 0755 $(@D)/usr/sbin/shareos-ups $(TARGET_DIR)/usr/sbin/shareos-ups $(INSTALL) -D -m 0755 $(@D)/usr/sbin/shareos-ddns $(TARGET_DIR)/usr/sbin/shareos-ddns $(INSTALL) -D -m 0755 $(@D)/usr/sbin/shareos-ca $(TARGET_DIR)/usr/sbin/shareos-ca $(INSTALL) -D -m 0755 $(@D)/usr/sbin/shareos-acme $(TARGET_DIR)/usr/sbin/shareos-acme diff --git a/src/shareos-core/etc/config/ups b/src/shareos-core/etc/config/ups new file mode 100644 index 00000000..05a625fc --- /dev/null +++ b/src/shareos-core/etc/config/ups @@ -0,0 +1,17 @@ +# UPS over USB (system app "nut": Network UPS Tools) – managed by +# shareos-ups (web interface: Services -> UPS). +# +# option shutdown 'low' # low | percent | minutes +# option percent '30' # shutdown percent: below this charge +# option minutes '5' # shutdown minutes: after this long on battery +# option killpower '1' # switch the UPS output off afterwards +# option server '0' # other computers may read it (user monitor) + +config ups 'main' + option enabled '0' + option driver 'auto' + option shutdown 'low' + option percent '30' + option minutes '5' + option killpower '1' + option server '0' diff --git a/src/shareos-core/usr/sbin/shareos-ups b/src/shareos-core/usr/sbin/shareos-ups new file mode 100755 index 00000000..f6663000 --- /dev/null +++ b/src/shareos-core/usr/sbin/shareos-ups @@ -0,0 +1,233 @@ +#!/bin/sh +# +# shareos-ups – uninterruptible power supply over USB (system app "nut") +# +# shareos-ups start | stop | restart +# shareos-ups status key=value lines for the web interface +# shareos-ups boot start at boot when it is switched on +# shareos-ups stop-all stop (system app removed, shutdown) +# shareos-ups event upsmon's NOTIFYCMD (NOTIFYTYPE in the environment) +# shareos-ups timer (internal) minutes on battery are over +# shareos-ups shutdown upsmon's SHUTDOWNCMD: power the box off +# shareos-ups killpower (S05board, last step of a shutdown on battery) +# tell the UPS to switch its output off; it +# switches on again when the power is back, +# and so does the box +# +# shareos-service: boot start stop-all +# +# Settings: UCI ups.main – enabled, driver auto|usbhid-ups|nutdrv_qx| +# blazer_usb, shutdown low|percent|minutes (when the box shuts down while on +# battery: when the UPS reports a low battery / below % / after +# on battery), percent, minutes, killpower '1' (switch the UPS +# output off afterwards, default on), server '1' + password (other computers +# may read the UPS as "monitor" on port 3493 and shut down with the box). +# Network UPS Tools run as root with their files in /tmp/shareos-ups +# (NUT_CONFPATH/NUT_STATEPATH); messages go to shareos-notify. +# SHAREOS_ROOT= fake root (tests) SHAREOS_UPS_BIN= the programs (tests) +# +[ -n "$SHAREOS_ROOT" ] || PATH=/usr/sbin:/usr/bin:/sbin:/bin:/opt/shareos/bin +R=${SHAREOS_ROOT:-} +BIN=${SHAREOS_UPS_BIN:-/opt/shareos/bin} +RUN=$R/tmp/shareos-ups +export NUT_CONFPATH=$RUN/conf NUT_STATEPATH=$RUN/state NUT_ALTPIDPATH=$RUN/state + +die() { echo "error: $*"; exit 1; } +uget() { if [ -n "$SHAREOS_ROOT" ]; then uci -q -c "$R/etc/config" get "$1"; else uci -q get "$1"; fi; } +enabled() { [ "$(uget ups.main.enabled)" = 1 ]; } +installed() { [ -x "$BIN/upsd" ] && [ -x "$BIN/upsmon" ] && [ -x "$BIN/usbhid-ups" ]; } +num() { echo "$1" | grep -qE '^[0-9]+$' && [ "$1" -ge "$2" ] && [ "$1" -le "$3" ] && echo "$1" || echo "$4"; } +notify() { # [ ] + [ -n "$SHAREOS_ROOT" ] && { echo "notify $*" >> $RUN/notify.log; return; } + /usr/sbin/shareos-notify send "$1" ups "$2" "$3" "$4" "${5:-}" "${6:-}" >/dev/null 2>&1 +} +clear_note() { [ -n "$SHAREOS_ROOT" ] && { echo "clear $1" >> $RUN/notify.log; return; }; /usr/sbin/shareos-notify clear "$1" >/dev/null 2>&1; } + +# the USB UPS: of the first one known to usbhid-ups (HID +# power device) or another USB device the driver list names +detect() { + for d in ${SHAREOS_UPS_SYS:-/sys}/bus/usb/devices/*; do + [ -f "$d/idVendor" ] || continue + v=$(cat "$d/idVendor"); p=$(cat "$d/idProduct") + case "$v" in + 051d|0463|0764|09ae|050d|0d9f|06da|2b2d|0665|ffff|0001|0925|10af|047c|1cb0|04d8|2e51|3016) echo "$v:$p"; return ;; + esac + # HID power devices of other makers: interface class 3 with "UPS" in the name + grep -qi 'ups\|usv\|power' "$d/product" 2>/dev/null && ls "$d"/*:*/bInterfaceClass >/dev/null 2>&1 \ + && grep -q 03 "$d"/*:*/bInterfaceClass 2>/dev/null && { echo "$v:$p"; return; } + done +} + +driver() { + d=$(uget ups.main.driver) + case "$d" in usbhid-ups|nutdrv_qx|blazer_usb) echo "$d"; return ;; esac + # auto: Megatec/Q1 boxes (cheap UPSs) use nutdrv_qx, everything else HID + case "$(detect)" in 0665:5161|ffff:0000|0001:0000|0925:1234|06da:0003) echo nutdrv_qx ;; *) echo usbhid-ups ;; esac +} + +secret() { + [ -s $RUN/secret ] || { mkdir -p $RUN; head -c 12 /dev/urandom | od -An -tx1 | tr -d ' \n' > $RUN/secret; chmod 600 $RUN/secret; } + cat $RUN/secret +} + +write_conf() { + mkdir -p $RUN/conf $RUN/state + chmod 700 $RUN/conf $RUN/state + mode=$(uget ups.main.shutdown) + pct=$(num "$(uget ups.main.percent)" 10 90 30) + pw=$(secret) + { + echo "# written by shareos-ups at every start" + echo "maxretry = 3" + echo "user = root" + echo "[ups]" + echo " driver = $(driver)" + echo " port = auto" + echo " desc = \"ShareOS\"" + echo " pollinterval = 5" + echo " offdelay = 30" + echo " ondelay = 40" + if [ "$mode" = percent ]; then + # the driver reports a low battery itself below that charge + echo " ignorelb" + echo " override.battery.charge.low = $pct" + fi + } > $RUN/conf/ups.conf + { + echo "LISTEN 127.0.0.1 3493" + if [ "$(uget ups.main.server)" = 1 ]; then echo "LISTEN 0.0.0.0 3493"; echo "LISTEN :: 3493"; fi + } > $RUN/conf/upsd.conf + { + printf '[shareos]\n password = %s\n upsmon primary\n' "$pw" + mp=$(uget ups.main.password) + [ "$(uget ups.main.server)" = 1 ] && [ -n "$mp" ] && printf '[monitor]\n password = %s\n upsmon secondary\n' "$mp" + } > $RUN/conf/upsd.users + { + echo "RUN_AS_USER root" + echo "MONITOR ups@localhost 1 shareos $pw primary" + echo "MINSUPPLIES 1" + echo "SHUTDOWNCMD \"/usr/sbin/shareos-ups shutdown\"" + echo "NOTIFYCMD /usr/sbin/shareos-ups" + for e in ONLINE ONBATT LOWBATT FSD COMMOK COMMBAD SHUTDOWN REPLBATT NOCOMM; do echo "NOTIFYFLAG $e SYSLOG+EXEC"; done + echo "POLLFREQ 5" + echo "POLLFREQALERT 5" + echo "HOSTSYNC 15" + echo "DEADTIME 25" + echo "FINALDELAY 5" + [ "$(uget ups.main.killpower)" = 0 ] || echo "POWERDOWNFLAG $RUN/killpower" + } > $RUN/conf/upsmon.conf + chmod 600 $RUN/conf/* +} + +# upsd and upsmon keep their pid in /run (as root), the drivers in the state folder +pid() { for f in "$R/run/$1.pid" "$R/var/run/$1.pid" "$RUN/state/$1.pid"; do p=$(cat "$f" 2>/dev/null); [ -n "$p" ] && kill -0 "$p" 2>/dev/null && { echo "$p"; return; }; done; } +drv_pid() { for f in $RUN/state/*-ups.pid; do p=$(cat "$f" 2>/dev/null); [ -n "$p" ] && kill -0 "$p" 2>/dev/null && { echo "$p"; return; }; done; } + +start() { + installed || die "the system app \"nut\" is not installed (store)" + [ -n "$(pid upsmon)" ] && return 0 + write_conf + rm -f $RUN/killpower + d=$(driver) + "$BIN/$d" -a ups -u root >$RUN/driver.log 2>&1 || { + [ "$(uget ups.main.driver)" = auto ] || [ -z "$(uget ups.main.driver)" ] || die "no UPS found ($d): $(tail -n1 $RUN/driver.log)" + # auto: the other driver family + alt=nutdrv_qx; [ "$d" = nutdrv_qx ] && alt=usbhid-ups + sed -i "s/^ driver = .*/ driver = $alt/" $RUN/conf/ups.conf + "$BIN/$alt" -a ups -u root >$RUN/driver.log 2>&1 || die "no UPS found: $(tail -n1 $RUN/driver.log)" + } + "$BIN/upsd" -u root >$RUN/upsd.log 2>&1 || die "upsd did not start: $(tail -n1 $RUN/upsd.log)" + "$BIN/upsmon" >$RUN/upsmon.log 2>&1 || die "upsmon did not start: $(tail -n1 $RUN/upsmon.log)" +} + +stop() { + timer_stop + "$BIN/upsmon" -c stop >/dev/null 2>&1 + "$BIN/upsd" -c stop >/dev/null 2>&1 + p=$(drv_pid); [ -n "$p" ] && kill "$p" 2>/dev/null + i=0; while [ $i -lt 25 ] && { [ -n "$(pid upsmon)" ] || [ -n "$(pid upsd)" ] || [ -n "$(drv_pid)" ]; }; do sleep 0.2; i=$((i + 1)); done + return 0 +} + +timer_stop() { p=$(cat $RUN/timer.pid 2>/dev/null); [ -n "$p" ] && kill "$p" 2>/dev/null; rm -f $RUN/timer.pid; } + +# upsmon calls us with the message as argument, NOTIFYTYPE says what happened +event() { + mkdir -p $RUN + echo "$(date +%s) $NOTIFYTYPE" >> $RUN/events + tail -n 50 $RUN/events > $RUN/events.tmp && mv $RUN/events.tmp $RUN/events + case "$NOTIFYTYPE" in + ONBATT) + m=$(uget ups.main.minutes) + notify warn ups-onbatt "Stromausfall – die Box läuft über die USV" "Power failure – the box runs on the UPS" \ + "$( [ "$(uget ups.main.shutdown)" = minutes ] && echo "Sie fährt nach $(num "$m" 1 120 5) Minuten herunter, wenn der Strom nicht zurückkommt." )" \ + "$( [ "$(uget ups.main.shutdown)" = minutes ] && echo "It shuts down after $(num "$m" 1 120 5) minutes unless the power comes back." )" + if [ "$(uget ups.main.shutdown)" = minutes ]; then + timer_stop + ( sleep $(( $(num "$m" 1 120 5) * 60 )); "$0" timer ) >/dev/null 2>&1 & + echo $! > $RUN/timer.pid + fi ;; + ONLINE) + timer_stop + clear_note ups-onbatt + notify info ups-online "Strom wieder da" "Power is back" ;; + LOWBATT) notify error ups-lowbatt "USV-Akku fast leer – die Box fährt herunter" "UPS battery low – the box shuts down" ;; + FSD|SHUTDOWN) notify error ups-shutdown "Die Box fährt wegen der USV herunter" "The box shuts down because of the UPS" ;; + COMMBAD|NOCOMM) notify warn ups-comm "Keine Verbindung zur USV" "No connection to the UPS" "USB-Kabel prüfen." "Check the USB cable." ;; + COMMOK) clear_note ups-comm ;; + REPLBATT) notify warn ups-replbatt "USV-Akku tauschen" "Replace the UPS battery" "Die USV meldet einen verbrauchten Akku." "The UPS reports a worn battery." ;; + esac +} + +# minutes on battery are over: still on battery? then shut down (all secondaries too) +timer() { + rm -f $RUN/timer.pid + "$BIN/upsc" ups@localhost ups.status 2>/dev/null | grep -qw OB || return 0 + "$BIN/upsmon" -c fsd >/dev/null 2>&1 +} + +shutdown_box() { + logger -t shareos-ups "shutting down on UPS battery" 2>/dev/null + [ -n "$SHAREOS_ROOT" ] && { echo poweroff >> $RUN/notify.log; return; } + # the next switch-on of the power schedule into the wake-up alarm first + /usr/sbin/shareos-power arm >/dev/null 2>&1 + poweroff +} + +killpower() { + [ -f $RUN/killpower ] || return 0 + d=$(sed -n 's/^ driver = //p' $RUN/conf/ups.conf) + [ -n "$d" ] && [ -x "$BIN/$d" ] || return 0 + echo "UPS: output off in 30 s, on again when the power is back" + "$BIN/$d" -a ups -u root -k >/dev/null 2>&1 +} + +status() { + echo "installed=$(installed && echo 1 || echo 0)" + echo "enabled=$(enabled && echo 1 || echo 0)" + echo "running=$([ -n "$(pid upsmon)" ] && echo 1 || echo 0)" + echo "detected=$(detect)" + echo "driver=$(driver)" + [ -n "$(pid upsd)" ] || return 0 + "$BIN/upsc" ups@localhost 2>/dev/null | sed -n \ + -e 's/^ups\.status: /status=/p' -e 's/^battery\.charge: /charge=/p' -e 's/^battery\.runtime: /runtime=/p' \ + -e 's/^ups\.load: /load=/p' -e 's/^device\.mfr: /mfr=/p' -e 's/^device\.model: /model=/p' \ + -e 's/^input\.voltage: /voltage=/p' -e 's/^battery\.charge\.low: /charge_low=/p' -e 's/^battery\.date: /battery_date=/p' + tail -n 10 $RUN/events 2>/dev/null | sed 's/^/event=/' +} + +case "$1" in + start) enabled || die "switched off"; start ;; + boot) enabled && installed && start; exit 0 ;; + stop|stop-all) stop ;; + restart) stop; enabled && start ;; + status) status ;; + event) event ;; + timer) timer ;; + shutdown) shutdown_box ;; + killpower) killpower ;; + *) # upsmon's NOTIFYCMD passes the message text as first argument + if [ -n "$NOTIFYTYPE" ]; then event; exit 0; fi + sed -n '3,30s/^# \{0,1\}//p' "$0"; exit 2 ;; +esac +exit 0 diff --git a/src/shareos-core/usr/share/rpcd/acl.d/shareos.json b/src/shareos-core/usr/share/rpcd/acl.d/shareos.json index 4fa43195..bd486297 100644 --- a/src/shareos-core/usr/share/rpcd/acl.d/shareos.json +++ b/src/shareos-core/usr/share/rpcd/acl.d/shareos.json @@ -110,6 +110,9 @@ ], "shareos.printer": [ "status" + ], + "shareos.ups": [ + "status" ] }, "uci": [ @@ -306,6 +309,9 @@ "shareos.printer": [ "set", "test" + ], + "shareos.ups": [ + "set" ] }, "uci": [ diff --git a/src/shareos-core/usr/share/rpcd/ucode/shareos.uc b/src/shareos-core/usr/share/rpcd/ucode/shareos.uc index fc7ca3b4..73e43084 100644 --- a/src/shareos-core/usr/share/rpcd/ucode/shareos.uc +++ b/src/shareos-core/usr/share/rpcd/ucode/shareos.uc @@ -849,7 +849,7 @@ function default_password() { /* ---------- settings backup ---------- */ const CRYPT = '/usr/sbin/shareos-crypt'; -const BACKUP_CONFIGS = [ 'shareos', 'storage', 'network', 'accounts', 'shares', 'paperless', 'cloud', 'apps', 'snapshots', 'dlna', 'dockerhosts', 'zabbix', 'printer' ]; +const BACKUP_CONFIGS = [ 'shareos', 'storage', 'network', 'accounts', 'shares', 'paperless', 'cloud', 'apps', 'snapshots', 'dlna', 'dockerhosts', 'zabbix', 'printer', 'ups' ]; const BACKUP_FILES = [ '/etc/ksmbd/ksmbdpwd.db', '/etc/ssl/shareos/server.crt', '/etc/ssl/shareos/server.key', '/etc/hostname', '/etc/timezone', '/etc/shareos/zabbix.psk', '/etc/shareos/ca/ca.crt', '/etc/shareos/ca/ca.key' ]; @@ -1194,6 +1194,27 @@ function printer_status() { hostname: trim(readfile('/proc/sys/kernel/hostname') ?? '') }; } +/* ---------- UPS (system app "nut", shareos-ups) ---------- */ + +const UPS = '/usr/sbin/shareos-ups'; +function ups_status() { + let st = {}, events = []; + for (let l in split(sh(UPS + ' status').out, '\n')) { + let m = match(l, /^([a-z_]+)=(.*)$/); + if (!m) continue; + if (m[1] == 'event') { let f = split(m[2], ' '); push(events, { time: +f[0], type: f[1] }); } + else st[m[1]] = m[2]; + } + let c = cursor(), g = (k, d) => c.get('ups', 'main', k) ?? d; + return { installed: st.installed == '1', enabled: st.enabled == '1', running: st.running == '1', detected: st.detected || null, + driver: st.driver ?? null, status: st.status ?? null, charge: st.charge != null ? +st.charge : null, + runtime: st.runtime != null ? +st.runtime : null, load: st.load != null ? +st.load : null, voltage: st.voltage != null ? +st.voltage : null, + mfr: st.mfr ?? null, model: st.model ?? null, charge_low: st.charge_low != null ? +st.charge_low : null, battery_date: st.battery_date ?? null, + events: reverse(events), + settings: { driver: g('driver', 'auto'), shutdown: g('shutdown', 'low'), percent: +g('percent', '30'), minutes: +g('minutes', '5'), + killpower: g('killpower', '1') != '0', server: g('server', '0') == '1', password_set: !!g('password', '') } }; +} + /* ---------- snapshots (rsync with hard links, shareos-snapshot) ---------- */ const SNAP = '/usr/sbin/shareos-snapshot'; @@ -3881,6 +3902,38 @@ return { } }, + 'shareos.ups': { + status: { call: function() { return ups_status(); } }, + + set: { + args: { enabled: false, driver: '', shutdown: '', percent: 0, minutes: 0, killpower: true, server: false, password: '' }, + call: function(req) { + let a = req.args, c = cursor(); + if (a.driver && !(a.driver in { auto: 1, 'usbhid-ups': 1, nutdrv_qx: 1, blazer_usb: 1 })) return { error: 'invalid driver' }; + if (a.shutdown && !(a.shutdown in { low: 1, percent: 1, minutes: 1 })) return { error: 'invalid shutdown' }; + if (a.percent != null && a.percent != 0 && (a.percent < 10 || a.percent > 90)) return { error: 'percent: 10 … 90' }; + if (a.minutes != null && a.minutes != 0 && (a.minutes < 1 || a.minutes > 120)) return { error: 'minutes: 1 … 120' }; + let pw = a.password ?? ''; + if (pw != '' && !match(pw, /^[A-Za-z0-9._-]{6,64}$/)) return { error: 'password: 6 … 64 letters, digits, . _ -' }; + c.set('ups', 'main', 'ups'); + c.set('ups', 'main', 'enabled', a.enabled === true ? '1' : '0'); + if (a.driver) c.set('ups', 'main', 'driver', a.driver); + if (a.shutdown) c.set('ups', 'main', 'shutdown', a.shutdown); + if (a.percent) c.set('ups', 'main', 'percent', '' + a.percent); + if (a.minutes) c.set('ups', 'main', 'minutes', '' + a.minutes); + if (a.killpower != null) c.set('ups', 'main', 'killpower', a.killpower ? '1' : '0'); + if (a.server != null) c.set('ups', 'main', 'server', a.server ? '1' : '0'); + if (pw != '') c.set('ups', 'main', 'password', pw); + if (a.server && !c.get('ups', 'main', 'password')) return { error: 'the network needs a password' }; + c.commit('ups'); + let r = sh(UPS + (a.enabled === true ? ' restart' : ' stop')); + let m = match(r.out, /error: ([^\n]*)/); + if (m || r.rc != 0) return { error: m ? m[1] : (trim(r.out) || 'failed') }; + return ups_status(); + } + } + }, + // chips in the header of the interface: services and apps of the account 'shareos.status': { chips: { diff --git a/src/shareos-core/www/app.js b/src/shareos-core/www/app.js index 24b8c4cd..97c61246 100644 --- a/src/shareos-core/www/app.js +++ b/src/shareos-core/www/app.js @@ -156,7 +156,7 @@ const TABS = [ ['apps', 'APPS'], ['cloud', 'CLOUD'], ['paperless', 'PAPERLESS'], ['system', 'SYSTEM'] ]; const SYS_SUBS = [['general', 'ALLGEMEIN'], ['appearance', 'DARSTELLUNG'], ['network', 'NETZWERK'], ['notifications', 'MELDUNGEN'], ['logs', 'PROTOKOLLE'], ['terminal', 'TERMINAL'], ['time', 'ZEIT'], ['power', 'ENERGIE & RESET'], ['firmware', 'FIRMWARE']]; -const SVC_SUBS = [['status', 'STATUS'], ['autostart', 'AUTOSTART'], ['dlna', 'MEDIENSERVER'], ['printer', 'DRUCKER']]; +const SVC_SUBS = [['status', 'STATUS'], ['autostart', 'AUTOSTART'], ['dlna', 'MEDIENSERVER'], ['printer', 'DRUCKER'], ['ups', 'USV']]; const SHR_SUBS = [['shares', 'FREIGABEN'], ['files', 'DATEIEN'], ['users', 'BENUTZER'], ['groups', 'GRUPPEN']]; const STO_SUBS = [['volumes', 'LAUFWERKE'], ['health', 'GESUNDHEIT'], ['snapshots', 'SNAPSHOTS']]; const ROLE_TXT = { admin: 'Administrator', user: 'Benutzer', share: 'Nur Freigaben' }; @@ -819,7 +819,7 @@ const VIEWS = { const sub = S.route.sub; return html` - ${sub === 'dlna' ? dlnaView() : sub === 'printer' ? printerView() : sub === 'autostart' ? autostartView() + ${sub === 'dlna' ? dlnaView() : sub === 'printer' ? printerView() : sub === 'ups' ? upsView() : sub === 'autostart' ? autostartView() : html`
${secHead('services', 'DIENSTE', 'Start, Stopp, Neustart')}${serviceCards(true, isTable('services'))}
`}`; }, @@ -1653,6 +1653,93 @@ function printerAfter() { }); } +/* ---------- UPS (Services -> UPS, system app "nut") ------------------- */ + +const UPS_EVENTS = { ONBATT: 'Stromausfall – läuft über die USV', ONLINE: 'Strom wieder da', LOWBATT: 'Akku fast leer', FSD: 'Herunterfahren ausgelöst', SHUTDOWN: 'Box fährt herunter', COMMBAD: 'Verbindung zur USV verloren', NOCOMM: 'Keine Verbindung zur USV', COMMOK: 'Verbindung zur USV wieder da', REPLBATT: 'Akku tauschen' }; + +async function loadUps() { + clearTimeout(S.upsTimer); + try { S.ups = await rpc('shareos.ups', 'status'); } catch (e) { toast('USV: ' + e.message, true); } + if (S.route.tab === 'services' && S.route.sub === 'ups') { + if (!document.activeElement?.matches('input,select')) renderMain(); + S.upsTimer = setTimeout(loadUps, S.ups?.running ? 5000 : 15000); + } +} + +function upsView() { + const d = S.ups; + if (!d) return html`
Lade USV …
`; + if (!d.installed) return html`
${svg(I.info)}
+ Die USV-Unterstützung ist eine System-App: Die Box zeigt dann Akkustand und Restlaufzeit, meldet einen Stromausfall und fährt rechtzeitig sauber herunter. +
`; + const st = (d.status || '').split(' '); + const ob = st.includes('OB'), lb = st.includes('LB'); + const s = d.settings, mode = s.shutdown; + const host = location.hostname; + return html`
+
+
+
USV${!d.running ? html`aus` : lb ? html`Akku fast leer` : ob ? html`läuft über Akku` : d.status ? html`am Netz` : html`keine Daten`}
+ ${d.running && d.status ? html`
+
AKKU
${d.charge != null ? d.charge + ' %' : '–'}
+
RESTLAUFZEIT
${d.runtime != null ? Math.round(d.runtime / 60) + ' min' : '–'}
+
LAST
${d.load != null ? d.load + ' %' : '–'}
+
+
Gerät
${[d.mfr, d.model].filter(Boolean).join(' ') || '–'}
+ ${d.voltage != null ? html`
Netzspannung
${d.voltage} V
` : ''} + ${d.battery_date ? html`
Akku seit
${d.battery_date}
` : ''} +
Treiber
${d.driver || '–'}
+
` + : d.running ? html`

Verbinde mit der USV …

` + : html`

${d.detected ? html`Erkannt: ${d.detected}. ` : ''}Unten einschalten.

`} +
+
+
EREIGNISSE
+ ${d.events.length ? html`
${d.events.map((e) => html`
${fmtTime(e.time)} · ${UPS_EVENTS[e.type] || e.type}
`)}
` + : html`

Noch keine – ein Stromausfall wird hier und als Meldung angezeigt.

`} +
+
+
+
+
EINSTELLUNGEN
+ +
+ + + + +
+ + +
+ +
+ ${s.server ? html`

Auf dem anderen Rechner (NUT, upsmon): MONITOR ups@${host} 1 monitor ••• secondary – er fährt dann vor der Box herunter.

` : ''} +

Die Box fährt nur herunter, solange sie über Akku läuft. Kommt der Strom vorher zurück, passiert nichts.

+
+
`; +} + +function upsAfter() { + if (!S.ups) { loadUps(); return; } + if (!S.upsTimer) S.upsTimer = setTimeout(loadUps, 5000); + $('#ups-mode')?.addEventListener('change', (e) => { $('#ups-pct').hidden = e.target.value !== 'percent'; $('#ups-min').hidden = e.target.value !== 'minutes'; }); + $('#ups-server')?.addEventListener('change', (e) => { $('#ups-net').hidden = !e.target.checked; }); + $('#f-ups')?.addEventListener('submit', async (e) => { + e.preventDefault(); + const f = e.target, b = $('button[type=submit]', f); b.disabled = true; + const v = { enabled: f.enabled.checked, shutdown: f.shutdown.value, driver: f.driver.value, killpower: f.killpower.checked, server: f.server.checked, password: f.password.value }; + if (v.shutdown === 'percent') v.percent = +f.percent.value; + if (v.shutdown === 'minutes') v.minutes = +f.minutes.value; + try { S.ups = await rpc('shareos.ups', 'set', v); toast('USV gespeichert.'); } + catch (err) { toast('USV: ' + err.message, true); } + loadUps(); + }); +} + function snapForm() { const f = $('#f-snap'); const keep = {}; @@ -3345,6 +3432,7 @@ const VIEWS_AFTER = { services() { if (S.route.sub === 'dlna') { dlnaAfter(); return; } if (S.route.sub === 'printer') { printerAfter(); return; } + if (S.route.sub === 'ups') { upsAfter(); return; } if (!S.services) loadServices(); document.querySelectorAll('.as-toggle').forEach((c) => c.addEventListener('change', async () => { c.disabled = true; @@ -4325,6 +4413,7 @@ window.addEventListener('hashchange', () => { if (S.route.tab !== 'shares') { S.shEdit = null; S.uEdit = null; } if (S.route.tab !== 'services' || S.route.sub !== 'dlna') clearTimeout(S.dlnaTimer); if (S.route.tab !== 'services' || S.route.sub !== 'printer') { clearTimeout(S.prnTimer); S.prnTimer = null; } + if (S.route.tab !== 'services' || S.route.sub !== 'ups') { clearTimeout(S.upsTimer); S.upsTimer = null; } if (S.fb && S.route.sub === 'files') S.fb.roots = null; // volumes may have changed if (S.route.tab !== 'paperless') { S.plEdit = null; S.plLog = null; } else if (S.pl) loadPaperless(); if (S.route.tab !== 'cloud') { clearTimeout(S.cloudTimer); if (S.cloud?.scope === 'admin') { S.cloud.remoteEdit = null; S.cloud.jobEdit = null; S.cloud.log = null; } } diff --git a/src/shareos-core/www/i18n/ar.json b/src/shareos-core/www/i18n/ar.json index bea59a51..7afb7b87 100644 --- a/src/shareos-core/www/i18n/ar.json +++ b/src/shareos-core/www/i18n/ar.json @@ -1354,7 +1354,52 @@ "Immer Port 9100 (Treiber am Rechner)": "دائمًا المنفذ 9100 (برنامج التشغيل على الحاسوب)", "Jeder im Netzwerk kann drucken, ohne Anmeldung. „Immer Port 9100“ nur, wenn ein Drucker ohne Treiber nicht richtig druckt.": "يستطيع أي شخص في الشبكة الطباعة دون تسجيل دخول. اختر «دائمًا المنفذ 9100» فقط إذا لم تطبع الطابعة جيدًا دون برنامج تشغيل.", "Drucker gespeichert.": "تم حفظ الطابعات.", - "Testseite geschickt.": "تم إرسال صفحة الاختبار." + "Testseite geschickt.": "تم إرسال صفحة الاختبار.", + "USV": "UPS", + "Lade USV …": "جارٍ تحميل UPS …", + "Im Store installieren (Network UPS Tools)": "التثبيت من المتجر (Network UPS Tools)", + "Die USV-Unterstützung ist eine System-App: Die Box zeigt dann Akkustand und Restlaufzeit, meldet einen Stromausfall und fährt rechtzeitig sauber herunter.": "دعم UPS تطبيق نظام: يعرض الجهاز الشحن ووقت التشغيل المتبقي، ويبلّغ عن انقطاع الكهرباء ويُغلق بشكل سليم في الوقت المناسب.", + "Akku fast leer": "البطارية شبه فارغة", + "läuft über Akku": "يعمل على البطارية", + "am Netz": "على الكهرباء", + "keine Daten": "لا بيانات", + "AKKU": "البطارية", + "RESTLAUFZEIT": "الوقت المتبقي", + "LAST": "الحِمل", + "Netzspannung": "جهد الشبكة", + "Akku seit": "البطارية منذ", + "Treiber": "برنامج التشغيل", + "Verbinde mit der USV …": "جارٍ الاتصال بـ UPS …", + "Erkannt:": "تم الاكتشاف:", + "Unten einschalten.": "شغّله أدناه.", + "EREIGNISSE": "الأحداث", + "Noch keine – ein Stromausfall wird hier und als Meldung angezeigt.": "لا شيء بعد – يظهر انقطاع الكهرباء هنا وكرسالة.", + "Stromausfall – läuft über die USV": "انقطاع الكهرباء – يعمل على UPS", + "Strom wieder da": "عادت الكهرباء", + "Herunterfahren ausgelöst": "تم تفعيل الإيقاف", + "Box fährt herunter": "الجهاز يُغلق", + "Verbindung zur USV verloren": "انقطع الاتصال بـ UPS", + "Keine Verbindung zur USV": "لا اتصال بـ UPS", + "Verbindung zur USV wieder da": "عاد الاتصال بـ UPS", + "Akku tauschen": "استبدل البطارية", + "USV überwachen (startet auch nach einem Neustart)": "مراقبة UPS (تبدأ أيضًا بعد إعادة التشغيل)", + "HERUNTERFAHREN": "الإيقاف", + "Wenn die USV „Akku fast leer“ meldet": "عندما يبلّغ UPS عن «بطارية منخفضة»", + "Unter einem Akkustand": "تحت نسبة شحن", + "Nach einer Zeit auf Akku": "بعد مدة على البطارية", + "AKKUSTAND (%)": "شحن البطارية (%)", + "MINUTEN AUF AKKU": "دقائق على البطارية", + "TREIBER": "برنامج التشغيل", + "Automatisch": "تلقائي", + "blazer_usb (ältere Megatec)": "blazer_usb (Megatec الأقدم)", + "Danach die USV ausschalten – die Box startet wieder, wenn der Strom zurück ist": "ثم إيقاف UPS – يبدأ الجهاز مجددًا عند عودة الكهرباء", + "Andere Rechner dürfen die USV über das Netzwerk lesen (Port 3493)": "يمكن لأجهزة أخرى قراءة UPS عبر الشبكة (المنفذ 3493)", + "PASSWORT FÜR „MONITOR“": "كلمة المرور لـ «MONITOR»", + "gespeichert – leer lassen behält es": "محفوظة – اتركها فارغة للإبقاء عليها", + "Auf dem anderen Rechner (NUT, upsmon):": "على الحاسوب الآخر (NUT، upsmon):", + "– er fährt dann vor der Box herunter.": "– وعندها يُغلق قبل الجهاز.", + "Die Box fährt nur herunter, solange sie über Akku läuft. Kommt der Strom vorher zurück, passiert nichts.": "لا يُغلق الجهاز إلا أثناء عمله على البطارية. إذا عادت الكهرباء قبل ذلك فلا يحدث شيء.", + "USV gespeichert.": "تم حفظ UPS." }, "patterns": [ [ diff --git a/src/shareos-core/www/i18n/bn.json b/src/shareos-core/www/i18n/bn.json index e7b7a8de..a625076a 100644 --- a/src/shareos-core/www/i18n/bn.json +++ b/src/shareos-core/www/i18n/bn.json @@ -1354,7 +1354,52 @@ "Immer Port 9100 (Treiber am Rechner)": "সবসময় পোর্ট 9100 (কম্পিউটারে ড্রাইভার)", "Jeder im Netzwerk kann drucken, ohne Anmeldung. „Immer Port 9100“ nur, wenn ein Drucker ohne Treiber nicht richtig druckt.": "নেটওয়ার্কের যে কেউ সাইন-ইন ছাড়াই প্রিন্ট করতে পারে। “সবসময় পোর্ট 9100” কেবল তখনই, যখন প্রিন্টার ড্রাইভার ছাড়া ঠিকমতো প্রিন্ট করে না।", "Drucker gespeichert.": "প্রিন্টার সংরক্ষিত।", - "Testseite geschickt.": "টেস্ট পেজ পাঠানো হয়েছে।" + "Testseite geschickt.": "টেস্ট পেজ পাঠানো হয়েছে।", + "USV": "UPS", + "Lade USV …": "UPS লোড হচ্ছে …", + "Im Store installieren (Network UPS Tools)": "স্টোর থেকে ইনস্টল করুন (Network UPS Tools)", + "Die USV-Unterstützung ist eine System-App: Die Box zeigt dann Akkustand und Restlaufzeit, meldet einen Stromausfall und fährt rechtzeitig sauber herunter.": "UPS সাপোর্ট একটি সিস্টেম অ্যাপ: বক্স চার্জ ও বাকি সময় দেখায়, বিদ্যুৎ চলে গেলে জানায় এবং সময়মতো ঠিকভাবে বন্ধ হয়।", + "Akku fast leer": "ব্যাটারি প্রায় শেষ", + "läuft über Akku": "ব্যাটারিতে চলছে", + "am Netz": "বিদ্যুৎ লাইনে", + "keine Daten": "কোনো ডেটা নেই", + "AKKU": "ব্যাটারি", + "RESTLAUFZEIT": "বাকি সময়", + "LAST": "লোড", + "Netzspannung": "লাইন ভোল্টেজ", + "Akku seit": "ব্যাটারি কবে থেকে", + "Treiber": "ড্রাইভার", + "Verbinde mit der USV …": "UPS-এ সংযোগ হচ্ছে …", + "Erkannt:": "শনাক্ত:", + "Unten einschalten.": "নিচে চালু করুন।", + "EREIGNISSE": "ঘটনা", + "Noch keine – ein Stromausfall wird hier und als Meldung angezeigt.": "এখনো নেই – বিদ্যুৎ গেলে এখানে ও বার্তা হিসেবে দেখাবে।", + "Stromausfall – läuft über die USV": "বিদ্যুৎ বিভ্রাট – UPS-এ চলছে", + "Strom wieder da": "বিদ্যুৎ ফিরে এসেছে", + "Herunterfahren ausgelöst": "শাটডাউন শুরু", + "Box fährt herunter": "বক্স বন্ধ হচ্ছে", + "Verbindung zur USV verloren": "UPS-এর সাথে সংযোগ বিচ্ছিন্ন", + "Keine Verbindung zur USV": "UPS-এর সাথে সংযোগ নেই", + "Verbindung zur USV wieder da": "UPS-এর সাথে সংযোগ ফিরে এসেছে", + "Akku tauschen": "ব্যাটারি বদলান", + "USV überwachen (startet auch nach einem Neustart)": "UPS নজরে রাখুন (রিস্টার্টের পরেও চালু হবে)", + "HERUNTERFAHREN": "শাটডাউন", + "Wenn die USV „Akku fast leer“ meldet": "যখন UPS “ব্যাটারি কম” জানায়", + "Unter einem Akkustand": "একটি চার্জের নিচে", + "Nach einer Zeit auf Akku": "ব্যাটারিতে কিছু সময় পরে", + "AKKUSTAND (%)": "ব্যাটারি চার্জ (%)", + "MINUTEN AUF AKKU": "ব্যাটারিতে মিনিট", + "TREIBER": "ড্রাইভার", + "Automatisch": "স্বয়ংক্রিয়", + "blazer_usb (ältere Megatec)": "blazer_usb (পুরোনো Megatec)", + "Danach die USV ausschalten – die Box startet wieder, wenn der Strom zurück ist": "তারপর UPS বন্ধ করুন – বিদ্যুৎ ফিরলে বক্স আবার চালু হবে", + "Andere Rechner dürfen die USV über das Netzwerk lesen (Port 3493)": "অন্য কম্পিউটার নেটওয়ার্কে UPS পড়তে পারবে (পোর্ট 3493)", + "PASSWORT FÜR „MONITOR“": "“MONITOR”-এর পাসওয়ার্ড", + "gespeichert – leer lassen behält es": "সংরক্ষিত – ফাঁকা রাখলে আগেরটাই থাকবে", + "Auf dem anderen Rechner (NUT, upsmon):": "অন্য কম্পিউটারে (NUT, upsmon):", + "– er fährt dann vor der Box herunter.": "– তখন সেটি বক্সের আগে বন্ধ হবে।", + "Die Box fährt nur herunter, solange sie über Akku läuft. Kommt der Strom vorher zurück, passiert nichts.": "বক্স কেবল ব্যাটারিতে চলার সময়ই বন্ধ হয়। আগে বিদ্যুৎ ফিরলে কিছুই হয় না।", + "USV gespeichert.": "UPS সংরক্ষিত।" }, "patterns": [ [ diff --git a/src/shareos-core/www/i18n/en.json b/src/shareos-core/www/i18n/en.json index d1db9e1f..39cd88f4 100644 --- a/src/shareos-core/www/i18n/en.json +++ b/src/shareos-core/www/i18n/en.json @@ -1354,7 +1354,52 @@ "Immer Port 9100 (Treiber am Rechner)": "Always port 9100 (driver on the computer)", "Jeder im Netzwerk kann drucken, ohne Anmeldung. „Immer Port 9100“ nur, wenn ein Drucker ohne Treiber nicht richtig druckt.": "Everyone in the network can print, without signing in. “Always port 9100” only if a printer does not print properly without a driver.", "Drucker gespeichert.": "Printers saved.", - "Testseite geschickt.": "Test page sent." + "Testseite geschickt.": "Test page sent.", + "USV": "UPS", + "Lade USV …": "Loading UPS …", + "Im Store installieren (Network UPS Tools)": "Install from the store (Network UPS Tools)", + "Die USV-Unterstützung ist eine System-App: Die Box zeigt dann Akkustand und Restlaufzeit, meldet einen Stromausfall und fährt rechtzeitig sauber herunter.": "UPS support is a system app: the box then shows charge and runtime, reports a power failure and shuts down cleanly in time.", + "Akku fast leer": "Battery almost empty", + "läuft über Akku": "on battery", + "am Netz": "on mains", + "keine Daten": "no data", + "AKKU": "BATTERY", + "RESTLAUFZEIT": "RUNTIME", + "LAST": "LOAD", + "Netzspannung": "Mains voltage", + "Akku seit": "Battery since", + "Treiber": "Driver", + "Verbinde mit der USV …": "Connecting to the UPS …", + "Erkannt:": "Detected:", + "Unten einschalten.": "Switch it on below.", + "EREIGNISSE": "EVENTS", + "Noch keine – ein Stromausfall wird hier und als Meldung angezeigt.": "None yet – a power failure is shown here and as a message.", + "Stromausfall – läuft über die USV": "Power failure – running on the UPS", + "Strom wieder da": "Power is back", + "Herunterfahren ausgelöst": "Shutdown triggered", + "Box fährt herunter": "Box shuts down", + "Verbindung zur USV verloren": "Connection to the UPS lost", + "Keine Verbindung zur USV": "No connection to the UPS", + "Verbindung zur USV wieder da": "Connection to the UPS is back", + "Akku tauschen": "Replace the battery", + "USV überwachen (startet auch nach einem Neustart)": "Monitor the UPS (also starts after a restart)", + "HERUNTERFAHREN": "SHUT DOWN", + "Wenn die USV „Akku fast leer“ meldet": "When the UPS reports “battery low”", + "Unter einem Akkustand": "Below a battery charge", + "Nach einer Zeit auf Akku": "After some time on battery", + "AKKUSTAND (%)": "BATTERY CHARGE (%)", + "MINUTEN AUF AKKU": "MINUTES ON BATTERY", + "TREIBER": "DRIVER", + "Automatisch": "Automatic", + "blazer_usb (ältere Megatec)": "blazer_usb (older Megatec)", + "Danach die USV ausschalten – die Box startet wieder, wenn der Strom zurück ist": "Then switch the UPS off – the box starts again when the power is back", + "Andere Rechner dürfen die USV über das Netzwerk lesen (Port 3493)": "Other computers may read the UPS over the network (port 3493)", + "PASSWORT FÜR „MONITOR“": "PASSWORD FOR “MONITOR”", + "gespeichert – leer lassen behält es": "saved – leave empty to keep it", + "Auf dem anderen Rechner (NUT, upsmon):": "On the other computer (NUT, upsmon):", + "– er fährt dann vor der Box herunter.": "– it then shuts down before the box.", + "Die Box fährt nur herunter, solange sie über Akku läuft. Kommt der Strom vorher zurück, passiert nichts.": "The box only shuts down while it runs on battery. If the power comes back before, nothing happens.", + "USV gespeichert.": "UPS saved." }, "patterns": [ [ diff --git a/src/shareos-core/www/i18n/es.json b/src/shareos-core/www/i18n/es.json index 10be64ff..04780084 100644 --- a/src/shareos-core/www/i18n/es.json +++ b/src/shareos-core/www/i18n/es.json @@ -1354,7 +1354,52 @@ "Immer Port 9100 (Treiber am Rechner)": "Siempre puerto 9100 (controlador en el ordenador)", "Jeder im Netzwerk kann drucken, ohne Anmeldung. „Immer Port 9100“ nur, wenn ein Drucker ohne Treiber nicht richtig druckt.": "Cualquiera en la red puede imprimir, sin iniciar sesión. «Siempre puerto 9100» solo si una impresora no imprime bien sin controlador.", "Drucker gespeichert.": "Impresoras guardadas.", - "Testseite geschickt.": "Página de prueba enviada." + "Testseite geschickt.": "Página de prueba enviada.", + "USV": "SAI", + "Lade USV …": "Cargando SAI …", + "Im Store installieren (Network UPS Tools)": "Instalar desde la tienda (Network UPS Tools)", + "Die USV-Unterstützung ist eine System-App: Die Box zeigt dann Akkustand und Restlaufzeit, meldet einen Stromausfall und fährt rechtzeitig sauber herunter.": "El soporte de SAI es una app del sistema: la caja muestra la carga y la autonomía, avisa de un corte de luz y se apaga a tiempo de forma limpia.", + "Akku fast leer": "Batería casi vacía", + "läuft über Akku": "con batería", + "am Netz": "con red eléctrica", + "keine Daten": "sin datos", + "AKKU": "BATERÍA", + "RESTLAUFZEIT": "AUTONOMÍA", + "LAST": "CARGA", + "Netzspannung": "Tensión de red", + "Akku seit": "Batería desde", + "Treiber": "Controlador", + "Verbinde mit der USV …": "Conectando con el SAI …", + "Erkannt:": "Detectado:", + "Unten einschalten.": "Actívalo abajo.", + "EREIGNISSE": "EVENTOS", + "Noch keine – ein Stromausfall wird hier und als Meldung angezeigt.": "Ninguno aún – un corte de luz se muestra aquí y como mensaje.", + "Stromausfall – läuft über die USV": "Corte de luz – funcionando con el SAI", + "Strom wieder da": "Vuelve la luz", + "Herunterfahren ausgelöst": "Apagado iniciado", + "Box fährt herunter": "La caja se apaga", + "Verbindung zur USV verloren": "Conexión con el SAI perdida", + "Keine Verbindung zur USV": "Sin conexión con el SAI", + "Verbindung zur USV wieder da": "Conexión con el SAI restablecida", + "Akku tauschen": "Cambiar la batería", + "USV überwachen (startet auch nach einem Neustart)": "Supervisar el SAI (también arranca tras reiniciar)", + "HERUNTERFAHREN": "APAGADO", + "Wenn die USV „Akku fast leer“ meldet": "Cuando el SAI avisa de «batería baja»", + "Unter einem Akkustand": "Por debajo de una carga", + "Nach einer Zeit auf Akku": "Tras un tiempo con batería", + "AKKUSTAND (%)": "CARGA DE LA BATERÍA (%)", + "MINUTEN AUF AKKU": "MINUTOS CON BATERÍA", + "TREIBER": "CONTROLADOR", + "Automatisch": "Automático", + "blazer_usb (ältere Megatec)": "blazer_usb (Megatec antiguos)", + "Danach die USV ausschalten – die Box startet wieder, wenn der Strom zurück ist": "Después apagar el SAI – la caja arranca de nuevo al volver la luz", + "Andere Rechner dürfen die USV über das Netzwerk lesen (Port 3493)": "Otros ordenadores pueden leer el SAI por la red (puerto 3493)", + "PASSWORT FÜR „MONITOR“": "CONTRASEÑA PARA «MONITOR»", + "gespeichert – leer lassen behält es": "guardada – déjala vacía para conservarla", + "Auf dem anderen Rechner (NUT, upsmon):": "En el otro ordenador (NUT, upsmon):", + "– er fährt dann vor der Box herunter.": "– entonces se apaga antes que la caja.", + "Die Box fährt nur herunter, solange sie über Akku läuft. Kommt der Strom vorher zurück, passiert nichts.": "La caja solo se apaga mientras funciona con batería. Si la luz vuelve antes, no pasa nada.", + "USV gespeichert.": "SAI guardado." }, "patterns": [ [ diff --git a/src/shareos-core/www/i18n/fr.json b/src/shareos-core/www/i18n/fr.json index 22bf0cf4..11e26adc 100644 --- a/src/shareos-core/www/i18n/fr.json +++ b/src/shareos-core/www/i18n/fr.json @@ -1354,7 +1354,52 @@ "Immer Port 9100 (Treiber am Rechner)": "Toujours le port 9100 (pilote sur l'ordinateur)", "Jeder im Netzwerk kann drucken, ohne Anmeldung. „Immer Port 9100“ nur, wenn ein Drucker ohne Treiber nicht richtig druckt.": "Tout le monde sur le réseau peut imprimer, sans connexion. « Toujours le port 9100 » seulement si une imprimante n'imprime pas correctement sans pilote.", "Drucker gespeichert.": "Imprimantes enregistrées.", - "Testseite geschickt.": "Page de test envoyée." + "Testseite geschickt.": "Page de test envoyée.", + "USV": "ONDULEUR", + "Lade USV …": "Chargement de l'onduleur …", + "Im Store installieren (Network UPS Tools)": "Installer depuis le store (Network UPS Tools)", + "Die USV-Unterstützung ist eine System-App: Die Box zeigt dann Akkustand und Restlaufzeit, meldet einen Stromausfall und fährt rechtzeitig sauber herunter.": "La prise en charge de l'onduleur est une application système : la box affiche alors la charge et l'autonomie, signale une coupure de courant et s'arrête proprement à temps.", + "Akku fast leer": "Batterie presque vide", + "läuft über Akku": "sur batterie", + "am Netz": "sur secteur", + "keine Daten": "pas de données", + "AKKU": "BATTERIE", + "RESTLAUFZEIT": "AUTONOMIE", + "LAST": "CHARGE", + "Netzspannung": "Tension secteur", + "Akku seit": "Batterie depuis", + "Treiber": "Pilote", + "Verbinde mit der USV …": "Connexion à l'onduleur …", + "Erkannt:": "Détecté :", + "Unten einschalten.": "Activez-le ci-dessous.", + "EREIGNISSE": "ÉVÉNEMENTS", + "Noch keine – ein Stromausfall wird hier und als Meldung angezeigt.": "Aucun pour l'instant – une coupure de courant s'affiche ici et comme message.", + "Stromausfall – läuft über die USV": "Coupure de courant – sur onduleur", + "Strom wieder da": "Courant rétabli", + "Herunterfahren ausgelöst": "Arrêt déclenché", + "Box fährt herunter": "La box s'arrête", + "Verbindung zur USV verloren": "Connexion à l'onduleur perdue", + "Keine Verbindung zur USV": "Pas de connexion à l'onduleur", + "Verbindung zur USV wieder da": "Connexion à l'onduleur rétablie", + "Akku tauschen": "Remplacer la batterie", + "USV überwachen (startet auch nach einem Neustart)": "Surveiller l'onduleur (démarre aussi après un redémarrage)", + "HERUNTERFAHREN": "ARRÊT", + "Wenn die USV „Akku fast leer“ meldet": "Quand l'onduleur signale « batterie faible »", + "Unter einem Akkustand": "Sous un niveau de charge", + "Nach einer Zeit auf Akku": "Après un temps sur batterie", + "AKKUSTAND (%)": "CHARGE DE LA BATTERIE (%)", + "MINUTEN AUF AKKU": "MINUTES SUR BATTERIE", + "TREIBER": "PILOTE", + "Automatisch": "Automatique", + "blazer_usb (ältere Megatec)": "blazer_usb (anciens Megatec)", + "Danach die USV ausschalten – die Box startet wieder, wenn der Strom zurück ist": "Ensuite éteindre l'onduleur – la box redémarre au retour du courant", + "Andere Rechner dürfen die USV über das Netzwerk lesen (Port 3493)": "D'autres ordinateurs peuvent lire l'onduleur via le réseau (port 3493)", + "PASSWORT FÜR „MONITOR“": "MOT DE PASSE POUR « MONITOR »", + "gespeichert – leer lassen behält es": "enregistré – laisser vide pour le conserver", + "Auf dem anderen Rechner (NUT, upsmon):": "Sur l'autre ordinateur (NUT, upsmon) :", + "– er fährt dann vor der Box herunter.": "– il s'arrête alors avant la box.", + "Die Box fährt nur herunter, solange sie über Akku läuft. Kommt der Strom vorher zurück, passiert nichts.": "La box ne s'arrête que tant qu'elle est sur batterie. Si le courant revient avant, rien ne se passe.", + "USV gespeichert.": "Onduleur enregistré." }, "patterns": [ [ diff --git a/src/shareos-core/www/i18n/hi.json b/src/shareos-core/www/i18n/hi.json index 457e61b2..4ee9c0ae 100644 --- a/src/shareos-core/www/i18n/hi.json +++ b/src/shareos-core/www/i18n/hi.json @@ -1354,7 +1354,52 @@ "Immer Port 9100 (Treiber am Rechner)": "हमेशा पोर्ट 9100 (कंप्यूटर पर ड्राइवर)", "Jeder im Netzwerk kann drucken, ohne Anmeldung. „Immer Port 9100“ nur, wenn ein Drucker ohne Treiber nicht richtig druckt.": "नेटवर्क में कोई भी बिना साइन-इन प्रिंट कर सकता है। “हमेशा पोर्ट 9100” तभी चुनें जब प्रिंटर बिना ड्राइवर ठीक से प्रिंट न करे।", "Drucker gespeichert.": "प्रिंटर सहेजे गए।", - "Testseite geschickt.": "टेस्ट पेज भेजा गया।" + "Testseite geschickt.": "टेस्ट पेज भेजा गया।", + "USV": "UPS", + "Lade USV …": "UPS लोड हो रहा है …", + "Im Store installieren (Network UPS Tools)": "स्टोर से इंस्टॉल करें (Network UPS Tools)", + "Die USV-Unterstützung ist eine System-App: Die Box zeigt dann Akkustand und Restlaufzeit, meldet einen Stromausfall und fährt rechtzeitig sauber herunter.": "UPS सपोर्ट एक सिस्टम ऐप है: बॉक्स चार्ज और शेष समय दिखाता है, बिजली जाने की सूचना देता है और समय पर ठीक से बंद होता है।", + "Akku fast leer": "बैटरी लगभग खाली", + "läuft über Akku": "बैटरी पर", + "am Netz": "मेन्स पर", + "keine Daten": "कोई डेटा नहीं", + "AKKU": "बैटरी", + "RESTLAUFZEIT": "शेष समय", + "LAST": "लोड", + "Netzspannung": "मेन्स वोल्टेज", + "Akku seit": "बैटरी कब से", + "Treiber": "ड्राइवर", + "Verbinde mit der USV …": "UPS से जुड़ रहा है …", + "Erkannt:": "पहचाना गया:", + "Unten einschalten.": "नीचे चालू करें।", + "EREIGNISSE": "घटनाएँ", + "Noch keine – ein Stromausfall wird hier und als Meldung angezeigt.": "अभी कोई नहीं – बिजली जाने पर यहाँ और संदेश के रूप में दिखेगा।", + "Stromausfall – läuft über die USV": "बिजली गई – UPS पर चल रहा है", + "Strom wieder da": "बिजली वापस आई", + "Herunterfahren ausgelöst": "शटडाउन शुरू", + "Box fährt herunter": "बॉक्स बंद हो रहा है", + "Verbindung zur USV verloren": "UPS से कनेक्शन टूटा", + "Keine Verbindung zur USV": "UPS से कोई कनेक्शन नहीं", + "Verbindung zur USV wieder da": "UPS से कनेक्शन वापस आया", + "Akku tauschen": "बैटरी बदलें", + "USV überwachen (startet auch nach einem Neustart)": "UPS की निगरानी करें (रीस्टार्ट के बाद भी शुरू होगा)", + "HERUNTERFAHREN": "शटडाउन", + "Wenn die USV „Akku fast leer“ meldet": "जब UPS “बैटरी कम” बताए", + "Unter einem Akkustand": "एक चार्ज स्तर से नीचे", + "Nach einer Zeit auf Akku": "बैटरी पर कुछ समय बाद", + "AKKUSTAND (%)": "बैटरी चार्ज (%)", + "MINUTEN AUF AKKU": "बैटरी पर मिनट", + "TREIBER": "ड्राइवर", + "Automatisch": "स्वचालित", + "blazer_usb (ältere Megatec)": "blazer_usb (पुराने Megatec)", + "Danach die USV ausschalten – die Box startet wieder, wenn der Strom zurück ist": "फिर UPS बंद करें – बिजली लौटने पर बॉक्स फिर शुरू होगा", + "Andere Rechner dürfen die USV über das Netzwerk lesen (Port 3493)": "दूसरे कंप्यूटर नेटवर्क से UPS पढ़ सकते हैं (पोर्ट 3493)", + "PASSWORT FÜR „MONITOR“": "“MONITOR” के लिए पासवर्ड", + "gespeichert – leer lassen behält es": "सहेजा गया – खाली छोड़ें तो वही रहेगा", + "Auf dem anderen Rechner (NUT, upsmon):": "दूसरे कंप्यूटर पर (NUT, upsmon):", + "– er fährt dann vor der Box herunter.": "– तब वह बॉक्स से पहले बंद होगा।", + "Die Box fährt nur herunter, solange sie über Akku läuft. Kommt der Strom vorher zurück, passiert nichts.": "बॉक्स तभी बंद होता है जब वह बैटरी पर चल रहा हो। बिजली पहले लौट आए तो कुछ नहीं होता।", + "USV gespeichert.": "UPS सहेजा गया।" }, "patterns": [ [ diff --git a/src/shareos-core/www/i18n/pt.json b/src/shareos-core/www/i18n/pt.json index b2dce5ea..c0808a36 100644 --- a/src/shareos-core/www/i18n/pt.json +++ b/src/shareos-core/www/i18n/pt.json @@ -1354,7 +1354,52 @@ "Immer Port 9100 (Treiber am Rechner)": "Sempre porta 9100 (controlador no computador)", "Jeder im Netzwerk kann drucken, ohne Anmeldung. „Immer Port 9100“ nur, wenn ein Drucker ohne Treiber nicht richtig druckt.": "Qualquer pessoa na rede pode imprimir, sem iniciar sessão. «Sempre porta 9100» só se uma impressora não imprimir bem sem controlador.", "Drucker gespeichert.": "Impressoras guardadas.", - "Testseite geschickt.": "Página de teste enviada." + "Testseite geschickt.": "Página de teste enviada.", + "USV": "UPS", + "Lade USV …": "A carregar UPS …", + "Im Store installieren (Network UPS Tools)": "Instalar a partir da loja (Network UPS Tools)", + "Die USV-Unterstützung ist eine System-App: Die Box zeigt dann Akkustand und Restlaufzeit, meldet einen Stromausfall und fährt rechtzeitig sauber herunter.": "O suporte a UPS é uma app de sistema: a caixa mostra a carga e a autonomia, avisa de um corte de energia e desliga-se a tempo de forma limpa.", + "Akku fast leer": "Bateria quase vazia", + "läuft über Akku": "em bateria", + "am Netz": "na rede elétrica", + "keine Daten": "sem dados", + "AKKU": "BATERIA", + "RESTLAUFZEIT": "AUTONOMIA", + "LAST": "CARGA", + "Netzspannung": "Tensão da rede", + "Akku seit": "Bateria desde", + "Treiber": "Controlador", + "Verbinde mit der USV …": "A ligar à UPS …", + "Erkannt:": "Detetado:", + "Unten einschalten.": "Ligue-o abaixo.", + "EREIGNISSE": "EVENTOS", + "Noch keine – ein Stromausfall wird hier und als Meldung angezeigt.": "Nenhum ainda – um corte de energia aparece aqui e como mensagem.", + "Stromausfall – läuft über die USV": "Corte de energia – a funcionar com a UPS", + "Strom wieder da": "Energia reposta", + "Herunterfahren ausgelöst": "Encerramento iniciado", + "Box fährt herunter": "A caixa está a desligar", + "Verbindung zur USV verloren": "Ligação à UPS perdida", + "Keine Verbindung zur USV": "Sem ligação à UPS", + "Verbindung zur USV wieder da": "Ligação à UPS restabelecida", + "Akku tauschen": "Substituir a bateria", + "USV überwachen (startet auch nach einem Neustart)": "Monitorizar a UPS (também arranca após reiniciar)", + "HERUNTERFAHREN": "ENCERRAR", + "Wenn die USV „Akku fast leer“ meldet": "Quando a UPS indica «bateria fraca»", + "Unter einem Akkustand": "Abaixo de uma carga", + "Nach einer Zeit auf Akku": "Após algum tempo em bateria", + "AKKUSTAND (%)": "CARGA DA BATERIA (%)", + "MINUTEN AUF AKKU": "MINUTOS EM BATERIA", + "TREIBER": "CONTROLADOR", + "Automatisch": "Automático", + "blazer_usb (ältere Megatec)": "blazer_usb (Megatec antigos)", + "Danach die USV ausschalten – die Box startet wieder, wenn der Strom zurück ist": "Depois desligar a UPS – a caixa volta a arrancar quando a energia regressar", + "Andere Rechner dürfen die USV über das Netzwerk lesen (Port 3493)": "Outros computadores podem ler a UPS pela rede (porta 3493)", + "PASSWORT FÜR „MONITOR“": "PALAVRA-PASSE PARA «MONITOR»", + "gespeichert – leer lassen behält es": "guardada – deixe vazio para a manter", + "Auf dem anderen Rechner (NUT, upsmon):": "No outro computador (NUT, upsmon):", + "– er fährt dann vor der Box herunter.": "– então desliga-se antes da caixa.", + "Die Box fährt nur herunter, solange sie über Akku läuft. Kommt der Strom vorher zurück, passiert nichts.": "A caixa só se desliga enquanto estiver em bateria. Se a energia voltar antes, nada acontece.", + "USV gespeichert.": "UPS guardada." }, "patterns": [ [ diff --git a/src/shareos-core/www/i18n/ru.json b/src/shareos-core/www/i18n/ru.json index ae2dad4f..ae87bacb 100644 --- a/src/shareos-core/www/i18n/ru.json +++ b/src/shareos-core/www/i18n/ru.json @@ -1354,7 +1354,52 @@ "Immer Port 9100 (Treiber am Rechner)": "Всегда порт 9100 (драйвер на компьютере)", "Jeder im Netzwerk kann drucken, ohne Anmeldung. „Immer Port 9100“ nur, wenn ein Drucker ohne Treiber nicht richtig druckt.": "Печатать может любой в сети, без входа. «Всегда порт 9100» – только если принтер без драйвера печатает неправильно.", "Drucker gespeichert.": "Принтеры сохранены.", - "Testseite geschickt.": "Тестовая страница отправлена." + "Testseite geschickt.": "Тестовая страница отправлена.", + "USV": "ИБП", + "Lade USV …": "Загрузка ИБП …", + "Im Store installieren (Network UPS Tools)": "Установить из магазина (Network UPS Tools)", + "Die USV-Unterstützung ist eine System-App: Die Box zeigt dann Akkustand und Restlaufzeit, meldet einen Stromausfall und fährt rechtzeitig sauber herunter.": "Поддержка ИБП – системное приложение: устройство показывает заряд и время работы, сообщает об отключении питания и вовремя корректно выключается.", + "Akku fast leer": "Батарея почти разряжена", + "läuft über Akku": "от батареи", + "am Netz": "от сети", + "keine Daten": "нет данных", + "AKKU": "БАТАРЕЯ", + "RESTLAUFZEIT": "ВРЕМЯ РАБОТЫ", + "LAST": "НАГРУЗКА", + "Netzspannung": "Напряжение сети", + "Akku seit": "Батарея с", + "Treiber": "Драйвер", + "Verbinde mit der USV …": "Подключение к ИБП …", + "Erkannt:": "Обнаружено:", + "Unten einschalten.": "Включите ниже.", + "EREIGNISSE": "СОБЫТИЯ", + "Noch keine – ein Stromausfall wird hier und als Meldung angezeigt.": "Пока нет – отключение питания появится здесь и в сообщениях.", + "Stromausfall – läuft über die USV": "Отключение питания – работа от ИБП", + "Strom wieder da": "Питание восстановлено", + "Herunterfahren ausgelöst": "Запущено выключение", + "Box fährt herunter": "Устройство выключается", + "Verbindung zur USV verloren": "Связь с ИБП потеряна", + "Keine Verbindung zur USV": "Нет связи с ИБП", + "Verbindung zur USV wieder da": "Связь с ИБП восстановлена", + "Akku tauschen": "Замените батарею", + "USV überwachen (startet auch nach einem Neustart)": "Следить за ИБП (запускается и после перезагрузки)", + "HERUNTERFAHREN": "ВЫКЛЮЧЕНИЕ", + "Wenn die USV „Akku fast leer“ meldet": "Когда ИБП сообщает «батарея разряжена»", + "Unter einem Akkustand": "Ниже уровня заряда", + "Nach einer Zeit auf Akku": "Через время работы от батареи", + "AKKUSTAND (%)": "ЗАРЯД БАТАРЕИ (%)", + "MINUTEN AUF AKKU": "МИНУТ ОТ БАТАРЕИ", + "TREIBER": "ДРАЙВЕР", + "Automatisch": "Автоматически", + "blazer_usb (ältere Megatec)": "blazer_usb (старые Megatec)", + "Danach die USV ausschalten – die Box startet wieder, wenn der Strom zurück ist": "Затем выключить ИБП – устройство снова запустится при появлении питания", + "Andere Rechner dürfen die USV über das Netzwerk lesen (Port 3493)": "Другие компьютеры могут читать данные ИБП по сети (порт 3493)", + "PASSWORT FÜR „MONITOR“": "ПАРОЛЬ ДЛЯ «MONITOR»", + "gespeichert – leer lassen behält es": "сохранён – оставьте пустым, чтобы не менять", + "Auf dem anderen Rechner (NUT, upsmon):": "На другом компьютере (NUT, upsmon):", + "– er fährt dann vor der Box herunter.": "– тогда он выключится раньше устройства.", + "Die Box fährt nur herunter, solange sie über Akku läuft. Kommt der Strom vorher zurück, passiert nichts.": "Устройство выключается, только пока работает от батареи. Если питание вернётся раньше, ничего не произойдёт.", + "USV gespeichert.": "ИБП сохранён." }, "patterns": [ [ diff --git a/src/shareos-core/www/i18n/zh.json b/src/shareos-core/www/i18n/zh.json index 9e26fd04..26db5d76 100644 --- a/src/shareos-core/www/i18n/zh.json +++ b/src/shareos-core/www/i18n/zh.json @@ -1354,7 +1354,52 @@ "Immer Port 9100 (Treiber am Rechner)": "始终使用 9100 端口(驱动装在电脑上)", "Jeder im Netzwerk kann drucken, ohne Anmeldung. „Immer Port 9100“ nur, wenn ein Drucker ohne Treiber nicht richtig druckt.": "网络中的任何人都可以打印,无需登录。仅当打印机免驱动打印不正常时才选“始终使用 9100 端口”。", "Drucker gespeichert.": "打印机设置已保存。", - "Testseite geschickt.": "测试页已发送。" + "Testseite geschickt.": "测试页已发送。", + "USV": "UPS", + "Lade USV …": "正在加载 UPS……", + "Im Store installieren (Network UPS Tools)": "从商店安装 (Network UPS Tools)", + "Die USV-Unterstützung ist eine System-App: Die Box zeigt dann Akkustand und Restlaufzeit, meldet einen Stromausfall und fährt rechtzeitig sauber herunter.": "UPS 支持是一个系统应用:设备会显示电量和剩余时间,报告停电并及时安全关机。", + "Akku fast leer": "电池电量低", + "läuft über Akku": "电池供电中", + "am Netz": "市电供电", + "keine Daten": "无数据", + "AKKU": "电池", + "RESTLAUFZEIT": "剩余时间", + "LAST": "负载", + "Netzspannung": "市电电压", + "Akku seit": "电池启用于", + "Treiber": "驱动", + "Verbinde mit der USV …": "正在连接 UPS……", + "Erkannt:": "已检测到:", + "Unten einschalten.": "在下方启用。", + "EREIGNISSE": "事件", + "Noch keine – ein Stromausfall wird hier und als Meldung angezeigt.": "暂无——停电会显示在这里并作为消息通知。", + "Stromausfall – läuft über die USV": "停电——由 UPS 供电", + "Strom wieder da": "供电恢复", + "Herunterfahren ausgelöst": "已触发关机", + "Box fährt herunter": "设备正在关机", + "Verbindung zur USV verloren": "与 UPS 的连接中断", + "Keine Verbindung zur USV": "无法连接 UPS", + "Verbindung zur USV wieder da": "与 UPS 的连接已恢复", + "Akku tauschen": "更换电池", + "USV überwachen (startet auch nach einem Neustart)": "监控 UPS(重启后也会启动)", + "HERUNTERFAHREN": "关机", + "Wenn die USV „Akku fast leer“ meldet": "当 UPS 报告“电池电量低”时", + "Unter einem Akkustand": "低于某电量时", + "Nach einer Zeit auf Akku": "电池供电一段时间后", + "AKKUSTAND (%)": "电池电量 (%)", + "MINUTEN AUF AKKU": "电池供电分钟数", + "TREIBER": "驱动", + "Automatisch": "自动", + "blazer_usb (ältere Megatec)": "blazer_usb(旧款 Megatec)", + "Danach die USV ausschalten – die Box startet wieder, wenn der Strom zurück ist": "随后关闭 UPS——供电恢复时设备会重新启动", + "Andere Rechner dürfen die USV über das Netzwerk lesen (Port 3493)": "允许其他电脑通过网络读取 UPS(端口 3493)", + "PASSWORT FÜR „MONITOR“": "“MONITOR”的密码", + "gespeichert – leer lassen behält es": "已保存——留空则保留", + "Auf dem anderen Rechner (NUT, upsmon):": "在另一台电脑上(NUT、upsmon):", + "– er fährt dann vor der Box herunter.": "——它会在本设备之前关机。", + "Die Box fährt nur herunter, solange sie über Akku läuft. Kommt der Strom vorher zurück, passiert nichts.": "只有在电池供电期间设备才会关机。如果在此之前恢复供电,则不会发生任何事。", + "USV gespeichert.": "UPS 设置已保存。" }, "patterns": [ [ diff --git a/sysapps/nut/app.json b/sysapps/nut/app.json new file mode 100644 index 00000000..9f725a1c --- /dev/null +++ b/sysapps/nut/app.json @@ -0,0 +1,19 @@ +{ + "id": "nut", + "name": "Network UPS Tools", + "version": "2.8.5", + "publisher": "UniqueDroid", + "description": { + "de": "USV (unterbrechungsfreie Stromversorgung) über USB: Die Box zeigt Akkustand und Restlaufzeit, meldet einen Stromausfall und fährt rechtzeitig sauber herunter – bei leerem Akku, unter einem Prozentwert oder nach einigen Minuten auf Akku. Danach schaltet die USV ab und mit dem Strom wieder ein. Andere Rechner können die USV über das Netzwerk mitlesen. Für die meisten USVs (APC, Eaton, CyberPower, Megatec …). Einstellungen unter Dienste → USV.", + "en": "UPS (uninterruptible power supply) over USB: the box shows charge and runtime, reports a power failure and shuts down cleanly in time – on a low battery, below a percentage or after some minutes on battery. Then the UPS switches off and on again with the power. Other computers can read the UPS over the network. For most UPSs (APC, Eaton, CyberPower, Megatec …). Settings under Services → UPS." + }, + "type": "system", + "commands": [ "usbhid-ups", "nutdrv_qx", "blazer_usb", "upsd", "upsmon", "upsc" ], + "service": { "control": "ups", "delay": 0 }, + "flash": true, + "icon": "icon.svg", + "category": "system", + "license": "GPL-2.0-or-later", + "homepage": "https://networkupstools.org/", + "min_shareos": "0.2.0-beta.18" +} diff --git a/sysapps/nut/icon.svg b/sysapps/nut/icon.svg new file mode 100644 index 00000000..2c16d0ef --- /dev/null +++ b/sysapps/nut/icon.svg @@ -0,0 +1 @@ + diff --git a/sysapps/nut/pack.json b/sysapps/nut/pack.json new file mode 100644 index 00000000..328619a2 --- /dev/null +++ b/sysapps/nut/pack.json @@ -0,0 +1 @@ +{ "bin": [ "usr/bin/usbhid-ups", "usr/bin/nutdrv_qx", "usr/bin/blazer_usb", "usr/sbin/upsd", "usr/sbin/upsmon", "usr/bin/upsc" ] } diff --git a/tests/mock-ubus.py b/tests/mock-ubus.py index 9172c02f..9073baa2 100644 --- a/tests/mock-ubus.py +++ b/tests/mock-ubus.py @@ -711,6 +711,22 @@ def call(obj, method, args): if method == 'test': return {'ok': True} return pr + if obj == 'shareos.ups': + u = STATE.setdefault('ups', {'installed': True, 'enabled': False, 'running': False, 'detected': '051d:0002', 'driver': 'usbhid-ups', + 'status': None, 'charge': None, 'runtime': None, 'load': None, 'voltage': None, 'mfr': None, 'model': None, 'charge_low': None, 'battery_date': None, + 'events': [], 'settings': {'driver': 'auto', 'shutdown': 'low', 'percent': 30, 'minutes': 5, 'killpower': True, 'server': False, 'password_set': False}}) + if method == 'set': + u['enabled'] = bool(args.get('enabled')) + for k in ('driver', 'shutdown', 'percent', 'minutes', 'killpower', 'server'): + if args.get(k) not in (None, '', 0): u['settings'][k] = args[k] + u['settings']['killpower'] = bool(args.get('killpower')); u['settings']['server'] = bool(args.get('server')) + if args.get('password'): u['settings']['password_set'] = True + if u['enabled']: + u.update({'running': True, 'status': 'OB DISCHRG', 'charge': 87, 'runtime': 1260, 'load': 18, 'voltage': 0, 'mfr': 'APC', 'model': 'Back-UPS ES 700G', 'battery_date': '2024/03/11', + 'events': [{'time': 1793530000, 'type': 'ONBATT'}, {'time': 1793520000, 'type': 'ONLINE'}, {'time': 1793519000, 'type': 'ONBATT'}]}) + else: + u.update({'running': False, 'status': None}) + return u if obj == 'shareos.web' and method.startswith(('ddns', 'ca_', 'acme')): d = STATE.setdefault('ddns', {'enabled': False, 'provider': '', 'host': '', 'user': '', 'url': '', 'token_set': False, 'ipv4': True, 'ipv6': False, 'last': None}) ca = STATE.setdefault('ca', {'exists': False, 'certs': [], 'web': False}) diff --git a/tests/test-ups.sh b/tests/test-ups.sh new file mode 100755 index 00000000..7fd06145 --- /dev/null +++ b/tests/test-ups.sh @@ -0,0 +1,86 @@ +#!/bin/sh +# +# Tests shareos-ups (system app "nut") with fake NUT programs: the files it +# writes for each setting, the driver chosen, the events (messages, the timer +# for "minutes on battery"), the status lines and the final UPS power-off. +# +# tests/test-ups.sh [dir with uci] +# +TOP=$(cd "$(dirname "$0")/.." && pwd) +P="$TOP/src/shareos-core/usr/sbin/shareos-ups" +T=$(mktemp -d) +trap 'SHAREOS_ROOT=$T sh "$P" stop-all >/dev/null 2>&1; rm -rf "$T"' EXIT +export PATH="${1:+$1:}$PATH" +mkdir -p $T/etc/config $T/bin $T/run $T/sys/bus/usb/devices/1-1 +echo 051d > $T/sys/bus/usb/devices/1-1/idVendor; echo 0002 > $T/sys/bus/usb/devices/1-1/idProduct +# fake NUT: daemons stay in the background with a pid file, upsc answers from a file +for p in usbhid-ups nutdrv_qx blazer_usb upsd upsmon; do + cat > $T/bin/$p <> $T/calls.log +case "\$*" in *-c\ stop*) kill \$(cat $T/run/$p.pid 2>/dev/null) 2>/dev/null; exit 0 ;; *-c\ fsd*) echo FSD >> $T/calls.log; exit 0 ;; *-k*) exit 0 ;; esac +[ -f $T/fail-$p ] && { echo "no matching device"; exit 1; } +( while :; do sleep 1; done ) & +case $p in upsd|upsmon) echo \$! > $T/run/$p.pid ;; *) echo \$! > \$NUT_STATEPATH/$p-ups.pid ;; esac +EOF + chmod 755 $T/bin/$p +done +printf '#!/bin/sh\ncat %s/upsc.out\n' $T > $T/bin/upsc; chmod 755 $T/bin/upsc +printf 'ups.status: OL\nbattery.charge: 100\nbattery.runtime: 1800\nups.load: 12\ndevice.mfr: APC\ndevice.model: Back-UPS ES 700\n' > $T/upsc.out +export SHAREOS_ROOT=$T SHAREOS_UPS_BIN=$T/bin SHAREOS_UPS_SYS=$T/sys +run() { sh "$P" "$@"; } +pass=0; failed=0 +check() { if eval "$2"; then echo "PASS $1"; pass=$((pass+1)); else echo "FAIL $1"; failed=$((failed+1)); fi; } +conf() { printf "config ups 'main'\n" > $T/etc/config/ups; for kv; do printf "\toption %s '%s'\n" "${kv%%=*}" "${kv#*=}" >> $T/etc/config/ups; done; } +C=$T/tmp/shareos-ups/conf + +conf enabled=1 shutdown=low +run start +check "start: driver, upsd and upsmon run" "grep -q '^usbhid-ups -a ups -u root' $T/calls.log && grep -q '^upsd -u root' $T/calls.log && grep -q '^upsmon' $T/calls.log" +check "APC (051d) is detected, usbhid-ups chosen" "grep -q 'driver = usbhid-ups' $C/ups.conf && run status | grep -qx 'detected=051d:0002'" +check "low battery: no override of the UPS limit" "! grep -q ignorelb $C/ups.conf" +check "upsd only on localhost without the server option" "[ \"\$(cat $C/upsd.conf)\" = 'LISTEN 127.0.0.1 3493' ]" +check "upsmon: primary, our shutdown and event commands, killpower flag" "grep -q 'MONITOR ups@localhost 1 shareos .* primary' $C/upsmon.conf && grep -q 'SHUTDOWNCMD \"/usr/sbin/shareos-ups shutdown\"' $C/upsmon.conf && grep -q 'NOTIFYFLAG ONBATT SYSLOG+EXEC' $C/upsmon.conf && grep -q POWERDOWNFLAG $C/upsmon.conf" +check "config files root only" "[ \$(stat -c %a $C/upsd.users) = 600 ]" +check "status: values of the UPS" "run status | grep -qx 'status=OL' && run status | grep -qx 'charge=100' && run status | grep -qx 'model=Back-UPS ES 700'" +run stop +check "stop: nothing runs" "! run status | grep -qx 'running=1'" + +conf enabled=1 shutdown=percent percent=40 server=1 password=geheim killpower=0 +: > $T/calls.log +run start +check "percent: the driver reports low battery below 40 %" "grep -q ignorelb $C/ups.conf && grep -q 'override.battery.charge.low = 40' $C/ups.conf" +check "server: upsd listens on the network, user monitor" "grep -q 'LISTEN 0.0.0.0 3493' $C/upsd.conf && grep -q -A2 '\\[monitor\\]' $C/upsd.users && grep -q 'password = geheim' $C/upsd.users" +check "killpower off: no flag" "! grep -q POWERDOWNFLAG $C/upsmon.conf" +run stop + +conf enabled=1 driver=auto; touch $T/fail-usbhid-ups; : > $T/calls.log +run start +check "auto: usbhid-ups finds nothing – nutdrv_qx is tried" "grep -q '^nutdrv_qx -a ups' $T/calls.log && grep -q 'driver = nutdrv_qx' $C/ups.conf" +run stop; rm $T/fail-usbhid-ups + +conf enabled=1 shutdown=minutes minutes=1 +run start +NOTIFYTYPE=ONBATT run "UPS ups@localhost on battery" +check "on battery: message and a timer" "grep -q 'notify warn ups-onbatt' $T/tmp/shareos-ups/notify.log && [ -s $T/tmp/shareos-ups/timer.pid ]" +NOTIFYTYPE=ONLINE run "UPS ups@localhost on line power" +check "power back: timer stopped, message cleared" "[ ! -e $T/tmp/shareos-ups/timer.pid ] && grep -q 'clear ups-onbatt' $T/tmp/shareos-ups/notify.log && grep -q 'notify info ups-online' $T/tmp/shareos-ups/notify.log" +printf 'ups.status: OB DISCHRG\n' > $T/upsc.out +run timer +check "timer over and still on battery: forced shutdown" "grep -q FSD $T/calls.log" +: > $T/calls.log; printf 'ups.status: OL\n' > $T/upsc.out +run timer +check "timer over but power back: nothing" "! grep -q FSD $T/calls.log" +check "events listed in the status" "run status | grep -q '^event=[0-9]* ONBATT'" +run shutdown +check "shutdown command powers off" "grep -qx poweroff $T/tmp/shareos-ups/notify.log" +touch $T/tmp/shareos-ups/killpower; : > $T/calls.log +run killpower +check "killpower: the driver tells the UPS to switch off" "grep -q 'usbhid-ups -a ups -u root -k' $T/calls.log" +run stop + +conf enabled=0 +check "boot while switched off: nothing" "run boot && ! [ -s $T/run/upsmon.pid ] || ! kill -0 \$(cat $T/run/upsmon.pid) 2>/dev/null" + +echo "test-ups: $pass passed, $failed failed" +[ $failed = 0 ] diff --git a/tools/i18n/ar.tsv b/tools/i18n/ar.tsv index d3e024f2..1fe64e66 100644 --- a/tools/i18n/ar.tsv +++ b/tools/i18n/ar.tsv @@ -1717,3 +1717,48 @@ E1348 دائمًا المنفذ 9100 (برنامج التشغيل على الح E1349 يستطيع أي شخص في الشبكة الطباعة دون تسجيل دخول. اختر «دائمًا المنفذ 9100» فقط إذا لم تطبع الطابعة جيدًا دون برنامج تشغيل. E1350 تم حفظ الطابعات. E1351 تم إرسال صفحة الاختبار. +E1352 UPS +E1353 جارٍ تحميل UPS … +E1354 التثبيت من المتجر (Network UPS Tools) +E1355 دعم UPS تطبيق نظام: يعرض الجهاز الشحن ووقت التشغيل المتبقي، ويبلّغ عن انقطاع الكهرباء ويُغلق بشكل سليم في الوقت المناسب. +E1356 البطارية شبه فارغة +E1357 يعمل على البطارية +E1358 على الكهرباء +E1359 لا بيانات +E1360 البطارية +E1361 الوقت المتبقي +E1362 الحِمل +E1363 جهد الشبكة +E1364 البطارية منذ +E1365 برنامج التشغيل +E1366 جارٍ الاتصال بـ UPS … +E1367 تم الاكتشاف: +E1368 شغّله أدناه. +E1369 الأحداث +E1370 لا شيء بعد – يظهر انقطاع الكهرباء هنا وكرسالة. +E1371 انقطاع الكهرباء – يعمل على UPS +E1372 عادت الكهرباء +E1373 تم تفعيل الإيقاف +E1374 الجهاز يُغلق +E1375 انقطع الاتصال بـ UPS +E1376 لا اتصال بـ UPS +E1377 عاد الاتصال بـ UPS +E1378 استبدل البطارية +E1379 مراقبة UPS (تبدأ أيضًا بعد إعادة التشغيل) +E1380 الإيقاف +E1381 عندما يبلّغ UPS عن «بطارية منخفضة» +E1382 تحت نسبة شحن +E1383 بعد مدة على البطارية +E1384 شحن البطارية (%) +E1385 دقائق على البطارية +E1386 برنامج التشغيل +E1387 تلقائي +E1388 blazer_usb (Megatec الأقدم) +E1389 ثم إيقاف UPS – يبدأ الجهاز مجددًا عند عودة الكهرباء +E1390 يمكن لأجهزة أخرى قراءة UPS عبر الشبكة (المنفذ 3493) +E1391 كلمة المرور لـ «MONITOR» +E1392 محفوظة – اتركها فارغة للإبقاء عليها +E1393 على الحاسوب الآخر (NUT، upsmon): +E1394 – وعندها يُغلق قبل الجهاز. +E1395 لا يُغلق الجهاز إلا أثناء عمله على البطارية. إذا عادت الكهرباء قبل ذلك فلا يحدث شيء. +E1396 تم حفظ UPS. diff --git a/tools/i18n/bn.tsv b/tools/i18n/bn.tsv index 90d8193f..772e1b11 100644 --- a/tools/i18n/bn.tsv +++ b/tools/i18n/bn.tsv @@ -1717,3 +1717,48 @@ E1348 সবসময় পোর্ট 9100 (কম্পিউটারে E1349 নেটওয়ার্কের যে কেউ সাইন-ইন ছাড়াই প্রিন্ট করতে পারে। “সবসময় পোর্ট 9100” কেবল তখনই, যখন প্রিন্টার ড্রাইভার ছাড়া ঠিকমতো প্রিন্ট করে না। E1350 প্রিন্টার সংরক্ষিত। E1351 টেস্ট পেজ পাঠানো হয়েছে। +E1352 UPS +E1353 UPS লোড হচ্ছে … +E1354 স্টোর থেকে ইনস্টল করুন (Network UPS Tools) +E1355 UPS সাপোর্ট একটি সিস্টেম অ্যাপ: বক্স চার্জ ও বাকি সময় দেখায়, বিদ্যুৎ চলে গেলে জানায় এবং সময়মতো ঠিকভাবে বন্ধ হয়। +E1356 ব্যাটারি প্রায় শেষ +E1357 ব্যাটারিতে চলছে +E1358 বিদ্যুৎ লাইনে +E1359 কোনো ডেটা নেই +E1360 ব্যাটারি +E1361 বাকি সময় +E1362 লোড +E1363 লাইন ভোল্টেজ +E1364 ব্যাটারি কবে থেকে +E1365 ড্রাইভার +E1366 UPS-এ সংযোগ হচ্ছে … +E1367 শনাক্ত: +E1368 নিচে চালু করুন। +E1369 ঘটনা +E1370 এখনো নেই – বিদ্যুৎ গেলে এখানে ও বার্তা হিসেবে দেখাবে। +E1371 বিদ্যুৎ বিভ্রাট – UPS-এ চলছে +E1372 বিদ্যুৎ ফিরে এসেছে +E1373 শাটডাউন শুরু +E1374 বক্স বন্ধ হচ্ছে +E1375 UPS-এর সাথে সংযোগ বিচ্ছিন্ন +E1376 UPS-এর সাথে সংযোগ নেই +E1377 UPS-এর সাথে সংযোগ ফিরে এসেছে +E1378 ব্যাটারি বদলান +E1379 UPS নজরে রাখুন (রিস্টার্টের পরেও চালু হবে) +E1380 শাটডাউন +E1381 যখন UPS “ব্যাটারি কম” জানায় +E1382 একটি চার্জের নিচে +E1383 ব্যাটারিতে কিছু সময় পরে +E1384 ব্যাটারি চার্জ (%) +E1385 ব্যাটারিতে মিনিট +E1386 ড্রাইভার +E1387 স্বয়ংক্রিয় +E1388 blazer_usb (পুরোনো Megatec) +E1389 তারপর UPS বন্ধ করুন – বিদ্যুৎ ফিরলে বক্স আবার চালু হবে +E1390 অন্য কম্পিউটার নেটওয়ার্কে UPS পড়তে পারবে (পোর্ট 3493) +E1391 “MONITOR”-এর পাসওয়ার্ড +E1392 সংরক্ষিত – ফাঁকা রাখলে আগেরটাই থাকবে +E1393 অন্য কম্পিউটারে (NUT, upsmon): +E1394 – তখন সেটি বক্সের আগে বন্ধ হবে। +E1395 বক্স কেবল ব্যাটারিতে চলার সময়ই বন্ধ হয়। আগে বিদ্যুৎ ফিরলে কিছুই হয় না। +E1396 UPS সংরক্ষিত। diff --git a/tools/i18n/es.tsv b/tools/i18n/es.tsv index a18e20c8..9cb9b088 100644 --- a/tools/i18n/es.tsv +++ b/tools/i18n/es.tsv @@ -1717,3 +1717,48 @@ E1348 Siempre puerto 9100 (controlador en el ordenador) E1349 Cualquiera en la red puede imprimir, sin iniciar sesión. «Siempre puerto 9100» solo si una impresora no imprime bien sin controlador. E1350 Impresoras guardadas. E1351 Página de prueba enviada. +E1352 SAI +E1353 Cargando SAI … +E1354 Instalar desde la tienda (Network UPS Tools) +E1355 El soporte de SAI es una app del sistema: la caja muestra la carga y la autonomía, avisa de un corte de luz y se apaga a tiempo de forma limpia. +E1356 Batería casi vacía +E1357 con batería +E1358 con red eléctrica +E1359 sin datos +E1360 BATERÍA +E1361 AUTONOMÍA +E1362 CARGA +E1363 Tensión de red +E1364 Batería desde +E1365 Controlador +E1366 Conectando con el SAI … +E1367 Detectado: +E1368 Actívalo abajo. +E1369 EVENTOS +E1370 Ninguno aún – un corte de luz se muestra aquí y como mensaje. +E1371 Corte de luz – funcionando con el SAI +E1372 Vuelve la luz +E1373 Apagado iniciado +E1374 La caja se apaga +E1375 Conexión con el SAI perdida +E1376 Sin conexión con el SAI +E1377 Conexión con el SAI restablecida +E1378 Cambiar la batería +E1379 Supervisar el SAI (también arranca tras reiniciar) +E1380 APAGADO +E1381 Cuando el SAI avisa de «batería baja» +E1382 Por debajo de una carga +E1383 Tras un tiempo con batería +E1384 CARGA DE LA BATERÍA (%) +E1385 MINUTOS CON BATERÍA +E1386 CONTROLADOR +E1387 Automático +E1388 blazer_usb (Megatec antiguos) +E1389 Después apagar el SAI – la caja arranca de nuevo al volver la luz +E1390 Otros ordenadores pueden leer el SAI por la red (puerto 3493) +E1391 CONTRASEÑA PARA «MONITOR» +E1392 guardada – déjala vacía para conservarla +E1393 En el otro ordenador (NUT, upsmon): +E1394 – entonces se apaga antes que la caja. +E1395 La caja solo se apaga mientras funciona con batería. Si la luz vuelve antes, no pasa nada. +E1396 SAI guardado. diff --git a/tools/i18n/fr.tsv b/tools/i18n/fr.tsv index 8b758d85..04752494 100644 --- a/tools/i18n/fr.tsv +++ b/tools/i18n/fr.tsv @@ -1717,3 +1717,48 @@ E1348 Toujours le port 9100 (pilote sur l'ordinateur) E1349 Tout le monde sur le réseau peut imprimer, sans connexion. « Toujours le port 9100 » seulement si une imprimante n'imprime pas correctement sans pilote. E1350 Imprimantes enregistrées. E1351 Page de test envoyée. +E1352 ONDULEUR +E1353 Chargement de l'onduleur … +E1354 Installer depuis le store (Network UPS Tools) +E1355 La prise en charge de l'onduleur est une application système : la box affiche alors la charge et l'autonomie, signale une coupure de courant et s'arrête proprement à temps. +E1356 Batterie presque vide +E1357 sur batterie +E1358 sur secteur +E1359 pas de données +E1360 BATTERIE +E1361 AUTONOMIE +E1362 CHARGE +E1363 Tension secteur +E1364 Batterie depuis +E1365 Pilote +E1366 Connexion à l'onduleur … +E1367 Détecté : +E1368 Activez-le ci-dessous. +E1369 ÉVÉNEMENTS +E1370 Aucun pour l'instant – une coupure de courant s'affiche ici et comme message. +E1371 Coupure de courant – sur onduleur +E1372 Courant rétabli +E1373 Arrêt déclenché +E1374 La box s'arrête +E1375 Connexion à l'onduleur perdue +E1376 Pas de connexion à l'onduleur +E1377 Connexion à l'onduleur rétablie +E1378 Remplacer la batterie +E1379 Surveiller l'onduleur (démarre aussi après un redémarrage) +E1380 ARRÊT +E1381 Quand l'onduleur signale « batterie faible » +E1382 Sous un niveau de charge +E1383 Après un temps sur batterie +E1384 CHARGE DE LA BATTERIE (%) +E1385 MINUTES SUR BATTERIE +E1386 PILOTE +E1387 Automatique +E1388 blazer_usb (anciens Megatec) +E1389 Ensuite éteindre l'onduleur – la box redémarre au retour du courant +E1390 D'autres ordinateurs peuvent lire l'onduleur via le réseau (port 3493) +E1391 MOT DE PASSE POUR « MONITOR » +E1392 enregistré – laisser vide pour le conserver +E1393 Sur l'autre ordinateur (NUT, upsmon) : +E1394 – il s'arrête alors avant la box. +E1395 La box ne s'arrête que tant qu'elle est sur batterie. Si le courant revient avant, rien ne se passe. +E1396 Onduleur enregistré. diff --git a/tools/i18n/hi.tsv b/tools/i18n/hi.tsv index b624f8dc..eae10dba 100644 --- a/tools/i18n/hi.tsv +++ b/tools/i18n/hi.tsv @@ -1717,3 +1717,48 @@ E1348 हमेशा पोर्ट 9100 (कंप्यूटर पर ड E1349 नेटवर्क में कोई भी बिना साइन-इन प्रिंट कर सकता है। “हमेशा पोर्ट 9100” तभी चुनें जब प्रिंटर बिना ड्राइवर ठीक से प्रिंट न करे। E1350 प्रिंटर सहेजे गए। E1351 टेस्ट पेज भेजा गया। +E1352 UPS +E1353 UPS लोड हो रहा है … +E1354 स्टोर से इंस्टॉल करें (Network UPS Tools) +E1355 UPS सपोर्ट एक सिस्टम ऐप है: बॉक्स चार्ज और शेष समय दिखाता है, बिजली जाने की सूचना देता है और समय पर ठीक से बंद होता है। +E1356 बैटरी लगभग खाली +E1357 बैटरी पर +E1358 मेन्स पर +E1359 कोई डेटा नहीं +E1360 बैटरी +E1361 शेष समय +E1362 लोड +E1363 मेन्स वोल्टेज +E1364 बैटरी कब से +E1365 ड्राइवर +E1366 UPS से जुड़ रहा है … +E1367 पहचाना गया: +E1368 नीचे चालू करें। +E1369 घटनाएँ +E1370 अभी कोई नहीं – बिजली जाने पर यहाँ और संदेश के रूप में दिखेगा। +E1371 बिजली गई – UPS पर चल रहा है +E1372 बिजली वापस आई +E1373 शटडाउन शुरू +E1374 बॉक्स बंद हो रहा है +E1375 UPS से कनेक्शन टूटा +E1376 UPS से कोई कनेक्शन नहीं +E1377 UPS से कनेक्शन वापस आया +E1378 बैटरी बदलें +E1379 UPS की निगरानी करें (रीस्टार्ट के बाद भी शुरू होगा) +E1380 शटडाउन +E1381 जब UPS “बैटरी कम” बताए +E1382 एक चार्ज स्तर से नीचे +E1383 बैटरी पर कुछ समय बाद +E1384 बैटरी चार्ज (%) +E1385 बैटरी पर मिनट +E1386 ड्राइवर +E1387 स्वचालित +E1388 blazer_usb (पुराने Megatec) +E1389 फिर UPS बंद करें – बिजली लौटने पर बॉक्स फिर शुरू होगा +E1390 दूसरे कंप्यूटर नेटवर्क से UPS पढ़ सकते हैं (पोर्ट 3493) +E1391 “MONITOR” के लिए पासवर्ड +E1392 सहेजा गया – खाली छोड़ें तो वही रहेगा +E1393 दूसरे कंप्यूटर पर (NUT, upsmon): +E1394 – तब वह बॉक्स से पहले बंद होगा। +E1395 बॉक्स तभी बंद होता है जब वह बैटरी पर चल रहा हो। बिजली पहले लौट आए तो कुछ नहीं होता। +E1396 UPS सहेजा गया। diff --git a/tools/i18n/pt.tsv b/tools/i18n/pt.tsv index 0c1bcb63..95801738 100644 --- a/tools/i18n/pt.tsv +++ b/tools/i18n/pt.tsv @@ -1717,3 +1717,48 @@ E1348 Sempre porta 9100 (controlador no computador) E1349 Qualquer pessoa na rede pode imprimir, sem iniciar sessão. «Sempre porta 9100» só se uma impressora não imprimir bem sem controlador. E1350 Impressoras guardadas. E1351 Página de teste enviada. +E1352 UPS +E1353 A carregar UPS … +E1354 Instalar a partir da loja (Network UPS Tools) +E1355 O suporte a UPS é uma app de sistema: a caixa mostra a carga e a autonomia, avisa de um corte de energia e desliga-se a tempo de forma limpa. +E1356 Bateria quase vazia +E1357 em bateria +E1358 na rede elétrica +E1359 sem dados +E1360 BATERIA +E1361 AUTONOMIA +E1362 CARGA +E1363 Tensão da rede +E1364 Bateria desde +E1365 Controlador +E1366 A ligar à UPS … +E1367 Detetado: +E1368 Ligue-o abaixo. +E1369 EVENTOS +E1370 Nenhum ainda – um corte de energia aparece aqui e como mensagem. +E1371 Corte de energia – a funcionar com a UPS +E1372 Energia reposta +E1373 Encerramento iniciado +E1374 A caixa está a desligar +E1375 Ligação à UPS perdida +E1376 Sem ligação à UPS +E1377 Ligação à UPS restabelecida +E1378 Substituir a bateria +E1379 Monitorizar a UPS (também arranca após reiniciar) +E1380 ENCERRAR +E1381 Quando a UPS indica «bateria fraca» +E1382 Abaixo de uma carga +E1383 Após algum tempo em bateria +E1384 CARGA DA BATERIA (%) +E1385 MINUTOS EM BATERIA +E1386 CONTROLADOR +E1387 Automático +E1388 blazer_usb (Megatec antigos) +E1389 Depois desligar a UPS – a caixa volta a arrancar quando a energia regressar +E1390 Outros computadores podem ler a UPS pela rede (porta 3493) +E1391 PALAVRA-PASSE PARA «MONITOR» +E1392 guardada – deixe vazio para a manter +E1393 No outro computador (NUT, upsmon): +E1394 – então desliga-se antes da caixa. +E1395 A caixa só se desliga enquanto estiver em bateria. Se a energia voltar antes, nada acontece. +E1396 UPS guardada. diff --git a/tools/i18n/ru.tsv b/tools/i18n/ru.tsv index e72d80a7..19b902e3 100644 --- a/tools/i18n/ru.tsv +++ b/tools/i18n/ru.tsv @@ -1717,3 +1717,48 @@ E1348 Всегда порт 9100 (драйвер на компьютере) E1349 Печатать может любой в сети, без входа. «Всегда порт 9100» – только если принтер без драйвера печатает неправильно. E1350 Принтеры сохранены. E1351 Тестовая страница отправлена. +E1352 ИБП +E1353 Загрузка ИБП … +E1354 Установить из магазина (Network UPS Tools) +E1355 Поддержка ИБП – системное приложение: устройство показывает заряд и время работы, сообщает об отключении питания и вовремя корректно выключается. +E1356 Батарея почти разряжена +E1357 от батареи +E1358 от сети +E1359 нет данных +E1360 БАТАРЕЯ +E1361 ВРЕМЯ РАБОТЫ +E1362 НАГРУЗКА +E1363 Напряжение сети +E1364 Батарея с +E1365 Драйвер +E1366 Подключение к ИБП … +E1367 Обнаружено: +E1368 Включите ниже. +E1369 СОБЫТИЯ +E1370 Пока нет – отключение питания появится здесь и в сообщениях. +E1371 Отключение питания – работа от ИБП +E1372 Питание восстановлено +E1373 Запущено выключение +E1374 Устройство выключается +E1375 Связь с ИБП потеряна +E1376 Нет связи с ИБП +E1377 Связь с ИБП восстановлена +E1378 Замените батарею +E1379 Следить за ИБП (запускается и после перезагрузки) +E1380 ВЫКЛЮЧЕНИЕ +E1381 Когда ИБП сообщает «батарея разряжена» +E1382 Ниже уровня заряда +E1383 Через время работы от батареи +E1384 ЗАРЯД БАТАРЕИ (%) +E1385 МИНУТ ОТ БАТАРЕИ +E1386 ДРАЙВЕР +E1387 Автоматически +E1388 blazer_usb (старые Megatec) +E1389 Затем выключить ИБП – устройство снова запустится при появлении питания +E1390 Другие компьютеры могут читать данные ИБП по сети (порт 3493) +E1391 ПАРОЛЬ ДЛЯ «MONITOR» +E1392 сохранён – оставьте пустым, чтобы не менять +E1393 На другом компьютере (NUT, upsmon): +E1394 – тогда он выключится раньше устройства. +E1395 Устройство выключается, только пока работает от батареи. Если питание вернётся раньше, ничего не произойдёт. +E1396 ИБП сохранён. diff --git a/tools/i18n/zh.tsv b/tools/i18n/zh.tsv index 025024fb..9496c2c1 100644 --- a/tools/i18n/zh.tsv +++ b/tools/i18n/zh.tsv @@ -1717,3 +1717,48 @@ E1348 始终使用 9100 端口(驱动装在电脑上) E1349 网络中的任何人都可以打印,无需登录。仅当打印机免驱动打印不正常时才选“始终使用 9100 端口”。 E1350 打印机设置已保存。 E1351 测试页已发送。 +E1352 UPS +E1353 正在加载 UPS…… +E1354 从商店安装 (Network UPS Tools) +E1355 UPS 支持是一个系统应用:设备会显示电量和剩余时间,报告停电并及时安全关机。 +E1356 电池电量低 +E1357 电池供电中 +E1358 市电供电 +E1359 无数据 +E1360 电池 +E1361 剩余时间 +E1362 负载 +E1363 市电电压 +E1364 电池启用于 +E1365 驱动 +E1366 正在连接 UPS…… +E1367 已检测到: +E1368 在下方启用。 +E1369 事件 +E1370 暂无——停电会显示在这里并作为消息通知。 +E1371 停电——由 UPS 供电 +E1372 供电恢复 +E1373 已触发关机 +E1374 设备正在关机 +E1375 与 UPS 的连接中断 +E1376 无法连接 UPS +E1377 与 UPS 的连接已恢复 +E1378 更换电池 +E1379 监控 UPS(重启后也会启动) +E1380 关机 +E1381 当 UPS 报告“电池电量低”时 +E1382 低于某电量时 +E1383 电池供电一段时间后 +E1384 电池电量 (%) +E1385 电池供电分钟数 +E1386 驱动 +E1387 自动 +E1388 blazer_usb(旧款 Megatec) +E1389 随后关闭 UPS——供电恢复时设备会重新启动 +E1390 允许其他电脑通过网络读取 UPS(端口 3493) +E1391 “MONITOR”的密码 +E1392 已保存——留空则保留 +E1393 在另一台电脑上(NUT、upsmon): +E1394 ——它会在本设备之前关机。 +E1395 只有在电池供电期间设备才会关机。如果在此之前恢复供电,则不会发生任何事。 +E1396 UPS 设置已保存。