Page 1 of 1

PureBasic and AccuWeather APIs - A LIVE Working Example

Posted: Mon Aug 11, 2025 9:18 am
by TI-994A
I had posted some examples earlier, demonstrating the use of REST APIs, through PureBasic's HTTP library, as well as the more conventional Network library, mostly consuming demo APIs on my own servers.

This example demonstrates the use of publicly available APIs, namely from AccuWeather, to retrieve daily weather forecasts. It uses a static, preconfigured list of city/state values from a text file downloaded from my server, then queries two API services from AccuWeather to retrieve the daily forecasts of the selected cities.

It should be noted that AccuWeather APIs require unique API keys in order to work. To obtain an API key, simply register for a free-tier account with them, which allows up to 50 API calls per day.

> AccuWeather API Packages

The fine-grained nature of AccuWeather's services, which attempts to offer pinpoint accuracy, employs the use of location keys. Instead of returning forecast reports based on city/state queries, it drills down to more precise latitude/longitude geopositioning, where a single city/state could correspond to multiple location keys. However, it also indexes city/state entries to common, perhaps more central, location keys, which could be queried just by the city/state names.

Nevertheless, it still requires calls to two APIs, one for retrieving the location key, and another for retrieving the actual forecast report. The following examples demonstrate these two API calls, which could be directly called from the address bar of any browser.

> the first API call to retrieve the location key for Rome/Italy:

Code: Select all

http://dataservice.accuweather.com/locations/v1/cities/search?apikey=[insert API key here]&q=rome%2Citaly

> the second API call to retrieve the daily forecast for Rome/Italy (location key = 213490 returned from the above API call):

Code: Select all

http://dataservice.accuweather.com/forecasts/v1/daily/1day/213490?apikey=[insert API key here]&metric=true

Here is the full working code, a modification of my earlier timeanddate.com example. For convenience, I have included the API key from one of my AccuWeather accounts, but I'm sure it would exceed the 50 daily call-limit in no time. To test it effectively, please register for a free-tier account and utilise that API key instead.

Code: Select all

;==========================================================
;   
;   AccuWeather REST API example:
;   - retrieving location keys from city/state names   
;   - retrieivng weather forecasts with location keys
;
;   * an API key is required to run this example which
;     can be obtained with a free-tier registration 
;     https://developer.accuweather.com/packages
;
;   * an API key has been included for testing but
;     is limited to only 50 API calls per day
;
;   tested & working on:
;   1. Windows 8.1 w/ PureBasic 5.73 LTS (x64)
;   2. Windows 10 w/ PureBasic 6.11 LTS (x64)
;   3. Windows 11 w/ PureBasic 6.21 (x64)
;   4. macOS Catalina w/ PureBasic 5.73 LTS (x64)
;   5. macOS Sonoma w/ PureBasic 6.11 LTS (arm64)
;   6. macOS Sequoia w/ PureBasic 6.21 (arm64)
;   7. Ubuntu Linux 18.04.2 w/ PureBasic 6.11 LTS (x64)
;
;   by TI-994A - free to use, improve, share...
;
;   10th August 2025 - Singapore
;
;==========================================================

EnableExplicit

CompilerIf #PB_Compiler_Version < 600
  InitNetwork()
CompilerEndIf

#API_URL = "http://dataservice.accuweather.com/"
#API_Key = "oMBJLzGG3g2tkUwy5z6BgApVL2e9WGqL"
#API_Key_Invalid = "Api Authorization failed"
#API_Exceed = "The allowed number of requests has been exceeded."
#httpSuccess = 200

Define event, appQuit, cityList, cityLabel, forecastLabel, forecastDescriptionLabel

Procedure initData()
  Shared cityList  
  Dim cities.s(149)
  Define i, citiesFile.s
  
  citiesFile = GetTemporaryDirectory() + "cities.txt"  
  If FileSize(citiesFile) < 1  
    ReceiveHTTPFile("syed.sg/tutorials/cities.txt", citiesFile)
  EndIf
  
  If ReadFile(0, citiesFile)
    While Eof(0) = 0       
      cities(i) = Trim(ReadString(0)) 
      i + 1      
    Wend
    CloseFile(0) 
  EndIf
  
  For i = 0 To 149
    AddGadgetItem (cityList, -1, cities(i))
  Next i
  
EndProcedure

Procedure validResponse(response.s)
  Define valid = #True  
  
  If FindString(response, #API_Exceed) > 0
    valid = #False
    MessageRequester("AccuWeather API Error", #API_Exceed)
  ElseIf FindString(response, #API_Key_Invalid) > 0
    valid = #False
    MessageRequester("AccuWeather API Error", "Invalid API Key!")
  EndIf        
  
  ProcedureReturn valid
EndProcedure

Procedure.s getLocationKey(locationResults)
  Define i, jsonObject, countryElement
  Define.s countryName, locationKey
  
  For i = 0 To JSONArraySize(locationResults) - 1
    jsonObject = GetJSONElement(locationResults, i)    
    countryElement = GetJSONMember(jsonObject, "Country")    
    countryName = GetJSONString(GetJSONMember(countryElement, "EnglishName"))
    locationKey = GetJSONString(GetJSONMember(jsonObject, "Key"))
    Break
  Next i
  
  ProcedureReturn locationKey
EndProcedure

Procedure getForecast(city.s, forecastResults)
  Shared cityLabel, forecastLabel, forecastDescriptionLabel
  Define.d minTemp, maxTemp, forecastDescription.s
  Define i, forecastHeader, forecastArray, forecast, temperatures  
  
  forecastHeader = GetJSONMember(forecastResults, "Headline")
  forecastDescription = GetJSONString(GetJSONMember(forecastHeader, "Text"))    
  forecastArray.i = GetJSONMember(forecastResults, "DailyForecasts")
  
  For i = 0 To JSONArraySize(forecastArray) - 1
    forecast = GetJSONElement(forecastArray, i)
    temperatures = GetJSONMember(forecast, "Temperature")    
    minTemp = GetJSONDouble(GetJSONMember(GetJSONMember(temperatures, "Minimum"), "Value"))
    maxTemp = GetJSONDouble(GetJSONMember(GetJSONMember(temperatures, "Maximum"), "Value"))
    Break
  Next i
  
  SetGadgetText(cityLabel, city)
  SetGadgetText(forecastLabel, "" + minTemp + " / " + maxTemp + " °C")  
  SetGadgetText(forecastDescriptionLabel, forecastDescription)      
  
EndProcedure

Procedure accuWeather(city.s)
  Define.s weatherURL, locationURL, cityQuery, locationKey, serverResponse, httpRequest.i
  
  cityQuery = ReplaceString(ReplaceString(city, ", ", "%2C"), " ", "%20")  
  locationURL = #API_URL + "locations/v1/cities/search?q=" + LCase(cityQuery) + "&apikey=" + #API_Key      
  httpRequest = HTTPRequest(#PB_HTTP_Get, locationURL)
  
  If httpRequest
    If Val(HTTPInfo(httpRequest, #PB_HTTP_StatusCode)) = #httpSuccess
      serverResponse = HTTPInfo(httpRequest, #PB_HTTP_Response)          
      If validResponse(serverResponse)
        locationKey = getLocationKey(JSONValue(ParseJSON(#PB_Any, serverResponse)))                  
        If Trim(locationKey) <> ""
          weatherURL = #API_URL + "forecasts/v1/daily/1day/" + locationKey + "?metric=true&apikey=" + #API_Key 
          httpRequest = HTTPRequest(#PB_HTTP_Get, weatherURL)          
          If httpRequest
            If Val(HTTPInfo(httpRequest, #PB_HTTP_StatusCode)) = #httpSuccess
              serverResponse = HTTPInfo(httpRequest, #PB_HTTP_Response)                  
              If validResponse(serverResponse)
                getForecast(city, JSONValue(ParseJSON(#PB_Any, serverResponse)))  
              EndIf              
            EndIf                   
          EndIf          
        EndIf                      
      EndIf            
    EndIf      
  EndIf  
  
EndProcedure

OpenWindow(0, 0, 0, 1000, 500, "AccuWeather API Demo", 
           #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
cityList = ListViewGadget(#PB_Any, 10, 10, 350, 420)
cityLabel = TextGadget(#PB_Any, 400, 30, 480, 100, "Select a city...")
forecastLabel = TextGadget(#PB_Any, 450, 135, 530, 300, "High / Low")
forecastDescriptionLabel = TextGadget(#PB_Any, 10, 460, 980, 30, #API_URL, #PB_Text_Center)

If LoadFont(0, "Arial", 30)
  SetGadgetFont(cityLabel, FontID(0))
EndIf

If LoadFont(1, "Arial", 70)
  SetGadgetFont(forecastLabel, FontID(1))
EndIf

If LoadFont(2, "Arial", 20)  
  SetGadgetFont(forecastDescriptionLabel, FontID(2))
EndIf

initData()

Repeat
  event = WaitWindowEvent()
  Select event
    Case #PB_Event_CloseWindow
      appQuit = #True
    Case #PB_Event_Gadget
      Select EventGadget()
        Case cityList          
          If EventType() = #PB_EventType_LeftClick            
            accuWeather(GetGadgetText(cityList))
          EndIf
      EndSelect          
  EndSelect   
Until appQuit

Re: PureBasic and AccuWeather APIs - A LIVE Working Example

Posted: Mon Aug 11, 2025 4:32 pm
by JHPJHP
Hi TI-994A,

Great example, very helpful post.

I can appreciate the amount of time and effort involved in the process: Registering for an API key, studying the documentation, and composing the accompanying write-up; the coding itself is often the easiest part.

Re: PureBasic and AccuWeather APIs - A LIVE Working Example

Posted: Mon Aug 11, 2025 5:31 pm
by idle
Nice thanks for sharing very useful

Re: PureBasic and AccuWeather APIs - A LIVE Working Example

Posted: Mon Aug 11, 2025 7:48 pm
by TI-994A
JHPJHP wrote: Mon Aug 11, 2025 4:32 pm Hi TI-994A,

Great example, very helpful post.

I can appreciate the amount of time and effort involved in the process: Registering for an API key, studying the documentation, and composing the accompanying write-up; the coding itself is often the easiest part.
idle wrote: Mon Aug 11, 2025 5:31 pm Nice thanks for sharing very useful
Thank you, @JHPJHP and @idle. Your kind words are most appreciated and very encouraging as well. :D

Re: PureBasic and AccuWeather APIs - A LIVE Working Example

Posted: Tue Aug 12, 2025 8:37 am
by Sofos
Thank you @TI-994A for your great work!

AccuWeather is a very popular weather service and I know that the web has many examples on its use. But to find a working example complete with queries and translating raw responses from the server, and extracting and presenting results, this is a first for me. It's far from just an example, it's a tutorial, really. Plus, you went the extra mile to share a key with us.

I think @JHPJHP had said it all. Thank you, by the way, @JHPJHP for sharing your OpenWeather code with me.

So far, I must say, this is a great forum with very helpful experts. Just as I was told.


Sofia

Re: PureBasic and AccuWeather APIs - A LIVE Working Example

Posted: Tue Aug 12, 2025 1:59 pm
by TI-994A
Sofos wrote: Tue Aug 12, 2025 8:37 am Thank you @TI-994A for your great work!

AccuWeather is a very popular weather service and I know that the web has many examples on its use. But to find a working example complete with queries and translating raw responses from the server, and extracting and presenting results, this is a first for me. It's far from just an example, it's a tutorial, really. Plus, you went the extra mile to share a key with us.
Thank you for your kind words, Sofia. It's true that AccuWeather is widely used, and there are indeed many code examples demonstrating the implementation of their various APIs, albeit for other programming languages. So, having used it myself, I thought it useful to post a working example here for PureBasic. At least now, if a search for "AccuWeather" were to pop up in this forum, it would yield a viable result. :lol:

One point to note: the extraction of the JSON server responses, from the APIs being used in the example, are statically structured, and cannot be implemented for other APIs. For each API, the responses must be studied and corresponding JSON extraction structures must be designed and implemented accordingly.

Please do keep us posted of your progress.

Re: PureBasic and AccuWeather APIs - A LIVE Working Example

Posted: Wed Aug 13, 2025 3:33 pm
by HeX0R
I hope you don't mind TI-994A, but I played a little bit with your code.
Now you have access to all values from the JSON answer and I made a module out of it.
Might need changes, when the API version changes.

Code: Select all

;==========================================================
;
;   AccuWeather REST API example:
;   - retrieving location keys from city/state names
;   - retrieivng weather forecasts with location keys
;
;   * an API key is required to run this example which
;     can be obtained with a free-tier registration
;     https://developer.accuweather.com/packages
;
;   by TI-994A - free to use, improve, share...
;
;   10th August 2025 - Singapore
;
;   improved JSON evaluation, you now have access to all values
;   changed into module
;   13th August 2025 - HeX0R
;
;==========================================================

DeclareModule AccuWeather
	
	Enumeration
		#Update_Daily
		#Update_5Days
		#Update_12Hours
	EndEnumeration
	
	Enumeration Error
		#Error_None
		#Error_Bad_Syntax
		#Error_API_Authorization_Failed
		#Error_Permission_Endpoint_Denied
		#Error_No_Route_To_URL
		#Error_Unexpected
		#Error_Not_Reachable
		#Error_No_LocationKey_Found
		#Error_JSON_Parsing_Failed
	EndEnumeration
	
	;Accuweather structures
	Structure ACCU_UNIT
		Value.f
		Unit.s
		UnitType.i
	EndStructure
	
	Structure ACCU_TEMPERATURE
		Minimum.ACCU_UNIT
		Maximum.ACCU_UNIT
	EndStructure
	
	Structure ACCU_ICON
		Icon.i
		IconPhrase.s
		HasPrecipitation.s
	EndStructure
	
	Structure ACCU_DAILY_FORECAST_ITEMS
		Date.s
		EpochDate.l
		Temperature.ACCU_TEMPERATURE
		Day.ACCU_ICON
		Night.ACCU_ICON
		List Sources.s()
		MobileLink.s
		Link.s
	EndStructure
	
	Structure ACCU_FORECAST_HEADER
		EffectiveDate.s
		EffectiveEpochDate.q
		Severity.i
		Text.s
		Category.s
		EndDate.s
		EndEpochDate.q
		MobileLink.s
		Link.s
	EndStructure
	
	Structure ACCU_FORECAST_DAILY
		Headline.ACCU_FORECAST_HEADER
		List DailyForecasts.ACCU_DAILY_FORECAST_ITEMS()
	EndStructure
	
	Structure ACCU_FORECAST_HOURLY
		DateTime.s
		EpochDateTime.q
		WeatherIcon.i
		IconPhrase.s
		HasPrecipitation.s
		IsDaylight.s
		Temperature.ACCU_UNIT
		PrecipitationProbability.i
		MobileLink.s
		Link.s
	EndStructure
	
	Structure ACCU_ADMINAREAS
		Level.i
		LocalizedName.s
		EnglishName.s
	EndStructure
	
	Structure ACCU_LOCATION_ID
		ID.s
		LocalizedName.s
		EnglishName.s
	EndStructure
	
	Structure ACCU_ADMIN_AREA Extends ACCU_LOCATION_ID
		Level.i
		LocalizedType.s
		EnglishType.s
		CountryID.s
	EndStructure
	
	Structure ACCU_TIMEZONE
		Code.s
		Name.s
		GmtOffset.f
		IsDaylightSaving.s
		NextOffsetChange.s
	EndStructure
	
	Structure ACCU_GEOPOSITION_UNITS
		Metric.ACCU_UNIT
		Imperial.ACCU_UNIT
	EndStructure
	
	Structure ACCU_GEOPOSITION
		Latitude.f
		Longitude.f
		Elevation.ACCU_GEOPOSITION_UNITS
	EndStructure
	
	Structure ACCU_LOCATION
		Version.i
		Key.s
		Type.s
		Rank.i
		LocalizedName.s
		EnglishName.s
		PrimaryPostalCode.s
		Region.ACCU_LOCATION_ID
		Country.ACCU_LOCATION_ID
		AdministrativeArea.ACCU_ADMIN_AREA
		TimeZone.ACCU_TIMEZONE
		GeoPosition.ACCU_GEOPOSITION
		IsAlias.s
		List SupplementalAdminAreas.ACCU_ADMINAREAS()
		List DataSets.s()
	EndStructure
	
	Declare Init(ApiKey$, Language$, UseMetric.i = #True, City$ = "", LocationKey$ = "", UpdateMode = #Update_Daily)
	Declare.s GetLocationKey(City$, RememberIt = #True)
	Declare GetLastDailyResult()
	Declare GetLastHourlyResult(List Forecast_Hourly.ACCU_FORECAST_HOURLY())
	Declare GetLastLocations(List Locations.ACCU_LOCATION())
	Declare GetLastError()
	Declare UpdateForecast(City$ = "")
	
EndDeclareModule

Module AccuWeather
	EnableExplicit
	
	CompilerIf #PB_Compiler_Version < 600
		InitNetwork()
	CompilerEndIf
	
	#ACCU_FORECAST_API_URL = "http://dataservice.accuweather.com/forecasts/v1/"
	#ACCU_LOCATION_API_URL = "http://dataservice.accuweather.com/locations/v1/"
	
	EnumerationBinary
		#Daily_Forecast_Loaded
		#Hourly_Forecast_Loaded
		#Locations_Loaded
	EndEnumeration
	
	Structure _GLOBALS_
		ApiKey$
		LocationKey$
		City$
		UseMetric.i
		UpdateMode.i
		Lang$
		LastError.i
		State.i
		List Locations.ACCU_LOCATION()
		List ForeCastHourly.ACCU_FORECAST_HOURLY()
		ForeCast.ACCU_FORECAST_DAILY
	EndStructure
	
	Global GL._GLOBALS_
	
	Procedure Init(ApiKey$, Language$, UseMetric.i = #True, City$ = "", LocationKey$ = "", UpdateMode = #Update_Daily)
		GL\State        = 0
		GL\ApiKey$      = ApiKey$
		GL\LocationKey$ = locationKey$
		GL\City$        = City$
		GL\UseMetric    = UseMetric
		GL\UpdateMode   = UpdateMode
		GL\Lang$        = Language$
	EndProcedure
	
	Procedure CheckRequestResult(Handle.i)
		Protected Result
		
		If Handle = 0
			GL\LastError = #Error_Not_Reachable
			ProcedureReturn 0
		EndIf
		
		Select Val(HTTPInfo(Handle, #PB_HTTP_StatusCode))
			Case 200
				GL\LastError = #Error_None
				Result       = #True
			Case 400
				GL\LastError = #Error_Bad_Syntax
			Case 401
				GL\LastError = #Error_API_Authorization_Failed
			Case 403
				GL\LastError = #Error_Permission_Endpoint_Denied
			Case 404
				GL\LastError = #Error_No_Route_To_URL
			Case 500
				GL\LastError = #Error_Unexpected
		EndSelect
		
		ProcedureReturn Result
	EndProcedure
	
	Procedure GetLastError()
		ProcedureReturn GL\LastError
	EndProcedure
	
	Procedure.s GetLocationKey(City$, RememberIt = #True)
		Protected cityQuery$, Result$, Response$, httpRequest.i, locationURL$, JSON
		
		cityQuery$   = ReplaceString(URLEncoder(city$), ",", "%2C")
		locationURL$ = #ACCU_LOCATION_API_URL + "cities/search?q=" + LCase(cityQuery$) + "&apikey=" + GL\ApiKey$
		If GL\Lang$
			locationURL$ + "&language=" + GL\Lang$
		EndIf
		httpRequest = HTTPRequest(#PB_HTTP_Get, locationURL$)
		
		If CheckRequestResult(httpRequest)
			Response$ = HTTPInfo(httpRequest, #PB_HTTP_Response, #PB_UTF8)
			JSON      = ParseJSON(#PB_Any, Response$)
			If JSON = 0
				GL\LastError = #Error_JSON_Parsing_Failed
				Debug Response$
				Debug JSONErrorMessage()
			Else
				ExtractJSONList(JSONValue(JSON), GL\Locations())
				If FirstElement(GL\Locations())
					Result$ = GL\Locations()\Key
					GL\State | #Locations_Loaded
					If RememberIt
						GL\LocationKey$ = Result$
					EndIf
				Else
					GL\LastError = #Error_No_LocationKey_Found
				EndIf
				FreeJSON(JSON)
			EndIf
		EndIf
		
		ProcedureReturn Result$
	EndProcedure
	
	Procedure GetLastDailyResult()
		Protected *Result
		
		If GL\State & #Daily_Forecast_Loaded
			*Result = AllocateStructure(ACCU_FORECAST_DAILY)
			If *Result
				CopyStructure(@GL\ForeCast, *Result, ACCU_FORECAST_DAILY)
			EndIf
		EndIf
		
		ProcedureReturn *Result
	EndProcedure
	
	Procedure GetLastHourlyResult(List Forecast_Hourly.ACCU_FORECAST_HOURLY())
		Protected Result
		
		If GL\State & #Hourly_Forecast_Loaded
			Result = CopyList(GL\ForeCastHourly(), Forecast_Hourly())
		EndIf
		
		ProcedureReturn Result
	EndProcedure
	
	Procedure GetLastLocations(List Locations.ACCU_LOCATION())
		Protected Result

		If GL\State & #Locations_Loaded
			Result = CopyList(GL\Locations(), Locations())
		EndIf
		
		ProcedureReturn Result
	EndProcedure
	
	Procedure UpdateForecast(City$ = "")
		Protected weatherURL$, Response$, httpRequest.i, JSON, Hourly, State
		
		If City$ = ""
			City$ = GL\City$
		EndIf
		If GL\LocationKey$ = ""
			GL\LocationKey$ = GetLocationKey(City$)
		EndIf
		If GL\LocationKey$ = ""
			GL\LastError = #Error_No_LocationKey_Found
			ProcedureReturn
		EndIf
		
		weatherURL$ = #ACCU_FORECAST_API_URL
		Select GL\UpdateMode
			Case #Update_5Days
				weatherURL$ + "daily/5day/"
				State = #Daily_Forecast_Loaded
			Case #Update_12Hours
				weatherURL$ + "hourly/12hour/"
				Hourly = #True
				State  = #Hourly_Forecast_Loaded
			Default
				weatherURL$ + "daily/1day/"
				State = #Daily_Forecast_Loaded
		EndSelect
		
		weatherURL$ + GL\LocationKey$ + "?apikey=" + GL\ApiKey$
		If GL\UseMetric
			weatherURL$ + "&metric=true"
		EndIf
		If GL\Lang$
			weatherURL$ + "&language=" + GL\Lang$
		EndIf
		
		httpRequest = HTTPRequest(#PB_HTTP_Get, weatherURL$)
		If CheckRequestResult(httpRequest)
			Response$ = HTTPInfo(httpRequest, #PB_HTTP_Response)
			JSON      = ParseJSON(#PB_Any, Response$)
			If JSON = 0
				GL\LastError = #Error_JSON_Parsing_Failed
				Debug Response$
				Debug JSONErrorMessage()
			Else
				GL\State | State
				If Hourly
					ExtractJSONList(JSONValue(JSON), GL\ForeCastHourly())
				Else
					ExtractJSONStructure(JSONValue(JSON), @GL\ForeCast, ACCU_FORECAST_DAILY)
				EndIf
				FreeJSON(JSON)
			EndIf
		EndIf
		
		ProcedureReturn JSON
	EndProcedure
	
EndModule

CompilerIf #PB_Compiler_IsMainFile
	;example
	
	CompilerIf #PB_Compiler_Version > 609
		UseDialogWebGadget()
	CompilerEndIf
	
	Runtime Enumeration Gadgets
		#frame_1
		#text_4
		#string_api_key
		#text_1
		#string_city
		#text_2
		#combo_lang
		#check_metric
		#text_3
		#combo_update_time
		#button_load
		#frame_2
		#web
	EndEnumeration
	
	Procedure.s GetXMLString()
		Protected XML$
		
		XML$ + "<?xml version='1.0' encoding='UTF-16'?>"
		XML$ + ""
		XML$ + "<dialogs><!--Created by Dialog Design0R V1.87 => get it from: https://hex0rs.coderbu.de/en/sdm_downloads/dialogdesign0r/-->"
		XML$ + "  <window flags='#PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_ScreenCentered' text='AccuWeather Example' name='window_main' xpos='94' ypos='142'>"
		XML$ + "    <vbox expand='item:2'>"
		XML$ + "      <frame text='Settings' id='#frame_1'>"
		XML$ + "        <vbox>"
		XML$ + "          <hbox expand='item:2'>"
		XML$ + "            <text text='API key:' id='#text_4'/>"
		XML$ + "            <string id='#string_api_key' onchange='OnChange_ApiKey()'/>"
		XML$ + "          </hbox>"
		XML$ + "          <hbox>"
		XML$ + "            <text text='Country,City:' id='#text_1'/>"
		XML$ + "            <string width='100' id='#string_city'/>"
		XML$ + "            <text text='Language:' id='#text_2'/>"
		XML$ + "            <combobox width='80' id='#combo_lang'/>"
		XML$ + "            <checkbox text='Metric units' id='#check_metric'/>"
		XML$ + "            <text text='Update Time:' id='#text_3'/>"
		XML$ + "            <combobox width='100' id='#combo_update_time'/> "
		XML$ + "          </hbox>"
		XML$ + "          <singlebox expand='no' align='center'>"
		XML$ + "            <button text='Load' id='#button_load' onevent='OnClick_Load()'/> "
		XML$ + "          </singlebox>"
		XML$ + "        </vbox> "
		XML$ + "      </frame>"
		XML$ + "      <frame text='Weather Details:' id='#frame_2'>"
		XML$ + "        <web height='200' id='#web'/>"
		XML$ + "      </frame>"
		XML$ + "    </vbox> "
		XML$ + "  </window>"
		XML$ + "</dialogs><!--DDesign0R Definition: PureBasic|1|1|1|__|example_with_declares|1-->"
		
		ProcedureReturn XML$
	EndProcedure
	
	Procedure.s GetErrorText(Error)
		Protected Result$
		
		Select Error
			Case AccuWeather::#Error_Bad_Syntax
				Result$ = "Syntax Error!"
			Case AccuWeather::#Error_API_Authorization_Failed
				Result$ = "API Authorization failed!"
			Case AccuWeather::#Error_Permission_Endpoint_Denied
				Result$ = "Permission Denied!"
			Case AccuWeather::#Error_No_Route_To_URL
				Result$ = "URL not available!"
			Case AccuWeather::#Error_Unexpected
				Result$ = "Unexpected Error!"
			Case AccuWeather::#Error_Not_Reachable
				Result$ = "AccuWeather not reachable!"
			Case AccuWeather::#Error_No_LocationKey_Found
				Result$ = "No Location key found!"
			Case AccuWeather::#Error_JSON_Parsing_Failed
				Result$ = "JSON Parsing failed!"
		EndSelect
		
		ProcedureReturn Result$
	EndProcedure
	
	Procedure.q GetDate(d$)
		Protected Result.q
		
		d$ = Left(d$, Len(d$) - 6)  ;ignore timezone at end
		Result = ParseDate("%yyyy-%mm-%ddT%hh:%ii:%ss", d$)
		
		ProcedureReturn Result
	EndProcedure
	
	Procedure OutputResult(Mode)
		Protected HTML$, Result, d.q
		Protected *DailyForecast.AccuWeather::ACCU_FORECAST_DAILY
		NewList Location.AccuWeather::ACCU_LOCATION()
		NewList HourlyForecast.AccuWeather::ACCU_FORECAST_HOURLY()
		
		HTML$ = "<html><head><meta charset='UTF-8'></head><body style='background-color:powderblue;'><table border='1' cellspacing='10'><tr>"
		If AccuWeather::GetLastLocations(Location())
			If Mode = AccuWeather::#Update_12Hours
				Result = AccuWeather::GetLastHourlyResult(HourlyForecast())
			Else
				*DailyForecast = AccuWeather::GetLastDailyResult()
				Result         = *DailyForecast
			EndIf
		EndIf
		
		If Result = 0
			HTML$ + "<td>" + GetErrorText(AccuWeather::GetLastError()) + "</td>"
		Else
			If Mode = AccuWeather::#Update_12Hours
				HTML$ + "<td colspan='" + Str(ListSize(HourlyForecast())) + "'>Weather in " + Location()\Country\LocalizedName + ", " + Location()\LocalizedName + " (" + Location()\PrimaryPostalCode + ")</td></tr><tr>"
				With HourlyForecast()
					ForEach HourlyForecast()
						d = GetDate(\DateTime)
						HTML$ + "<td><img src='https://coderbu.de/accuicons/" + RSet(Str(\WeatherIcon), 2, "0") + "-s.png'><br>"
						HTML$ + FormatDate("%hh:%ii", d) + "<br>"
						HTML$ + "Temp: " + StrF(\Temperature\Value, 1) + \Temperature\Unit + "<br></td>"
					Next
				EndWith
			Else
				HTML$ + "<td colspan='" + Str(ListSize(*DailyForecast\DailyForecasts())) + "'>Weather in " + Location()\Country\LocalizedName + ", " + Location()\LocalizedName + 
				        " (" + Location()\PrimaryPostalCode + ")<br>Info: " + *DailyForecast\Headline\Text + "</td></tr><tr>"
				
				With *DailyForecast\DailyForecasts()
					ForEach *DailyForecast\DailyForecasts()
						d = GetDate(\Date)
						HTML$ + "<td>" + FormatDate("%dd.%mm %hh:%ii", d) + "<br>"
						HTML$ + "<hr>Day:<br><img src='https://coderbu.de/accuicons/" + RSet(Str(\Day\Icon), 2, "0") + "-s.png'><br>"
						HTML$ + \Day\IconPhrase + "<br>"
						HTML$ + "<hr>Night:<br><img src='https://coderbu.de/accuicons/" + RSet(Str(\Night\Icon), 2, "0") + "-s.png'><br>"
						HTML$ + \Night\IconPhrase + "<br><hr>"
						HTML$ + "Temp. min: " + StrF(\Temperature\Minimum\Value, 1) + \Temperature\Minimum\Unit + "<br>"
						HTML$ + "Temp. max: " + StrF(\Temperature\Maximum\Value, 1) + \Temperature\Maximum\Unit + "<br></td>"
					Next
				EndWith
			EndIf
		EndIf
		
		HTML$ + "</tr></body></html>"
		SetGadgetItemText(#web, #PB_Web_HtmlCode, HTML$)
		If *DailyForecast
			FreeStructure(*DailyForecast)
		EndIf
		
	EndProcedure
	
	Runtime Procedure OnChange_ApiKey()
		If GetGadgetText(#string_api_key)
			DisableGadget(#button_load, 0)
		Else
			DisableGadget(#button_load, 1)
		EndIf
	EndProcedure
	
	Runtime Procedure OnClick_Load()
		Protected i, Mode
		
		i    = GetGadgetState(#combo_update_time)
		Mode = GetGadgetItemData(#combo_update_time, i)
		AccuWeather::Init(GetGadgetText(#string_api_key), GetGadgetText(#combo_lang), GetGadgetState(#check_metric), GetGadgetText(#string_city), "", Mode)
		AccuWeather::UpdateForecast()
		OutputResult(Mode)
	EndProcedure
	
	
	Procedure main()
		Protected a$
		
		a$ = GetXMLString()
		ParseXML(0, a$)
		CreateDialog(0)
		OpenXMLDialog(0, 0, "window_main")
		AddGadgetItem(#combo_lang, - 1, "en-us")
		AddGadgetItem(#combo_lang, - 1, "de-de")
		AddGadgetItem(#combo_update_time, - 1, "one Day") : SetGadgetItemData(#combo_update_time, 0, AccuWeather::#Update_Daily)
		AddGadgetItem(#combo_update_time, - 1, "five Days") : SetGadgetItemData(#combo_update_time, 1, AccuWeather::#Update_5Days)
		AddGadgetItem(#combo_update_time, - 1, "12 hours") : SetGadgetItemData(#combo_update_time, 2, AccuWeather::#Update_12Hours)
		SetGadgetState(#combo_lang, 0)
		SetGadgetState(#combo_update_time, 0)
		SetGadgetText(#string_city, "Germany,Munich")
		SetGadgetState(#check_metric, 1)
		DisableGadget(#button_load, 1)
		
		Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
		
	EndProcedure
	
	main()
CompilerEndIf

Re: PureBasic and AccuWeather APIs - A LIVE Working Example

Posted: Wed Aug 13, 2025 6:34 pm
by TI-994A
HeX0R wrote: Wed Aug 13, 2025 3:33 pmI hope you don't mind TI-994A, but I played a little bit with your code.
Not at all, @HeX0R. Free to use, improve, share. :lol:

Great job, btw. It looks like quite a formidable upgrade.

Re: PureBasic and AccuWeather APIs - A LIVE Working Example

Posted: Thu Aug 14, 2025 7:35 pm
by Sofos
Hello Syed (@TI-994A). I wanted to post an update on my progress, as requested.

Your code works seamlessly on the demo version of PureBasic, but more importantly, it clearly demonstrates the concepts that I need to interact with the AccuWeather APIs. Amazing built-in JSON functions in PureBasic, by the way.

Thank you for the clean code, verbose naming conventions, and most of all, for keeping it simple. I truly appreciate your help.


Sofia

Re: PureBasic and AccuWeather APIs - A LIVE Working Example

Posted: Fri Aug 15, 2025 7:17 pm
by TI-994A
Sofos wrote: Thu Aug 14, 2025 7:35 pmYour code works seamlessly on the demo version of PureBasic, but more importantly, it clearly demonstrates the concepts that I need to interact with the AccuWeather APIs. ...

Thank you for the clean code, verbose naming conventions, and most of all, for keeping it simple. I truly appreciate your help.

My absolute pleasure. I'm glad to hear it. :D