mod_led_control: added netdev trigger, more options for timer trigger.

This commit is contained in:
gSpot
2025-08-09 18:37:11 +03:00
parent 9067d3d3ab
commit 1c02ace538
11 changed files with 466 additions and 115 deletions

View File

@@ -8,6 +8,7 @@ Internet-detector is an application for checking the availability of the Interne
![](https://github.com/gSpotx2f/luci-app-internet-detector/blob/master/screenshots/internet-led.jpg) ![](https://github.com/gSpotx2f/luci-app-internet-detector/blob/master/screenshots/internet-led.jpg)
- Performing actions when connecting and disconnecting the Internet: rebooting device, restarting network or modem (internet-detector-mod-modem-restart), executing custom shell scripts. - Performing actions when connecting and disconnecting the Internet: rebooting device, restarting network or modem (internet-detector-mod-modem-restart), executing custom shell scripts.
- Sending email notification when Internet access is restored (internet-detector-mod-email). - Sending email notification when Internet access is restored (internet-detector-mod-email).
- Sending telegtam notification when Internet access is restored (internet-detector-mod-telegram).
- The daemon is written entirely in Lua using the luaposix library. - The daemon is written entirely in Lua using the luaposix library.
**OpenWrt >= 21.02.** **OpenWrt >= 21.02.**
@@ -19,22 +20,22 @@ Internet-detector is an application for checking the availability of the Interne
## Installation notes: ## Installation notes:
opkg update opkg update
wget --no-check-certificate -O /tmp/internet-detector_1.6.1-r1_all.ipk https://github.com/gSpotx2f/packages-openwrt/raw/master/current/internet-detector_1.6.1-r1_all.ipk wget --no-check-certificate -O /tmp/internet-detector_1.6.2-r1_all.ipk https://github.com/gSpotx2f/packages-openwrt/raw/master/current/internet-detector_1.6.2-r1_all.ipk
opkg install /tmp/internet-detector_1.6.1-r1_all.ipk opkg install /tmp/internet-detector_1.6.2-r1_all.ipk
rm /tmp/internet-detector_1.6.1-r1_all.ipk rm /tmp/internet-detector_1.6.2-r1_all.ipk
service internet-detector start service internet-detector start
service internet-detector enable service internet-detector enable
wget --no-check-certificate -O /tmp/luci-app-internet-detector_1.6.1-r1_all.ipk https://github.com/gSpotx2f/packages-openwrt/raw/master/current/luci-app-internet-detector_1.6.1-r1_all.ipk wget --no-check-certificate -O /tmp/luci-app-internet-detector_1.6.2-r1_all.ipk https://github.com/gSpotx2f/packages-openwrt/raw/master/current/luci-app-internet-detector_1.6.2-r1_all.ipk
opkg install /tmp/luci-app-internet-detector_1.6.1-r1_all.ipk opkg install /tmp/luci-app-internet-detector_1.6.2-r1_all.ipk
rm /tmp/luci-app-internet-detector_1.6.1-r1_all.ipk rm /tmp/luci-app-internet-detector_1.6.2-r1_all.ipk
service rpcd restart service rpcd restart
i18n-ru: i18n-ru:
wget --no-check-certificate -O /tmp/luci-i18n-internet-detector-ru_1.6.1-r1_all.ipk https://github.com/gSpotx2f/packages-openwrt/raw/master/current/luci-i18n-internet-detector-ru_1.6.1-r1_all.ipk wget --no-check-certificate -O /tmp/luci-i18n-internet-detector-ru_1.6.2-r1_all.ipk https://github.com/gSpotx2f/packages-openwrt/raw/master/current/luci-i18n-internet-detector-ru_1.6.2-r1_all.ipk
opkg install /tmp/luci-i18n-internet-detector-ru_1.6.1-r1_all.ipk opkg install /tmp/luci-i18n-internet-detector-ru_1.6.2-r1_all.ipk
rm /tmp/luci-i18n-internet-detector-ru_1.6.1-r1_all.ipk rm /tmp/luci-i18n-internet-detector-ru_1.6.2-r1_all.ipk
## Screenshots: ## Screenshots:
@@ -46,9 +47,9 @@ i18n-ru:
**Dependences:** modemmanager. **Dependences:** modemmanager.
wget --no-check-certificate -O /tmp/internet-detector-mod-modem-restart_1.6.1-r1_all.ipk https://github.com/gSpotx2f/packages-openwrt/raw/master/current/internet-detector-mod-modem-restart_1.6.1-r1_all.ipk wget --no-check-certificate -O /tmp/internet-detector-mod-modem-restart_1.6.2-r1_all.ipk https://github.com/gSpotx2f/packages-openwrt/raw/master/current/internet-detector-mod-modem-restart_1.6.2-r1_all.ipk
opkg install /tmp/internet-detector-mod-modem-restart_1.6.1-r1_all.ipk opkg install /tmp/internet-detector-mod-modem-restart_1.6.2-r1_all.ipk
rm /tmp/internet-detector-mod-modem-restart_1.6.1-r1_all.ipk rm /tmp/internet-detector-mod-modem-restart_1.6.2-r1_all.ipk
service internet-detector restart service internet-detector restart
![](https://github.com/gSpotx2f/luci-app-internet-detector/blob/master/screenshots/04.jpg) ![](https://github.com/gSpotx2f/luci-app-internet-detector/blob/master/screenshots/04.jpg)
@@ -57,9 +58,9 @@ i18n-ru:
**Dependences:** mailsend. **Dependences:** mailsend.
wget --no-check-certificate -O /tmp/internet-detector-mod-email_1.6.1-r1_all.ipk https://github.com/gSpotx2f/packages-openwrt/raw/master/current/internet-detector-mod-email_1.6.1-r1_all.ipk wget --no-check-certificate -O /tmp/internet-detector-mod-email_1.6.2-r1_all.ipk https://github.com/gSpotx2f/packages-openwrt/raw/master/current/internet-detector-mod-email_1.6.2-r1_all.ipk
opkg install /tmp/internet-detector-mod-email_1.6.1-r1_all.ipk opkg install /tmp/internet-detector-mod-email_1.6.2-r1_all.ipk
rm /tmp/internet-detector-mod-email_1.6.1-r1_all.ipk rm /tmp/internet-detector-mod-email_1.6.2-r1_all.ipk
service internet-detector restart service internet-detector restart
![](https://github.com/gSpotx2f/luci-app-internet-detector/blob/master/screenshots/05.jpg) ![](https://github.com/gSpotx2f/luci-app-internet-detector/blob/master/screenshots/05.jpg)
@@ -68,9 +69,9 @@ i18n-ru:
**Dependences:** curl. **Dependences:** curl.
wget --no-check-certificate -O /tmp/internet-detector-mod-telegram_1.6.1-r1_all.ipk https://github.com/gSpotx2f/packages-openwrt/raw/master/current/internet-detector-mod-telegram_1.6.1-r1_all.ipk wget --no-check-certificate -O /tmp/internet-detector-mod-telegram_1.6.2-r1_all.ipk https://github.com/gSpotx2f/packages-openwrt/raw/master/current/internet-detector-mod-telegram_1.6.2-r1_all.ipk
opkg install /tmp/internet-detector-mod-telegram_1.6.1-r1_all.ipk opkg install /tmp/internet-detector-mod-telegram_1.6.2-r1_all.ipk
rm /tmp/internet-detector-mod-telegram_1.6.1-r1_all.ipk rm /tmp/internet-detector-mod-telegram_1.6.2-r1_all.ipk
service internet-detector restart service internet-detector restart
![](https://github.com/gSpotx2f/luci-app-internet-detector/blob/master/screenshots/06.jpg) ![](https://github.com/gSpotx2f/luci-app-internet-detector/blob/master/screenshots/06.jpg)

View File

@@ -5,7 +5,7 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=internet-detector-mod-email PKG_NAME:=internet-detector-mod-email
PKG_VERSION:=1.6.1 PKG_VERSION:=1.6.2
PKG_RELEASE:=1 PKG_RELEASE:=1
PKG_MAINTAINER:=gSpot <https://github.com/gSpotx2f/luci-app-internet-detector> PKG_MAINTAINER:=gSpot <https://github.com/gSpotx2f/luci-app-internet-detector>

View File

@@ -5,7 +5,7 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=internet-detector-mod-modem-restart PKG_NAME:=internet-detector-mod-modem-restart
PKG_VERSION:=1.6.1 PKG_VERSION:=1.6.2
PKG_RELEASE:=1 PKG_RELEASE:=1
PKG_MAINTAINER:=gSpot <https://github.com/gSpotx2f/luci-app-internet-detector> PKG_MAINTAINER:=gSpot <https://github.com/gSpotx2f/luci-app-internet-detector>

View File

@@ -5,7 +5,7 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=internet-detector-mod-telegram PKG_NAME:=internet-detector-mod-telegram
PKG_VERSION:=1.6.1 PKG_VERSION:=1.6.2
PKG_RELEASE:=1 PKG_RELEASE:=1
PKG_MAINTAINER:=gSpot <https://github.com/gSpotx2f/luci-app-internet-detector> PKG_MAINTAINER:=gSpot <https://github.com/gSpotx2f/luci-app-internet-detector>

View File

@@ -5,7 +5,7 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=internet-detector PKG_NAME:=internet-detector
PKG_VERSION:=1.6.1 PKG_VERSION:=1.6.2
PKG_RELEASE:=1 PKG_RELEASE:=1
PKG_MAINTAINER:=gSpot <https://github.com/gSpotx2f/luci-app-internet-detector> PKG_MAINTAINER:=gSpot <https://github.com/gSpotx2f/luci-app-internet-detector>

View File

@@ -1,6 +1,7 @@
local unistd = require("posix.unistd")
local dirent = require("posix.dirent") local dirent = require("posix.dirent")
local time = require("posix.time")
local unistd = require("posix.unistd")
local Module = { local Module = {
name = "mod_led_control", name = "mod_led_control",
@@ -13,39 +14,40 @@ local Module = {
runInterval = 5, runInterval = 5,
sysLedsDir = "/sys/class/leds", sysLedsDir = "/sys/class/leds",
ledsPerInstance = 3, ledsPerInstance = 3,
ledAction1Default = 1, -- 1: off, 2: on, 3: blink ledAction1Default = 1, -- 1: off, 2: on, 3: blinking, 4: netdev
ledAction2Default = 1, ledAction2Default = 1,
ledBlinkDelayDefault = 500,
ledNetlinkDeviceDefault = nil,
ledNetdevModeLinkDefault = "1",
ledNetdevModeRxDefault = "0",
ledNetdevModeTxDefault = "0",
status = nil, status = nil,
_enabled = false, _enabled = false,
_leds = {}, _leds = {},
_counter = 0, _counter = 0,
} }
function Module:resetLeds()
local ok, dir = pcall(dirent.files, self.sysLedsDir)
if not ok then
return
end
for led in dir do
local brightness = string.format("%s/%s/brightness", self.sysLedsDir, led)
if unistd.access(brightness, "w") then
self.writeValue(brightness, 0)
end
end
end
function Module:setLedAttrs(t) function Module:setLedAttrs(t)
t.ledDir = string.format("%s/%s", self.sysLedsDir, t.ledName) t.ledDir = string.format("%s/%s", self.sysLedsDir, t.ledName)
t.ledMaxBrightnessFile = string.format("%s/max_brightness", t.ledDir) t.ledMaxBrightnessFile = string.format("%s/max_brightness", t.ledDir)
t.ledBrightnessFile = string.format("%s/brightness", t.ledDir) t.ledBrightnessFile = string.format("%s/brightness", t.ledDir)
t.ledMaxBrightness = self.readValue(t.ledMaxBrightnessFile) or 1 t.ledMaxBrightness = self.readValue(t.ledMaxBrightnessFile) or "1"
t.ledTriggerFile = string.format("%s/trigger", t.ledDir) t.ledTriggerFile = string.format("%s/trigger", t.ledDir)
t.ledDelayOnFile = string.format("%s/delay_on", t.ledDir)
t.ledDelayOffFile = string.format("%s/delay_off", t.ledDir)
t.ledDeviceNameFile = string.format("%s/device_name", t.ledDir)
t.ledLinkFile = string.format("%s/link", t.ledDir)
t.ledRxFile = string.format("%s/rx", t.ledDir)
t.ledTxFile = string.format("%s/tx", t.ledDir)
t.ledPrevState = { t.ledPrevState = {
brightness = self.readValue(t.ledBrightnessFile), brightness = self.readValue(t.ledBrightnessFile),
trigger = self.readValue(t.ledTriggerFile), trigger = self.readValue(t.ledTriggerFile),
} }
if t.ledPrevState.trigger then if t.ledPrevState.trigger then
t.ledPrevState.trigger = t.ledPrevState.trigger:match("%[[%w%-_]+%]"):gsub("[%]%[]", "") local val = t.ledPrevState.trigger:match("%[[%w%-_]+%]")
if val then
t.ledPrevState.trigger = val:gsub("[%]%[]", "")
end
end end
end end
@@ -65,10 +67,43 @@ function Module:init(t)
return return
end end
for i, l in ipairs(self._leds) do for i, l in ipairs(self._leds) do
if t["led" .. i .. "_name"] ~= nil then local led = "led" .. i
l.ledName = t["led" .. i .. "_name"] if t[led .. "_name"] ~= nil then
l.ledAction1 = tonumber(t["led" .. i .. "_action_1"]) or self.ledAction1Default l.ledName = t[led .. "_name"]
l.ledAction2 = tonumber(t["led" .. i .. "_action_2"]) or self.ledAction2Default l.ledAction1 = tonumber(t[led .. "_action_1"]) or self.ledAction1Default
l.ledAction2 = tonumber(t[led .. "_action_2"]) or self.ledAction2Default
l.ledBlinkOnDelay1 = tonumber(t[led .. "_blink_on_delay_1"]) or self.ledBlinkDelayDefault
l.ledBlinkOffDelay1 = tonumber(t[led .. "_blink_off_delay_1"]) or self.ledBlinkDelayDefault
l.ledBlinkOnDelay2 = tonumber(t[led .. "_blink_on_delay_2"]) or self.ledBlinkDelayDefault
l.ledBlinkOffDelay2 = tonumber(t[led .. "_blink_off_delay_2"]) or self.ledBlinkDelayDefault
l.ledNetlinkDevice1 = t[led .. "_netdev_device_1"] or self.ledNetlinkDeviceDefault
l.ledNetlinkDevice2 = t[led .. "_netdev_device_2"] or self.ledNetlinkDeviceDefault
l.ledNetdevModeLink1 = self.ledNetdevModeLinkDefault
l.ledNetdevModeTx1 = self.ledNetdevModeTxDefault
l.ledNetdevModeRx1 = self.ledNetdevModeRxDefault
l.ledNetdevModeLink2 = self.ledNetdevModeLinkDefault
l.ledNetdevModeTx2 = self.ledNetdevModeTxDefault
l.ledNetdevModeRx2 = self.ledNetdevModeRxDefault
local ndm1 = t[led .. "_netdev_mode_1"]
if ndm1 ~= nil and type(ndm1) == "table" then
local enabledFlags = {}
for _, v in ipairs(ndm1) do
enabledFlags[v] = "1"
end
l.ledNetdevModeLink1 = enabledFlags.link or "0"
l.ledNetdevModeTx1 = enabledFlags.tx or "0"
l.ledNetdevModeRx1 = enabledFlags.rx or "0"
end
local ndm2 = t[led .. "_netdev_mode_2"]
if ndm2 ~= nil and type(ndm2) == "table" then
local enabledFlags = {}
for _, v in ipairs(ndm2) do
enabledFlags[v] = "1"
end
l.ledNetdevModeLink2 = enabledFlags.link or "0"
l.ledNetdevModeTx2 = enabledFlags.tx or "0"
l.ledNetdevModeRx2 = enabledFlags.rx or "0"
end
self:setLedAttrs(l) self:setLedAttrs(l)
l.enabled = true l.enabled = true
else else
@@ -82,29 +117,119 @@ function Module:init(t)
end end
end end
function Module:setTriggerTimer(t) function Module:checkLedTimer(t)
self.writeValue(t.ledTriggerFile, "timer") return (unistd.access(t.ledDelayOnFile, "rw") and unistd.access(t.ledDelayOffFile, "rw"))
end
function Module:checkLedNetdev(t)
return (unistd.access(t.ledDeviceNameFile, "rw") and
unistd.access(t.ledLinkFile, "rw") and
unistd.access(t.ledRxFile, "rw") and
unistd.access(t.ledTxFile, "rw"))
end end
function Module:setTriggerNone(t) function Module:setTriggerNone(t)
self.writeValue(t.ledTriggerFile, "none") self.writeValue(t.ledTriggerFile, "none")
self.debugOutput(string.format(
"%s: LED TRIGGER SET: none, %s", self.name, t.ledTriggerFile))
end
function Module:setTriggerTimer(t, delayOn, delayOff)
if not delayOn then
delayOn = self.ledBlinkDelayDefault
end
if not delayOff then
delayOff = self.ledBlinkDelayDefault
end
self.writeValue(t.ledTriggerFile, "timer")
for i = 0, 10 do
if self:checkLedTimer(t) then
self.writeValue(t.ledDelayOnFile, delayOn)
self.writeValue(t.ledDelayOffFile, delayOff)
break
else
time.nanosleep({ tv_sec = 0, tv_nsec = 500000 })
end
end
self.debugOutput(string.format(
"%s: LED TRIGGER SET: timer, %s; delayOn = %s, delayOff = %s",
self.name, t.ledTriggerFile, tostring(delayOn), tostring(delayOff))
)
end
function Module:setTriggerNetdev(t, device, link, tx, rx)
if not device then
return
end
self.writeValue(t.ledTriggerFile, "netdev")
for i = 0, 10 do
if self:checkLedNetdev(t) then
self.writeValue(t.ledDeviceNameFile, device)
self.writeValue(t.ledLinkFile, link)
self.writeValue(t.ledTxFile, tx)
self.writeValue(t.ledRxFile, rx)
break
else
time.nanosleep({ tv_sec = 0, tv_nsec = 500000 })
end
end
self.debugOutput(string.format(
"%s: LED TRIGGER SET: netdev, %s; device = %s, link = %s, rx = %s, tx = %s",
self.name, t.ledTriggerFile, tostring(device), tostring(link), tostring(rx), tostring(tx))
)
end end
function Module:getCurrentTrigger(t) function Module:getCurrentTrigger(t)
local trigger = self.readValue(t.ledTriggerFile) local trigger = self.readValue(t.ledTriggerFile)
if trigger and trigger:match("%[timer%]") then if trigger then
return true if trigger:match("%[timer%]") then
return "timer"
elseif trigger:match("%[netdev%]") then
return "netdev"
end end
end
end
function Module:getTriggerValues(t, trigger)
local currentTrigger = self:getCurrentTrigger(t)
if trigger == currentTrigger then
if trigger == "timer" then
return {
trigger = currentTrigger,
delayOn = tonumber(self.readValue(t.ledDelayOnFile)),
delayOff = tonumber(self.readValue(t.ledDelayOffFile)),
}
elseif trigger == "netdev" then
return {
trigger = currentTrigger,
device = self.readValue(t.ledDeviceNameFile),
link = self.readValue(t.ledLinkFile),
tx = self.readValue(t.ledTxFile),
rx = self.readValue(t.ledRxFile),
}
end
end
return {}
end end
function Module:on(t) function Module:on(t)
self:setTriggerNone(t) self:setTriggerNone(t)
self.writeValue(t.ledBrightnessFile, t.ledMaxBrightness) self.writeValue(t.ledBrightnessFile, t.ledMaxBrightness)
self.debugOutput(string.format("%s: LED ON: %s", self.name, t.ledBrightnessFile))
end end
function Module:off(t) function Module:off(t)
self:setTriggerNone(t) self:setTriggerNone(t)
self.writeValue(t.ledBrightnessFile, 0) self.writeValue(t.ledBrightnessFile, "0")
self.debugOutput(string.format("%s: LED OFF: %s", self.name, t.ledBrightnessFile))
end end
function Module:getCurrentState(t) function Module:getCurrentState(t)
@@ -125,8 +250,21 @@ function Module:ledRunFunc(t, currentStatus)
self:on(t) self:on(t)
end end
elseif t.ledAction1 == 3 then elseif t.ledAction1 == 3 then
if not self:getCurrentTrigger(t) then local triggerValues = self:getTriggerValues(t, "timer")
self:setTriggerTimer(t) if (not next(triggerValues)) or (triggerValues.delayOn ~= t.ledBlinkOnDelay1 or
triggerValues.delayOff ~= t.ledBlinkOffDelay1) then
self:setTriggerTimer(t, t.ledBlinkOnDelay1, t.ledBlinkOffDelay1)
end
elseif t.ledAction1 == 4 then
local triggerValues = self:getTriggerValues(t, "netdev")
if (not next(triggerValues)) or (triggerValues.device ~= t.ledNetlinkDevice1 or
triggerValues.link ~= t.ledNetdevModeLink1 or
triggerValues.tx ~= t.ledNetdevModeTx1 or
triggerValues.rx ~= t.ledNetdevModeRx1) then
self:setTriggerNetdev(t,
t.ledNetlinkDevice1, t.ledNetdevModeLink1,
t.ledNetdevModeTx1, t.ledNetdevModeRx1
)
end end
end end
else else
@@ -139,8 +277,21 @@ function Module:ledRunFunc(t, currentStatus)
self:on(t) self:on(t)
end end
elseif t.ledAction2 == 3 then elseif t.ledAction2 == 3 then
if not self:getCurrentTrigger(t) then local triggerValues = self:getTriggerValues(t, "timer")
self:setTriggerTimer(t) if (not next(triggerValues)) or (triggerValues.delayOn ~= t.ledBlinkOnDelay2 or
triggerValues.delayOff ~= t.ledBlinkOffDelay2) then
self:setTriggerTimer(t, t.ledBlinkOnDelay2, t.ledBlinkOffDelay2)
end
elseif t.ledAction2 == 4 then
local triggerValues = self:getTriggerValues(t, "netdev")
if (not next(triggerValues)) or (triggerValues.device ~= t.ledNetlinkDevice2 or
triggerValues.link ~= t.ledNetdevModeLink2 or
triggerValues.tx ~= t.ledNetdevModeTx2 or
triggerValues.rx ~= t.ledNetdevModeRx2) then
self:setTriggerNetdev(t,
t.ledNetlinkDevice2, t.ledNetdevModeLink2,
t.ledNetdevModeTx2, t.ledNetdevModeRx2
)
end end
end end
end end

View File

@@ -350,12 +350,15 @@ end
function Module:httpRequest(url) function Module:httpRequest(url)
local retCode = 1, data local retCode = 1, data
local iface = "" local curl = string.format(
if self.config.serviceConfig.iface then '%s%s --connect-timeout %s %s "%s"; printf "\n$?";',
iface = " --interface " .. self.config.serviceConfig.iface self.curlExec,
end self.config.serviceConfig.iface and (" --interface " .. self.config.serviceConfig.iface) or "",
local fh = io.popen(string.format( self.timeout,
'%s%s --connect-timeout %s %s "%s"; printf "\n$?";', self.curlExec, iface, self.timeout, self.curlParams, url), "r") self.curlParams,
url
)
local fh = io.popen(curl, "r")
if fh then if fh then
data = fh:read("*a") data = fh:read("*a")
fh:close() fh:close()

View File

@@ -5,7 +5,7 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-internet-detector PKG_NAME:=luci-app-internet-detector
PKG_VERSION:=1.6.1 PKG_VERSION:=1.6.2
PKG_RELEASE:=1 PKG_RELEASE:=1
LUCI_TITLE:=LuCI support for internet-detector LUCI_TITLE:=LuCI support for internet-detector
LUCI_DEPENDS:=+internet-detector LUCI_DEPENDS:=+internet-detector

View File

@@ -624,7 +624,8 @@ return view.extend({
this.getInit(), this.getInit(),
uci.load(this.appName), uci.load(this.appName),
]).catch(e => { ]).catch(e => {
ui.addNotification(null, E('p', _('An error has occurred') + ': %s'.format(e.message))); ui.addNotification(
null, E('p', _('An error has occurred') + ': %s'.format(e.message)));
}); });
}, },
@@ -749,6 +750,18 @@ return view.extend({
list.value(600, '10 ' + _('min')); list.value(600, '10 ' + _('min'));
} }
function makeTimerDelayOptions(list) {
list.value(25, '25 ' + _('msec'));
list.value(50, '50 ' + _('msec'));
list.value(100, '100 ' + _('msec'));
list.value(250, '250 ' + _('msec'));
list.value(500, '500 ' + _('msec'));
list.value(750, '750 ' + _('msec'));
list.value(1000, '1 ' + _('sec'));
list.value(1500, '1.5 ' + _('sec'));
list.value(2000, '2 ' + _('sec'));
}
// enabled // enabled
o = s.taboption('main', form.Flag, 'enabled', o = s.taboption('main', form.Flag, 'enabled',
_('Enabled'), _('Enabled'),
@@ -898,13 +911,14 @@ return view.extend({
this.leds.sort((a, b) => a.name > b.name); this.leds.sort((a, b) => a.name > b.name);
// enabled // enabled
o = s.taboption('led_control', form.Flag, 'mod_led_control_enabled', o = s.taboption('led_control', form.Flag,
'mod_led_control_enabled',
_('Enabled')); _('Enabled'));
o.rmempty = false; o.rmempty = false;
o.modalonly = true; o.modalonly = true;
o = s.taboption('led_control', form.SectionValue, s.section, form.NamedSection, o = s.taboption('led_control', form.SectionValue,
s.section); s.section, form.NamedSection, s.section);
o.depends({ mod_led_control_enabled: '1' }); o.depends({ mod_led_control_enabled: '1' });
ss = o.subsection; ss = o.subsection;
@@ -912,7 +926,8 @@ return view.extend({
ss.tab('led' + i + '_tab', _('LED') + ' ' + i); ss.tab('led' + i + '_tab', _('LED') + ' ' + i);
// led_name // led_name
o = ss.taboption('led' + i + '_tab', form.ListValue, 'mod_led_control_led' + i + '_name', o = ss.taboption('led' + i + '_tab', form.ListValue,
'mod_led_control_led' + i + '_name',
_('<abbr title="Light Emitting Diode">LED</abbr> Name')); _('<abbr title="Light Emitting Diode">LED</abbr> Name'));
o.modalonly = true; o.modalonly = true;
if(i > 1) { if(i > 1) {
@@ -922,24 +937,117 @@ return view.extend({
this.leds.forEach(e => o.value(e.name)); this.leds.forEach(e => o.value(e.name));
// led_action_1 // led_action_1
o = ss.taboption('led' + i + '_tab', form.ListValue, 'mod_led_control_led' + i + '_action_1', o = ss.taboption('led' + i + '_tab', form.ListValue,
_('Action when connected')); 'mod_led_control_led' + i + '_action_1',
_('After connection'));
o.depends({ ['mod_led_control_led' + i + '_name']: /.+/ }); o.depends({ ['mod_led_control_led' + i + '_name']: /.+/ });
o.modalonly = true; o.modalonly = true;
o.value(1, _('Off')); o.value(1, _('Off'));
o.value(2, _('On')); o.value(2, _('On'));
o.value(3, _('Blink')); o.value(3, _('Blinking (kernel: timer)'));
o.value(4, _('Network device activity (kernel: netdev)'));
o.default = '2'; o.default = '2';
// blink_on_delay_1
o = ss.taboption('led' + i + '_tab', form.ListValue,
'mod_led_control_led' + i + '_blink_on_delay_1',
_('On-state delay'),
_('On-state delay for blinking option.'));
makeTimerDelayOptions(o);
o.depends({ ['mod_led_control_led' + i + '_action_1']: '3' });
o.modalonly = true;
o.default = '500';
// blink_off_delay_1
o = ss.taboption('led' + i + '_tab', form.ListValue,
'mod_led_control_led' + i + '_blink_off_delay_1',
_('Off-state delay'),
_('Off-state delay for blinking option.'));
makeTimerDelayOptions(o);
o.depends({ ['mod_led_control_led' + i + '_action_1']: '3' });
o.modalonly = true;
o.default = '500';
// netdev_device_1
o = ss.taboption('led' + i + '_tab', widgets.DeviceSelect,
'mod_led_control_led' + i + '_netdev_device_1',
_('Device'),
_('<abbr title="Light Emitting Diode">LED</abbr> will display the link activity of this network device.')
);
o.depends({ ['mod_led_control_led' + i + '_action_1']: '4' });
o.modalonly = true;
o.rmempty = false;
o.noaliases = true;
// netdev_mode_1
o = ss.taboption('led' + i + '_tab', form.MultiValue,
'mod_led_control_led' + i + '_netdev_mode_1',
_('<abbr title="Light Emitting Diode">LED</abbr> mode')
);
o.depends({ ['mod_led_control_led' + i + '_action_1']: '4' });
o.modalonly = true;
o.value('link', _('Link On'));
o.value('tx', _('Tramsmit'));
o.value('rx', _('Receive'));
o.default = 'link';
o.rmempty = false;
// led_action_2 // led_action_2
o = ss.taboption('led' + i + '_tab', form.ListValue, 'mod_led_control_led' + i + '_action_2', o = ss.taboption('led' + i + '_tab', form.ListValue,
_('Action when disconnected')); 'mod_led_control_led' + i + '_action_2',
_('After disconnection'));
o.depends({ ['mod_led_control_led' + i + '_name']: /.+/ }); o.depends({ ['mod_led_control_led' + i + '_name']: /.+/ });
o.modalonly = true; o.modalonly = true;
o.value(1, _('Off')); o.value(1, _('Off'));
o.value(2, _('On')); o.value(2, _('On'));
o.value(3, _('Blink')); o.value(3, _('Blinking (kernel: timer)'));
o.value(4, _('Network device activity (kernel: netdev)'));
o.default = '1'; o.default = '1';
// blink_on_delay_2
o = ss.taboption('led' + i + '_tab', form.ListValue,
'mod_led_control_led' + i + '_blink_on_delay_2',
_('On-state delay'),
_('On-state delay for blinking option.'));
makeTimerDelayOptions(o);
o.depends({ ['mod_led_control_led' + i + '_action_2']: '3' });
o.modalonly = true;
o.default = '500';
// blink_off_delay_2
o = ss.taboption('led' + i + '_tab', form.ListValue,
'mod_led_control_led' + i + '_blink_off_delay_2',
_('Off-state delay'),
_('Off-state delay for blinking option.'));
makeTimerDelayOptions(o);
o.depends({ ['mod_led_control_led' + i + '_action_2']: '3' });
o.modalonly = true;
o.default = '500';
// netdev_device_2
o = ss.taboption('led' + i + '_tab', widgets.DeviceSelect,
'mod_led_control_led' + i + '_netdev_device_2',
_('Device'),
_('<abbr title="Light Emitting Diode">LED</abbr> will display the link activity of this network device.')
);
o.depends({ ['mod_led_control_led' + i + '_action_2']: '4' });
o.modalonly = true;
o.rmempty = false;
o.noaliases = true;
// netdev_mode_2
o = ss.taboption('led' + i + '_tab', form.MultiValue,
'mod_led_control_led' + i + '_netdev_mode_2',
_('<abbr title="Light Emitting Diode">LED</abbr> mode')
);
o.depends({ ['mod_led_control_led' + i + '_action_2']: '4' });
o.modalonly = true;
o.value('link', _('Link On'));
o.value('tx', _('Tramsmit'));
o.value('rx', _('Receive'));
o.default = 'link';
o.rmempty = false;
}; };
} else { } else {
o = s.taboption('led_control', form.DummyValue, '_dummy'); o = s.taboption('led_control', form.DummyValue, '_dummy');
@@ -990,7 +1098,8 @@ return view.extend({
o.default = '300'; o.default = '300';
// disconnected_at_startup // disconnected_at_startup
o = s.taboption('reboot_device', form.Flag, 'mod_reboot_disconnected_at_startup', o = s.taboption('reboot_device', form.Flag,
'mod_reboot_disconnected_at_startup',
_('On startup'), _('On startup'),
_('Reboot device if the Internet is disconnected at service startup.') _('Reboot device if the Internet is disconnected at service startup.')
); );
@@ -1007,7 +1116,8 @@ return view.extend({
o.modalonly = true; o.modalonly = true;
// enabled // enabled
o = s.taboption('restart_network', form.Flag, 'mod_network_restart_enabled', o = s.taboption('restart_network', form.Flag,
'mod_network_restart_enabled',
_('Enabled')); _('Enabled'));
o.rmempty = false; o.rmempty = false;
o.modalonly = true; o.modalonly = true;
@@ -1022,7 +1132,8 @@ return view.extend({
o.modalonly = true; o.modalonly = true;
// ifaces // ifaces
o = s.taboption('restart_network', widgets.DeviceSelect, 'mod_network_restart_ifaces', o = s.taboption('restart_network', widgets.DeviceSelect,
'mod_network_restart_ifaces',
_('Device'), _('Device'),
_('Network device or interface to restart. If not specified, then the network service is restarted.') _('Network device or interface to restart. If not specified, then the network service is restarted.')
); );
@@ -1073,7 +1184,8 @@ return view.extend({
o.default = '0'; o.default = '0';
// disconnected_at_startup // disconnected_at_startup
o = s.taboption('restart_network', form.Flag, 'mod_network_restart_disconnected_at_startup', o = s.taboption('restart_network', form.Flag,
'mod_network_restart_disconnected_at_startup',
_('On startup'), _('On startup'),
_('Restart network if the Internet is disconnected at service startup.') _('Restart network if the Internet is disconnected at service startup.')
); );
@@ -1092,7 +1204,8 @@ return view.extend({
o.modalonly = true; o.modalonly = true;
// enabled // enabled
o = s.taboption('restart_modem', form.Flag, 'mod_modem_restart_enabled', o = s.taboption('restart_modem', form.Flag,
'mod_modem_restart_enabled',
_('Enabled'), _('Enabled'),
); );
o.rmempty = false; o.rmempty = false;
@@ -1140,7 +1253,8 @@ return view.extend({
o.modalonly = true; o.modalonly = true;
// iface // iface
o = s.taboption('restart_modem', widgets.NetworkSelect, 'mod_modem_restart_iface', o = s.taboption('restart_modem', widgets.NetworkSelect,
'mod_modem_restart_iface',
_('Interface'), _('Interface'),
_('ModemManger interface. If specified, it will be restarted after restarting ModemManager.') _('ModemManger interface. If specified, it will be restarted after restarting ModemManager.')
); );
@@ -1168,7 +1282,8 @@ return view.extend({
o.default = '0'; o.default = '0';
// disconnected_at_startup // disconnected_at_startup
o = s.taboption('restart_modem', form.Flag, 'mod_modem_restart_disconnected_at_startup', o = s.taboption('restart_modem', form.Flag,
'mod_modem_restart_disconnected_at_startup',
_('On startup'), _('On startup'),
_('Restart modem if the Internet is disconnected at service startup.') _('Restart modem if the Internet is disconnected at service startup.')
); );
@@ -1292,7 +1407,8 @@ return view.extend({
if(this.currentAppMode !== '2') { if(this.currentAppMode !== '2') {
// enable_ip_script // enable_ip_script
o = s.taboption('public_ip', form.Flag, 'mod_public_ip_enable_ip_script', o = s.taboption('public_ip', form.Flag,
'mod_public_ip_enable_ip_script',
_('Enable public-ip-script')); _('Enable public-ip-script'));
o.rmempty = false; o.rmempty = false;
o.modalonly = true; o.modalonly = true;
@@ -1356,7 +1472,8 @@ return view.extend({
o.default = '0'; o.default = '0';
// host_alias // host_alias
o = s.taboption('email', form.Value, 'mod_email_host_alias', o = s.taboption('email', form.Value,
'mod_email_host_alias',
_('Host alias'), _('Host alias'),
_('Host identifier in messages. If not specified, hostname will be used.')); _('Host identifier in messages. If not specified, hostname will be used.'));
o.modalonly = true; o.modalonly = true;
@@ -1412,7 +1529,8 @@ return view.extend({
o.modalonly = true; o.modalonly = true;
// message_at_startup // message_at_startup
o = s.taboption('email', form.Flag, 'mod_email_message_at_startup', o = s.taboption('email', form.Flag,
'mod_email_message_at_startup',
_('On startup'), _('On startup'),
_('Send message on service startup.') _('Send message on service startup.')
); );
@@ -1443,7 +1561,8 @@ return view.extend({
o.modalonly = true; o.modalonly = true;
// enabled // enabled
o = s.taboption('telegram', form.Flag, 'mod_telegram_enabled', o = s.taboption('telegram', form.Flag,
'mod_telegram_enabled',
_('Enable')); _('Enable'));
o.rmempty = false; o.rmempty = false;
o.modalonly = true; o.modalonly = true;
@@ -1481,7 +1600,8 @@ return view.extend({
o.default = '0'; o.default = '0';
// host_alias // host_alias
o = s.taboption('telegram', form.Value, 'mod_telegram_host_alias', o = s.taboption('telegram', form.Value,
'mod_telegram_host_alias',
_('Host alias'), _('Host alias'),
_('Host identifier in messages. If not specified, hostname will be used.')); _('Host identifier in messages. If not specified, hostname will be used.'));
o.modalonly = true; o.modalonly = true;
@@ -1509,7 +1629,8 @@ return view.extend({
o.depends({ 'mod_telegram_api_token': /.+/ }); o.depends({ 'mod_telegram_api_token': /.+/ });
// message_at_startup // message_at_startup
o = s.taboption('telegram', form.Flag, 'mod_telegram_message_at_startup', o = s.taboption('telegram', form.Flag,
'mod_telegram_message_at_startup',
_('On startup'), _('On startup'),
_('Send message on service startup.') _('Send message on service startup.')
); );
@@ -1540,8 +1661,8 @@ return view.extend({
o.rmempty = false; o.rmempty = false;
o.modalonly = true; o.modalonly = true;
o = s.taboption('user_scripts', form.SectionValue, 'user_scripts_section', form.NamedSection, o = s.taboption('user_scripts', form.SectionValue,
s.section); 'user_scripts_section', form.NamedSection, s.section);
ss = o.subsection; ss = o.subsection;
// up-script tab // up-script tab
@@ -1588,7 +1709,8 @@ return view.extend({
// up_script_attempt_interval // up_script_attempt_interval
o = ss.taboption('user_scripts_up_script', this.CBITimeInput, o = ss.taboption('user_scripts_up_script', this.CBITimeInput,
'mod_user_scripts_up_script_attempt_interval', _('Attempt interval'), 'mod_user_scripts_up_script_attempt_interval',
_('Attempt interval'),
_('Interval between up-script runs.') _('Interval between up-script runs.')
); );
o.default = '60'; o.default = '60';
@@ -1596,7 +1718,8 @@ return view.extend({
o.modalonly = true; o.modalonly = true;
// connected_at_startup // connected_at_startup
o = ss.taboption('user_scripts_up_script', form.Flag, 'mod_user_scripts_connected_at_startup', o = ss.taboption('user_scripts_up_script', form.Flag,
'mod_user_scripts_connected_at_startup',
_('On startup'), _('On startup'),
_('Run up-script if the Internet is connected at service startup.') _('Run up-script if the Internet is connected at service startup.')
); );
@@ -1647,7 +1770,8 @@ return view.extend({
// down_script_attempt_interval // down_script_attempt_interval
o = ss.taboption('user_scripts_down_script', this.CBITimeInput, o = ss.taboption('user_scripts_down_script', this.CBITimeInput,
'mod_user_scripts_down_script_attempt_interval', _('Attempt interval'), 'mod_user_scripts_down_script_attempt_interval',
_('Attempt interval'),
_('Interval between down-script runs.') _('Interval between down-script runs.')
); );
o.default = '60'; o.default = '60';
@@ -1655,7 +1779,8 @@ return view.extend({
o.modalonly = true; o.modalonly = true;
// disconnected_at_startup // disconnected_at_startup
o = ss.taboption('user_scripts_down_script', form.Flag, 'mod_user_scripts_disconnected_at_startup', o = ss.taboption('user_scripts_down_script', form.Flag,
'mod_user_scripts_disconnected_at_startup',
_('On startup'), _('On startup'),
_('Run down-script if the Internet is disconnected at service startup.') _('Run down-script if the Internet is disconnected at service startup.')
); );
@@ -1672,13 +1797,15 @@ return view.extend({
o.modalonly = true; o.modalonly = true;
// enabled // enabled
o = s.taboption('regular_script', form.Flag, 'mod_regular_script_enabled', o = s.taboption('regular_script', form.Flag,
'mod_regular_script_enabled',
_('Enabled')); _('Enabled'));
o.rmempty = false; o.rmempty = false;
o.modalonly = true; o.modalonly = true;
// next run // next run
o = s.taboption('regular_script', form.DummyValue, '_dummy', _('Next run')); o = s.taboption('regular_script', form.DummyValue,
'_dummy', _('Next run'));
o.rawhtml = true; o.rawhtml = true;
o.default = '<span id="id_next_run_' + s.section + '">' + (this.modRegularScriptNextRun[s.section] || _('Not scheduled')) + '</span>'; o.default = '<span id="id_next_run_' + s.section + '">' + (this.modRegularScriptNextRun[s.section] || _('Not scheduled')) + '</span>';
o.modalonly = true; o.modalonly = true;

View File

@@ -23,15 +23,23 @@ msgid ""
"<abbr title=\"Light Emitting Diode\">LED</abbr> indicates the Internet status." "<abbr title=\"Light Emitting Diode\">LED</abbr> indicates the Internet status."
msgstr "<abbr title=\"Светодиод\">LED</abbr> отображает статус Интернет." msgstr "<abbr title=\"Светодиод\">LED</abbr> отображает статус Интернет."
msgid "Action when connected" msgid "<abbr title=\"Light Emitting Diode\">LED</abbr> mode"
msgstr "Действие при подключении" msgstr "Режим <abbr title=\"Светодиод\">LED</abbr>"
msgid "Action when disconnected" msgid ""
msgstr "Действие при отключении" "<abbr title=\"Light Emitting Diode\">LED</abbr> will display the link activity of this network device."
msgstr ""
"<abbr title=\"Светодиод\">LED</abbr> будет отображать активность линка этого сетевого устройства."
msgid "Add instance" msgid "Add instance"
msgstr "Добавить экземпляр" msgstr "Добавить экземпляр"
msgid "After connection"
msgstr "После подключения"
msgid "After disconnection"
msgstr "После отключения"
msgid "Alive interval" msgid "Alive interval"
msgstr "Интервал при подключении" msgstr "Интервал при подключении"
@@ -53,8 +61,8 @@ msgstr "Попытки"
msgid "Big: 248 bytes" msgid "Big: 248 bytes"
msgstr "Большой: 248 байт" msgstr "Большой: 248 байт"
msgid "Blink" msgid "Blinking (kernel: timer)"
msgstr "Мигание" msgstr "Мигание (kernel: timer)"
msgid "Bot API token is missing!" msgid "Bot API token is missing!"
msgstr "Отсутствует API токен бота!" msgstr "Отсутствует API токен бота!"
@@ -267,6 +275,9 @@ msgstr "Гигантский: 9000 байт"
msgid "LED control" msgid "LED control"
msgstr "Управление LED" msgstr "Управление LED"
msgid "Link On"
msgstr "Подключение"
msgid "Loading" msgid "Loading"
msgstr "Загрузка" msgstr "Загрузка"
@@ -326,6 +337,9 @@ msgstr ""
"Сетевое устройство для доступа в Интернет. Если не указано, используется " "Сетевое устройство для доступа в Интернет. Если не указано, используется "
"устройство по умолчанию." "устройство по умолчанию."
msgid "Network device activity (kernel: netdev)"
msgstr "Активность сетевого устройства (kernel: netdev)"
msgid "" msgid ""
"Network device or interface to restart. If not specified, then the network service is restarted." "Network device or interface to restart. If not specified, then the network service is restarted."
msgstr "" msgstr ""
@@ -356,12 +370,24 @@ msgstr "Количество попыток запроса IP адреса"
msgid "Off" msgid "Off"
msgstr "Выключить" msgstr "Выключить"
msgid "Off-state delay"
msgstr "Задержка выключенного состояния"
msgid "Off-state delay for blinking option."
msgstr "Задержка выключенного состояния для опции мигания."
msgid "On" msgid "On"
msgstr "Включить" msgstr "Включить"
msgid "On startup" msgid "On startup"
msgstr "При запуске" msgstr "При запуске"
msgid "On-state delay"
msgstr "Задержка включённого состояния"
msgid "On-state delay for blinking option."
msgstr "Задержка включённого состояния для опции мигания."
msgid "One of the following:" msgid "One of the following:"
msgstr "Одно из следующих значений:" msgstr "Одно из следующих значений:"
@@ -421,6 +447,9 @@ msgstr "Перезагрузка устройства"
msgid "Reboot device if the Internet is disconnected at service startup." msgid "Reboot device if the Internet is disconnected at service startup."
msgstr "Перезагрузка устройства если Интренет отключен при запуске службы." msgstr "Перезагрузка устройства если Интренет отключен при запуске службы."
msgid "Receive"
msgstr "Приём"
msgid "Recipient" msgid "Recipient"
msgstr "Получатель" msgstr "Получатель"
@@ -565,6 +594,9 @@ msgstr "Таймаут между остановкой и запуском ин
msgid "To support HTTP services you need to install curl." msgid "To support HTTP services you need to install curl."
msgstr "Для поддержки HTTP сервисов необходимо установить curl." msgstr "Для поддержки HTTP сервисов необходимо установить curl."
msgid "Tramsmit"
msgstr "Передача"
msgid "Type a time string" msgid "Type a time string"
msgstr "Введите строку времени" msgstr "Введите строку времени"
@@ -658,6 +690,9 @@ msgstr "мин"
msgid "minutes" msgid "minutes"
msgstr "минуты" msgstr "минуты"
msgid "msec"
msgstr "мсек"
msgid "sec" msgid "sec"
msgstr "сек" msgstr "сек"

View File

@@ -11,15 +11,22 @@ msgid ""
"<abbr title=\"Light Emitting Diode\">LED</abbr> indicates the Internet status." "<abbr title=\"Light Emitting Diode\">LED</abbr> indicates the Internet status."
msgstr "" msgstr ""
msgid "Action when connected" msgid "<abbr title=\"Light Emitting Diode\">LED</abbr> mode"
msgstr "" msgstr ""
msgid "Action when disconnected" msgid ""
"<abbr title=\"Light Emitting Diode\">LED</abbr> will display the link activity of this network device."
msgstr "" msgstr ""
msgid "Add instance" msgid "Add instance"
msgstr "" msgstr ""
msgid "After connection"
msgstr ""
msgid "After disconnection"
msgstr ""
msgid "Alive interval" msgid "Alive interval"
msgstr "" msgstr ""
@@ -41,7 +48,7 @@ msgstr ""
msgid "Big: 248 bytes" msgid "Big: 248 bytes"
msgstr "" msgstr ""
msgid "Blink" msgid "Blinking (kernel: timer)"
msgstr "" msgstr ""
msgid "Bot API token is missing!" msgid "Bot API token is missing!"
@@ -253,6 +260,9 @@ msgstr ""
msgid "LED control" msgid "LED control"
msgstr "" msgstr ""
msgid "Link On"
msgstr ""
msgid "Loading" msgid "Loading"
msgstr "" msgstr ""
@@ -303,6 +313,9 @@ msgid ""
"interface is used." "interface is used."
msgstr "" msgstr ""
msgid "Network device activity (kernel: netdev)"
msgstr ""
msgid "" msgid ""
"Network device or interface to restart. If not specified, then the network service is restarted." "Network device or interface to restart. If not specified, then the network service is restarted."
msgstr "" msgstr ""
@@ -331,12 +344,24 @@ msgstr ""
msgid "Off" msgid "Off"
msgstr "" msgstr ""
msgid "Off-state delay"
msgstr ""
msgid "Off-state delay for blinking option."
msgstr ""
msgid "On" msgid "On"
msgstr "" msgstr ""
msgid "On startup" msgid "On startup"
msgstr "" msgstr ""
msgid "On-state delay"
msgstr ""
msgid "On-state delay for blinking option."
msgstr ""
msgid "One of the following:" msgid "One of the following:"
msgstr "" msgstr ""
@@ -393,6 +418,9 @@ msgstr ""
msgid "Reboot device if the Internet is disconnected at service startup." msgid "Reboot device if the Internet is disconnected at service startup."
msgstr "" msgstr ""
msgid "Receive"
msgstr ""
msgid "Recipient" msgid "Recipient"
msgstr "" msgstr ""
@@ -537,6 +565,9 @@ msgstr ""
msgid "To support HTTP services you need to install curl." msgid "To support HTTP services you need to install curl."
msgstr "" msgstr ""
msgid "Tramsmit"
msgstr ""
msgid "Type a time string" msgid "Type a time string"
msgstr "" msgstr ""
@@ -622,6 +653,9 @@ msgstr ""
msgid "minutes" msgid "minutes"
msgstr "" msgstr ""
msgid "msec"
msgstr ""
msgid "sec" msgid "sec"
msgstr "" msgstr ""