Get-Weather cmdlet for PowerShell, using the OpenWeatherMap API

From Svendsen Tech PowerShell Wiki
Jump to: navigation, search

This Get-Weather cmdlet, called Get-OWMWeather, uses the OpenWeatherMap API v2.5 from PowerShell - using an API key, or not - the former recently introduced as a highly recommended practice as per 2013-04-12. As per 2014-06-22, this works, but they broke v2.1 of the API, which is why I had to review and update the module.

NB! This module requires PowerShell version 3 or later to use the Invoke-RestMethod cmdlet.

You can get an OpenWeatherMap API key / app ID here from OpenWeatherMap.org. The module should work a limited number of times without an API key, if you specify the -NoAPIKey parameter to Get-OWMWeather.

The module featured in this article uses version 2.5 of the OpenWeatherMap API, and supports an API key.

Get-Weather-OpenWeatherMap-PowerShell-Example3.png



Download

  • OpenWeatherMap.zip - Contains a folder called "OpenWeatherMap" with the module script and module manifest files.

2014-06-22: Updated for v2.5 of the OpenWeatherMap API after they broke v2.1 (thanks).

Tip! To install a PowerShell module, check "$env:PSModulePath -split ';' ", and create the necessary directory structure if needed, then copy the OpenWeatherMap folder into a suited Modules folder, and finally import it with "Import-Module OpenWeatherMap". You can also just put it somewhere and import it with "Import-Module X:\foo\OpenWeatherMap".

Exported Functions/Cmdlets

These are the exported functions and cmdlets in the OpenWeatherMap module.

Get-OWMWeather Gets weather data. See examples below.
Get-OWMCache Displays the cached data. See examples below.
Get-OWMCacheMinutes Displays the current value for the number of minutes the weather data is cached. Default: 60 minutes.
Set-OWMCacheMinutes Set the number of minutes the weather data is cached. Minimum 10. Maximum is the max int32 size on your system. The default is 20 minutes.

Get-OWMWeather

Here's a description of the parameters for the Get-OWMWeather cmdlet, and some examples.

Get-OWMWeather Parameters

City Required. Can be piped to the cmdlet. City name/string to get weather data for.
Object Optional. Default is to return a formatted weather string with what I pseudo-randomly considered to be the most relevant data. If you specify this, you get the returned JSON represented as a custom PowerShell object, and errors will be represented in custom PS objects with an "Error" property (default is strings).
NoAPIKey Optional. Default is to prompt for a $Global:OWMAPIKey variable to be set unless you specify this parameter.

Introductory Get-OWMWeather Example

This is without an API key.

PS C:\> Import-Module OpenWeatherMap

PS D:\> 'London, UK' | Get-OWMWeather -NoAPIKey
Place: London. Temperature 14.55C / 58.19F. Humidity: 67 %. Pressure: 1023 hpa.
Date: 2014-06-23 02:30:01. Country: GB.
Wind speed: 1 m/s. Weather: broken clouds.
Sunrise: 2014-06-23 03:43:42. Sunset: 2014-06-23 20:21:56

If you don't specify -NoAPIKey, you will get a message saying this:

PS C:\> 'Oslo' | Get-OWMWeather
You need to set the global variable $Global:OWMAPIKey or to try with -NoAPIKey.

There's a caching of the data (by default for 20 minutes).

Get-OWMWeather Example With A Full Object Returned

Here I get a full object back. The data is now cached from the previous retrieval, but the city name will be translated to an ID with a repeated web request. I considered caching the city strings to ID data as well.

PS D:\> 'London, UK' | Get-OWMWeather -Object

coord   : @{lon=-0.13; lat=51.51}
sys     : @{message=0.013; country=GB; sunrise=1403408603; sunset=1403468509}
weather : {@{id=800; main=Clear; description=sky is clear; icon=02n}}
base    : cmc stations
main    : @{temp=286.73; humidity=86; pressure=1019; temp_min=284.82; temp_max=288.71}
wind    : @{speed=1.26; deg=39.5024}
rain    : @{3h=0}
clouds  : @{all=8}
dt      : 1403405702
id      : 2643743
name    : London
cod     : 200

Get-OWMCache

The data that has been retrieved is cached. Repeated attempts to retrieve the data will result in data from the cache being used instead for 20 minutes by default (you can change the number of minutes with Set-OWMCacheMinutes). After 20 minutes, a repeated, already-cached, city ID look-up will cause new data to be retrieved. The Get-OWMWeather string result (unless using -Object) will say that the data is cached, and how many minutes remain before new data will be fetched, on repeated queries.

Note that the city search string to ID conversion will be done via a repeated web request (this isn't cached).

The cache is a hash indexed by city IDs as keys, and each value is a custom PSObject that has three properties:

  • WeatherObject: The full returned JSON object, represented as a custom PSObject.
  • WeatherString: The pre-formatted string.
  • Date: A datetime object that represents when the data was collected.

Here's an example where I look at the single entry in the cache, and get its WeatherObject value.

PS C:\> Get-OWMCache | Select -Exp Value | Select -Exp WeatherObject

id      : 3143244
coord   : @{lat=59.912731; lon=10.74609}
name    : Oslo
main    : @{temp=2.25; humidity=70; pressure=1006; temp_min=0.56; temp_max=4.44}
dt      : 1365791996
date    : 2013-04-12 18:39:56
wind    : @{speed=0; gust=0; deg=0}
rain    : @{1h=0.42}
clouds  : @{all=92}
weather : {@{id=500; main=Rain; description=light rain; icon=10n}}
sys     : @{country=NO}
url     : http://openweathermap.org/city/3143244
cod     : 200

Here I get the weather string for the first city in the cache:

PS E:\> Get-OWMCache | Select -First 1 -Exp Value | Select -Exp WeatherString
Place: Lillehammer. Temperature 5.00C / 41.00F. Humidity: 86 %. Pressure: 1002 hpa.
Date: 2013-04-12 18:50:00. Country: NO. Wind speed: 10.8 m/s.
Weather: overcast clouds. URL: http://openweathermap.org/city/3147474

Source Code

#requires -version 3
Set-StrictMode -Version Latest

# Set the default cache time to 20 minutes. Less than 10 minutes is against their wishes,
# so I prevent that in Set-CacheMinutes (almost impossible to crack this!).
$script:CacheMinutes = 20

# Cache hash, indexed on city IDs (presumably they are unique).
$script:Cache = @{}

# Hard-coded URI bases
$script:CitySearchUri = 'http://api.openweathermap.org/data/2.5/weather?q=' #"http://api.openweathermap.org/data/2.1/find/name?q="
#$script:CityInfoUriBase = 'http://api.openweathermap.org/data/2.1/weather/city/'

function Get-OWMCacheMinutes {
    $script:CacheMinutes
}

function Set-OWMCacheMinutes {
    
    param([Parameter(Mandatory)][int] $Minutes)
    
    if ($Minutes -lt 10) {
        throw "Minutes value needs to be 10 or higher."
    }
    
    $script:CacheMinutes = $Minutes
    
}

function Get-OWMCache {
    
    if (-not $script:Cache.Count) { "Cache is empty."; return }
    $script:Cache.GetEnumerator() | Sort Name

}

function Write-MyError {
    
    param(
        [Parameter(Mandatory)][string] $Output,
        [switch] $Object
    )

    if ($Object) {
        New-Object PSObject -Property @{ Error = $Output }
    }
    else {
        $Output
    }

}

<#
.SYNOPSIS
Svendsen Tech's OpenWeatherMap module.
This Get-OWMWeather gets weather data for a city string using the OpenWeatherMap API v2.5.

Date: 2014-06-22
Copyright (c) 2013, Svendsen Tech. All rights reserved.

.DESCRIPTION
The Get-OWMWeather cmdlet gets weather data for a city string using the OpenWeatherMap API v2.5.

.EXAMPLE
PS D:\PS> Get-OWMWeather -City 'london,uk' -Object

coord   : @{lon=-0.13; lat=51.51}
sys     : @{message=0.2092; country=GB; sunrise=1403408603; sunset=1403468509}
weather : {@{id=802; main=Clouds; description=scattered clouds; icon=03n}}
base    : cmc stations
main    : @{temp=287.76; pressure=1010; temp_min=285.93; temp_max=289.82; humidity=75}
wind    : @{speed=1.81; deg=0.00396729}
clouds  : @{all=36}
dt      : 1403399701
id      : 2643743
name    : London
cod     : 200

.EXAMPLE
PS D:\PowerShell> Get-OWMWeather -City 'london,uk'
Place: London. Temperature 14.61C / 58.30F. Humidity: 75 %. Pressure: 1010 hpa. Date: 2014-06-22 01:15:01. Country: GB.
 Wind speed: 1.81 m/s. Weather: scattered clouds. Sunrise: 2014-06-22 03:43:23. Sunset: 2014-06-22 20:21:49

PS D:\PowerShell> 'london,uk' | Get-OWMWeather
Place: London. Temperature 14.61C / 58.30F. Humidity: 75 %. Pressure: 1010 hpa. Date: 2014-06-22 01:15:01. Country: GB.
 Wind speed: 1.81 m/s. Weather: scattered clouds. Sunrise: 2014-06-22 03:43:23. Sunset: 2014-06-22 20:21:49 (cached; wi
ll refresh in 20 minutes)

.PARAMETER City
Required. City search string.
.PARAMETER Object
Optional. Returns the full JSON object rather than a formatted weather string.
.PARAMETER NoAPIKey
Optional. Makes the module not complain about a missing API key. The API is currently
"somewhat available" without an API key.
#>

function Get-OWMWeather {
    
    [CmdletBinding()]
    param(
        [Parameter(Mandatory,ValueFromPipeline)][string[]] $City,
        [switch] $Object,
        [switch] $NoAPIKey
    )
    
    if (-not (Get-Variable -Scope Global -Name OWMAPIKey -EA SilentlyContinue) -and -not $NoAPIKey) {
        throw "You need to set the global variable `$Global:OWMAPIKey or to try with -NoAPIKey."
    }
    
    #Add-Type -AssemblyName System.Web # added to manifest file
    
    foreach ($IterCity in $City) {
        
        $Uri = $script:CitySearchUri + [Web.HttpUtility]::UrlEncode($IterCity) + $(if ($NoAPIKey) { '' } else { "&APPID=$global:OWMAPIKey" })
    
        try {
            $t = Invoke-RestMethod -Uri $Uri
        }
        catch {
            Write-MyError -Output "Unable to execute Invoke-RestMethod to retrieve ID(s) for ${City}: $_" -Object:$Object
            return
        }
        
        if (($t | Get-Member -Name Message -EA 0) -and $t.Message -ilike '*Not found*') {
            Write-MyError -output "Did not find $IterCity." -Object:$Object
            return
        }
        
        if ($script:Cache.ContainsKey($IterCity)) {
            if (((Get-Date) - $script:Cache.$IterCity.Date).TotalMinutes -lt $script:CacheMinutes ) {
                $RefreshMins = [int]($script:CacheMinutes - ((Get-Date) - $script:Cache.$IterCity.Date).TotalMinutes)
                if ($Object) {
                    $script:Cache.$IterCity.WeatherObject
                }
                else {
                    $script:Cache.$IterCity.WeatherString + " (cached; will refresh in $RefreshMins minutes)"
                }
                continue
            }
        }
        
        # Get both C and F for temperatures
        if ($Kelvin = ($t.main.temp)) {
            $CelsiusTemp = $Kelvin - 273.15
            $FahrenheitTemp = $CelsiusTemp * 9/5 + 32
        }
        else {
            $CelsiusTemp = 'Unknown '
            $FahrenheitTemp = 'Unknown '
        }
        
        $IterCityData = @(
            $t.name,
            $CelsiusTemp,
            $FahrenheitTemp,
            $t.main.humidity,
            $t.main.pressure,
            (Get-Date -Date '1970-01-01T00:00:00').AddSeconds($t.dt).ToString('yyyy-MM-dd HH:mm:ss'),
            $t.sys.country,
            $t.wind.speed,
            $t.weather.description,
            (Get-Date -Date '1970-01-01T00:00:00').AddSeconds($t.sys.sunrise).ToString('yyyy-MM-dd HH:mm:ss'),
            (Get-Date -Date '1970-01-01T00:00:00').AddSeconds($t.sys.sunset).ToString('yyyy-MM-dd HH:mm:ss')
            #$(if ($t = ($IterCityJson | Select -EA SilentlyContinue -Exp url)) { $t } else { 'Unknown' })
        )
        
        if ($Object) {
            $t
        }
        else {
            "Place: {0}. Temperature {1:N2}C / {2:N2}F. Humidity: {3} %. Pressure: {4} hpa. Date: {5}. Country: {6}. Wind speed: {7} m/s. Weather: {8}. Sunrise: {9}. Sunset: {10}" -f $IterCityData
        }
        # Store data in cache
        $Script:Cache.$IterCity = New-Object PSObject -Property @{
            'WeatherObject' = $t
            'WeatherString' = "Place: {0}. Temperature {1:N2}C / {2:N2}F. Humidity: {3} %. Pressure: {4} hpa. Date: {5}. Country: {6}. Wind speed: {7} m/s. Weather: {8}. Sunrise: {9}. Sunset: {10}" -f $IterCityData
            'Date' = Get-Date
        }
            
        #Start-Sleep -Seconds 3

    } # end of foreach $IterCity

}

#$Global:OWMAPIKey = ''

Export-ModuleMember -Function Get-OWMCacheMinutes, Set-OWMCacheMinutes, Get-OWMWeather, Get-OWMCache