Und noch ein Batteriecheck Script HC3

So, damit wir es auch in der richtigen Kategorie haben und ich so wieso weitergebastelt habe.
Ich warte aber mit weiteren Änderungen bis Fibaro das Mailproblem gelöst hat.
Ich habe nie direkt im Script nachgeschaut sondern hab mich auf die Mails verlassen.

Was ich noch plane: da ich schon seit längerem den ER4 aus dem Fibaro Forum nutze (den kann ich wirklich jedem empfehlen) steuere ich viele Sachen für die ich vorher ein Script hatte, nur noch über eine Regel im ER4, könnte ich mir vorstellen das Batterie Script nicht mehr über das Gateway starten sondern über eine ER4 Regel. Oder zur Auswahl. Mal sehen.

 -- Trigger auf Gateway-start ändern
--[[
file    : Battery&devicestatus.lua
version : 0.1.4 - DE, released on Feb. 2021
Copyright: Mar-2017 - 2021 {jwi}  

purpose : This script checks the batteries and sends reports via
email. Since version 0.6.1 and higher: The script also performs a check on
non battery devices and reports all devices marked as dead by HC2.
The batterie status is checked daily and several reports per week can be
emailed.

Due to some limitations in the HC2 with FW less then 4.130, only 980
characters per per e-mail could be sent.

For batteries that are almost empty, or a dead device is recognized,
a push and/or telegram messages will be sent immediately.

Also the status of lastBreached can be checked, so you can easely identify
devices whose battery may have been empty for a longer time.
This is very important to identify your dead devices.

A manual check is possible at any time. Simply press the Start button.
For more information, please read the attached PDF. 

--]]

--local sourceTrigger = fibaro.getSourceTrigger();
local version   = "0.1.0 - DE"
local oldnodeId = nil ;
-- The variables below can be changed by user.
local subject   = "Battery and device status";
local userIDs   = {2,}     -- mailuser ids
local devpEmail = 25      -- devices per email
local maildays  = {1,2,3,4,5,6,7} -- 1=Sunday, 2=Monday, 3=Tuesday ...7=Saturday
local pushactiv = true;   -- true - sendpush notification
local teleactiv = false;   -- true - send message via telegram
local phoneID   = {2,}   -- phone IDs for push notification
local checktime = "19:25" -- At this time the script is triggerd
local dtformat  = '%d %b. %Y - %X' --Datumsformat
local full      = 100 --between 50 and 100 the battery status is assumed to be good
local warning   = 50  --between 30 and 50 battery is running low
local critical  = 30  --below 30 battery status is asumed as critical
local empty     = 255 --probably a completely empty battery
local nBreacheds= 24  --not Breached since nn hours
local checklBreached = true; -- should lastBreached be checked
local checkSection   = true; -- if false, the section is not output.
local onlySummaryM   = false; -- send only a summary email if true
local debugout = true  -- | true | false
local htmlout  = true  -- | true | false
local sortout  = true  -- | true | false
--
local function printf(...) fibaro.debug(tag, string.format(...)) end
--local Debug = function ( color, message )
--  fibaro.debug(string.format('<%s style="font-family:Courier New;font-size:12px;color:%s;">%s', "span", color, message, "span"))
--end
 
local function contains(days, val)
   for i=1,#days do
      if days[i] == val then
         return true
      end
   end
   return false
end
 
--function printf(fmt, ...)
 --   print(string.format(fmt, ...))
--end

local function sendPush(text)
 if (phoneID[1] ~= nil) then
   for i=1, #phoneID do
      if phoneID[i] ~= nil then
         fibaro.alert("push", {phoneID[i]}, text) --phoneID[i]
         -- fibaro.call(phoneID[i],'sendPush', text)
      end
   end
 end
end
 
local function sendMail(userIDs,subject,mstat,m)
  local subject = subject..'-'..m
  if (userIDs[1] ~= nil) then
    for u=1, #userIDs do
      if userIDs[u] ~= nil then
       -- fibaro.call(userIDs[u], "sendEmail", subject, mstat)
        fibaro.alert("email",{2},mstat)
       -- fibaro.alert("email",{48},mstat)
      end
   end
   mstat = ' ';
   if debugout then print('<font color="green">'..'Mail Nr. '..m.. ' sent.') end 
  end
end
 
local function checklastBreached(uxtimeStamp, lbreached)
  local xseconds = (uxtimeStamp - lbreached)
  local xtime    = (xseconds /60) / 60
  return  math.floor(xtime)
end  
 
local function formatString(dname, room)
   local slength = string.len(dname) + string.len(room)
   local dlength = 30 - slength
   return string.rep ("-", dlength)
end  

local function getSNamebyID(sectionID)
   if checkSection then
      if sectionID == nil or sectionID == "unassigned" then return ' ' end
      if tonumber(sectionID) > 0 then
         local section = api.get("/sections/" .. tostring(sectionID))
         return string.sub(tostring(section.name),1, 2)..','
      end
  end
  return ''  
end

local function validate(phoneID) 
  local ios = api.get('/iosDevices')
  local nID = 0
  for n = 1,#phoneID do
     for _,i in pairs(ios) do
       if phoneID[n] == i.id then
          nID = nID+1
       end 
     end --for
  end --for
  if #phoneID ~= nID then fibaro.warning('phoneID','Hinweis - unbekannte phoneID entdeckt, bitte korrigieren!\n<font color=yellow>Oder Sie können keine Push-Meldungen empfangen.') end
end

local function createlink(id)
  local devID = id  
  local linkID = string.format(
        "<a href=\"../devices/configuration.html?id=%u#bookmark-advanced\" target=\"_blank\""..
        "style=\"display:inline;color:Cyan\">%u</a>",id,id ) 
  return devID, linkID
end

local function checkhc2()
  local info = api.get("/settings/info")
  FW = info.softVersion 
  local date = info.serverStatus
  local daysfrom = (os.time() - date) / (24 * 60 * 60)
  local wholedays = math.floor(daysfrom)
  return os.date('%d-%m-%Y %H:%M:%S', date)..', ('..wholedays..') Tage' 
end 
 

local fc_o  = "<font color=orange>"
local fc_r = "<font color=red>"
local fc_c = "<font color=cyan>"
local fc_g = "<font color=green>"
local sym ={warn='🔔', ok='✔️', empty='❓', crit='⛔️', err='❌' }
local sectiontab = api.get("/sections")
local values = #sectiontab
--  
local function checkallDevices()
 -- print('Start checckalldevices')
  local devices_all = api.get('/devices/?enabled=true&visible=true&isPlugin=false');
  --print(devices_all)
  local uxtimeStamp = os.time(dt);
  local mstat   = '';
  local vstatus = '';
  local bstatus = '';
  local bstatm = '';
  local vstatm = '---------------~= Zusammenfassung der Überprüfung. =~----------------\r\n';
  local stathtml= "<pre><span style='border: 1px solid grey'>"..'Nr |id |Sec| Geräte Name | Raum Name ------------ ----- |Wert% |Sym ' .. "</span></pre>";
  local tdev = {}; 
  local n = 0; -- battery device counter
  local v = 0; -- non battery device counter
  local m = 0; -- mail counter
  local x = 0; -- other devices
  local nb= 0; 
  local b_full,b_low,b_warn,b_empty = 0,0,0,0
  printf('<font color="cyan">'..os.date(dtformat).. ' Version: '..version);
  validate(phoneID)
  printf('<font color="skyblue">Daten werden aufbereitet...')
  if debugout then print('<span style="border:1px solid green"><font color="green">Nr |Sec| Geräte Name     | Raum Name ------------ ----- |XWert% |Sym</font></span>') end
  for id = 1, #devices_all do
    --print('for id')
    if(devices_all[id].properties.batteryLevel ~= nil) then
      --print('ist batterie')
       batteryLevel = devices_all[id].properties.batteryLevel;
       --print(batteryLevel)
    else
       batteryLevel = nil;
    end
 
    if(devices_all[id].properties.nodeId ~= nil) then
       nodeId  = devices_all[id].properties.nodeId;
       --print(nodeId)
    else
       nodeId = nil;
    end
     
    if(devices_all[id].properties.dead ~= nil)  then
       if(devices_all[id].properties.dead == false) then
         --print('isdead ist false')
          isdead = 0;
       else
          isdead = 1;
       end
    else
       isdead = nil;
    end
   -- print(isdead)
    local devID, linkID = createlink(devices_all[id].id )     
    if(tonumber(devices_all[id].roomID) == 0) then
       room = 'unassigned';
       sectionID = 'unassigned';
    else
       room = fibaro.getRoomNameByDeviceID(devices_all[id].id)
       sectionID = tonumber(fibaro.getSectionID(devices_all[id].id))
    end
 --  print(room)
  -- print(sectionID)
    if batteryLevel ~= nil and nodeId ~= nil and batteryLevel ~= '' then
        dname   = devices_all[id].name
       -- print(dname)
        if oldnodeId ~= nodeId then   -- alt lbreached = fibaro.get(devices[id], 'lastBreached')
           if(devices_all[id].properties.dead ~= nil) then
               lbreached = devices_all[id].properties.lastBreached;
           else
               lbreached = nil;
           end
           local sname = getSNamebyID(sectionID)
           --if not(room == "unassigned") then
             n = string.format('%02d',n+1)
             if tonumber(batteryLevel) >= warning and tonumber(batteryLevel) <= full then
                local dots = formatString(dname, room)
                local tableOK  = string.format("%s %s, im Raum %s hat %s %s %% %s", sname, dname, room, dots, batteryLevel, sym.ok)
                local statusOK = n..') ' ..tableOK
                local htmlOK   = "<pre>"..n..') '.. linkID..' '..tableOK .. "</pre>"
                if debugout then print('<font color="green">'.. statusOK) end
                stathtml = stathtml .. htmlOK;b_full = b_full+1
                mstat = mstat .. statusOK ..'\n' 
                table.insert(tdev, tableOK )
             elseif
                tonumber(batteryLevel) >= critical and tonumber(batteryLevel) <= warning then
                local dots = formatString(dname, room)
                local tablewarn  = string.format("%s %s, im Raum %s hat %s %s %% %s", sname, dname, room, dots, batteryLevel, sym.warn)    
                local statuswarn = n..') '..tablewarn
                local htmlWarn   = "<pre>" ..n..') '..linkID..' '.. tablewarn .. "</pre>"  
                if debugout then print('<font color ="yellow">'.. statuswarn) end
                stathtml = stathtml .. htmlWarn;b_low = b_low+1
                mstat = mstat .. statuswarn ..'\n'
                table.insert(tdev, tablewarn )
             elseif
                tonumber(batteryLevel) < critical  then
                local dots = formatString(dname, room)
                local tablecrit = string.format("%s %s, im Raum %s hat %s %s %% %s", sname, dname, room, dots, batteryLevel, sym.crit)    
                local statuscrit= n..') '.. tablecrit    
                local htmlcrit  = "<pre>"..n..') '..linkID..' '.. tablecrit .. "</pre>"
                if debugout then print('<font color="red">'.. statuscrit) end
                stathtml = stathtml .. htmlcrit;b_warn = b_warn+1
                if pushactiv then          
                   sendPush(statuscrit)
                end
                mstat = mstat .. statuscrit ..'\n'
                table.insert(tdev, tablecrit )
             elseif
                tonumber(batteryLevel) > full or tonumber(batteryLevel) == empty then
                local dots = formatString(dname, room)
                local tableempty  = string.format("%s %s, im Raum %s hat %s %s %% %s", sname, dname, room, dots, batteryLevel, sym.empty)
                local statusempty = n..') ' .. tableempty
                local htmlempty = "<pre>"..n..') '.. linkID..' '..tableempty .. "</pre>"
                stathtml = stathtml .. htmlempty;b_empty = b_empty+1
                if debugout then print('fontcolor="red">'.. statusempty) end
                if pushactiv then
                   sendPush(statusempty)
                end
                mstat = mstat .. statusempty..'\n';
                table.insert(tdev, tableempty )
             end
         
             if tonumber(isdead) == 1 then
                local dots = formatString(dname, room)
                local tabledead  = string.format("%s %s, im Raum %s ist tot. %s %s", sname, dname, room, dots, sym.err)
                local statusv = string.format("%s %s %s | room %s is dead! ----%s %s", linkID, sname, dname, room, dots, sym.err)
                local statusdead = n..') '.. tabledead
                local htmldead = "<pre>"..n..') '..linkID.. ' '..tabledead .. "</pre>"
                if debugout then print('<font color="red">', statusdead) end
                mstat = mstat .. statusdead .. '\n';
                stathtml = stathtml .. htmldead
                vstatus = vstatus ..fc_r..n..') '..linkID..'  '..tabledead .. '\n';
                vstatm  = vstatm ..n..') '.. devID ..' '.. tabledead..'\n';
                sendPush(statusdead)
                table.insert(tdev, tabledead )
                if teleactiv then  --Telegram sender
                   fibaro.setGlobal('telegram_text', dname.. ' is dead! '.. os.date('%c'))  
                   fibaro.startScene(61)
                 end  
              end
              -- [[ Check last breached time ]] 
              if lbreached ~= nil and checklBreached and tonumber(lbreached) > 0 then
                 local xhours = checklastBreached(uxtimeStamp, lbreached)
                 if xhours > nBreacheds then
                    local sname = getSNamebyID(sectionID)
                    if xhours >= 72 then -- laenger als 3 Tage 
                        timestr = string.format('%.2f',xhours / 24)..' Tagen.'
                    else
                        timestr = tostring(xhours) .. ' Stunden.'
                    end
                    bstatus = bstatus..'</br>' ..fc_o .. sname ..' '.. dname ..' ('..room..') nicht ausgelöst seit: ' ..timestr.. '\n'
                    bstatm  = bstatm .. sname ..' '.. dname ..' ('..room..') nicht ausgelöst seit: ' ..timestr.. '\n'
                    nb = nb+1
                 end
              end
              if lbreached ~= nil and checklBreached and tonumber(lbreached) == 0 then
                 bstatus = bstatus..'</br>' ..fc_o.. sname ..' '.. dname ..' ('..room..') noch nie ausgelöst.\n'
                 bstatm  = bstatm .. sname ..' '.. dname ..' ('..room..') noch nie ausgelöst.\n'
                 nb = nb+1
              end
              if (n % devpEmail == 0) and oldNodeId == nodeID then
                 if contains(maildays, weekday) and onlySummaryM == false then
                    m = m+1
                    sendMail(userIDs,subject,mstat,m)
                    mstat = ' '
                    fibaro.sleep(500)
                 end
              end  
          --end
       end
       oldnodeId = nodeId
    else -- [[ device is non battery device ]]
       if(devices_all[id].type ~= nil) then
          dtype = devices_all[id].type;
       else
          dtype = nil;
       end
 
       if (dtype == 'virtual_device') or (dtype == 'HC_user') or (dtype == 'iOS_device') then  
          x = x+1
       else
          v = v+1
          local dname = devices_all[id].name
          if(tonumber(devices_all[id].roomID) == 0) then
              room = 'unassigned';
          else
              room = fibaro.getRoomNameByDeviceID(devices_all[id].id)
          end
 
          if (isdead ~= nil and tonumber(isdead) == 1) then
             local dots = formatString(dname, room)
             local sectionID = fibaro.getSectionID(devices_all[id].id)
             local sname = getSNamebyID(sectionID) 
             local statusdead = string.format("%s %s, im Raum %s ist tot! --%s %s", sname, dname, room, dots, sym.err)
             local statusv = string.format("nb)%s %s %s, im Raum %s ist tot! --%s %s", linkID, sname, dname, room, dots, sym.err)
             local htmldead =  "<pre>nb) "..string.format("%s %s %s im Raum %s ist tot! --%s %s", linkID, sname, dname, room, dots, sym.err).."</pre>"
             mstat = mstat .. statusdead .. '\n';
             vstatm = vstatm..'nb) '.. devID..' '..sname..' '..dname..' im Raum'..room..' ist tot'.. ' '..sym.err..'\n';
             table.insert(tdev, statusdead )
             if debugout then print('<font color="red">'.. statusdead) end
             vstatus = vstatus .. fc_r.. statusv .. '\n';
             stathtml = stathtml .. htmldead
             sendPush(statusdead)
          end
       end
    end  
    fibaro.sleep(80)
  end  --for
  -- [[ data output and last mail send ]]
  if sortout then
    print('')
    table.sort(tdev)
    table.insert(tdev, 1, "<span style='border:1px solid grey'>"..'Sec| Geräte Name | Raum Name - - - - - - - - - - |Wert% |Sym.' .. "</span>")
    printf('<font color="skyblue" --- Sortierte Ausgabe nach Bereichen ---')
    for i,j in ipairs(tdev) do
       --print(j)
       print(fc_c..j)
    end
  end
  --end sending battery status----------------------------------
   if contains(maildays, weekday) and onlySummaryM == false then
     m = m+1
     sendMail(userIDs,subject,mstat,m)
     mstat = ' ';
  end 
   --end sending battery status----------------------------------
  
  if ( not checklBreached) then bstatus = "Der 'last breached status' wurde nicht gecheckt. \n" end
  if (vstatus == '') then 
     vstatus = vstatus .."\nKeine toten Geräte gefunden.\n" 
     vstatm  = vstatm .. vstatus     
  end
  hc2runtime = "HC3 läuft seit: " .. checkhc2().. ' mit FW: '.. FW
  vstatus = vstatus ..'</br>'.. fc_o.. n .. ' Batterie Geräte wurden gecheckt.\n</br>'
  vstatus = vstatus ..fc_o.. 'Davon: '..b_full..' Voll, '..b_low.. ' Medium, '..b_warn..' Schwach, '..b_empty..' Leer\n'    
  vstatus = vstatus ..'</br>'..fc_o.. 'Alle nicht Batterie Geräte gecheckt.'
  b_stat  = n .. ' Batterie Geräte wurden gecheckt.\n'
  b_stat  = b_stat..'Davon: '..b_full..' Voll, '..b_low.. ' Medium, '..b_warn..' Schwach, '..b_empty..' Leer'    
  bstatus = bstatus ..'</br>'..fc_r..'Anzahl: '..nb..' <- Behalten sie diesen Wert im Auge.' 
  separator = '-----------------------------------------------------------------'
  
  if htmlout then  print('<font color="lightgreen">'.. stathtml) end
  printf('<font color="cyan"> -------------~= Zusammenfassung der Überprüfung. =~--------------')
  printf('<font color="orange">'..vstatus)
  
  printf(fc_c.. separator)
  print(fc_c..'--- Geräte deren Status "not breached" seit längerem besteht. ---')
  print(fc_o.. bstatus)
  print(fc_g..hc2runtime )
  print(fc_c.. "Geräte geprüft am: ".. os.date(dtformat))
  mstat = vstatm..b_stat..'\n'
  mstat = mstat..separator..'\n'..bstatm
  mstat = mstat..'Anzahl: '..nb.. ' <- Behalten sie diesen Wert im Auge.\n'
  mstat = mstat .. hc2runtime ..  '\n \rGeprüft am: '..os.date(dtformat)..' Ver.: '..version
  if contains(maildays, weekday) then
     m = m+1
     sendMail(userIDs,subject,mstat,m)
     mstat = ' ';
  end  
end  -- function 
 
function main()
   -- print("Main Start")
    local currentDate = os.date("*t");
    local currenthour = string.format("%02d", currentDate.hour) .. ":" .. string.format("%02d", currentDate.min)
    weekday = currentDate.wday
    --local startSource = fibaro.getSourceTrigger();
    if currenthour == checktime then
       checkallDevices()
    end
    fibaro.setTimeout(60*1000,main)
end
 
if (sourceTrigger["type"] == "se-start") then
    main()
else
   if (sourceTrigger["type"] == "user") then
      print('User start')
      local currentDate = os.date("*t");
      weekday = currentDate.wday
      checkallDevices()
    end
end
main()

Debug Ausgabe:D

Wie muss denn der „Trigger auf Gateway-start ändern“ aussehen bei dem HC3 damit das funktioniert?
Ich habe z.Zt. {} und so geht es nur manuell :frowning:

wäre das so richtig?
{
conditions = { {
isTrigger = true,
operator = „==“,
property = „start“,
type = „se-start“,
value = true
} },
operator = „all“
}
p.s. Danke für dein Script :slight_smile:

Hier mal ein Beispiel:

{
  conditions = { {
      isTrigger = true,
      operator = "match==",
      property = "cron",
      type = "date",
      value = { "35", "20", "*", "*", "1,3,6", "*" }
    } },
  operator = "all"
}

oder einfach so:

{
  isTrigger = true,
  operator = "==",
  property = "start",
  type = "se-start",
  value = true
}

Das se-start ist ja nur da, dass das script bei einem Neustart des Gateways startet. Da kann man beliebig damit umgehen.
Wenn es dann läuft läuft es permanent durch und die Hauptfunktion wird durch „fibaro.setTimeout(60*1000,main)“ in der function „main“ minütlich aufgerufen.
Also auch bei manuellem User Start, läuft das Script dann permanent. Die Prüfung findet dann allerdings sofort statt und dann immer wenn die Zeit die „checktime“ ermittelt hat.
Schau dir die Variablen am Anfang des Scripts an, dann ist es leichter alles zu verstehen.

Suppi & Danke!
Ich hab es jetzt mit der zweiten Variante gemacht und es läuft :slight_smile:

1 „Gefällt mir“

thanks for the awesome information.

Hallo @jeep ,
Habe meine HC2 noch immer nicht auf die HC3 übersiedelt, weil ich Email Notifikation sehr intensiv nutze :wink: Dein Batteriescript läuft immer och hervorragend!

Wer oder was ist ER4??
Sollte ich mir das ansehen?

Hallo @pblacky,

der ER4 (EventRunner4) ist eine QA die inzwischen von fast jedem benutzt wird der ein HC3 hat.
Die Möglichkeiten sind schier unbegrenzt und der Programmierer(jgab) bietet Dir auch immer gerne Hilfe dazu an.
Beispiel:

--Rollladen Büro  
  rule("@22:15+rnd(00:05,00:15) => fibaro.call(89, 'setValue', 0)") -- Rollladen Büro schließen

--  Markise bei Regen schließen 
  rule([[RainSensor:value == true & Markise:value < 99 => fibaro.call(Markise,'setValue',99)]])  

--Terrasse vorne
  rule("@sunset+00:30 => Terrasse_vorne:on;fibaro.call(22,'sendPush','Terrasse vorne ist an.')")  --30 Min nach Sonnenuntergang

-- Keyfob Büro
  rule([[Keyfob:central.keyId == 5 & Keyfob:central.keyAttribute == 'Pressed' => RGBWBuero:off]])
  rule([[Keyfob:central.keyId == 6 & Keyfob:central.keyAttribute == 'Pressed' => fibaro.call(212, 'setColor', 0, 0, 0, 170)]])

 -- rule("@@00:00:10 => 8:start") --starte Szene mit ID 8

Das sind nur ein paar meiner einfachen Regeln. Wie gesagt schau dir das im Forum an und überzeuge Dich selbst. Es erspart dir viel Arbeit beim Umzug vom HC3. Ich würde immer nur einzelne Geräte umziehen und nicht alle auf einmal.
Ach ja, das Batteriecheckscript läuft auch gut auf dem HC3.

/cu

Das klingt sehr interessant, danke!!

Echt? Hast du das mit den Zeilenumbrüchen auch schon gelöst ?
Ich glaube ich muss mein HC3 echt mal wieder aktivieren :wink:

Ich hatte keinen Fehler im Script :grinning:, (das sagte ich auch schon vor über einem Jahr) aber Fibaro hat es nach einem Jahr und vielen Beschwerden geschafft ein Update zu liefern das den Fehler behebt.

Sorry das kam falsch rüber!!
Ich weiß dass es kein Fehler in deinem Skript war, sondern Fibaro keine Zeilenumbrüche im Skript übertragen konnte!
Das ist also gelöst, dann bin ich wieder mehr motiviert mich der HC3 zu widmen…

Frage zum Umstieg:
Wie machst du das mit verbauten Fibaro Switches wo man nicht mehr so leicht rankommt?
Da stell ich mir das neu Anlernen schwierig vor!!
Oder gibt es hier einen Trick?
Hab jedenfalls hier einen neuen Thread aufgemacht, vielleicht steig ich nun doch endlich um :wink:

thanks my issue has been fixed.

Moin Gemeinde!

Ich bin echt zu doof! Welcher Teil der Szene kommt in die Bedingung und welcher in die Aktion? Früher, bei der HC2 habe ich zumindest logische Zusammenhänge verstanden. Da ich aber von LUA keinen blassen Schimmer habe, wäre ich über einen Rat sehr dankbar.

Viele Grüße

Christian

Das ist die linke Seite Bedingung/Trigger und das ganze Script kommt rechts unter der Aktion.