Back to APIs list

Weather API examples & templates

Use these vals as a playground to view and fork Weather API examples and templates on Val Town. Run any example below or find templates that can be used as a pre-built solution.
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import { fetchJSON } from "https://esm.town/v/stevekrouse/fetchJSON?v=41";
import { toDatesWithTz } from "https://esm.town/v/wilt/toDatesWithTz";
// See https://open-meteo.com/en/docs for usage
type OMHourlyMeasures =
| "temperature_2m"
| "relativehumidity_2m"
| "dewpoint_2m"
| "apparent_temperature"
| "pressure_msl"
| "surface_pressure"
| "cloudcover"
| "cloudcover_low"
| "cloudcover_mid"
| "cloudcover_high"
| "windspeed_10m"
| "windspeed_80m"
| "windspeed_120m"
| "windspeed_180m"
| "winddirection_10m"
| "winddirection_80m"
| "winddirection_120m"
| "winddirection_180m"
| "windgusts_10m"
| "shortwave_radiation"
| "direct_radiation"
| "direct_normal_irradiance"
| "diffuse_radiation"
| "vapor_pressure_deficit"
| "cape"
| "evapotranspiration"
| "et0_fao_evapotranspiration"
| "precipitation"
| "snowfall"
| "precipitation_probability"
| "rain"
| "showers"
| "weathercode"
| "snow_depth"
| "freezinglevel_height"
| "visibility"
| "soil_temperature_0cm"
| "soil_temperature_6cm"
| "soil_temperature_18cm"
| "soil_temperature_54cm"
| "soil_moisture_0_1cm"
| "soil_moisture_1_3cm"
| "soil_moisture_3_9cm"
| "soil_moisture_9_27cm"
| "soil_moisture_27_81cm"
| "is_day";
type OMDailyMeasures =
| "temperature_2m_max"
| "temperature_2m_min"
| "apparent_temperature_max"
| "apparent_temperature_min"
| "precipitation_sum"
| "rain_sum"
| "showers_sum"
| "snowfall_sum"
| "precipitation_hours"
| "precipitation_probability_max"
| "precipitation_probability_min"
| "precipitation_probability_mean"
| "weathercode"
| "sunrise"
| "sunset"
| "windspeed_10m_max"
| "windgusts_10m_max"
| "winddirection_10m_dominant"
| "shortwave_radiation_sum"
| "et0_fao_evapotranspiration"
| "uv_index_max"
| "uv_index_clear_sky_max";
interface OMParams {
latitude: number;
longitude: number;
hourly?: OMHourlyMeasures[];
daily?: OMDailyMeasures[];
current_weather?: boolean;
temperature_unit?: "celcius" | "fahrenheit";
windspeed_unit?: "kmh" | "ms" | "mph" | "kn";
precipitation_unit?: "mm" | "inch";
timeformat?: string;
timezone?: string;
past_days?: number;
forecast_days?: number;
start_date?: string;
end_date?: string;
models?: string[];
cell_selection?: "land" | "sea" | "nearest";
}
export async function getOpenMeteoForecast(params: OMParams) {
const data = await fetchJSON(
`https://api.open-meteo.com/v1/forecast?${
new URLSearchParams(
params as {}, // Typescript is stricter than Deno here
).toString()
}`,
);

☔️ Umbrella reminder if there's rain today

Screenshot 2023-09-14 at 12.31.32.png

Setup

  1. Fork this val 👉 https://val.town/v/stevekrouse.umbrellaReminder/fork
  2. Customize the location (line 8). You can supply any free-form description of a location.

⚠️ Only works for US-based locations (where weather.gov covers).

How it works

  1. Geocodes an free-form description of a location to latitude and longitude – @stevekrouse.nominatimSearch
  2. Converts a latitude and longitude to weather.gov grid – @stevekrouse.weatherGovGrid
  3. Gets the hourly forecast for that grid
  4. Filters the forecast for periods that are today and >30% chance of rain
  5. If there are any, it formats them appropriately, and sends me an email
Readme
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
30
31
32
33
34
import { email } from "https://esm.town/v/std/email?v=9";
import { fetchJSON } from "https://esm.town/v/stevekrouse/fetchJSON";
import { nominatimSearch } from "https://esm.town/v/stevekrouse/nominatimSearch";
import { weatherGovGrid } from "https://esm.town/v/stevekrouse/weatherGovGrid";
export const umbrellaReminder = async (arg) => {
if (arg.method) return Response.json("");
let location = "lansing, mi"; // <---- customize this line
let [{ lat, lon }] = await nominatimSearch({
q: location,
});
let { properties: grid } = await weatherGovGrid({
lat,
lon,
});
let { properties: { periods } } = await fetchJSON(
grid.forecastHourly,
);
let { DateTime } = await import("npm:luxon");
let parse = (iso) => DateTime.fromISO(iso).setZone(grid.timeZone);
let today = periods.filter((x) =>
parse(x.startTime).toLocaleString()
=== DateTime.now().setZone(grid.timeZone).toLocaleString()
);
console.log(today);
if (today.every((x) => x.probabilityOfPrecipitation.value < 30))
return today;
let format = (iso) => parse(iso).toFormat("ha").toLowerCase();
let html = `The probabilities of rain in <b>${location}</b> today:<br><br>`
+ today.map((
{ startTime, endTime, probabilityOfPrecipitation: { value: p } },
) => `${format(startTime)}-${format(endTime)}: ${p}%`).join("<br>");
return email({ html, subject: "☔️ Carry an umbrella today!" });
};
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import { latLngOfCity } from "https://esm.town/v/jdan/latLngOfCity";
import { fetchWebpage } from "https://esm.town/v/jdan/fetchWebpage";
import { weatherOfLatLon } from "https://esm.town/v/jdan/weatherOfLatLon";
import { OpenAI } from "https://esm.town/v/std/openai?v=4";
const openai = new OpenAI();
const toolbox = {
"latLngOfCity": {
openAiTool: {
type: "function",
function: {
name: "latLngOfCity",
description: "Get the latitude and longitude of a city",
parameters: {
type: "object",
properties: {
cityName: {
type: "string",
description: "The name of the city",
example: "Hoboken",
},
},
},
response: {
type: "string",
description: "The latitude and longitude of the city",
example: "40°44′42″N 74°01′57″W",
},
},
},
call: latLngOfCity,
},
"weatherOfLatLon": {
openAiTool: {
type: "function",
function: {
name: "weatherOfLatLon",
description: "Get the latitude and longitude of a city",
parameters: {
type: "object",
properties: {
lat: {
type: "number",
description: "The latitude of the city",
},
lon: {
type: "number",
description: "The longitude of the city",
},
},
},
response: {
type: "object",
description: "A large JSON objecft describing the weather",
},
},
},
call: weatherOfLatLon
},
"fetchWebpage": {
openAiTool: {
type: "function",
function: {
name: "fetchWebpage",
description: "Fetch the weather forecast from the contents of a forecast URL",
parameters: {
type: "object",
properties: {
url: {
type: "string"
}
}
},
response: {
type: "string"
}
}
},
call: fetchWebpage
}
};
const tools = Object.values(toolbox).map(({ openAiTool }) => openAiTool);
const transcript = [
{ role: "user", content: "What's the weather in Hoboken, NJ? Do your best to follow URLs and summarize the weather instead of having the user do it." },
];
function truncate(response: string | object) {
const TRUNCATE_LEN = 60
const responseStr = typeof response === "string" ? response : JSON.stringify(response)
return responseStr.length > TRUNCATE_LEN ? responseStr.split("\n").join("").slice(0, TRUNCATE_LEN) + "..." : responseStr
}
async function runConversation() {
const response = await openai.chat.completions.create({
messages: transcript,
tools,
model: "gpt-4-turbo-preview",
});
1
2
3
4
5
6
7
import { weatherGovGrid } from "https://esm.town/v/stevekrouse/weatherGovGrid?v=4";
export async function weatherOfLatLon(args: { lat: number; lon: number }) {
const { lat, lon } = args;
const grid = await weatherGovGrid({ lat, lon });
return grid;
}

If you fork this, you'll need to set OPENAI_API_KEY in your Val Town Secrets.

Readme
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
30
31
32
33
34
35
36
37
38
39
40
/** @jsxImportSource https://esm.sh/react */
import { Canvas } from "https://esm.sh/react-three-fiber";
import { useEffect, useState } from "https://esm.sh/react@18.2.0";
import { html } from "https://esm.town/v/stevekrouse/html?v=5";
export function WeatherSuggest() {
// const onClick = () => alert("Button clicked!");
const [data, setData] = useState(null);
useEffect(async () => {
const results = await fetch("https://seflless-weatherGPT.web.val.run/data");
const data = await results.json();
console.log(data);
setData(data);
}, []);
console.log(Canvas);
return !data ? <p>Loading...</p> : <p>{data}</p>;
}
export default async function weatherGPT(req: Request) {
const { OpenAI } = await import("npm:openai");
if (new URL(req.url).pathname === "/data") {
return Response.json({
weather: "Sunny",
});
}
return html(`<html><head></head><body><script type="module">
import { hydrateRoot } from "https://esm.sh/react-dom@18.2.0/client";
import { jsx as _jsx } from "https://esm.sh/react/jsx-runtime";
import { WeatherSuggest } from "https://esm.town/v/seflless/weatherGPT";
let props = {}
hydrateRoot(document, _jsx(WeatherSuggest, props));
</script>
</body>
`);
}
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
import { OpenAI } from "https://esm.town/v/std/openai?v=2";
const openai = new OpenAI();
const functionExpression = await openai.chat.completions.create({
"messages": [
{ "role": "user", "content": "whats the weather in sf" },
],
tools: [
{
function: {
name: "weather",
parameters: {
"type": "object",
"properties": {
"location": { "type": "string", "description": "The city and state e.g. San Francisco, CA" },
"unit": { "type": "string", "enum": ["c", "f"] },
},
"required": ["location"],
},
},
type: "function",
},
],
model: "gpt-4",
max_tokens: 30,
});
console.log(functionExpression.choices);
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import { OpenAI } from "https://esm.town/v/pomdtr/OpenAI";
const client = new OpenAI();
async function getCurrentLocation() {
return "Boston"; // Simulate lookup
}
async function getWeather(args: { location: string }) {
const { location } = args;
// … do lookup …
return { temperature: "10", precipitation: "10" };
}
const runner = client.beta.chat.completions
.runTools({
model: "gpt-3.5-turbo",
messages: [{ role: "user", content: "How is the weather this week?" }],
tools: [
{
type: "function",
function: {
function: getCurrentLocation,
parameters: { type: "object", properties: {} },
},
},
{
type: "function",
function: {
function: getWeather,
parse: JSON.parse, // or use a validation library like zod for typesafe parsing.
parameters: {
type: "object",
properties: {
location: { type: "string" },
},
},
},
},
],
})
.on("message", (message) => console.log(message));
const finalContent = await runner.finalContent();
console.log();
console.log("Final content:", finalContent);

Inspector to browser json data in HTTP vals

Screenshot 2024-02-23 at 9.31.42 AM.png

Example: https://val.town/v/stevekrouse/weatherDescription

Thanks @mmcgrana (https://markmcgranaghan.com/) for the idea!

Readme
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import { html } from "https://esm.town/v/stevekrouse/html";
import { accepts } from "https://esm.town/v/vladimyr/accepts";
export const json_viewer = (data) => (req: Request) => {
const accept = accepts(req);
if (!accept.type("html")) {
return Response.json(data);
}
return html(`<!DOCTYPE html>
<html lang="en">
<body>
<div id="json-viewer"></div>
<script src="https://cdn.jsdelivr.net/npm/@textea/json-viewer@3"></script>
<script>
new JsonViewer({
value: ${JSON.stringify(data)}
}).render('#json-viewer')
</script>
</body>
</html>`);
};

Inspector to browser json data in HTTP vals

Screenshot 2024-02-23 at 9.31.42 AM.png

Live example: https://stevekrouse-weatherdescription.web.val.run/

Installation

Create valimport { fetch } from "https://esm.town/v/std/fetch"; import { json_viewer } from "https://esm.town/v/stevekrouse/json_viewer"; export const weatherDescription = async (params: string[]): Promise<unknown> => { let data = await fetch(`https://wttr.in/${params["city"]}?format=j1`); let jsonData = await data.json(); return json_viewer(jsonData); };

https://val.town/v/stevekrouse/weatherDescription

Thanks @mmcgrana (https://markmcgranaghan.com/) for the idea!

Readme
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import { html } from "https://esm.town/v/stevekrouse/html";
import { accepts } from "https://esm.town/v/vladimyr/accepts";
export const json_viewer = (req: Request, data) => {
const accept = accepts(req);
if (!accept.type("html")) {
return Response.json(data);
}
return html(`<!DOCTYPE html>
<html lang="en">
<body>
<div id="json-viewer"></div>
<script src="https://cdn.jsdelivr.net/npm/@textea/json-viewer@3"></script>
<script>
new JsonViewer({
value: ${JSON.stringify(data)}
}).render('#json-viewer')
</script>
</body>
</html>`);
};

☔️ Umbrella reminder if there's rain today

Screenshot 2023-09-14 at 12.31.32.png

Setup

  1. Fork this val 👉 https://val.town/v/stevekrouse.umbrellaReminder/fork
  2. Customize the location (line 8). You can supply any free-form description of a location.

⚠️ Only works for US-based locations (where weather.gov covers).

How it works

  1. Geocodes an free-form description of a location to latitude and longitude – @stevekrouse.nominatimSearch
  2. Converts a latitude and longitude to weather.gov grid – @stevekrouse.weatherGovGrid
  3. Gets the hourly forecast for that grid
  4. Filters the forecast for periods that are today and >30% chance of rain
  5. If there are any, it formats them appropriately, and sends me an email
Readme
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
30
31
32
33
import { email } from "https://esm.town/v/std/email?v=9";
import { fetchJSON } from "https://esm.town/v/stevekrouse/fetchJSON";
import { nominatimSearch } from "https://esm.town/v/stevekrouse/nominatimSearch";
import { weatherGovGrid } from "https://esm.town/v/stevekrouse/weatherGovGrid";
export const umbrellaReminder = async (arg) => {
if (arg.method) return Response.json("");
let location = "ross, california"; // <---- customize this line
let [{ lat, lon }] = await nominatimSearch({
q: location,
});
let { properties: grid } = await weatherGovGrid({
lat,
lon,
});
let { properties: { periods } } = await fetchJSON(
grid.forecastHourly,
);
let { DateTime } = await import("npm:luxon");
let parse = (iso) => DateTime.fromISO(iso).setZone(grid.timeZone);
let today = periods.filter((x) =>
parse(x.startTime).toLocaleString()
=== DateTime.now().setZone(grid.timeZone).toLocaleString()
);
if (today.every((x) => x.probabilityOfPrecipitation.value < 30))
return today;
let format = (iso) => parse(iso).toFormat("ha").toLowerCase();
let html = `The probabilities of rain in <b>${location}</b> today:<br><br>`
+ today.map((
{ startTime, endTime, probabilityOfPrecipitation: { value: p } },
) => `${format(startTime)}-${format(endTime)}: ${p}%`).join("<br>");
return email({ html, subject: "☔️ Carry an umbrella today!" });
};

If you fork this, you'll need to set OPENAI_API_KEY in your Val Town Secrets.

Readme
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
30
31
32
33
34
35
import { email } from "https://esm.town/v/std/email?v=11";
import { OpenAI } from "npm:openai";
let location = "brooklyn ny";
let lang = "en";
const weather = await fetch(
`https://wttr.in/${location}?lang=${lang}&format=j1`,
).then(r => r.json());
const openai = new OpenAI();
let chatCompletion = await openai.chat.completions.create({
messages: [{
role: "user",
content: `Based the weather data below,
give me suggestions on how warmly to dress,
ie pants or shorts, a light jacket or a warm jacket,
a scarf and gloves or not, if I should carry an umbrella, etc.
In your response, use temperature data from the weather data below
throughout the day to explain your reccomendation.
Be as concice as possible. Assume I'll wear the same thing the whole day.
Do not use a bulleted list. Use 2-3 sentences. Only use Fahrenheit`.replaceAll("\n", ""),
}, {
role: "user",
content: JSON.stringify(weather),
}],
model: "gpt-4-1106-preview",
max_tokens: 150,
});
const text = chatCompletion.choices[0].message.content;
console.log(text);
export async function weatherGPT() {
await email({ subject: "Weather Today", text });
}

If you fork this, you'll need to set OPENAI_API_KEY in your Val Town Secrets.

Readme
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
30
31
32
33
34
35
import { email } from "https://esm.town/v/std/email?v=11";
import { OpenAI } from "npm:openai";
let location = "brooklyn ny";
let lang = "en";
const weather = await fetch(
`https://wttr.in/${location}?lang=${lang}&format=j1`,
).then(r => r.json());
const openai = new OpenAI();
let chatCompletion = await openai.chat.completions.create({
messages: [{
role: "user",
content: `Based the weather data below,
give me suggestions on how warmly to dress,
ie pants or shorts, a light jacket or a warm jacket,
a scarf and gloves or not, if I should carry an umbrella, etc.
In your response, use temperature data from the weather data below
throughout the day to explain your reccomendation.
Be as concice as possible. Assume I'll wear the same thing the whole day.
Do not use a bulleted list. Use 2-3 sentences. Only use Fahrenheit`.replaceAll("\n", ""),
}, {
role: "user",
content: JSON.stringify(weather),
}],
model: "gpt-4-1106-preview",
max_tokens: 150,
});
const text = chatCompletion.choices[0].message.content;
console.log(text);
export async function weatherGPT() {
await email({ subject: "Weather Today", text });
}

If you fork this, you'll need to set OPENAI_API_KEY in your Val Town Secrets.

Readme
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
30
31
32
33
34
35
36
37
38
39
40
import { email } from "https://esm.town/v/std/email?v=11";
import { fetch } from "https://esm.town/v/std/fetch";
import { OpenAI } from "npm:openai";
let location = "shenzhen";
let lang = "zh";
const weather = await fetch(
`https://wttr.in/${location}?lang=${lang}&format=j1`,
).then(r => r.json());
const openai = new OpenAI();
let chatCompletion = await openai.chat.completions.create({
messages: [{
role: "user",
content: `基于下面的天气预报信息,播报当天深圳的天气情况,给我相关的建议告诉我如何穿衣,如果下雨我是否需要携带雨伞。
给出合理的理由同时带上温度和天气播报。
尽可能的准确,假设我一天都不会更换衣服的。
使用2-3个句子概述一下。`.replaceAll("\n", ""),
}, {
role: "user",
content: JSON.stringify(weather),
}],
model: "gpt-3.5-turbo",
max_tokens: 150,
});
const text = chatCompletion.choices[0].message.content;
console.log(text);
export async function weatherGPT() {
const result = await fetch(`https://hello.liaolile.com/test/weather`, {
method: "POST",
body: JSON.stringify({
"info": text,
}),
}).then(r => r.json());
console.log(result);
await email({ subject: "Weather Today", text });
}

get weather forecast for a city. pass ?city=Cleveland,OH. e.g.: https://jamiedubs-weatherInCity.web.val.run/?city=brooklyn,ny

Readme
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
export const getWeatherForecast = async (req: Request) => {
const url = new URL(req.url);
const city = url.searchParams.get("city");
if (!city) {
return new Response("City parameter is required", {
status: 400, // Bad Request
headers: { "Content-Type": "text/plain" },
});
}
const weatherApiUrl = `https://wttr.in/${encodeURIComponent(city)}?format=j1`;
try {
const weatherResponse = await fetch(weatherApiUrl);
if (!weatherResponse.ok) {
throw new Error(`Weather API request failed with status: ${weatherResponse.status}`);
}
const weatherData = await weatherResponse.json();
const currentTempC = weatherData.current_condition[0].temp_C;
const currentTempF = weatherData.current_condition[0].temp_F;
const forecast = weatherData.weather[0].hourly.slice(0, 8).map(hour => ({
time: hour.time,
tempC: hour.tempC,
tempF: hour.tempF,
desc: hour.weatherDesc[0].value,
}));
const forecastString = forecast.reduce((acc, hour) => {
return `${acc}Time: ${hour.time}, Temp: ${hour.tempC}°C/${hour.tempF}°F, Description: ${hour.desc}\n`;
}, `Current Temperature: ${currentTempC}°C/${currentTempF}°F\nForecast for the next 8 hours:\n`);
return new Response(forecastString, {
status: 200, // OK
headers: { "Content-Type": "text/plain" },
});
} catch (err) {
return new Response("Error fetching weather data: " + err.message, {
status: 500, // Internal Server Error
headers: { "Content-Type": "text/plain" },
});
}
};
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
30
31
32
33
import { chat } from "https://esm.town/v/cosmo/chat_openai";
import { getCurrentWeather } from "https://esm.town/v/cosmo/get_current_weather";
export async function getWeatherMessage(apiKey, latitude, longitude) {
const currentWeather = await getCurrentWeather(latitude, longitude),
{ icon } = currentWeather,
weather = Object.fromEntries(
[
"isDaytime",
"temperature",
"temperatureUnit",
"shortForecast",
"windSpeed",
"windDirection",
"probabilityOfPrecipitation",
"relativeHumidity",
]
.filter(key => key in currentWeather)
.map(key => [key, currentWeather[key]]),
),
messages = [
{
role: "system",
content:
`Concisely relay the provided weather data in natural language. Don't prefix or embellish it (such as "the current weather is as follows:" or "the weather is"), just relay the weather. Mention the weather properties in the same order as the JSON
},
{ role: "user", content: JSON.stringify(weather) },
],
chatResponse = await chat(apiKey, messages),
message = chatResponse.choices[0].message.content;
return { icon, message };
}