Procedurally generates a radio weather report

add weather class

Changed files
+110 -1
config
src
+6
config/config.example.json5
··· 114 "directory": "audio/voice/cory/", 115 "extension": "flac" 116 } 117 } 118 }
··· 114 "directory": "audio/voice/cory/", 115 "extension": "flac" 116 } 117 + }, 118 + "weather": { 119 + // Provide an OpenWeatherMap API key 120 + // https://openweathermap.org/price 121 + "key": "not0a0real0key00281f631aef6ad3a1", 122 + "city": "chicago" 123 } 124 }
+3 -1
src/index.ts
··· 4 import Sequencer from './sequencer.js'; 5 import type {Programs, Segments, Sequences} from './sequencer.js'; 6 import type { Voices } from './voice.js'; 7 8 9 interface Config { 10 programs: Programs, 11 segments: Segments, 12 sequences: Sequences, 13 - voices: Voices 14 } 15 16 console.log('morning-report\nCory Sanin 2025\n');
··· 4 import Sequencer from './sequencer.js'; 5 import type {Programs, Segments, Sequences} from './sequencer.js'; 6 import type { Voices } from './voice.js'; 7 + import type { WeatherConfig } from './weather.js'; 8 9 10 interface Config { 11 programs: Programs, 12 segments: Segments, 13 sequences: Sequences, 14 + voices: Voices, 15 + weather: WeatherConfig 16 } 17 18 console.log('morning-report\nCory Sanin 2025\n');
+101
src/weather.ts
···
··· 1 + import OpenWeatherMap from 'openweathermap-ts'; 2 + import type { ThreeHourResponse, CurrentResponse, CountryCode } from 'openweathermap-ts/dist/types/index.js'; 3 + 4 + interface CityName { 5 + cityName: string; 6 + state: string; 7 + countryCode: CountryCode; 8 + } 9 + 10 + interface WeatherConfig { 11 + key: string; 12 + lang?: string; 13 + coordinates?: number[] | string; 14 + zip?: number; 15 + country?: CountryCode; 16 + cityid?: number; 17 + city?: CityName; 18 + } 19 + 20 + type LocationType = 'coordinates' | 'zip' | 'cityid' | 'city' | null; 21 + 22 + function parseCoords(coords: number[] | string): number[] { 23 + if (typeof coords == 'string') { 24 + return coords.replace(/\s/g, '').split(',').map(Number.parseFloat); 25 + } 26 + return coords; 27 + } 28 + 29 + 30 + class Weather { 31 + private openWeather: OpenWeatherMap.default; 32 + private locationType: LocationType; 33 + private current: CurrentResponse; 34 + private threeDay: ThreeHourResponse; 35 + 36 + constructor(options: WeatherConfig) { 37 + this.locationType = this.current = this.threeDay = null; 38 + this.openWeather = new OpenWeatherMap.default({ 39 + apiKey: options.key 40 + }); 41 + if ('city' in options && 'cityName' in options.city && 'state' in options.city && 'countryCode' in options.city) { 42 + this.openWeather.setCityName(options.city); 43 + this.locationType = 'city'; 44 + } 45 + if ('cityid' in options) { 46 + this.openWeather.setCityId(options.cityid); 47 + this.locationType = 'cityid'; 48 + } 49 + if ('zip' in options && 'country' in options) { 50 + this.openWeather.setZipCode(options.zip, options.country) 51 + this.locationType = 'zip'; 52 + } 53 + if ('coordinates' in options) { 54 + const coords = parseCoords(options.coordinates); 55 + if (coords.length >= 2) { 56 + this.openWeather.setGeoCoordinates(coords[0], coords[1]); 57 + this.locationType = 'coordinates'; 58 + } 59 + } 60 + } 61 + 62 + async getCurrentWeather(): Promise<CurrentResponse> { 63 + if (this.current) { 64 + return this.current; 65 + } 66 + switch (this.locationType) { 67 + case 'city': 68 + return this.current = await this.openWeather.getCurrentWeatherByCityName(); 69 + case 'cityid': 70 + return this.current = await this.openWeather.getCurrentWeatherByCityId(); 71 + case 'zip': 72 + return this.current = await this.openWeather.getCurrentWeatherByZipcode(); 73 + case 'coordinates': 74 + return this.current = await this.openWeather.getCurrentWeatherByGeoCoordinates(); 75 + default: 76 + throw new Error(`Can't fetch weather for location type '${this.locationType}'`); 77 + } 78 + } 79 + 80 + async getThreeHourForecast(): Promise<ThreeHourResponse> { 81 + if (this.threeDay) { 82 + return this.threeDay; 83 + } 84 + switch (this.locationType) { 85 + case 'city': 86 + return this.threeDay = await this.openWeather.getThreeHourForecastByCityName(); 87 + case 'cityid': 88 + return this.threeDay = await this.openWeather.getThreeHourForecastByCityId(); 89 + case 'zip': 90 + return this.threeDay = await this.openWeather.getThreeHourForecastByZipcode(); 91 + case 'coordinates': 92 + return this.threeDay = await this.openWeather.getThreeHourForecastByGeoCoordinates(); 93 + default: 94 + throw new Error(`Can't fetch weather for location type '${this.locationType}'`); 95 + } 96 + } 97 + } 98 + 99 + export default Weather; 100 + export { Weather }; 101 + export type { WeatherConfig, CityName };