node.lua: add basic fahrplan display for standalone usage

This commit is contained in:
Franzi 2023-12-22 14:37:08 +01:00
parent f87985115d
commit 9de0f5fd60

203
node.lua
View file

@ -1,7 +1,204 @@
util.init_hosted()
local json = require "json"
local TOPBAR_FONT_SIZE = 70
local TALK_FONT_SIZE = 50
local PADDING = 20
local font_clock
local font_day
local font_room
local font_talk
local font_text
local day = 0
local time = 0
local clock = "??"
local schedule = {}
local all_next_talks = {}
local show_language = true
local show_track = true
gl.setup(NATIVE_WIDTH, NATIVE_HEIGHT)
function node.render()
CONFIG.font:write(10, 10, "This is a plugin for scheduled player, don't use it individually", 30, 1,1,1,1)
local function log(what)
return print("[pretalx] " .. what)
end
util.file_watch("schedule.json", function(new_schedule)
log("new schedule")
schedule = json.decode(new_schedule).talks
end)
util.file_watch("config.json", function(content)
local config = json.decode(content)
log("running on device ".. tostring(sys.get_env "SERIAL"))
show_language = config.show_language
show_track = config.show_track
font_clock = resource.load_font(config.font_clock.asset_name)
font_day = resource.load_font(config.font_day.asset_name)
font_room = resource.load_font(config.font_room.asset_name)
font_talk = resource.load_font(config.font_talk.asset_name)
font_text = resource.load_font(config.font_text.asset_name)
end)
util.data_mapper{
["(.*)"] = function(path, data)
log("received data '" .. data .. "' on " .. path)
if path == "day" then
day = tonumber(data)
elseif path == "clock" then
clock = data
elseif path == "time" then
time = tonumber(data)
end
end,
}
local function parse_rgb(hex)
hex = hex:gsub("#","")
return tonumber("0x"..hex:sub(1,2))/255, tonumber("0x"..hex:sub(3,4))/255, tonumber("0x"..hex:sub(5,6))/255
end
local function check_next_talks()
if time == 0 then
log("No time info yet, cannot check for next talks")
return
end
all_next_talks = {}
local min_start = time - 25 * 60
for idx = 1, #schedule do
local talk = schedule[idx]
-- Ignore all talks that have already ended here. We don't want
-- to announce these.
if talk.end_ts > time then
all_next_talks[#all_next_talks+1] = talk
end
end
local function sort_talks(a, b)
return a.start_ts < b.start_ts or (a.start_ts == b.start_ts and a.room < b.room)
end
table.sort(all_next_talks, sort_talks)
end
local function wrap(str, font, size, max_w)
local lines = {}
local space_w = font:width(" ", size)
local remaining = max_w
local line = {}
for non_space in str:gmatch("%S+") do
local w = font:width(non_space, size)
if remaining - w < 0 then
lines[#lines+1] = table.concat(line, "")
line = {}
remaining = max_w
end
line[#line+1] = non_space
line[#line+1] = " "
remaining = remaining - w - space_w
end
if #line > 0 then
lines[#lines+1] = table.concat(line, "")
end
return lines
end
function node.render()
gl.clear(0, 0, 0, 1)
y = PADDING
font_day:write(PADDING, y, string.format("Day %d", day), TOPBAR_FONT_SIZE, 1, 1, 1, 1)
local clock_width = font_clock:width(clock, TOPBAR_FONT_SIZE)
font_clock:write(NATIVE_WIDTH-PADDING-clock_width, y, clock, TOPBAR_FONT_SIZE, 1, 1, 1, 1)
y = y + TOPBAR_FONT_SIZE + PADDING*2
check_next_talks()
local time_size = TALK_FONT_SIZE
local info_size = math.floor(TALK_FONT_SIZE * 0.8)
local col1 = PADDING
local col2 = PADDING*2 + 15 + font_text:width("XXX min ago", time_size)
if #schedule == 0 then
font_text:write(col2, y, "Fetching talks...", TALK_FONT_SIZE, 1, 1, 1, 1)
elseif #all_next_talks == 0 and #schedule > 0 and sys.now() > 30 then
font_text:write(col2, y, "Nope. That's it.", TALK_FONT_SIZE, 1, 1, 1, 1)
end
for idx = 1, #all_next_talks do
local talk = all_next_talks[idx]
local title = talk.title
if show_language and talk.locale then
title = title .. " (" .. talk.locale .. ")"
end
local title_lines = wrap(
title,
font_talk, TALK_FONT_SIZE, NATIVE_WIDTH - col2 - PADDING
)
local info_line = talk.room
if #talk.persons > 0 then
local joiner = ({
de = "mit",
})[talk.locale] or "with"
info_line = info_line .. " " .. joiner .. " " .. table.concat(talk.persons, ", ")
end
local info_lines = wrap(
info_line,
font_text, info_size, NATIVE_WIDTH - col2 - PADDING
)
if y + #title_lines * TALK_FONT_SIZE + 3 + #info_lines * info_size > NATIVE_HEIGHT then
break
end
-- time
local talk_time
local delta = talk.start_ts - time
if delta > -60 and delta < 60 then
talk_time = "Now"
elseif delta > 30*60 then
talk_time = talk.start_str
elseif delta > 0 then
talk_time = string.format("in %d min", math.floor(delta/60)+1)
else
talk_time = string.format("%d min ago", math.ceil(-delta/60))
end
local time_width = font_text:width(talk_time, time_size)
font_text:write(col2 - 15 - PADDING - time_width, y, talk_time, time_size, 1, 1, 1, 1)
-- track
if talk.track ~= json.null and talk.track.color ~= json.null then
local r,g,b = parse_rgb(talk.track.color)
resource.create_colored_texture(r,g,b,1):draw(col2 - 5 - PADDING, y, col2 - 10, y + #title_lines*TALK_FONT_SIZE + 3 + #info_lines*info_size)
end
-- title
for idx = 1, #title_lines do
font_talk:write(col2, y, title_lines[idx], TALK_FONT_SIZE, 1, 1, 1, 1)
y = y + TALK_FONT_SIZE
end
y = y + 3
-- info
for idx = 1, #info_lines do
font_text:write(col2, y, info_lines[idx], info_size, 1, 1, 1, .8)
y = y + info_size
end
y = y + PADDING
end
end