vNemesis

IC2 Energy Monitor

Oct 10th, 2025 (edited)
71
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 10.52 KB | None | 0 0
  1. -- Usage: em <monitorSide> <redstoneSide> [activate%] [deactivate%]
  2. -- Example: em right back 30 80
  3.  
  4. local tArgs = {...}
  5. if #tArgs < 2 then
  6.     print("Usage: em <monitorSide> <redstoneSide> [activate%] [deactivate%]")
  7.     return
  8. end
  9.  
  10. local monitorSide = tArgs[1]
  11. local redstoneSide = tArgs[2]
  12. local activateThreshold = tonumber(tArgs[3]) or 25
  13. local deactivateThreshold = tonumber(tArgs[4]) or 75
  14.  
  15. local monitor = peripheral.wrap(monitorSide)
  16. if not monitor then
  17.     print("Error: No monitor on side '" .. monitorSide .. "'")
  18.     return
  19. end
  20.  
  21. monitor.setTextScale(0.8)
  22. local w, h = monitor.getSize()
  23.  
  24. -- Smart line redraw (no flicker)
  25. local lastDrawn = {}
  26. local function smartWriteLine(y, text, colour, bg)
  27.     if lastDrawn[y] ~= text then
  28.         monitor.setCursorPos(1, y)
  29.         monitor.setTextColour(colour or colours.white)
  30.         monitor.setBackgroundColour(bg or colours.black)
  31.         monitor.clearLine()
  32.         monitor.write(text)
  33.         monitor.setTextColour(colours.white)
  34.         monitor.setBackgroundColour(colours.black)
  35.         lastDrawn[y] = text
  36.     end
  37. end
  38.  
  39. local function divider(y)
  40.     smartWriteLine(y, string.rep("-", w))
  41. end
  42.  
  43. -- Fill bar with colour ramp
  44. local function drawFillBar(y, percent)
  45.     local barWidth = w - 2
  46.     local filledBars = math.floor((percent / 100) * barWidth)
  47.     monitor.setCursorPos(1, y)
  48.     monitor.write("[")
  49.     for i = 1, barWidth do
  50.         if i <= filledBars then
  51.             local p = (i / barWidth) * 100
  52.             local colour = colours.red
  53.             if p > 80 then colour = colours.green
  54.             elseif p > 50 then colour = colours.yellow
  55.             elseif p > 20 then colour = colours.orange end
  56.             monitor.setBackgroundColour(colour)
  57.             monitor.write(" ")
  58.             monitor.setBackgroundColour(colours.black)
  59.         else
  60.             monitor.write(" ")
  61.         end
  62.     end
  63.     monitor.write("]")
  64. end
  65.  
  66. -- Discover IC2 storage blocks
  67. local function findStorages()
  68.     local list = {}
  69.     for _, name in ipairs(peripheral.getNames()) do
  70.         if string.find(name, "ic2:batbox") or
  71.            string.find(name, "ic2:cesu") or
  72.            string.find(name, "ic2:mfe") or
  73.            string.find(name, "ic2:mfsu") then
  74.             table.insert(list, {peripheral.wrap(name), name})
  75.         end
  76.     end
  77.     return list
  78. end
  79.  
  80. local function formatEU(energy)
  81.     if energy >= 1000000 then
  82.         return string.format("%.1f M EU", energy / 1000000)
  83.     elseif energy >= 1000 then
  84.         return string.format("%.1f k EU", energy / 1000)
  85.     else
  86.         return string.format("%.0f EU", energy)
  87.     end
  88. end
  89.  
  90. local function formatTime(seconds)
  91.     seconds = math.max(0, math.floor(seconds))
  92.     local hh = math.floor(seconds / 3600)
  93.     local mm = math.floor((seconds % 3600) / 60)
  94.     local ss = seconds % 60
  95.     return string.format("%02d:%02d:%02d", hh, mm, ss)
  96. end
  97.  
  98. -- State
  99. local lastTotals = {}   -- stores last total and per-block stored values
  100. local redstoneActive = false
  101. local currentPage = 1
  102. local totalPages = 1
  103. local lastPage = nil
  104.  
  105. -- Combined totals across all storages
  106. local function getTotals(storages)
  107.     local cap, stored = 0, 0
  108.     for _, s in ipairs(storages) do
  109.         cap = cap + (s[1].getEUCapacity() or 0)
  110.         stored = stored + (s[1].getEUStored() or 0)
  111.     end
  112.     return cap, stored
  113. end
  114.  
  115. -- Page 1: Combined overview
  116. local function drawCombined(storages)
  117.     local cap, stored = getTotals(storages)
  118.     local fill = (cap > 0) and (stored / cap * 100) or 0
  119.     local change = stored - (lastTotals.total or stored)
  120.     lastTotals.total = stored
  121.  
  122.     local status = "Stable"
  123.     if change > 0 then status = "Charging"
  124.     elseif change < 0 then status = "Discharging" end
  125.  
  126.     -- Time estimate (in seconds, assuming 1s cadence)
  127.     local timeString = "Stable"
  128.     if change > 0 then
  129.         local secondsToFull = (cap - stored) / change
  130.         timeString = "Full in " .. formatTime(secondsToFull)
  131.     elseif change < 0 then
  132.         local secondsToEmpty = stored / math.abs(change)
  133.         timeString = "Empty in " .. formatTime(secondsToEmpty)
  134.     end
  135.  
  136.     -- Redstone hysteresis by fill %
  137.     if fill < activateThreshold then
  138.         redstone.setOutput(redstoneSide, true)
  139.         redstoneActive = true
  140.     elseif fill > deactivateThreshold then
  141.         redstone.setOutput(redstoneSide, false)
  142.         redstoneActive = false
  143.     end
  144.  
  145.     smartWriteLine(1, "IC2 Network Storage (Combined)")
  146.     smartWriteLine(2, "Capacity : " .. formatEU(cap))
  147.     smartWriteLine(3, "Stored   : " .. formatEU(stored))
  148.     smartWriteLine(4, string.format("Filled   : %.2f%%", fill), fill < activateThreshold and colours.red or colours.white)
  149.  
  150.     -- Fill bar line cleared explicitly to avoid artifacts
  151.     monitor.setCursorPos(1, 5); monitor.clearLine()
  152.     drawFillBar(5, fill)
  153.  
  154.     divider(6)
  155.     smartWriteLine(7, "Status   : " .. status .. " (" .. change .. " EU/t)")
  156.     divider(8)
  157.     smartWriteLine(9, timeString)
  158.  
  159.     if change >= 0 then
  160.         smartWriteLine(10, "Actual In : " .. change .. " EU/t")
  161.         smartWriteLine(11, "Actual Out: 0 EU/t")
  162.     else
  163.         smartWriteLine(10, "Actual In : 0 EU/t")
  164.         smartWriteLine(11, "Actual Out: " .. math.abs(change) .. " EU/t")
  165.     end
  166.  
  167.     smartWriteLine(12, redstoneActive and "Redstone: ACTIVE" or "Redstone: Inactive",
  168.                    redstoneActive and colours.red or colours.white)
  169. end
  170.  
  171. -- Page 2+: Per-block breakdown with overflow paging
  172. local function drawBreakdown(storages, page)
  173.     local line = 1
  174.     local contentBottom = h - 4        -- reserve rows h-3 (label), h-2 (blank), h-1 & h (buttons)
  175.     local perPage = contentBottom      -- usable rows for content
  176.     local startIndex = (page - 2) * perPage + 1
  177.     local endIndex = math.min(startIndex + perPage - 1, #storages)
  178.  
  179.     for i = startIndex, endIndex do
  180.         local block, name = storages[i][1], storages[i][2]
  181.         local cap = block.getEUCapacity() or 0
  182.         local stored = block.getEUStored() or 0
  183.         local fill = (cap > 0) and (stored / cap * 100) or 0
  184.         local change = stored - (lastTotals[name] or stored)
  185.         lastTotals[name] = stored
  186.  
  187.         local status = "Stable"
  188.         if change > 0 then status = "Charging"
  189.         elseif change < 0 then status = "Discharging" end
  190.  
  191.         local timeString = "Stable"
  192.         if change > 0 then
  193.             local secondsToFull = (cap - stored) / change
  194.             timeString = "Full in " .. formatTime(secondsToFull)
  195.         elseif change < 0 then
  196.             local secondsToEmpty = stored / math.abs(change)
  197.             timeString = "Empty in " .. formatTime(secondsToEmpty)
  198.         end
  199.  
  200.         smartWriteLine(line, name .. " (" .. string.format("%.1f%%", fill) .. ")",
  201.                        fill < activateThreshold and colours.red or colours.white)
  202.         line = line + 1
  203.  
  204.         monitor.setCursorPos(1, line); monitor.clearLine()
  205.         drawFillBar(line, fill)
  206.         line = line + 1
  207.  
  208.         smartWriteLine(line, "Status: " .. status .. " (" .. change .. " EU/t)")
  209.         line = line + 1
  210.         smartWriteLine(line, timeString)
  211.         line = line + 1
  212.  
  213.         if change >= 0 then
  214.             smartWriteLine(line, "In : " .. change .. " EU/t")
  215.             line = line + 1
  216.             smartWriteLine(line, "Out: 0 EU/t")
  217.         else
  218.             smartWriteLine(line, "In : 0 EU/t")
  219.             line = line + 1
  220.             smartWriteLine(line, "Out: " .. math.abs(change) .. " EU/t")
  221.         end
  222.         line = line + 1
  223.  
  224.         divider(line)
  225.         line = line + 1
  226.  
  227.         if line > contentBottom then break end
  228.     end
  229. end
  230.  
  231. -- Two-row navigation bar with centered labels
  232. local function drawNav(currentPageView, totalPagesView)
  233.     -- Page indicator at h-3
  234.     local label = " Page " .. currentPageView .. " / " .. totalPagesView .. " "
  235.     local spacing = math.floor((w - #label) / 2)
  236.     smartWriteLine(h-3, string.rep(" ", spacing) .. label)
  237.  
  238.     local half = math.floor(w / 2)
  239.  
  240.     -- Draw button backgrounds (rows h-1 and h)
  241.     monitor.setBackgroundColour(colours.red)
  242.     monitor.setTextColour(colours.white)
  243.     for y = h-1, h do
  244.         monitor.setCursorPos(1, y)
  245.         monitor.write(string.rep(" ", half))
  246.     end
  247.  
  248.     monitor.setBackgroundColour(colours.green)
  249.     for y = h-1, h do
  250.         monitor.setCursorPos(half + 1, y)
  251.         monitor.write(string.rep(" ", w - half))
  252.     end
  253.  
  254.     -- Labels on bottom row (center vertically)
  255.     monitor.setBackgroundColour(colours.red)
  256.     monitor.setCursorPos(2, h)
  257.     monitor.write(" Prev ")
  258.  
  259.     monitor.setBackgroundColour(colours.green)
  260.     monitor.setCursorPos(half + 2, h)
  261.     monitor.write(" Next ")
  262.  
  263.     monitor.setBackgroundColour(colours.black)
  264.     monitor.setTextColour(colours.white)
  265. end
  266.  
  267. -- Calculate total pages (1 overview + N breakdown pages)
  268. local function updateTotalPages(storages)
  269.     local contentBottom = h - 4
  270.     local perPage = math.max(1, contentBottom)
  271.     local breakdownPages = math.ceil(#storages / perPage)
  272.     totalPages = 1 + breakdownPages
  273. end
  274.  
  275. -- Touch handler: detect presses on either row of the buttons
  276. local function handleTouch(x, y)
  277.     local half = math.floor(w / 2)
  278.     if y == h or y == h - 1 then
  279.         if x <= half then
  280.             currentPage = math.max(1, currentPage - 1)
  281.         else
  282.             currentPage = math.min(totalPages, currentPage + 1)
  283.         end
  284.         return true
  285.     end
  286.     return false
  287. end
  288.  
  289. -- Main loop: refresh, draw current page, draw nav, wait for touch or timer
  290. while true do
  291.     local storages = findStorages()
  292.     updateTotalPages(storages)
  293.  
  294.     -- One-time clear on page switch to avoid ghost text
  295.     if currentPage ~= lastPage then
  296.         monitor.clear()
  297.         lastDrawn = {}      -- reset cache so smartWriteLine redraws everything
  298.         lastPage = currentPage
  299.     else
  300.         -- Clear content cache each tick so changing lines update properly
  301.         for y = 1, h-4 do
  302.             lastDrawn[y] = nil
  303.         end
  304.     end
  305.  
  306.     if currentPage == 1 then
  307.         drawCombined(storages)
  308.     else
  309.         drawBreakdown(storages, currentPage)
  310.     end
  311.     drawNav(currentPage, totalPages)
  312.  
  313.     -- Timer for 1-second cadence while accepting touch
  314.     local timer = os.startTimer(1)
  315.     while true do
  316.         local e, p1, p2, p3 = os.pullEvent()
  317.         if e == "monitor_touch" then
  318.             local side, x, y = p1, p2, p3
  319.             if handleTouch(x, y) then
  320.                 break
  321.             end
  322.         elseif e == "timer" and p1 == timer then
  323.             break
  324.         end
  325.     end
  326. end
Advertisement
Add Comment
Please, Sign In to add comment