From 7f45387dacaef4ebd44176ef907d0a7214e0e802 Mon Sep 17 00:00:00 2001 From: Cory Sanin Date: Sat, 23 Aug 2025 03:04:28 -0500 Subject: [PATCH] add weather class --- config/config.example.json5 | 6 +++ src/index.ts | 4 +- src/weather.ts | 101 ++++++++++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+), 1 deletion(-) diff --git a/config/config.example.json5 b/config/config.example.json5 index 9712f30..826ef27 100644 --- a/config/config.example.json5 +++ b/config/config.example.json5 @@ -114,5 +114,11 @@ "directory": "audio/voice/cory/", "extension": "flac" } + }, + "weather": { + // Provide an OpenWeatherMap API key + // https://openweathermap.org/price + "key": "not0a0real0key00281f631aef6ad3a1", + "city": "chicago" } } diff --git a/src/index.ts b/src/index.ts index 0387df2..6079c33 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,13 +4,15 @@ import json5 from 'json5'; import Sequencer from './sequencer.js'; import type {Programs, Segments, Sequences} from './sequencer.js'; import type { Voices } from './voice.js'; +import type { WeatherConfig } from './weather.js'; interface Config { programs: Programs, segments: Segments, sequences: Sequences, - voices: Voices + voices: Voices, + weather: WeatherConfig } console.log('morning-report\nCory Sanin 2025\n'); diff --git a/src/weather.ts b/src/weather.ts index e69de29..e9119ec 100644 --- a/src/weather.ts +++ b/src/weather.ts @@ -0,0 +1,101 @@ +import OpenWeatherMap from 'openweathermap-ts'; +import type { ThreeHourResponse, CurrentResponse, CountryCode } from 'openweathermap-ts/dist/types/index.js'; + +interface CityName { + cityName: string; + state: string; + countryCode: CountryCode; +} + +interface WeatherConfig { + key: string; + lang?: string; + coordinates?: number[] | string; + zip?: number; + country?: CountryCode; + cityid?: number; + city?: CityName; +} + +type LocationType = 'coordinates' | 'zip' | 'cityid' | 'city' | null; + +function parseCoords(coords: number[] | string): number[] { + if (typeof coords == 'string') { + return coords.replace(/\s/g, '').split(',').map(Number.parseFloat); + } + return coords; +} + + +class Weather { + private openWeather: OpenWeatherMap.default; + private locationType: LocationType; + private current: CurrentResponse; + private threeDay: ThreeHourResponse; + + constructor(options: WeatherConfig) { + this.locationType = this.current = this.threeDay = null; + this.openWeather = new OpenWeatherMap.default({ + apiKey: options.key + }); + if ('city' in options && 'cityName' in options.city && 'state' in options.city && 'countryCode' in options.city) { + this.openWeather.setCityName(options.city); + this.locationType = 'city'; + } + if ('cityid' in options) { + this.openWeather.setCityId(options.cityid); + this.locationType = 'cityid'; + } + if ('zip' in options && 'country' in options) { + this.openWeather.setZipCode(options.zip, options.country) + this.locationType = 'zip'; + } + if ('coordinates' in options) { + const coords = parseCoords(options.coordinates); + if (coords.length >= 2) { + this.openWeather.setGeoCoordinates(coords[0], coords[1]); + this.locationType = 'coordinates'; + } + } + } + + async getCurrentWeather(): Promise { + if (this.current) { + return this.current; + } + switch (this.locationType) { + case 'city': + return this.current = await this.openWeather.getCurrentWeatherByCityName(); + case 'cityid': + return this.current = await this.openWeather.getCurrentWeatherByCityId(); + case 'zip': + return this.current = await this.openWeather.getCurrentWeatherByZipcode(); + case 'coordinates': + return this.current = await this.openWeather.getCurrentWeatherByGeoCoordinates(); + default: + throw new Error(`Can't fetch weather for location type '${this.locationType}'`); + } + } + + async getThreeHourForecast(): Promise { + if (this.threeDay) { + return this.threeDay; + } + switch (this.locationType) { + case 'city': + return this.threeDay = await this.openWeather.getThreeHourForecastByCityName(); + case 'cityid': + return this.threeDay = await this.openWeather.getThreeHourForecastByCityId(); + case 'zip': + return this.threeDay = await this.openWeather.getThreeHourForecastByZipcode(); + case 'coordinates': + return this.threeDay = await this.openWeather.getThreeHourForecastByGeoCoordinates(); + default: + throw new Error(`Can't fetch weather for location type '${this.locationType}'`); + } + } +} + +export default Weather; +export { Weather }; +export type { WeatherConfig, CityName };