Yesterday I posted a popular article about using Invoke-WebRequest to get weather conditions. That function used the Yahoo web site but really only worked for US cities. So I also cleaned up and revised another set of advanced PowerShell functions (required PowerShell 3) that can retrieve weather information for probably any location on Earth. The first piece of information you need is your WOEID, or "Where On Earth ID". Here is a function to do just that.
ManageEngine ADManager Plus - Download Free Trial
Exclusive offer on ADManager Plus for US and UK regions. Claim now!
Function Get-Woeid { <# .Synopsis Retrieve WOEID information .Description Search Yahoo's Geo Places for a WOEID (Where On Earth ID). You can search based on a postal code, or place name. Optionally, you can save the XML results to the pipeline. .Parameter Search A search parameter such as a postal or zip code. You can also use a place name. .Parameter AsXML Write the XML document to the pipeline. .Example PS C:\> get-woeid 89101 Country : US Region : Nevada Postal : 89101 Locale : Las Vegas WOEID : 12795439 .Example PS C:\> get-woeid -search "H1A0A1" Region : Quebec Postal : H1A 0A1 WOEID : 55963829 Locale : Montreal Country : CA .Example PS C:\> get-woeid "Helsinki, Finland" Country : FI Region : Uudenmaan maakunta Postal : Locale : Helsinki WOEID : 565346 .Example PS C:\> get-woeid "Helsinki, Finland" | get-weather -unit C Date : Mon, 16 Feb 2015 4:49 pm EET Location : Helsinki, Finland Temperature : -2 C Condition : Fair ForecastCondition : Clear ForecastLow : -6 C ForecastHigh : -2 C .Example PS C:\> [xml]$doc = get-woeid "omaha, ne" -AsXML PS C:\> $doc.Save("c:\work\omahaid.xml") The first command gets the raw XML document and the second command writes it to disk. Or you can explore the document interactively. PS C:\> $doc.query.results.place.timezone type woeid #text ---- ----- ----- Time Zone 56043661 America/Chicago .Inputs strings .Outputs custom object .Link Invoke-RestMethod .Notes NAME: Get-Woeid VERSION: 3.0 AUTHOR: Jeffery Hicks LASTEDIT: February 16, 2015 Learn more about PowerShell:Essential PowerShell Learning Resources#> Param( [parameter(Position=0,Mandatory,ValueFromPipeline, HelpMessage="Enter a place name or postal (zip) code.")] [string[]]$Search, [switch]$AsXML ) Begin { Write-Verbose "Starting $($myinvocation.mycommand)" } Process { foreach ($item in $search) { Write-Verbose "Querying for $search" $uri = "https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20geo.places%20where%20text%3D'$ITEM'%20limit%201" Write-Verbose $uri [xml]$xml= Invoke-RestMethod -Uri $uri if ($AsXML) { Write-Verbose "Writing XML document" $xml Write-Verbose "Ending function" #bail out since we're done Return } if ($xml.query.results.place.woeid) { Write-Verbose "Parsing XML into an ordered hashtable" $hash = [ordered]@{ WOEID = $xml.query.results.place.woeid Locale = $xml.query.results.place.locality1.'#text' Region = $xml.query.results.place.admin1.'#text' Postal = $xml.query.results.place.postal.'#text' Country = $xml.query.results.place.country.code } Write-Verbose "Writing custom object" New-Object -TypeName PSObject -Property $hash } #if $xml else { Write-Warning "Failed to find anything for $item" } } #foreach } #process End { Write-Verbose "Ending $($myinvocation.mycommand)" } } #end function
To use the function you specify some sort of search criteria such as a postal code or city name.
I saved my WOEID as a variable in my PowerShell profile. This function also uses Invoke-RestMethod. I included a parameter to write an XML document to the pipeline instead in case you want to modify the function and need some help discovering the data. The second function, Get-Weather, uses the WOIED to get the current weather conditions. It too uses Invoke-RestMethod to retrieve the data. The resulting XML document is then parsed out using Select-XML to build an output object.
Function Get-Weather { <# .Synopsis Retrieve weather information from Yahoo.com. .Description Using the Yahoo weather APIs, this function will retrieve weather information for the specified WOEID or "Where on Earth ID". If you don't know your WOED, open a web browser and go to http://weather.yahoo.com/. Enter in your zip or postal code. Your WOEID will be the number at the end of the url. Or you can use the Get-Woeid function. A custom object is written to the pipeline and you can control how much information is displayed by specifying Basic, Extended or All for the Detail parameter. Basic properties: Date,Location,Temperature,Condition,ForecastCondition,ForecastLow, and ForecastHigh. This is the default. Extended properties: Basic properties plus WindChill,WindSpeed,Humidity,Barometer,Visibility,and Tomorrow. All properties: Extended properties plus Sunrise,Sunset,City,Region,Zip,Latitude,Longitude,URL .Parameter Woeid Your WOEID, or "Where on Earth ID". .Parameter Unit Show temperature in Farenheit or Celsius .Parameter Online Open the web page in your default browser. .Parameter AsXML Write an XML document to the pipeline. .Example PS C:> get-weather 12795711 Date : Mon, 16 Feb 2015 3:51 am PST Location : Beverly Hills, CA Temperature : 56 F Condition : Fair ForecastCondition : Sunny ForecastLow : 55 F ForecastHigh : 72 F .Example PS C:\> get-weather 12795446 extended Date : Mon, 16 Feb 2015 6:56 am PST Location : Las Vegas, NV Temperature : 52 F Condition : Mostly Cloudy ForecastCondition : Sunny ForecastLow : 49 F ForecastHigh : 77 F WindChill : 52 F WindSpeed : 9 mph Humidity : 35% Barometer : 29.95 in and steady Visibility : 10 mi Tomorrow : Tue 17 Feb 2015 Sunny Low 49F: High: 70F .Example PS C:\> 12795711,12795446,12762736 | Get-Weather -detail all | Format-Table City,Temperature,Condition,Sunrise,Sunset City Temperature Condition Sunrise Sunset ---- ----------- --------- ------- ------ Beverly Hills 56 F Fair 6:37 am 5:38 pm Las Vegas 52 F Mostly Cloudy 6:26 am 5:21 pm Syracuse -3 F Partly Cloudy 7:01 am 5:34 pm .Example PS C:\> get-woeid "Bangor,ME" | get-weather Date : Mon, 16 Feb 2015 9:51 am EST Location : Bangor, ME Temperature : 5 F Condition : Partly Cloudy ForecastCondition : Partly Cloudy/Wind ForecastLow : -6 F ForecastHigh : 12 F .Example PS C:\> get-woeid "Juneau, AK","Miami,FL" | get-weather | Select Date,Location,Temperature Date Location Temperature ---- -------- ----------- Mon, 16 Feb 2015 8:54 am AKST Juneau, AK 35 F Mon, 16 Feb 2015 12:53 pm EST Miami, FL 75 F .Inputs Strings .Outputs Custom object .Link Invoke-RestMethod .Link Get-Woeid Select-XML .Notes NAME: Get-Weather VERSION: 3.0 AUTHOR: Jeffery Hicks LASTEDIT: February 16, 2015 Learn more about PowerShell:Essential PowerShell Learning Resources**************************************************************** * DO NOT USE IN A PRODUCTION ENVIRONMENT UNTIL YOU HAVE TESTED * * THOROUGHLY IN A LAB ENVIRONMENT. USE AT YOUR OWN RISK. IF * * YOU DO NOT UNDERSTAND WHAT THIS SCRIPT DOES OR HOW IT WORKS, * * DO NOT USE IT OUTSIDE OF A SECURE, TEST SETTING. * **************************************************************** #> [cmdletbinding(DefaultParameterSetName="detail")] Param( [Parameter(Position=0,Mandatory,HelpMessage="Enter a WOEID value", ValueFromPipeline, ValueFromPipelineByPropertyName )] [ValidateNotNullorEmpty()] [string[]]$woeid, [Parameter(Position=1,ParameterSetName="detail")] [ValidateSet("Basic","Extended","All")] [String]$Detail="Basic", [ValidateSet("f","c")] [String]$Unit="f", [Parameter(ParameterSetName="online")] [Switch]$Online, [Parameter(ParameterSetName="xml")] [Switch]$AsXML ) Begin { Write-Verbose "Starting $($myinvocation.mycommand)" #define property sets $BasicProp=@("Date","Location","Temperature","Condition","ForecastCondition", "ForecastLow","ForecastHigh") $ExtendedProp=$BasicProp $ExtendedProp+=@("WindChill","WindSpeed","Humidity","Barometer", "Visibility","Tomorrow") $AllProp=$ExtendedProp $AllProp+=@("Sunrise","Sunset","City","Region","Latitude", "Longitude","ID","URL") #unit must be lower case $Unit=$Unit.ToLower() Write-Verbose "Using unit $($unit.toLower())" #define base url string [string]$uribase = "http://weather.yahooapis.com/forecastrss" Write-Verbose "Base = $uribase" } #begin Process { Write-Verbose "Processing" foreach ($id in $woeid) { Write-Verbose "Getting weather info for woeid: $id" #define a uri for the given WOEID [string]$uri = "{0}?w={1}&u={2}" -f $uribase,$id,$unit if ($online) { Write-Verbose "Opening $uri in web browser" Start-Process -FilePath $uri #bail out since there's nothing else to do. Return } Write-Verbose "Downloading $uri" [xml]$xml= Invoke-WebRequest -uri $uri if ($AsXML) { Write-Verbose "Writing XML document" $xml Write-Verbose "Ending function" #bail out since we're done Return } if ($xml.rss.channel.item.Title -eq "City not found") { Write-Warning "Could not find a location for $id"} else { Write-Verbose "Processing xml" #initialize a new hash table $properties=@{} <# get the yweather nodes yweather information comes from a different namespace so we'll use Select-XML to extract the data. Parsing out all data regardless of requested detail since it doesn't take much effort. Later, only the requested detail will be written to the pipeline. #> #define the namespace hash $namespace=@{yweather=$xml.rss.yweather} $units=(Select-Xml -xml $xml -XPath "//yweather:units" -Namespace $namespace ).node $properties.Add("Condition",$xml.rss.channel.item.condition.text) $properties.Add("Temperature","$($xml.rss.channel.item.condition.temp) $($units.temperature)") #convert Date to a [datetime] object $dt = $xml.rss.channel.item.condition.date.Substring(0,$xml.rss.channel.item.condition.Date.LastIndexOf("m")+1) -as [datetime] $properties.Add("Date",$dt) #get forecast $properties.add("ForecastDate",$xml.rss.channel.item.forecast[0].date) $properties.add("ForecastCondition",$xml.rss.channel.item.forecast[0].text ) $properties.Add("ForecastLow","$($xml.rss.channel.item.forecast[0].low) $($units.temperature)" ) $properties.Add("ForecastHigh","$($xml.rss.channel.item.forecast[0].high) $($units.temperature)" ) #build tomorrow's foreacst $t=$xml.rss.channel.item.forecast[1] $tomorrow="{0} {1} {2} Low {3}{4}: High: {5}{6}" -f $t.day,$t.Date,$t.Text,$t.low, $($units.temperature),$t.high, $($units.temperature) $properties.add("Tomorrow",$tomorrow) #get optional information $properties.Add("Latitude",$xml.rss.channel.item.lat) $properties.Add("Longitude",$xml.rss.channel.item.long) $city=$xml.rss.channel.location.city $properties.Add("City",$city) $region=$xml.rss.channel.location.region $country=$xml.rss.channel.location.country if (-not ($region)) { #if no region found then use country Write-Verbose "No region found. Using Country" $region=$country } $properties.Add("Region",$region) $location="{0}, {1}" -f $city,$region $properties.Add("Location",$location) $properties.Add("ID",$id) #get additional yweather information $wind=(Select-Xml -xml $xml -XPath "//yweather:wind" -Namespace $namespace ).node $astronomy=(Select-Xml -xml $xml -XPath "//yweather:astronomy" -Namespace $namespace ).node $atmosphere=(Select-Xml -xml $xml -XPath "//yweather:atmosphere" -Namespace $namespace ).node $properties.Add("WindChill","$($wind.chill) $($units.temperature)") $properties.Add("WindSpeed","$($wind.speed) $($units.speed)") $properties.Add("Humidity","$($atmosphere.humidity)%") $properties.Add("Visibility","$($atmosphere.visibility) $($units.distance)") #decode rising switch ($atmosphere.rising) { 0 {$state="steady"} 1 {$state="rising"} 2 {$state="falling"} } $properties.Add("Barometer","$($atmosphere.pressure) $($units.pressure) and $state") $properties.Add("Sunrise",$astronomy.sunrise) $properties.Add("Sunset",$astronomy.sunset) $properties.Add("url",$uri) #create new object to hold values $obj= New-Object -TypeName PSObject -Property $properties #write object and properties. Default is Basic Switch ($detail) { "All" { Write-Verbose "Using All properties" $obj | Select-Object -Property $AllProp } #all "Extended" { Write-Verbose "Using Extended properties" $obj | Select-Object -Property $ExtendedProp } #extended Default { Write-Verbose "Using Basic properties" $obj | Select-Object -Property $BasicProp } #default } #Switch } #processing XML } #foreach $id } #process End { Write-Verbose "Ending $($myinvocation.mycommand)" } #end } #end function
You can also change the temperature units.
I wrote these as two separate functions, I suppose I could have nested Get-Woeid inside Get-Weather, although the better option is probably to build a module. I'll leave that for you. The functions are designed to take advantage of pipeline binding so that you can pipe Get-Woeid to Get-Weather.
You can even get weather for multiple locations.
This weather source includes a lot of information so I created a parameter to control how much detail to display. What you see above is basic information. But there is 'extended'.
Or you can see everything with a detail setting of 'all'.
Notice that url at the end? As the cherry on top, you can open the weather forecast in a browser using the Online parameter.
get-woeid 13244 | get-weather -Online
There you have it. No matter where you are you can check the weather. Or look out a window.
Enjoy.
What a work of art!!! It has a bit of everything… ValidationSet,ParameterSetName. I love the way you append the properties from Basis to Extended to All. Formating is there also. Last but not least XML and XPath!!! PowerShell at its finest!!! Thanks again!!!
Thanks for the kind words.
Seems a bit complex when you have to know the zip or postal code.
function weather{
$zip = $args
$url = “http://www.weather.com/weather/today/l/” + $zip
start $url
}
Sure, but the point was to display the results in PowerShell.