Skip to content

Commit 9604aef

Browse files
committed
feat: show hourly forecast
This took me quite some trial and error to figure out. Signed-off-by: André Jaenisch <[email protected]>
1 parent 4d05c96 commit 9604aef

File tree

2 files changed

+190
-7
lines changed

2 files changed

+190
-7
lines changed

weather-api-widget/README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ following config parameters:
1818
| units | `metric` | `metric` for celsius, `imperial` for fahrenheit |
1919
| icon_pack_name | `weather-underground-icons` | Name of the icon pack, could be `weather-underground-icon` or `VitalyGorbachev` or create your own, more details below |
2020
| icons_extension | `.png` | File extension of icons in the pack |
21-
| show_forecast | false | Show forecast for next three days |
21+
| show_daily_forecast | false | Show forecast for next three days |
22+
| show_hourly_forecast | false | Show hourly forecast section |
2223
| timeout | 120 | How often in seconds the widget refreshes |
2324

2425
### Icons:

weather-api-widget/weather.lua

+188-6
Original file line numberDiff line numberDiff line change
@@ -171,14 +171,16 @@ local function worker(user_args)
171171
local api_key = args.api_key
172172
local font_name = args.font_name or beautiful.font:gsub("%s%d+$", "")
173173
local units = args.units or 'metric'
174+
local time_format_12h = args.time_format_12h
174175
local both_units_widget = args.both_units_widget or false
175176
local icon_pack_name = args.icons or 'weather-underground-icons'
176177
local icons_extension = args.icons_extension or '.png'
177-
local show_forecast = args.show_forecast or false
178+
local show_daily_forecast = args.show_daily_forecast or false
179+
local show_hourly_forecast = args.show_hourly_forecast or false
178180
local timeout = args.timeout or 120
179181

180182
local ICONS_DIR = WIDGET_DIR .. '/icons/' .. icon_pack_name .. '/'
181-
-- Forecast endpoint includes current. I could map show_forecast to days here.
183+
-- Forecast endpoint includes current. I could map show_daily_forecast to days here.
182184
-- Currently overfetching but only showing when opting in.
183185
local weather_api =
184186
('https://api.weatherapi.com/v1/forecast.json' ..
@@ -304,7 +306,7 @@ local function worker(user_args)
304306
end
305307
}
306308

307-
local forecast_widget = {
309+
local daily_forecast_widget = {
308310
forced_width = 300,
309311
layout = wibox.layout.flex.horizontal,
310312
update = function(self, forecast)
@@ -364,6 +366,181 @@ local function worker(user_args)
364366
end
365367
}
366368

369+
local hourly_forecast_graph = wibox.widget {
370+
step_width = 12,
371+
color = '#EBCB8B',
372+
background_color = beautiful.bg_normal,
373+
forced_height = 100,
374+
forced_width = 300,
375+
widget = wibox.widget.graph,
376+
set_max_value = function(self, new_max_value)
377+
self.max_value = new_max_value
378+
end,
379+
set_min_value = function(self, new_min_value)
380+
self.min_value = new_min_value
381+
end,
382+
}
383+
384+
local hourly_forecast_negative_graph = wibox.widget {
385+
step_width = 12,
386+
color = '#5E81AC',
387+
background_color = beautiful.bg_normal,
388+
forced_height = 100,
389+
forced_width = 300,
390+
widget = wibox.widget.graph,
391+
set_max_value = function(self, new_max_value)
392+
self.max_value = new_max_value
393+
end,
394+
set_min_value = function(self, new_min_value)
395+
self.min_value = new_min_value
396+
end,
397+
}
398+
399+
local hourly_forecast_widget = {
400+
layout = wibox.layout.fixed.vertical,
401+
update = function(self, hourly)
402+
local hours_below = {
403+
id = 'hours',
404+
forced_width = 300,
405+
layout = wibox.layout.flex.horizontal
406+
}
407+
local temp_below = {
408+
id = 'temp',
409+
forced_width = 300,
410+
layout = wibox.layout.flex.horizontal
411+
}
412+
413+
local max_temp = -1000
414+
local min_temp = 1000
415+
local values= {}
416+
417+
-- Yeah, this looks weird. I would expect to have to use ipairs
418+
for i, hour in pairs(hourly) do
419+
if i > 25 then
420+
break
421+
end
422+
423+
values[i] = hour.temp_c
424+
425+
if max_temp < hour.temp_c then
426+
max_temp = hour.temp_c
427+
end
428+
429+
if min_temp > hour.temp_c then
430+
min_temp = hour.temp_c
431+
end
432+
433+
if (i - 1) % 5 == 0 then
434+
table.insert(hours_below, wibox.widget {
435+
text = os.date(time_format_12h and '%I%p' or '%H:00', tonumber(hour.time_epoch)),
436+
align = 'center',
437+
font = font_name .. ' 9',
438+
widget = wibox.widget.textbox
439+
})
440+
441+
table.insert(temp_below, wibox.widget {
442+
markup = '<span foreground=""'
443+
.. (tonumber(hour.temp_c) > 0 and '#2E3440' or '#ECEFF4') .. '">'
444+
.. string.format('%.0f', hour.temp_c) .. '°' .. '</span>',
445+
align = 'center',
446+
font = font_name .. ' 9',
447+
widget = wibox.widget.textbox
448+
})
449+
end
450+
end
451+
452+
hourly_forecast_graph:set_max_value(math.max(max_temp, math.abs(min_temp)))
453+
hourly_forecast_graph:set_min_value(min_temp > 0 and min_temp * 0.7 or 0) -- move graph a bit up
454+
455+
hourly_forecast_negative_graph:set_max_value(math.abs(min_temp))
456+
hourly_forecast_negative_graph:set_min_value(max_temp < 0 and math.abs(max_temp) * 0.7 or 0)
457+
458+
for _, value in ipairs(values) do
459+
if value >= 0 then
460+
hourly_forecast_graph:add_value(value)
461+
hourly_forecast_negative_graph:add_value(0)
462+
else
463+
hourly_forecast_graph:add_value(0)
464+
hourly_forecast_negative_graph:add_value(math.abs(value))
465+
end
466+
end
467+
468+
local count = #self
469+
for i = 0, count do
470+
self[i] = nil
471+
end
472+
473+
-- all temperatures are positive
474+
if min_temp > 0 then
475+
table.insert(self, wibox.widget {
476+
{
477+
hourly_forecast_graph,
478+
reflection = { horizontal = true },
479+
widget = wibox.container.mirror
480+
},
481+
{
482+
temp_below,
483+
valign = 'bottom',
484+
widget = wibox.container.place
485+
},
486+
id = 'graph',
487+
layout = wibox.layout.stack
488+
})
489+
table.insert(self, hours_below)
490+
491+
-- all temperatures are negative
492+
elseif max_temp < 0 then
493+
table.insert(self, hours_below)
494+
table.insert(self, wibox.widget {
495+
{
496+
hourly_forecast_negative_graph,
497+
reflection = { horizontal = true, vertical = true },
498+
widget = wibox.container.mirror
499+
},
500+
{
501+
temp_below,
502+
valign = 'top',
503+
widget = wibox.container.place
504+
},
505+
id = 'graph',
506+
layout = wibox.layout.stack
507+
})
508+
509+
-- mixed temperatures
510+
else
511+
table.insert(self, wibox.widget {
512+
{
513+
hourly_forecast_graph,
514+
reflection = { horizontal = true },
515+
widget = wibox.container.mirror
516+
},
517+
{
518+
temp_below,
519+
valign = 'bottom',
520+
widget = wibox.container.place
521+
},
522+
id = 'graph',
523+
layout = wibox.layout.stack
524+
})
525+
526+
table.insert(self, wibox.widget {
527+
{
528+
hourly_forecast_negative_graph,
529+
reflection = { horizontal = true, vertical = true },
530+
widget = wibox.container.mirror
531+
},
532+
{
533+
temp_below,
534+
valign = 'top',
535+
widget = wibox.container.place
536+
},
537+
id = 'graph',
538+
layout = wibox.layout.stack
539+
})
540+
end
541+
end
542+
}
543+
367544
local function update_widget(widget, stdout, stderr)
368545
if stderr ~= '' then
369546
if not warning_shown then
@@ -411,9 +588,14 @@ local function worker(user_args)
411588
}
412589

413590

414-
if show_forecast then
415-
forecast_widget:update(result.forecast.forecastday)
416-
table.insert(final_widget, forecast_widget)
591+
if show_hourly_forecast then
592+
hourly_forecast_widget:update(result.forecast.forecastday[1].hour)
593+
table.insert(final_widget, hourly_forecast_widget)
594+
end
595+
596+
if show_daily_forecast then
597+
daily_forecast_widget:update(result.forecast.forecastday)
598+
table.insert(final_widget, daily_forecast_widget)
417599
end
418600

419601
weather_popup:setup({

0 commit comments

Comments
 (0)