Skip to content

Commit

Permalink
Use yr.no as weather source (#354)
Browse files Browse the repository at this point in the history
* Use yr.no as weather source

* feat: adds emojis and more conditions

---------

Co-authored-by: Ahmed Elsakaan <[email protected]>
  • Loading branch information
Erb3 and ixahmedxi authored Nov 6, 2023
1 parent e047f47 commit ddf140b
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 65 deletions.
3 changes: 0 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,6 @@ env:
UPSTASH_REDIS_REST_URL: ${{secrets.UPSTASH_REDIS_REST_URL}}
UPSTASH_REDIS_REST_TOKEN: ${{secrets.UPSTASH_REDIS_REST_TOKEN}}

# Openweather
OPENWEATHER_API_KEY: ${{secrets.OPENWEATHER_API_KEY}}

jobs:
main:
name: CI
Expand Down
8 changes: 4 additions & 4 deletions src/app/(dashboard)/app/_components/weather.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ export const WeatherData: FC = () => {

return (
<span className="text-tiny text-default-500">
You can expect a 👆 high of {weatherData.main.temp_max.toFixed()}º and a
👇 low of {weatherData.main.temp_min.toFixed()}º with{" "}
{getFormattedWeatherDescription(weatherData.weather[0]?.description)}{" "}
today.
You can expect a 👆 high of {weatherData.temp_max.toFixed()}º and a 👇 low
of {weatherData.temp_min.toFixed()}º
{getFormattedWeatherDescription(weatherData.summary)} for today&apos;s
weather.
</span>
);
};
2 changes: 0 additions & 2 deletions src/env.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ export const env = createEnv({
UPSTASH_REDIS_REST_URL: z.string().optional(),
UPSTASH_REDIS_REST_TOKEN: z.string().optional(),
CLERK_SECRET_KEY: z.string().min(1),
OPENWEATHER_API_KEY: z.string().min(1),
},
client: {
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: z.string().min(1),
Expand All @@ -26,7 +25,6 @@ export const env = createEnv({
process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY,
UPSTASH_REDIS_REST_TOKEN: process.env.UPSTASH_REDIS_REST_TOKEN,
UPSTASH_REDIS_REST_URL: process.env.UPSTASH_REDIS_REST_URL,
OPENWEATHER_API_KEY: process.env.OPENWEATHER_API_KEY,
},
skipValidation: !!process.env.SKIP_ENV_VALIDATION,
});
62 changes: 50 additions & 12 deletions src/server/api/routers/weather.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,31 @@
import { TRPCError } from "@trpc/server";
import fetch from "node-fetch";
import { z } from "zod";

import { env } from "@/env.mjs";

import { createTRPCRouter, protectedProcedure } from "../trpc";

type RawWeatherData = {
properties: {
timeseries: {
data: {
next_12_hours: {
summary: {
symbol_code: string;
}
},
instant: {
details: {
air_temperature: number;
}
}
}
}[]
}
}

type WeatherData = {
main: {
temp_max: number;
temp_min: number;
};
weather: {
description: string;
}[];
temp_max: number;
temp_min: number;
summary: string;
};

export const weatherRouter = createTRPCRouter({
Expand All @@ -28,7 +40,12 @@ export const weatherRouter = createTRPCRouter({
const { latitude, longitude } = input;

const response = await fetch(
`https://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&appid=${env.OPENWEATHER_API_KEY}&units=metric`,
`https://api.met.no/weatherapi/locationforecast/2.0/compact?lat=${latitude}&lon=${longitude}`,
{
headers: {
"User-Agent": `noodle.run (https://github.com/noodle-run/noodle)`
}
}
);

if (!response.ok) {
Expand All @@ -38,6 +55,27 @@ export const weatherRouter = createTRPCRouter({
});
}

return response.json() as Promise<WeatherData>;
const rawWeatherData: RawWeatherData = await response.json() as RawWeatherData;

if (rawWeatherData.properties.timeseries.length < 12) {
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: "Partial weather data"
})
}

const temperatures = [];

for (const timeseries of rawWeatherData.properties.timeseries) {
temperatures.push(timeseries.data.instant.details.air_temperature);
}

const weatherData: WeatherData = {
summary: rawWeatherData.properties.timeseries[0]!.data.next_12_hours.summary.symbol_code,
temp_max: Math.max(...temperatures),
temp_min: Math.min(...temperatures)
}

return weatherData;
}),
});
92 changes: 48 additions & 44 deletions src/utils/getFormattedWeatherDescription.ts
Original file line number Diff line number Diff line change
@@ -1,48 +1,52 @@
const weatherCodeToEnglish: Record<string, string> = {
clearsky: "🌞 a clear sky",
cloudy: "☁️ clouds",
fair: "⛅ fair",
fair_day: "🌤️ fair day",
fog: "🌫️ fog",
heavyrain: "🌧️ heavy rain",
heavyrainandthunder: "⛈️ heavy rain and thunder",
heavyrainshowers: "🌧️ heavy rain showers",
heavyrainshowersandthunder: "⛈️ heavy rain showers and thunder",
heavysleet: "🌨️ heavy sleet",
heavysleetandthunder: "⛈️ heavy sleet and thunder",
heavysleetshowers: "🌨️ heavy sleet showers",
heavysleetshowersandthunder: "⛈️ heavy sleet showers and thunder",
heavysnow: "❄️ heavy snow",
heavysnowandthunder: "⛈️ heavy snow and thunder",
heavysnowshowers: "❄️ heavy snow showers",
heavysnowshowersandthunder: "⛈️ heavy snow showers and thunder",
lightrain: "🌦️ light rain",
lightrainandthunder: "⛈️ light rain and thunder",
lightrainshowers: "🌦️ light rain showers",
lightrainshowersandthunder: "⛈️ light rain showers and thunder",
lightsleet: "🌧️ light sleet",
lightsleetandthunder: "⛈️ light sleet and thunder",
lightsleetshowers: "🌧️ light sleet showers",
lightsnow: "🌨️ light snow",
lightsnowandthunder: "⛈️ light snow and thunder",
lightsnowshowers: "🌨️ light snow showers",
lightssleetshowersandthunder: "⛈️ light sleet showers and thunder",
lightssnowshowersandthunder: "⛈️ light snow showers and thunder",
partlycloudy: "🌥️ some clouds",
rain: "🌧️ rain",
rainandthunder: "⛈️ rain and thunder",
rainshowers: "🌧️ rain showers",
rainshowersandthunder: "⛈️ rain showers and thunder",
sleet: "🌨️ sleet",
sleetandthunder: "⛈️ sleet and thunder",
sleetshowers: "🌨️ sleet showers",
sleetshowersandthunder: "⛈️ sleet showers and thunder",
snow: "❄️ snow",
snowandthunder: "⛈️ snow and thunder",
snowshowers: "❄️ snow showers",
snowshowersandthunder: "⛈️ snow showers and thunder",
};

export const getFormattedWeatherDescription = (
condition: string | undefined,
) => {
if (!condition) return;

const weatherToEmoji: Record<string, string> = {
"clear sky": "☀️",
"few clouds": "🌤️",
"scattered clouds": "⛅",
"broken clouds": "☁️",
"overcast clouds": "☁️",
rain: "🌧️",
"light rain": "🌧️",
"moderate rain": "🌧️",
"heavy intensity rain": "🌧️",
"very heavy rain": "🌧️",
"extreme rain": "🌧️",
"freezing rain": "🌨️",
"light intensity shower rain": "🌦️",
"shower rain": "🌧️",
"heavy intensity shower rain": "🌧️",
"ragged shower rain": "🌧️",
"light snow": "❄️",
snow: "❄️",
"heavy snow": "❄️",
sleet: "🌨️",
"shower sleet": "🌨️",
"light rain and snow": "🌨️",
"rain and snow": "🌨️",
"light shower snow": "🌨️",
"shower snow": "🌨️",
"heavy shower snow": "🌨️",
mist: "🌫️",
smoke: "🌫️",
haze: "🌫️",
"sand/ dust whirls": "🌪️",
fog: "🌫️",
sand: "🌫️",
dust: "🌫️",
"volcanic ash": "🌫️",
squalls: "🌬️",
tornado: "🌪️",
clear: "☀️",
clouds: "☁️",
};

return `${weatherToEmoji[condition.toLowerCase()] ?? ""} ${condition}`;
if (!condition || !weatherCodeToEnglish[condition]) return;
const weatherDescription = weatherCodeToEnglish[condition];
return `with ${weatherDescription}`;
};

0 comments on commit ddf140b

Please sign in to comment.