From 985c38af5e02a23c792aaaa392d09d1f6354cf93 Mon Sep 17 00:00:00 2001 From: nojhan Date: Sat, 5 Feb 2011 18:53:19 +0100 Subject: [PATCH] first commit --- .ion3/statusbar_workspace.lua | 112 ++++++++++ .ion3/statusd_laptopstatus.lua | 213 ++++++++++++++++++ .ion3/statusd_vv.lua | 385 +++++++++++++++++++++++++++++++++ 3 files changed, 710 insertions(+) create mode 100644 .ion3/statusbar_workspace.lua create mode 100644 .ion3/statusd_laptopstatus.lua create mode 100644 .ion3/statusd_vv.lua diff --git a/.ion3/statusbar_workspace.lua b/.ion3/statusbar_workspace.lua new file mode 100644 index 0000000..7529aec --- /dev/null +++ b/.ion3/statusbar_workspace.lua @@ -0,0 +1,112 @@ +-- statusbar_workspace.lua +-- +-- Show current workspace name or number in the statusbar. +-- +-- Put any of these in cfg_statusbar.lua's template-line: +-- %workspace_name +-- %workspace_frame +-- %workspace_pager +-- %workspace_name_pager +-- %workspace_num_name_pager +-- +-- This is an internal statusbar monitor and does NOT require +-- a dopath statement (effective after a 2006-02-12 build). +-- +-- version 1 +-- author: Rico Schiekel +-- +-- version 2 +-- added 2006-02-14 by Canaan Hadley-Voth: +-- * %workspace_pager shows a list of workspace numbers +-- with the current one indicated: +-- +-- 1i 2i [3f] 4p 5c +-- +-- i=WIonWS, f=WFloatWS, p=WPaneWS, c=WClientWin/other +-- +-- * %workspace_frame - name of the active frame. +-- +-- * Added statusbar_ to the filename (since it *is* +-- an internal statusbar monitor) so that it works without +-- a "dopath" call. +-- +-- * Removed timer. Only needs to run on hook. +-- Much faster this way. +-- +-- version 3 +-- update for ion-3rc-20070506 on 2007-05-09 +-- by Kevin Granade +-- +-- Updated to use new wx_ api +-- Replaced region_activated_hook with region_notify_hook +-- Added %workspace_name_pager, which works similarly to %workspace_pager, +-- but instead displays the name of each workspace +-- Added display for WGroupWS to %workspace_pager, displayed as 'g' +-- + +local function update_frame() + local fr + ioncore.defer( function() + local cur=ioncore.current() + if obj_is(cur, "WClientWin") and + obj_is(cur:parent(), "WMPlex") then + cur=cur:parent() + end + fr=cur:name() + mod_statusbar.inform('workspace_frame', fr) + mod_statusbar.update() + end) +end + +local function update_workspace() + local scr=ioncore.find_screen_id(0) + local curws = scr:mx_current() + local wstype, c + local pager="" + local name_pager="" + local name_pager_plus="" + local curindex = scr:get_index(curws)+1 + n = scr:mx_count(1) + for i=1,n do + tmpws=scr:mx_nth(i-1) + wstype=obj_typename(tmpws) + if wstype=="WIonWS" then + c="i" + elseif wstype=="WFloatWS" then + c="f" + elseif wstype=="WPaneWS" then + c="p" + elseif wstype=="WGroupWS" then + c="g" + else + c="c" + end + if i==curindex then + name_pager_plus=name_pager_plus.."["..tmpws:name().."]" + name_pager=name_pager.."["..tmpws:name().."]" + pager=pager.."["..(i).."]" + --pager=pager.." ["..(i)..c.."] " + else + name_pager_plus=name_pager_plus.." "..(i)..":"..tmpws:name() + name_pager=name_pager.." "..tmpws:name() + pager=pager.." "..(i).." " + --pager=pager.." "..(i)..c.." " + end + end + + local fr,cur + + -- Older versions without an ioncore.current() should + -- skip update_frame. + update_frame() + + ioncore.defer( function() + mod_statusbar.inform('workspace_pager', pager) + mod_statusbar.inform('workspace_name', curws:name()) + mod_statusbar.inform('workspace_name_pager', name_pager) + mod_statusbar.inform('workspace_num_name_pager', name_pager_plus) + mod_statusbar.update() + end) +end + +ioncore.get_hook("region_notify_hook"):add(update_workspace) diff --git a/.ion3/statusd_laptopstatus.lua b/.ion3/statusd_laptopstatus.lua new file mode 100644 index 0000000..e9dda6f --- /dev/null +++ b/.ion3/statusd_laptopstatus.lua @@ -0,0 +1,213 @@ +-- statusd_laptopstatus.lua v0.0.2 (last modified 2005-06-13) +-- +-- Copyright (C) 2005 Jari Eskelinen +-- modified by RenĂ© van Bevern for error handling +-- +-- Permission to copy, redistirbute, modify etc. is granted under the terms +-- of GNU GENERAL PUBLIC LICENSE Version 2. +-- +-- This is script for displaying some interesting information about your +-- laptops power saving in Ion's status monitor. Script is very Linux +-- specific (uses /proc interface) and needs ACPI -support new enough (don't +-- know exactly but 2.6.x kernels should be fine). Also if you have some +-- kind of exotic hardware (multiple processors, multiple batteries etc.) +-- this script probably will fail or show incorrect information. +-- +-- Just throw this script under ~/.ion3 and add following keywords to your +-- cfg_statusbar.lua's template-line with your own taste: +-- +-- %laptopstatus_cpuspeed +-- %laptopstatus_temperature +-- %laptopstatus_batterypercent +-- %laptopstatus_batterytimeleft +-- %laptopstatus_batterydrain +-- +-- Template example: template="[ %date || load:% %>load || CPU: %laptopstatus_cpuspeed %laptopstatus_temperature || BATT: %laptopstatus_batterypercent %laptopstatus_batterytimeleft %laptopstatus_batterydrain ]" +-- +-- You can also run this script with lua interpreter and see if you get +-- right values. +-- +-- NOTICE: This is my first ion/lua-script, so probably this can be done better. +-- Feel free to improve this script. +-- +-- TODO: * Is it stupid to use file:read("*all"), can this cause infinite +-- loops in some weird situations? +-- * Do not poll for information not defined in template to be used +-- * Auto-detect right acpi devices instead of hardcoded BATT0 etc. + +-- +-- SETTINGS +-- + +if not statusd_laptopstatus then + statusd_laptopstatus = { + interval = 10, -- Polling interval in seconds + temperature_important = 66, -- Temperature which will cause important hint + temperature_critical = 71, -- Temperature which will cause critical hint + batterypercent_important = 10, -- Battery percent which will cause important hint + batterypercent_critical = 5, -- Battery percent which will cause critical hint + batterytimeleft_important = 600, -- Battery time left (in secs) which will cause important hint + batterytimeleft_critical = 300, -- Battery time left (in secs) which will cause critical hint + ac_state = {"/proc/acpi/ac_adapter/AC/state", + "/proc/acpi/ac_adapter/ACAD/state", + "/proc/acpi/ac_adapter/ADP0/state", + "/proc/acpi/ac_adapter/ADP1/state"}, + temp_info = {"/proc/acpi/thermal_zone/THRM/temperature", + "/proc/acpi/thermal_zone/THM/temperature", + "/proc/acpi/thermal_zone/THM0/temperature", + "/proc/acpi/thermal_zone/THM1/temperature"}, + bat_info = {"/proc/acpi/battery/BAT0/info", + "/proc/acpi/battery/BAT1/info"}, + bat_state = {"/proc/acpi/battery/BAT0/state", + "/proc/acpi/battery/BAT1/state"} + } +end + +statusd_laptopstatus=table.join(statusd.get_config("laptopstatus"), statusd_laptopstatus) + +-- +-- CODE +-- +local laptopstatus_timer + +function try_open(files, mode) + for _, file in pairs(files) do + local fd = io.open(file, mode) + if fd then return fd, file end + end +end + +local function get_cpu() + local mhz, hint + if pcall(function () + local status + local file = io.open("/proc/cpuinfo", "r") + status, _, mhz = string.find(file:read("*all"), + "cpu MHz%s+: (%d+)") + if not status then error("could not get MHz") end + file:close() + end) + then return {speed=string.format("%4dMHz", math.ceil(mhz/5)*5), + hint=hint} + else return {speed="n/a", hint=hint} end +end + + +local function get_ac() + local file = try_open(statusd_laptopstatus.ac_state, "r") + if not string.find(file:read("*all"), "state:%s+on.line") then return 0 + else return 1 end + file:close() +end + + +local function get_thermal() + local temp, hint = nil, "normal" + + if pcall(function () + local status + local file=try_open(statusd_laptopstatus.temp_info, "r") + status, _, temp = string.find(file:read("*all"), + "temperature:%s+(%d+).*") + if not status then error("could not get temperature") end + temp = tonumber(temp) + file:close(); + end) + then if temp >= statusd_laptopstatus.temperature_important then + hint = "important" end + if temp >= statusd_laptopstatus.temperature_critical then + hint = "critical" end + return {temperature=string.format("%02dC", temp), hint=hint} + else return {temperature="n/a", hint = "hint"} end +end + + +local function get_battery() + local percenthint = "normal" + local timelefthint = "normal" + local lastfull, rate, rateunit, remaining + + if pcall(function () + local status + local file=try_open(statusd_laptopstatus.bat_info, "r") + local infocontents = file:read("*all") + file:close(); + + local file=try_open(statusd_laptopstatus.bat_state, "r") + local statecontents = file:read("*all") + file:close(); + + status, _, lastfull = string.find(infocontents, "last full capacity:%s+(%d+).*") + if not status then error("could not get full battery capacity") end + lastfull = tonumber(lastfull) + if string.find(statecontents, "present rate:%s+unknown.*") then + rate = -1 + else + status, _, rate, rateunit = string.find(statecontents, "present rate:%s+(%d+)(.*)") + if not status then error("could not get battery draining-rate") end + rate = tonumber(rate) + end + status, _, remaining = string.find(statecontents, "remaining capacity:%s+(%d+).*") + if not status then error("could not get remaining capacity") end + remaining = tonumber(remaining) + end) then + local percent = math.floor(remaining / lastfull * 100 + 5/10) + local timeleft + local hours, secs, mins + if get_ac() == 1 then + timeleft = " *AC*" + elseif rate <= 0 then + timeleft = "n/a" + else + secs = 3600 * (remaining / rate) + mins = secs / 60 + hours = math.floor(mins / 60) + mins = math.floor(mins - (hours * 60)) + timeleft = string.format("%02d:%02d", hours, mins) + end + + if secs ~= nil and secs <= statusd_laptopstatus.batterytimeleft_important then timelefthint = "important" end + if secs ~= nil and secs <= statusd_laptopstatus.batterytimeleft_critical then timelefthint = "critical" end + if percent <= statusd_laptopstatus.batterypercent_important then percenthint = "important" end + if percent <= statusd_laptopstatus.batterypercent_critical then percenthint = "critical" end + + return { percent=string.format("%02d%%", percent), timeleft=timeleft, drain=tostring(rate)..rateunit, percenthint=percenthint, timelefthint=timelefthint} + else return { percent="n/a", timeleft="n/a", drain="n/a", percenthint=percenthint, timelefthint=timelefthint} end +end + +local last_timeleft = nil + +local function update_laptopstatus () + cpu = get_cpu() + thermal = get_thermal() + battery = get_battery() + + -- Informing statusd OR printing to stdout if statusd not present + if statusd ~= nil then + statusd.inform("laptopstatus_cpuspeed", cpu.speed) + statusd.inform("laptopstatus_cpuspeed_template", "xxxxMHz") + statusd.inform("laptopstatus_cpuspeed_hint", cpu.hint) + statusd.inform("laptopstatus_temperature", thermal.temperature) + statusd.inform("laptopstatus_temperature_template", "xxxC") + statusd.inform("laptopstatus_temperature_hint", thermal.hint) + statusd.inform("laptopstatus_batterypercent", battery.percent) + statusd.inform("laptopstatus_batterypercent_template", "xxx%") + statusd.inform("laptopstatus_batterypercent_hint", battery.percenthint) + if battery.timeleft ~= "n/a" or last_timeleft == " *AC*" then + statusd.inform("laptopstatus_batterytimeleft", battery.timeleft) + last_timeleft = battery.timeleft + end + statusd.inform("laptopstatus_batterytimeleft_template", "hh:mm") + statusd.inform("laptopstatus_batterytimeleft_hint", battery.timelefthint) + statusd.inform("laptopstatus_batterydrain", battery.drain) + laptopstatus_timer:set(statusd_laptopstatus.interval*1000, update_laptopstatus) + else + io.stdout:write("CPU: "..cpu.speed.." "..thermal.temperature.." || BATT: "..battery.percent.." "..battery.timeleft.."\n") + end +end + + +if statusd ~= nil then + laptopstatus_timer = statusd.create_timer() +end +update_laptopstatus() diff --git a/.ion3/statusd_vv.lua b/.ion3/statusd_vv.lua new file mode 100644 index 0000000..02d84a3 --- /dev/null +++ b/.ion3/statusd_vv.lua @@ -0,0 +1,385 @@ +require "lfs" + +local settings = { + -- refresh intervall in milliseconds + interval = 1000, + + -- only monitors below will be updated + -- numbers are multipliers of refresh intervall + monitors = { + --cpufreq = 1, + cpucharge = 1, + --temperature = 5, + memory = 1, + network = 1, + battery = 3, + mail = 5, + disk = 60, + sound = 1, + uptime = 60}, + + -- default settings + cpu0_important = 50, + cpu0_critical = 90, + cpu1_important = 50, + cpu1_critical = 90, + cpu2_important = 50, + cpu2_critical = 90, + cpufreq_important = 801, + cpufreq_critical = 2800, + temperature_important = 50, + temperature_critical = 70, + memfree_important = 2^20, + memfree_critical = 500*2^10, + battery_important = 10, + battery_critical = 5, + rxbytes_important = 5*10^5, + rxbytes_critical = 10^6, + rxtotal_important = 10^6, + rxtotal_critical = 10^9, + txbytes_important = 5*10^5, + txbytes_critical = 10^6, + txtotal_important = 10^6, + txtotal_critical = 10^9, + directories = {"/", "/home"}, + disk__important = 2*2^20, + disk__critical = 1*2^20, + disk_home_important = 2*2^20, + disk_home_critical = 1*2^20, + disk_tmp_important = 2^20, + disk_tmp_critical = 500*2^10, + mailbox = "mail/inbox", + sound_file = "/tmp/sound-modified" +} + +settings = table.join(statusd.get_config("vv"), settings) + +------------------------------------------------------------------------------------------------------------------------ +-- +-- Helpers +-- +------------------------------------------------------------------------------------------------------------------------ + +local function read_file(file, mode) + local file, value = io.open(file), nil + if file then value = file:read(mode) file:close() end + return value +end + +local function read_string(file) return read_file(file, "*l") end +local function read_number(file) return read_file(file, "*n") end + +local function read_popen(cmd, mode) + local file, value = io.popen(cmd), nil + if file then value = file:read("*a") file:close() end + return value +end + +local function read_string(file) return read_file(file, "*l") end +local function read_number(file) return read_file(file, "*n") end + +local modified_table = {} + +local function modified(file) + local modif = lfs.attributes(file, "change") + if not modif or modif == modified_table[file] then return false end + modified_table[file] = modif + return true +end + +local function mkunit(n) + if n > 2^20 then return string.format("%.1fG", n / 2^20) + elseif n > 2^10 then return string.format("%.1fM", n / 2^10) + else return n.."k" end +end + +------------------------------------------------------------------------------------------------------------------------ +-- +-- statusd.inform wrappers +-- +------------------------------------------------------------------------------------------------------------------------ + +local function inform1(name, value, hint) + statusd.inform("vv_"..name, value) +end + +function inform_hint(monitor, val) + local critical = settings[monitor.."_critical"] + local important = settings[monitor.."_important"] + if not critical or not important then return end + local dir = important <= critical + if (dir and val >= critical) or (not dir and val <= critical) then inform1(monitor.."_hint", "critical") + elseif (dir and val >= important) or (not dir and val <= important) then inform1(monitor.."_hint", "important") + else inform1(monitor.."_hint", "normal") end +end + +local function inform(name, value, hint) + inform1(name, value) + if hint then inform_hint(name, hint) end +end + +------------------------------------------------------------------------------------------------------------------------ +-- +-- CPU frequency +-- +------------------------------------------------------------------------------------------------------------------------ + +local nb_cpus = 2 +--tonumber(string.match(read_string("/sys/devices/system/cpu/online"), "(%d+)$")) + 1 + +function get_cpufreq() + local cpu = "" + for i = 0, nb_cpus - 1 do + if i > 0 then cpu = cpu.." " end + cpu = cpu..(read_number("/sys/devices/system/cpu/cpu"..i.."/cpufreq/scaling_cur_freq") / 1000).."MHz" + end + inform("cpufreq", "("..cpu..")", cpufreq) +end + +------------------------------------------------------------------------------------------------------------------------ +-- +-- CPU charge +-- +------------------------------------------------------------------------------------------------------------------------ + +local user, nice, system, idle = {}, {}, {}, {} +for i = 0, nb_cpus do user[i], nice[i], system[i], idle[i] = 0, 0, 0, 0 end + +function get_cpucharge() + local file = io.open("/proc/stat") + for i = 0, nb_cpus do + local n_user, n_nice, n_system, n_idle = string.match( file:read("*l"), "^cpu%d*%s+(%d+)%s+(%d+)%s+(%d+)%s+(%d+)") + local charge = n_user - user[i] + n_nice - nice[i] + n_system - system[i] + local allcharge = charge + n_idle - idle[i] + local cpu = (allcharge == 0 and 100) or math.ceil(100 * charge / allcharge) + user[i], nice[i], system[i], idle[i] = n_user, n_nice, n_system, n_idle + inform("cpu"..i, string.sub(cpu.."%", 1, 4), cpu) + end + file:close() +end + +------------------------------------------------------------------------------------------------------------------------ +-- +-- CPU temperature +-- +------------------------------------------------------------------------------------------------------------------------ + +function get_temperature() + local temperature = read_number("/sys/class/thermal/thermal_zone0/temp") / 1000 + inform("temperature", math.ceil(temperature).."°C", temperature) +end + +------------------------------------------------------------------------------------------------------------------------ +-- +-- Memory usage +-- +------------------------------------------------------------------------------------------------------------------------ + +function get_memory() + local mem = {} + for line in io.lines("/proc/meminfo") do + local attribute, qty = string.match(line, "^(%S+):%s+(%d+) kB$") + if attribute then string.gsub(attribute, "[()]", "") + mem[attribute] = qty + end + end + local memfree = tonumber(mem.MemFree) + tonumber(mem.Buffers) + tonumber(mem.Cached) + inform("memfree", mkunit(memfree), memfree) + inform("swapused", mkunit(tonumber(mem.SwapTotal) - tonumber(mem.SwapFree))) +end + +------------------------------------------------------------------------------------------------------------------------ +-- +-- Network load +-- +------------------------------------------------------------------------------------------------------------------------ + +local interfaces, iface_old, last_rx_bytes, last_tx_bytes = {}, nil, 0, 0 + +for iface in lfs.dir("/sys/class/net/") do + table.insert(interfaces, iface) +end + +function get_network() + local function mkunit(rx) + if rx > 10^9 then return string.sub(string.format("%.1fG", rx / 10^9), 1, 6) + elseif rx > 10^6 then return string.sub(string.format("%.1fM", rx / 10^6), 1, 6) + else return string.sub(string.format("%.1fk", rx / 1000), 1, 6) end + end + local path = "/sys/class/net/" + local iface + if old_iface and read_string(path..old_iface.."/operstate") == "up" then iface = old_iface + else + for _, i in ipairs(interfaces) do + if read_string(path..i.."/operstate") == "up" then iface, old_iface = i, i break end + end + end + if iface then + local rx = read_number(path..iface.."/statistics/rx_bytes") + local tx = read_number(path..iface.."/statistics/tx_bytes") + local rx2, tx2 = rx - last_rx_bytes, tx - last_tx_bytes + last_rx_bytes, last_tx_bytes = rx, tx + inform("iface", iface..":") + inform("rxbytes", mkunit(rx2), rx2) + inform("rxtotal", mkunit(rx), rx) + inform("txbytes", mkunit(tx2), tx2) + inform("txtotal", mkunit(tx), tx) + else + inform("iface", "net: off") + inform("rxbytes", "") + inform("rxtotal", "") + inform("txbytes", "") + inform("txtotal", "") + end +end + +------------------------------------------------------------------------------------------------------------------------ +-- +-- Disk usage +-- +------------------------------------------------------------------------------------------------------------------------ + +local dirnames = {} +for i, dir in ipairs(settings.directories) do + dirnames[i] = "disk"..string.gsub(dir, "/", "_") +end + +function get_disk() + local df = read_popen("/bin/df") + if df then + for i, dir in ipairs(settings.directories) do + local unused_s, used = string.match(df, "(%d+)%s+(%S+)%s+"..dir.."\n") + local unused = tonumber(unused_s) + if used then inform(dirnames[i], dir.." "..used.." "..mkunit(unused), unused) end + end + end +end + +------------------------------------------------------------------------------------------------------------------------ +-- +-- Sound (alsa) +-- +------------------------------------------------------------------------------------------------------------------------ + +function get_sound() + if modified(settings.sound_file) then + local function snd () + local master, status = string.match(read_popen("/usr/bin/amixer get Master"), "%[(.+)%] %[.+%[(.+)%]") + local hint = (status == "on" and "critical") or "normal" + inform("master", master) + inform("master_hint", hint) + inform("pcm", string.match(read_popen("/usr/bin/amixer get PCM"), "Front.*%[(.+)%] %[.+%].*Front")) + inform("pcm_hint", hint) + end + if not pcall(snd) then lfs.touch(settings.sound_file) end + end +end + +------------------------------------------------------------------------------------------------------------------------ +-- +-- Uptime +-- +------------------------------------------------------------------------------------------------------------------------ + +function get_uptime() + local sec = read_number("/proc/uptime") + local min = math.floor(sec / 60) + local hours = math.floor(min / 60) + local days = math.floor(hours / 24) + days = (days > 1 and days.." days, ") or (days > 0 and days.." day, ") or "" + inform("uptime", string.format(days.."%02d:%02d", hours % 24, min % 60)) +end + +------------------------------------------------------------------------------------------------------------------------ +-- +-- Mail (Maildir format) +-- +------------------------------------------------------------------------------------------------------------------------ + +inform("mailnew_hint", "critical") +inform("mailunread_hint", "important") + +function get_mail() + local function read_dir(mailbox) + if not modified(mailbox) then return nil end + local old, new = 0, -2 + for line in lfs.dir(mailbox) do + if string.match(line, "S$") then old = old + 1 else new = new + 1 end + end + return new, old + end + local new = read_dir(settings.mailbox.."/new") + local unread, old = read_dir(settings.mailbox.."/cur") + if new then + if new > 0 then inform("mailnew", "("..new.." new) ") + else inform("mailnew", "") end + end + if old then + if unread > 0 then inform("mailunread", "("..unread.." unread) ") + else inform("mailunread", "") end + inform("mailold", old.." ") + end +end + +------------------------------------------------------------------------------------------------------------------------ +-- +-- Battery usage +-- +------------------------------------------------------------------------------------------------------------------------ + +function get_battery(num) + local charge_now_total, charge_full_total, percent_max, charging, rate, status = 0, 0, 0, false, nil + local path = "/sys/class/power_supply/" + for device in lfs.dir(path) do + local batt = path..device + if read_string(batt.."/type") == "Battery" then + local state = read_string(batt.."/status") + local charge_full = read_number(batt.."/charge_full") + local charge_now = math.min(read_number(batt.."/charge_now"), charge_full) + local percent = math.floor(charge_now / charge_full * 100) + percent_max = math.max(percent, percent_max) + status = ((status and status.."|") or "")..percent.."%" + if state == "Discharging" then rate = (rate or 0) + read_number(batt.."/current_now") + elseif state == "Charging" then charging = true end + charge_now_total = charge_now_total + charge_now + charge_full_total = charge_full_total + charge_full + end + end + if not status then status = "none" + elseif charging then status = status.." - charging" + elseif rate then + local hours = charge_now_total / rate + status = status..string.format(" %02d:%02d %dmW", hours, hours * 60 % 60, rate / 1000) + end + inform("battery", status, percent_max) +end + +------------------------------------------------------------------------------------------------------------------------ +-- +-- Main +-- +------------------------------------------------------------------------------------------------------------------------ + +local cycle = 0 +local vv_timer = statusd.create_timer() +local monitors = {} + +inform("debug", "") +inform("debug_hint", "critical") + +for name, factor in pairs(settings.monitors) do + if factor > 0 then monitors[_G["get_"..name]] = factor end +end + +local function update_vv () + for func, factor in pairs(monitors) do + if cycle % factor == 0 then + local ret, mess = pcall(func) + if not ret then inform("debug", "err: "..mess) end + end + end + cycle = cycle + 1 + vv_timer:set(settings.interval, update_vv) +end + +update_vv()