Weather Underground Integration with Salesforce

Recently at Cloudjedi, we did a Weather Underground integration with Salesforce to regularly forecast temperatures for every city to enable our customer to predict demand for their popsicle business. The idea was to query Weather Underground API using a scheduled Apex batch and save the temperature forecast to the database pertaining to each city.  

You can browse the Weather Underground API from their website. But here's a developer guide on how we did the Weather Underground integration.

The Salesforce schema contained a Climate custom object. Each Climate object pertains to a city, and saves the weather forecast for the next 10 days.

 

1. Create a new Climate object and save the correct Weather Underground query string in the Climate record. Use an object field Weather_Underground_Query__c to save this query string. The following is an example of a well-formed HTTP request that queries Weather Underground for the weather condition of today and the next nine days:

http://api.wunderground.com/api/<API_Key>/forecast10day/q/CA/San_Francisco.json

The query string can come in many formats:

- StateName/CityName - CountryName/CityName - CityName - CountryISOCode/CityName

The desired response format should be appended at the end: “.json” is what we used. The Weather Underground API also provides unique city codes that can be used for querying. For example, to query a forecast for New York, the HTTP request should include the New York zmw code "zmw:31003.1.99999":

http://api.wunderground.com/api/<API_Key>/forecast/q/zmw:31003.1.99999.json

Ambigous queries return an array of possible cities from which the correct query can be selected. If a city is ambiguous, manually make sure that the query works before running the Apex job. For example if "Argentina/BuenosAires.json" doesn’t return the correct JSON response, try requesting for "BuenosAires.json" or "AR/BuenosAires" Save the working query in Weather_Underground_Query__c

 

2. Create an API class that interacts with Weather Underground and parses the HTTP Response data. We named it WundergroundAPI. It has the following methods:

public static WundergroundAPI callout(String path){
  HttpRequest req = new HttpRequest();
  req.setEndpoint('http://api.wunderground.com/api/' + wundergroundAPIKey + path);
  req.setMethod('GET');
  Http http = new Http();
  HttpResponse res = new HttpResponse();
  res = http.send(req);
  return WundergroundAPI.parse(res.getBody());
}
 

Since we used JSON, one of the benefits is that we can easily generate using JSON2Apex the corresponding Apex class components needed to parse the response using System.JSON.deserialize().

public static WundergroundAPI parse(String jsonString) {
  return (WundergroundAPI) System.JSON.deserialize(jsonString, WundergroundAPI.class); }

 

3. Create a Schedulable Batchable Apex class that schedules its own batch job daily.

In implementing the execute method of the Batchable interface, use the WundergroundAPI.callout() method to generate a response class. In this case the JSON2Apex deserialization created a WundergroundAPI object that has the JSON data in its attributes. Use this WundergroundAPI object to navigate through the response class properties to get the max temperatures for each day of the forecast period.

public void execute(Database.BatchableContext BC, List<Climates__c> climates){
  for (Climate__c climate : climates) {
    String cityAPICode = clima.Weather_Underground_Query__c;
    String fullRequestPath = formatURLPath('forecast10day',cityAPICode);
    WundergroundAPI calloutResponse = WundergroundAPI.callout(fullRequestPath);
    List<WundergroundAPI.Forecastday_Z> forecasts = calloutResponse.forecast.simpleforecast.forecastday;
    climate.TEMP_max_today__c = forecasts[0].high.celsius;
    climate.TEMP_max_tomorrow__c = forecasts[1].high.celsius;
    climate.TEMP_max_in_2_days__c = forecasts[2].high.celsius;
    climate.TEMP_max_in_3_days__c = forecasts[3].high.celsius;
    climate.TEMP_max_in_4_days__c = forecasts[4].high.celsius;
    climate.TEMP_max_in_5_days__c = forecasts[5].high.celsius;
    climate.TEMP_max_in_6_days__c = forecasts[6].high.celsius;
    climate.TEMP_max_in_7_days__c = forecasts[7].high.celsius;
    climate.TEMP_max_in_8_days__c = forecasts[8].high.celsius;
}
private static String formatURLPath(String feature, String query){
  return '/' + feature + '/q/' + query + '.json';
}
 

Do not forget to schedule the execute method for the Schedulable interface. public void execute(SchedulableContext sc){
  String actualQuery = 'SELECT Id, Name, Weather_Underground_Query__c FROM Climate__c)';
  WeatherForecastBatch batch = new WeatherForecastBatch  (actualQuery);
  Database.executeBatch(batch, 200);
}