local Y=require("yaru.core") local gfx=require("yaru.gfx") local ui=require("yaru.ui") local computer=require("computer") local component=require("component") local unicode=require("unicode") local M={} local BARS={"▁","▂","▃","▄","▅","▆","▇","█"} local function sources() local r={{id="computer",name="This computer",get=function()return computer.energy(),computer.maxEnergy()end}} for addr,typ in component.list()do local ok,m=pcall(component.methods,addr) if ok and m then local cur,mx=m.getEnergyStored and"getEnergyStored"or m.getEnergy and"getEnergy"or m.getStored and"getStored", m.getMaxEnergyStored and"getMaxEnergyStored"or m.getMaxEnergy and"getMaxEnergy"or m.getCapacity and"getCapacity" if cur and mx and typ~="computer"then local p=component.proxy(addr) r[#r+1]={id=addr,name=typ.." "..addr:sub(1,5),get=function()return p[cur](),p[mx]()end} end end end return r end local function human(v) local a=math.abs(v) if a>=1e9 then return string.format("%.1fG",v/1e9)elseif a>=1e6 then return string.format("%.1fM",v/1e6) elseif a>=1e4 then return string.format("%.1fk",v/1e3)end return string.format("%.0f",v) end function M.open() local st={src=sources(),hist={},sel=1} local win=Y.newWindow{app="energy",title="Power Statistics",w=90,h=28,minW=60,minH=14} win.tickInterval=2 local function sample() local now=computer.uptime() for _,s in ipairs(st.src)do local ok,cur,mx=pcall(s.get) local h=st.hist[s.id]or{} st.hist[s.id]=h if ok and cur then h[#h+1]={now,cur,mx} if#h>150 then table.remove(h,1)end end end end sample() function win.tick(w)sample();Y.redraw(w)end function win.draw(w,cw,ch) local t=Y.theme local SIDE=32 gfx.fill(1,1,SIDE,ch,t.sidebar) for i,s in ipairs(st.src)do local y=1+(i-1)*3 if y+2>ch then break end if i==st.sel then ui.round(2,y,SIDE-2,2,t.dark and 0x454545 or 0xDEDDDA)end local h=st.hist[s.id]or{} local last=h[#h] gfx.text(4,y,ui.trunc(s.name,SIDE-6),t.fg) if last and last[3]>0 then ui.progress(4,y+1,SIDE-12,last[2]/last[3],last[2]/last[3]<.2 and t.danger or t.success) gfx.text(SIDE-6,y+1,string.format("%3d%%",math.floor(last[2]*100/last[3])),t.dim) end ui.hit(w,1,y,SIDE,2,function()st.sel=i end) end local s=st.src[st.sel] if not s then return end local h=st.hist[s.id]or{} local last=h[#h] local x,pw=SIDE+3,cw-SIDE-4 gfx.text(x,1,s.name,t.fg) if not last then gfx.text(x,3,"no data",t.dim)return end local first=h[math.max(1,#h-10)] local rate=(#h>1 and last[1]>first[1])and(last[2]-first[2])/(last[1]-first[1])or 0 gfx.text(x,2,string.format("%s / %s (%d%%)",human(last[2]),human(last[3]),last[3]>0 and math.floor(last[2]*100/last[3])or 0),t.dim) local eta="" if rate<-0.01 then eta=string.format("empty in %d min",math.floor(last[2]/-rate/60)) elseif rate>0.01 and last[3]>last[2]then eta=string.format("full in %d min",math.floor((last[3]-last[2])/rate/60))end gfx.text(x,3,string.format("rate %s%s/s %s",rate>=0 and"+"or"",human(rate),eta),rate<0 and t.danger or t.success) local gy,gh=5,ch-6 local vals={} for i=1,#h do vals[i]=h[i][2]end ui.graph(x,gy,pw,gh,vals,last[3]>0 and last[3]or 1,t.accent) gfx.text(x,ch,string.format("last %d samples, every 2 s",#h),t.dim) end function win.key(w,char,code) if code==Y.K.down then st.sel=math.min(#st.src,st.sel+1)elseif code==Y.K.up then st.sel=math.max(1,st.sel-1)end end function win.onSignal(name)if name=="component_added"or name=="component_removed"then st.src=sources()end end return win end return M