Scen aktiverad på tid i lua

Erik
Medlem
Posts: 65
Joined: 10 Jan 2016, 18:46
8

local wind_km_h = ( tonumber(api.get('/weather')['Wind'])) ??? funkar det?
jang
Medlem
Posts: 388
Joined: 05 Jan 2014, 00:44
10
Location: Stockholm

Ok, insåg inte att HC2 använder Yahoo weather, så det går lika bra att använda api/weather (fördelen med att anropa det själv är att man även kan få forecasts... - och jag vet inte hur ofta HC2 frågar Yahoo...)
Koden blir iallafall lite enklare.
Obs, att nu stängs markiserna om det är dåligt väder eller det blåser för hårt... men den reagerar förmodligen inte så snabbt på vind då Yahoo weather inte uppdateras så ofta. Nu pollar vi det varannan minut... Ofta har markiser en liten propeller som trigger markisen att rulla in om det kommer en vindby. En egen z-wave vindmätare skulle kanske kunna fixa det.

Code: Select all

--[[
%% properties
%% events
5 CentralSceneEvent
%% globals
--]]

--[[
-- EventRunnerLight. Single scene instance framework
-- Copyright 2018 Jan Gabrielsson. All Rights Reserved.
-- Email: jan@gabrielsson.com
--]]

_version = "1.1" 
osTime = os.time
osDate = os.date
_debugFlags = { post=false,invoke=false,triggers=false,dailys=false,timers=false,rule=false,fibaro=true,fibaroGet=false }

if dofile then dofile("EventRunnerDebug.lua") end -- Support for running off-line on PC/Mac

---- Single scene instance, all fibaro triggers call main(sourceTrigger) ------------

function printf(...) fibaro:debug(string.format(...)) end

-- Table how much to open scheds depending on time and month, other time values are interpolated
SouthValue = {
  Jan = {{"09:00",80},{"10:00",65},{"11:00",50},{"11:30",33},{"12:30",50},{"13:00",65},{"14:00",80},{"15:00",100}},
  Feb = {{"09:00",80},{"10:00",65},{"11:00",50},{"11:30",33},{"12:30",50},{"13:00",65},{"14:00",80},{"15:00",100}},
  Mar = {{"09:00",80},{"10:00",65},{"11:00",50},{"11:30",33},{"12:30",50},{"13:00",65},{"14:00",80},{"15:00",100}},
  Apr = {{"09:00",80},{"10:00",65},{"11:00",50},{"11:30",33},{"12:30",50},{"13:00",65},{"14:00",80},{"15:00",100}},
  May = {{"09:00",80},{"10:00",65},{"11:00",50},{"11:30",33},{"12:30",50},{"13:00",65},{"14:00",80},{"15:00",100}},
  Jun = {{"09:00",80},{"10:00",65},{"11:00",50},{"11:30",33},{"12:30",50},{"13:00",65},{"14:00",80},{"15:00",100}},
  Jul = {{"09:00",80},{"10:00",65},{"11:00",50},{"11:30",33},{"12:30",50},{"13:00",65},{"14:00",80},{"15:00",100}},
  Aug = {{"09:00",80},{"10:00",65},{"11:00",50},{"11:30",33},{"12:30",50},{"13:00",65},{"14:00",80},{"15:00",100}},
  Sep = {{"09:00",80},{"10:00",65},{"11:00",50},{"11:30",33},{"12:30",50},{"13:00",65},{"14:00",80},{"15:00",99}},
  Oct = {{"09:00",80},{"10:00",65},{"11:00",50},{"11:30",33},{"12:30",50},{"13:00",65},{"14:00",80},{"15:00",100}},
  Nov = {{"09:00",80},{"10:00",65},{"11:00",50},{"11:30",33},{"12:30",50},{"13:00",65},{"14:00",80},{"15:00",100}},
  Dec = {{"09:00",80},{"10:00",65},{"11:00",50},{"11:30",33},{"12:30",50},{"13:00",65},{"14:00",80},{"15:00",100}},
}

WestValue = {
  Jan = {{"09:00",80},{"10:00",65},{"11:00",50},{"11:30",33},{"12:30",50},{"13:00",65},{"14:00",80},{"15:00",100}},
  Feb = {{"09:00",80},{"10:00",65},{"11:00",50},{"11:30",33},{"12:30",50},{"13:00",65},{"14:00",80},{"15:00",100}},
  Mar = {{"09:00",80},{"10:00",65},{"11:00",50},{"11:30",33},{"12:30",50},{"13:00",65},{"14:00",80},{"15:00",100}},
  Apr = {{"09:00",80},{"10:00",65},{"11:00",50},{"11:30",33},{"12:30",50},{"13:00",65},{"14:00",80},{"15:00",100}},
  May = {{"09:00",80},{"10:00",65},{"11:00",50},{"11:30",33},{"12:30",50},{"13:00",65},{"14:00",80},{"15:00",100}},
  Jun = {{"09:00",80},{"10:00",65},{"11:00",50},{"11:30",33},{"12:30",50},{"13:00",65},{"14:00",80},{"15:00",100}},
  Jul = {{"09:00",80},{"10:00",65},{"11:00",50},{"11:30",33},{"12:30",50},{"13:00",65},{"14:00",80},{"15:00",100}},
  Aug = {{"09:00",80},{"10:00",65},{"11:00",50},{"11:30",33},{"12:30",50},{"13:00",65},{"14:00",80},{"15:00",100}},
  Sep = {{"09:00",99},{"10:00",99},{"11:00",99},{"11:30",99},{"12:30",99},{"13:00",99},{"14:00",85},{"15:00",60},{"17:00",45},{"18:00",99},{"19:00",99},{"20:00",99}},
  Oct = {{"09:00",80},{"10:00",65},{"11:00",50},{"11:30",33},{"12:30",50},{"13:00",65},{"14:00",80},{"15:00",100}},
  Nov = {{"09:00",80},{"10:00",65},{"11:00",50},{"11:30",33},{"12:30",50},{"13:00",65},{"14:00",80},{"15:00",100}},
  Dec = {{"09:00",80},{"10:00",65},{"11:00",50},{"11:30",33},{"12:30",50},{"13:00",65},{"14:00",80},{"15:00",100}},
}

local houseSheds = {
--South
  {name="Matsal",start="09:00",stop="18:00",id=491,tab=SouthValue}, --Matsal
  {name="Kök syd",start="09:00",stop="18:00",id=485,tab=SouthValue}, --Kök syd
  {name="Sveas Rum",start="09:00",stop="18:00",id=497,tab=SouthValue}, --Sveas Rum
  {name="Augusts Rum",start="09:00",stop="18:00",id=500,tab=SouthValue}, --Augusts Rum

--West
  {name="Vardagsrum Nere",start="09:00",stop="18:00",id=488,tab=WestValue}, --Matsal
  {name="Bibliotek",start="09:00",stop="18:00",id=482,tab=WestValue}, --Kök syd
  {name="Lekrum Väst",start="09:00",stop="18:00",id=506,tab=WestValue}, --Vardagsrum Uppe

--Vardagsrum Uppe
  {name="Vardagsrum Uppe",start="09:00",stop="18:00",id=503,tab=WestValue} --Vardagsrum Uppe
}

function shedsValue(time,shed) -- Calculates how much sheds should be open given time and month
  local mt,stop,start = shed.tab[osDate("%b")]
  for _,t in ipairs(mt) do if t[1] <= time then start = t else break end end
  start = start or mt[1][1] -- lowest/equal before time or the first value
  for _,t in ipairs(mt) do if t[1] >= time then stop = t break end end 
  stop = stop or mt[#mt][1] -- highest/equal after time or the last value
  if time < mt[1][1] or time>mt[#mt][1] then return 0 end -- Time outside min & max returns 0
  local x1,y1,x2,y2 = toTime(start[1]),start[2],toTime(stop[1]),stop[2]
  return x1==x2 and y1 or math.floor((y2-y1)/(x2-x1)*(toTime(time)-x1)+y1+0.5) -- linear extrapolation for times not in table
end

local keepShedsClosed = false
local goodWeather = {[30]=true,[36]=true,[34]=true,[32]=true,[28]=true}  -- conditions when sheds should be open
local maxWind = 25.0

function main(sourceTrigger)
  local event = sourceTrigger

  if event.type=='schedule' then
    local shed = event.shed
    local value = shedsValue(osDate("%H:%M"),shed)
    local name,id = event.shed.name,event.shed.id
    if not keepShedsClosed then
      printf("Setting %s with id %s to value %s",name,id,value)
      fibaro:call(id,"setValue",value)
    else
      printf("Keeping %s with id %s closed",name,id)      
    end
    if osDate("%H:%M") > shed.stop then time = "n/"..shed.start else time = "+/00:30" end
    printf("Scheduling %s for %s",name,osDate("%X",toTime(time)))
    post(event,time)
  end

  -- setUp initial posts of daily events
  if event.type == 'autostart' or event.type == 'other' then
    local now,time = osDate("%H:%M")
    for _,shed in ipairs(houseSheds) do
      time = "t/"..osDate("%H:%M:%S")
      if now <= shed.start or now > shed.stop then time = "n/"..shed.start end
      printf("Scheduling %s for %s",shed.name,osDate("%X",toTime(time)))
      post({type='schedule', shed=shed},time)
    end
    post({type='checkWeather'})  -- and also start checking weather every 2 minutes
  end

  if event.type=='checkWeather' then
    local weather = api.get("/weather")
    local wind = weather.Wind
    local wcode = weather.ConditionCode
    if wind <= maxWind and goodWeather[wcode] then -- good weather, keep sheds open
      post({type='closeSheds', close=false}) -- open sheds
    else
      post({type='closeSheds', close=true})   -- close sheds
    end 
    post(event,"+/00:02") -- check weather every other minute
  end

  if event.type == 'closeSheds' and event.close and not keepShedsClosed then -- close all Sheds
    keepShedsClosed = true
    for _,shed in ipairs(houseSheds) do  
      fibaro:call(shed.id,"setValue",0)
    end
  elseif event.type == 'closeSheds' and (not event.close) and keepShedsClosed then -- restore all sheds
    local now = osDate("%H:%M")  -- open sheds again
    keepShedsClosed = false
    for _,shed in ipairs(houseSheds) do
      fibaro:call(shed.id,"setValue",shedsValue(now,shed))
    end
  end

  if event.type=='event' and event.event.data.deviceId==5 then -- Toggle sheds at keypress (any)
    post({type='sheds', close=(not keepShedsClosed)}) 
  end

end -- main()

------------------------ Framework, do not change ---------------------------  
-- Spawned scene instances post triggers back to starting scene instance ----
local _trigger = fibaro:getSourceTrigger()
local _type, _source = _trigger.type, _trigger
local _MAILBOX = "MAILBOX"..__fibaroSceneId 

if _type == 'other' and fibaro:args() then
  _trigger,_type = fibaro:args()[1],'remote'
end

function _midnight() t=osDate("*t"); t.min,t.hour,t.sec=0,0,0; return osTime(t) end

function hm2sec(hmstr)
  local sun,offs = hmstr:match("^(%a+)([+-]?%d*)")
  if sun and (sun == 'sunset' or sun == 'sunrise') then
    hmstr,offs = fibaro:getValue(1,sun.."Hour"), tonumber(offs) or 0
  end
  local h,m,s = hmstr:match("(%d+):(%d+):?(%d*)")
  return h*3600+m*60+(tonumber(s) or 0)+(offs or 0)*60
end

function toTime(time)
  if type(time) == 'number' then return time end
  local p = time:sub(1,2)
  if p == '+/' then return hm2sec(time:sub(3))+osTime() -- Plus now
  elseif p == 'n/' then
    local t1 = _midnight()+hm2sec(time:sub(3))-osTime()      -- Next
    return (t1 > 0 and t1 or t1+24*60*60)+osTime()
  elseif p == 't/' then return  _midnight()+hm2sec(time:sub(3))  -- Today
  else return hm2sec(time) end
end

function post(event, time) return setTimeout(function() main(event) end,((time and toTime(time)-osTime()) or 0)*1000) end
function cancel(ref) if ref then clearTimeout(ref) end return nil end
function postRemote(sceneID,event) event._from=__fibaroSceneId; fibaro:startScene(sceneID,{json.encode(event)}) end

---------- Producer(s) - Handing over incoming triggers to consumer --------------------
if ({property=true,global=true,event=true,remote=true})[_type] then
  local event = type(_trigger) ~= 'string' and json.encode(_trigger) or _trigger
  local ticket = string.format('<@>%s%s',tostring(_source),event)
  repeat 
    while(fibaro:getGlobal(_MAILBOX) ~= "") do fibaro:sleep(100) end -- try again in 100ms
    fibaro:setGlobal(_MAILBOX,ticket) -- try to acquire lock
  until fibaro:getGlobal(_MAILBOX) == ticket -- got lock
  fibaro:setGlobal(_MAILBOX,event) -- write msg
  fibaro:abort() -- and exit
end

---------- Consumer - Handing over incoming triggers to main() --------------------
local function _poll()
  local l = fibaro:getGlobal(_MAILBOX)
  if l and l ~= "" and l:sub(1,3) ~= '<@>' then -- Something in the mailbox
    fibaro:setGlobal(_MAILBOX,"") -- clear mailbox
    post(json.decode(l)) -- and "post" it to our "main()" in new "thread"
  end
  setTimeout(_poll,250) -- check every 250ms
end

---------- Startup --------------------
if _type == 'autostart' or _type == 'other' then
  printf("Starting EventRunnerLite demo")
  if not _OFFLINE then 
    if not string.find(json.encode((api.get("/globalVariables/"))),"\"".._MAILBOX.."\"") then
      api.post("/globalVariables/",{name=_MAILBOX}) 
    end
    fibaro:setGlobal(_MAILBOX,"") 
    _poll()  -- start polling mailbox
    main(_trigger)
  else
    collectgarbage("collect") GC=collectgarbage("count")
    _System.runOffline(function() main(_trigger) end) 
  end
end
Post Reply