Created
March 8, 2020 20:52
-
-
Save jgrahamc/ca536506173fe759a947d8c4b77206a7 to your computer and use it in GitHub Desktop.
Update version to work in Portugal using the IPMA API
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- totoro.lua | |
-- | |
-- Small program to get the weather forecast for a location and turn | |
-- on two LEDs that will illuminate Totoro's eyes if it is going to | |
-- rain, hail or snow today | |
-- | |
-- Copyright (c) 2017-2020 John Graham-Cumming | |
local config = require("totoro-config") | |
-- This is only used when running 'make check' and these lines are | |
-- removed by 'make upload' | |
local dummy = require("dummy") -- TEST_ONLY | |
local wifi = dummy -- TEST_ONLY | |
local http = dummy -- TEST_ONLY | |
local tmr = dummy -- TEST_ONLY | |
local cjson = dummy -- TEST_ONLY | |
local ws2812 = dummy -- TEST_ONLY | |
-- The LED strip used takes the colors in the order (G, R, B) | |
local red = string.char(0, 255, 0) | |
local blue = string.char(0, 0, 255) | |
local green = string.char(255, 0, 0) | |
local yellow = string.char(255, 255, 0) | |
local white = string.char(255, 255, 255) | |
local black = string.char(0, 0, 0) | |
-- Count of number of getForecast failures | |
local failures = 0 | |
-- setLED sets the appropriate LED color on the two LEDs | |
local function setLED(c) | |
ws2812.write(c .. c) | |
end | |
local color = {black, black} | |
local current | |
-- led sets the two LED colors that will illuminate the eyes | |
local function led(c0, c1) | |
color[1] = c0 | |
color[2] = c1 | |
current = 0 | |
setLED(color[current+1]) | |
end | |
-- connectWifi connects to the WiFi network defined above as a station | |
-- This will try to connect for 30 seconds and then give up | |
local function connectWifi() | |
led(yellow, black) | |
wifi.setmode(wifi.STATION) | |
local cfg = {} | |
cfg.ssid = config.SSID | |
cfg.pwd = config.PASS | |
wifi.sta.config(cfg) | |
wifi.sta.connect() | |
local i = 30 | |
tmr.alarm(1, 1000, tmr.ALARM_AUTO, function() | |
if wifi.sta.getip() ~= nil then | |
tmr.stop(1) | |
else | |
i = i - 1 | |
if i == 0 then | |
tmr.stop(1) | |
end | |
end | |
end) | |
dummy.run(1) -- TEST_ONLY | |
return i == 0 | |
end | |
local api = "http://api.ipma.pt/open-data/forecast/meteorology/cities/daily/hp-daily-forecast-day0.json" | |
-- update sets the LED to the current value and swaps the value for the | |
-- next update | |
local function update() | |
if color[0] == color[1] then return end | |
setLED(color[current+1]) | |
current = 1 - current | |
end | |
-- setEyes reads the JSON response from the API and extracts the weather for the | |
-- day and updates the eye LED colors | |
local function setEyes(code, data) | |
if code == 200 then | |
local ok, p = pcall(cjson.decode, data) | |
if ok and p ~= nil then | |
if p.data == nil then return end | |
-- Extract the weather for the city we want | |
-- | |
-- A single data point looks like this | |
-- | |
-- { | |
-- "precipitaProb": "3.0", | |
-- "tMin": 8, | |
-- "tMax": 18, | |
-- "predWindDir": "NW", | |
-- "idWeatherType": 2, | |
-- "classWindSpeed": 1, | |
-- "longitude": "-9.1286", | |
-- "globalIdLocal": 1110600, | |
-- "latitude": "38.7660" | |
-- } | |
-- | |
for _, c in ipairs(p.data) do | |
if c.globalIdLocal == 1110600 then | |
-- Once the city has been found it's just a matter of translating the weather | |
-- type to a simplified weather type displayable by Totoro | |
-- values we look for are: | |
-- | |
-- Type API Response Weather | |
-- ---- ------------ ------- | |
-- Heavy rain: 8, 9, 11, 14 0 | |
-- Hail: 21 1 | |
-- Light rain: 6, 7, 10, 12, 13, 15 2 | |
-- Sleet or snow: 18 3 | |
-- Sun: 1, 2, 3 4 | |
-- Thunder: 19, 20, 23 5 | |
-- Note because Lua arrays actually start at one looking up in this array is done by adding | |
-- 1 to the weather code returned by IMPA | |
-- 0, 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | |
local weather = {nil, 4, 4, 4, nil, nil, 2, 2, 0, 0, 2, 0, 2, 2, 0, 2, nil, nil, 3, 5, 5, 1, nil, 5, nil, nil, nil, nil, nil, nil} | |
p = weather[c.idWeatherType+1] | |
if p == 0 then led(red, red) | |
elseif p == 1 then led(white, black) | |
elseif p == 2 then led(blue, blue) | |
elseif p == 3 then led(white, white) | |
elseif p == 4 then led(yellow, yellow) | |
elseif p == 5 then led(white, red) | |
else led(black, black) | |
end | |
failures = 0 | |
return | |
end | |
end | |
end | |
end | |
-- If there's an API failure of some sort flash red/blue | |
failures = failures + 1 | |
led(red, blue) | |
end | |
-- getForecast calls the IMPA API for the defined location and then calls | |
-- setEyes with the JSON response or error code | |
local function getForecast() | |
led(green, black) | |
http.get(api, nil, setEyes) | |
end | |
-- watchdog resets Totoro if there hasn't been a successful forecast for | |
-- 5 minutes | |
local function watchdog() | |
if failures == 5 then | |
node.restart() | |
end | |
end | |
-- Connect to WiFi and then every minute get the latest weather forecast | |
ws2812.init() | |
tmr.alarm(0, 500, tmr.ALARM_AUTO, update) | |
connectWifi() | |
tmr.alarm(2, 1*60*1000, tmr.ALARM_AUTO, watchdog) | |
tmr.alarm(3, 1*60*1000, tmr.ALARM_AUTO, getForecast) | |
dummy.run(0) -- TEST_ONLY |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment