Get-Weather cmdlet for PowerShell, using the OpenWeatherMap API
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.
Contents
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