Dashboard pogody z pluginem Weather#

Plugin Weather pobiera pogodę z OpenWeatherMap, a jeden moduł Lua rysuje z niej dashboard: temperaturę, wilgotność, ciśnienie, wiatr, opady i wykresy z ostatniej doby. Całość zajmuje kwadrans, a większość z tego to założenie konta po klucz API.


Co zbudujemy#

  • plugin Weather skonfigurowany dla Twojej miejscowości
  • moduł pogoda, który czyta plugin
  • dashboard pod adresem /d/pogoda
  • na koniec automatykę, która reaguje na początek deszczu

Czego potrzebujesz#

CoUwagi
Działające vCLUnp. po tutorialu Standalone
Konto w OpenWeatherMapdarmowy plan wystarcza: 1000 zapytań dziennie, plugin robi 24

Krok 1: Klucz API#

  1. Załóż konto na openweathermap.org.
  2. Otwórz API keys i skopiuj klucz.

Nowy klucz zaczyna działać po kilkunastu minutach do kilku godzin. Do tego czasu plugin zgłasza w logach HTTP 401.

Krok 2: Instalacja i konfiguracja pluginu#

W panelu vCLU otwórz Plugins, znajdź Weather Plugin i kliknij Install, a potem Configure:

PoleWartość
apiKeyklucz z kroku 1
citymiejscowość z kodem kraju, np. Kraków,pl
intervalco ile sekund pobierać pogodę; 3600 to raz na godzinę
unitsmetric, czyli °C i m/s

Konfiguracja pluginu Weather w panelu vCLU

Kod kraju po przecinku ma znaczenie: bez niego OpenWeatherMap może wybrać miejscowość o tej samej nazwie w innym kraju.

Po zapisaniu plugin od razu pobiera pierwszy odczyt. W logach vCLU pojawi się linia w rodzaju:

Weather: Clear, 13.3°C, 62% humidity

Krok 3: Moduł pogoda#

Kod trzymamy w module: katalog modules/pogoda/ z plikiem init.lua. Moduł jest jedynym miejscem, które czyta plugin. Dashboard i wszystko, co dopiszesz później (eksport do InfluxDB, automatyki), bierze odczyty z modułu, więc wszędzie widać te same wartości.

W panelu otwórz Editor i utwórz plik modules/pogoda/init.lua:

-- modules/pogoda/init.lua
-- Pogoda z pluginu @vclu/weather (OpenWeatherMap): odczyty i metryki.

local weather = Plugin.getPlugin("@vclu/weather")

if not weather then
    print("[POGODA] Plugin @vclu/weather niezaładowany, moduł pominięty")
    return nil
end

-- Przed pierwszą odpowiedzią plugin trzyma zera, więc do tego czasu
-- zwracamy nil i widget zostaje pusty zamiast pokazywać 0 °C.
local function read(field)
    return function()
        local d = weather:getData()
        if not d.ready then return nil end
        return d[field]
    end
end

local M = {
    temp = read("temp"),
    feelsLike = read("feelsLike"),
    humidity = read("humidity"),
    pressure = read("pressure"),
    wind = read("windSpeed"),
    windDeg = read("windDeg"),
    clouds = read("clouds"),
    rain = read("rain"),
}

-- OpenWeatherMap podaje warunki po angielsku, w polu `condition`.
local CONDITIONS = {
    Clear = "Bezchmurnie", Clouds = "Pochmurno", Rain = "Deszcz",
    Drizzle = "Mżawka", Thunderstorm = "Burza", Snow = "Śnieg",
    Mist = "Mgła", Fog = "Mgła", Haze = "Zamglenie",
}

function M.condition()
    local c = read("condition")()
    if not c then return nil end
    return CONDITIONS[c] or c
end

-- Kierunek, z którego wieje, z kąta w stopniach.
local DIRECTIONS = { "N", "NE", "E", "SE", "S", "SW", "W", "NW" }

function M.windText()
    local speed, deg = M.wind(), M.windDeg()
    if not speed then return nil end
    local dir = DIRECTIONS[math.floor((deg + 22.5) / 45) % 8 + 1]
    return string.format("%.1f m/s %s", speed, dir)
end

function M.updated()
    local d = weather:getData()
    if not d.ready then return "czekam na pierwszy odczyt" end
    return os.date("%H:%M", d.lastUpdate) .. ", " .. d.city
end

function M.refresh() weather:refresh() end

-- Historia do wykresów na dashboardzie
metric("pogoda:temp", M.temp, { unit = "°C", interval = 600, decimals = 1 })
metric("pogoda:cisnienie", M.pressure, { unit = "hPa", interval = 600 })
metric("pogoda:wilgotnosc", M.humidity, { unit = "%", interval = 600 })

return M

Co tu się dzieje:

  • Plugin.getPlugin("@vclu/weather") zwraca zainstalowany plugin. Jeśli go nie ma, moduł kończy się bez błędu, a reszta systemu działa dalej.
  • read(field) tworzy funkcję, która przy każdym wywołaniu oddaje bieżący odczyt. Dashboard dostaje funkcje, a nie wartości, dlatego zawsze pokazuje to, co plugin pobrał ostatnio.
  • metric(...) co 10 minut zapisuje wartość do historii, z której korzystają wykresy. Historia zaczyna się od załadowania modułu, więc wykresy zapełnią się po dobie.
  • return M oddaje odczyty innym plikom: każdy, kto zrobi require("pogoda"), dostanie tę tabelę.

Krok 4: Plik dashboardu#

Dashboard tylko rysuje. Utwórz modules/dashboards/pogoda.lua:

-- modules/dashboards/pogoda.lua
local pogoda = require("pogoda")

-- Bez pluginu moduł zwraca nil, a require oddaje wtedy true, nie tabelę.
if type(pogoda) ~= "table" then return end

local dash = Dashboard.new("pogoda")
    :name("Pogoda")
    :icon("cloud-sun")
    :columns(3)
    :auth("session")
    :poll(60)

dash:header()
    :status("temp", "Temperatura", "thermometer-half", pogoda.temp, "°C",
        { spark = "pogoda:temp", sparkRange = "24h" })
    :status("warunki", "Warunki", "cloud-sun", pogoda.condition)
    :status("odczuwalna", "Odczuwalna", "person", pogoda.feelsLike, "°C")

dash:section("Powietrze", "droplet", { columns = 3 })
    :status("wilgotnosc", "Wilgotność", "droplet", pogoda.humidity, "%")
    :status("cisnienie", "Ciśnienie", "speedometer2", pogoda.pressure, "hPa")
    :status("chmury", "Zachmurzenie", "clouds", pogoda.clouds, "%")

dash:section("Wiatr i opady", "wind", { columns = 2 })
    :status("wiatr", "Wiatr", "wind", pogoda.windText)
    :status("opady", "Opady", "cloud-rain", pogoda.rain, "mm/h")

dash:section("Odczyt", "clock", { columns = 2 })
    :status("odczyt", "Ostatni odczyt", "clock", pogoda.updated)
    :button("odswiez", "Odśwież", "arrow-clockwise", pogoda.refresh)

dash:section("Historia — 24h", "graph-up", { columns = 1 })
    :chart("ch_temp", "Temperatura", "pogoda:temp",
        { unit = "°C", range = "24h", span = 3, fill = true })
    :chart("ch_cisnienie", "Ciśnienie", "pogoda:cisnienie",
        { unit = "hPa", range = "24h", span = 3 })

dash:build()

Przycisk Odśwież pobiera pogodę od razu, bez czekania na kolejny cykl pluginu.

Krok 5: Rejestracja w modules/init.lua#

vCLU sam ładuje tylko pliki leżące bezpośrednio w modules/. Katalogi trzeba wskazać w modules/init.lua, najpierw moduł, potem dashboard:

require("pogoda")             -- modules/pogoda/init.lua
require("dashboards/pogoda")  -- modules/dashboards/pogoda.lua

Zapisz (Ctrl+S) i kliknij Reload. Błędy zobaczysz w panelu na dole edytora.

Krok 6: Dashboard#

Otwórz http://<adres-vclu>:8080/d/pogoda. Dashboard jest też na liście w panelu, na stronie Dashboards.

Dashboard pogody

Plugin pobiera pogodę raz na godzinę, a dashboard odświeża się co minutę, więc wartości zmieniają się co godzinę. Pole Ostatni odczyt pokazuje, kiedy plugin pobrał dane.


Dalej: automatyka na deszcz#

Plugin ogłasza zdarzenie weather:rain, gdy po suchym odczycie przychodzi odczyt z opadem. Dopisz reakcję w module pogoda, przed return M, np. powiadomienie albo zwinięcie markizy:

EventBus:on("weather:rain", function(data)
    print(string.format("[POGODA] Zaczęło padać: %.1f mm/h", data.rain))
    -- tu np. markiza:close()
end)

Plugin pobiera pogodę co interval sekund, więc reakcja przyjdzie z takim opóźnieniem. Do markizy, która ma zwinąć się zanim zmoknie, lepszy jest czujnik deszczu na budynku. Pogoda z internetu wystarczy do powiadomień i rzeczy mniej pilnych.

Pozostałe metody pluginu (getTemperature(), getHumidity(), getWind()…) opisuje README pluginu.