@@ -96,28 +96,41 @@ class OpenWeatherMapProvider {
9696
9797 #handleResponse ( data ) {
9898 try {
99- // Set location name from timezone
100- if ( data . timezone ) {
101- this . locationName = data . timezone ;
102- }
103-
10499 let weatherData ;
105- const onecallData = this . #generateWeatherObjectsFromOnecall( data ) ;
106-
107- switch ( this . config . type ) {
108- case "current" :
109- weatherData = onecallData . current ;
110- break ;
111- case "forecast" :
112- case "daily" :
113- weatherData = onecallData . days ;
114- break ;
115- case "hourly" :
116- weatherData = onecallData . hours ;
117- break ;
118- default :
119- Log . error ( `[openweathermap] Unknown type: ${ this . config . type } ` ) ;
120- throw new Error ( `Unknown weather type: ${ this . config . type } ` ) ;
100+
101+ if ( this . config . weatherEndpoint === "/onecall" ) {
102+ // One Call API (v3.0)
103+ if ( data . timezone ) {
104+ this . locationName = data . timezone ;
105+ }
106+
107+ const onecallData = this . #generateWeatherObjectsFromOnecall( data ) ;
108+
109+ switch ( this . config . type ) {
110+ case "current" :
111+ weatherData = onecallData . current ;
112+ break ;
113+ case "forecast" :
114+ case "daily" :
115+ weatherData = onecallData . days ;
116+ break ;
117+ case "hourly" :
118+ weatherData = onecallData . hours ;
119+ break ;
120+ default :
121+ Log . error ( `[openweathermap] Unknown type: ${ this . config . type } ` ) ;
122+ throw new Error ( `Unknown weather type: ${ this . config . type } ` ) ;
123+ }
124+ } else if ( this . config . weatherEndpoint === "/weather" ) {
125+ // Current weather endpoint (API v2.5)
126+ weatherData = this . #generateWeatherObjectFromCurrentWeather( data ) ;
127+ } else if ( this . config . weatherEndpoint === "/forecast" ) {
128+ // 3-hourly forecast endpoint (API v2.5)
129+ weatherData = this . config . type === "hourly"
130+ ? this . #generateHourlyWeatherObjectsFromForecast( data )
131+ : this . #generateDailyWeatherObjectsFromForecast( data ) ;
132+ } else {
133+ throw new Error ( `Unknown weather endpoint: ${ this . config . weatherEndpoint } ` ) ;
121134 }
122135
123136 if ( weatherData && this . onDataCallback ) {
@@ -134,6 +147,123 @@ class OpenWeatherMapProvider {
134147 }
135148 }
136149
150+ #generateWeatherObjectFromCurrentWeather ( data ) {
151+ const timezoneOffsetMinutes = ( data . timezone ?? 0 ) / 60 ;
152+
153+ if ( data . name && data . sys ?. country ) {
154+ this . locationName = `${ data . name } , ${ data . sys . country } ` ;
155+ } else if ( data . name ) {
156+ this . locationName = data . name ;
157+ }
158+
159+ const weather = { } ;
160+ weather . date = weatherUtils . applyTimezoneOffset ( new Date ( data . dt * 1000 ) , timezoneOffsetMinutes ) ;
161+ weather . temperature = data . main . temp ;
162+ weather . feelsLikeTemp = data . main . feels_like ;
163+ weather . humidity = data . main . humidity ;
164+ weather . windSpeed = data . wind . speed ;
165+ weather . windFromDirection = data . wind . deg ;
166+ weather . weatherType = weatherUtils . convertWeatherType ( data . weather [ 0 ] . icon ) ;
167+ weather . sunrise = weatherUtils . applyTimezoneOffset ( new Date ( data . sys . sunrise * 1000 ) , timezoneOffsetMinutes ) ;
168+ weather . sunset = weatherUtils . applyTimezoneOffset ( new Date ( data . sys . sunset * 1000 ) , timezoneOffsetMinutes ) ;
169+
170+ return weather ;
171+ }
172+
173+ #extractThreeHourPrecipitation ( forecast ) {
174+ const rain = Number . parseFloat ( forecast . rain ?. [ "3h" ] ?? "" ) || 0 ;
175+ const snow = Number . parseFloat ( forecast . snow ?. [ "3h" ] ?? "" ) || 0 ;
176+ const precipitationAmount = rain + snow ;
177+
178+ return {
179+ rain,
180+ snow,
181+ precipitationAmount,
182+ hasPrecipitation : precipitationAmount > 0
183+ } ;
184+ }
185+
186+ #generateHourlyWeatherObjectsFromForecast ( data ) {
187+ const timezoneOffsetSeconds = data . city ?. timezone ?? 0 ;
188+ const timezoneOffsetMinutes = timezoneOffsetSeconds / 60 ;
189+
190+ if ( data . city ?. name && data . city ?. country ) {
191+ this . locationName = `${ data . city . name } , ${ data . city . country } ` ;
192+ }
193+
194+ return data . list . map ( ( forecast ) => {
195+ const weather = { } ;
196+ weather . date = weatherUtils . applyTimezoneOffset ( new Date ( forecast . dt * 1000 ) , timezoneOffsetMinutes ) ;
197+ weather . temperature = forecast . main . temp ;
198+ weather . feelsLikeTemp = forecast . main . feels_like ;
199+ weather . humidity = forecast . main . humidity ;
200+ weather . windSpeed = forecast . wind . speed ;
201+ weather . windFromDirection = forecast . wind . deg ;
202+ weather . weatherType = weatherUtils . convertWeatherType ( forecast . weather [ 0 ] . icon ) ;
203+ weather . precipitationProbability = forecast . pop !== undefined ? forecast . pop * 100 : undefined ;
204+
205+ const precipitation = this . #extractThreeHourPrecipitation( forecast ) ;
206+ if ( precipitation . hasPrecipitation ) {
207+ weather . rain = precipitation . rain ;
208+ weather . snow = precipitation . snow ;
209+ weather . precipitationAmount = precipitation . precipitationAmount ;
210+ }
211+
212+ return weather ;
213+ } ) ;
214+ }
215+
216+ #generateDailyWeatherObjectsFromForecast ( data ) {
217+ const timezoneOffsetSeconds = data . city ?. timezone ?? 0 ;
218+ const timezoneOffsetMinutes = timezoneOffsetSeconds / 60 ;
219+
220+ if ( data . city ?. name && data . city ?. country ) {
221+ this . locationName = `${ data . city . name } , ${ data . city . country } ` ;
222+ }
223+
224+ const dayMap = new Map ( ) ;
225+
226+ for ( const forecast of data . list ) {
227+ // Shift dt by timezone offset so UTC fields represent local time
228+ const localDate = new Date ( ( forecast . dt + timezoneOffsetSeconds ) * 1000 ) ;
229+ const dateKey = `${ localDate . getUTCFullYear ( ) } -${ String ( localDate . getUTCMonth ( ) + 1 ) . padStart ( 2 , "0" ) } -${ String ( localDate . getUTCDate ( ) ) . padStart ( 2 , "0" ) } ` ;
230+
231+ if ( ! dayMap . has ( dateKey ) ) {
232+ dayMap . set ( dateKey , {
233+ date : weatherUtils . applyTimezoneOffset ( new Date ( forecast . dt * 1000 ) , timezoneOffsetMinutes ) ,
234+ minTemps : [ ] ,
235+ maxTemps : [ ] ,
236+ rain : 0 ,
237+ snow : 0 ,
238+ weatherType : weatherUtils . convertWeatherType ( forecast . weather [ 0 ] . icon )
239+ } ) ;
240+ }
241+
242+ const day = dayMap . get ( dateKey ) ;
243+ day . minTemps . push ( forecast . main . temp_min ) ;
244+ day . maxTemps . push ( forecast . main . temp_max ) ;
245+
246+ const hour = localDate . getUTCHours ( ) ;
247+ if ( hour >= 8 && hour <= 17 ) {
248+ day . weatherType = weatherUtils . convertWeatherType ( forecast . weather [ 0 ] . icon ) ;
249+ }
250+
251+ const precipitation = this . #extractThreeHourPrecipitation( forecast ) ;
252+ day . rain += precipitation . rain ;
253+ day . snow += precipitation . snow ;
254+ }
255+
256+ return Array . from ( dayMap . values ( ) ) . map ( ( day ) => ( {
257+ date : day . date ,
258+ minTemperature : Math . min ( ...day . minTemps ) ,
259+ maxTemperature : Math . max ( ...day . maxTemps ) ,
260+ weatherType : day . weatherType ,
261+ rain : day . rain ,
262+ snow : day . snow ,
263+ precipitationAmount : day . rain + day . snow
264+ } ) ) ;
265+ }
266+
137267 #generateWeatherObjectsFromOnecall ( data ) {
138268 let precip ;
139269
0 commit comments