In the last post we learned how to use Weather Underground's API to resolve a city name and get data back from it. Once we get those two URLs we can use them to get data back from the API. I also tied this into my LIFX light, which I posted about here.
Requirements
- An API Key from Weather Underground.
- A basic understanding of how their API works.
- Being a weather geek. (ok, I guess that's optional)
- An email address if you'd like to email the forecasts.
Setup
[cmdletbinding()] param( [string] $city = (Read-Host "City?"), [string] $forecast = 'forecast', [boolean] $sendEmail, [boolean] $lifx, [boolean] $sendAlertEmail ) if (!$foregroundColor) {$foregroundColor = 'green'} $baseURL = 'http://api.wunderground.com/api/' $apiKey = 'yourAPIKey' $acceptHeader = 'application/json' #"password" | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString | Out-File .\emlpassword.txt $emailPass = Get-Content .\emlpassword.txt | ConvertTo-SecureString $weatherList = Get-Content .\emails.txt $alertList = Get-Content .\alertEmails.txt $emailUser = 'yourEmailUsername' $emailFrom = 'youremail@service.com' $smtpServer = 'smtp.gmail.com' $smtpPort = '587'
The Setup
- Name: Get-Weather.ps1
- Variables:
- $baseURL = (Weather Underground's base URL for the API, should be fine with default).
- $apiKey = Your API key that you get from signing up.
- $acceptHeader = Header/formatting settings (default is good).
- $emailPass = Set your password by running the commented line above it. This will be user/machine encrypted.
- $weatherList = Create a text file (named emails.txt) in the script directory that contains a list of people to email the forecasts to.
- $alertList = Create a text file (named alertEmails.txt) in the script directory that contains a list of people to email/text alerts to. I made this different so I could setup text message alerts.
- $emailUser = Your user name for the email address we're sending from.
- $emailFrom = Your from email address.
- $smtpServer = Your SMTP server. Default is Google's.
- $smtpPort = Your SMTP server's port. This is Google's default.
- Parameters
- city (will prompt for one if left blank)
- forecast (Will default to 4 day which is the value 'forecast')
Values:- forecast (4 day)
- hourly (next 36 hours)
- camera (emails an email address a random webcam from the city specified)
- sendEmail (accepts $true, defaults to $false) This is for sending the email forecast.
- lifx (accepts $true, defaults to $false) This is for LIFX light integration. Currently it will flash your LIFX light red when there is an alert. More coming soon with this!
- sendAlertEmail (accepts $true, defaults to $false) This is to send an email (which I use the carrier's email to text address to text myself) if an alert is found.
The first function: Get-Weather
This function will first use the auto-complete API and then from there create the URLs and use them to get the weather data. It will also return the API results at the end. That way you can run the script like this:
$weatherChicago = .\Get-Weather.ps1 -city Chicago
And it will return what the API returns, as well as display some information:
The variable $weatherChicago will contain what the API returned.
With this information you can really drill down and see what is available. It was by doing this that I got the initial ideas for parts of this script. Here's an example of the information returned via:
$weatherChicago.current_observation
After the function finds the city and displays the basic info, it then moves forward to check which forecast was specified and if that forecast is to be emailed.
Get-Weather function code
function Get-Weather { param() $findMe = $city $find = Invoke-RestMethod -Uri "http://autocomplete.wunderground.com/aq?query=$findMe" if ($find) { $cityAPI = $find.Results[0].l $city = $find.Results[0].name $fullURL = $baseURL + $apiKey + "/features/conditions/hourly/forecast/webcams/alerts" + "$cityAPI.json" $radarURL = "http://api.wunderground.com/api/$apiKey/animatedradar/animatedsatellite" + "$cityAPI.gif?num=6&delay=50&interval=30" Write-Host `n"API URLS for $city" -foregroundcolor $foregroundColor Write-Host `t$fullURL Write-Host `t$radarURL $weatherForecast = Invoke-RestMethod -Uri $fullURL -ContentType $acceptHeader $currentCond = $weatherForecast.current_observation Write-Host `n"Current Conditions for: $city" -foregroundColor $foregroundColor Write-Host $currentCond.Weather Write-Host "Temperature:" $currentCond.temp_f"F" Write-Host "Winds:" $currentCond.wind_string $curAlerts = $weatherForecast.alerts if ($curAlerts) { if ($lifx) { Get-LIFXAPI -action flashcolor -brightness 1 -color Red -state on } $typeName = Get-WeatherFunction -Weather 'alert' -value $weatherForecast.alerts $alertDate = $curAlerts.date $alertExp = $curAlerts.expires $alertMsg = $curAlerts.message Write-Host `n"Weather Alert! ($typeName)" -foregroundcolor Red Write-Host "Date: $alertDate Expires: $alertExp" Write-Host "$alertMsg" if ($sendAlertEmail) { Foreach ($email in $alertList) { Send-WeatherEmail -to $email -Subject "Weather Alert!" -Body "Alert Type: $typeName City: $city Message: $alertMsg" } } } } Switch ($forecast) { {$_ -eq 'hourly'} { if ($sendEmail) { $hourlyForecast = $weatherForecast.hourly_forecast $body = "<p></p>" $body += "<p>Here is your hourly forecast!</p>" $selCam = Get-Random $weatherForecast.webcams.count $camImg = $weatherforecast.webcams[$selCam].CURRENTIMAGEURL $camName = $weatherForecast.webcams[$selCam].linktext $camLink = $weatherForecast.webcams[$selCam].link $body += "<p>Random webcam shot from: <a href=`"$camLink`">$camName</a></p>" $body += "<p><img src=`"$camImg`"></p>" $body += "<p>$city Radar:</p>" $body += "<p><img src=`"$radarURL`"></p>" if ($curAlerts) { $body += "<p><b><font color=`"red`">Weather Alert! ($typeName)</font></b></p>" $body += "<p>Date: $alertDate Expires: $alertExp</p>" $body += "<p>$alertMsg</p>" } foreach ($hour in $hourlyForecast) { $body += "<p></p>" $body += "<p></p>" $prettyTime = $hour.fcttime.pretty $hourTemp = $hour.temp.english $hourImg = $hour.icon_url [int]$hourChill = $hour.windchill.english if ($hourChill -eq -9999) { $hourChilltxt = 'N/A' } else { $hourChilltxt = $hourChill.ToString() + 'F' } $hourWind = $hour.wspd.english $windDir = $hour.wdir.dir $hourUV = $hour.uvi $dewPoint = $hour.dewpoint.english $hourFeels = $hour.feelslike.english $hourHum = $hour.humidity $conditions = $hour.condition [int]$hourPrecip = $hour.pop $popText = Get-WeatherFunction -Weather 'preciptext' -value $hourPrecip $body += "<p><b>$prettyTime</b></p>" $body += "<p><img src=`"$hourImg`">$conditions</p>" $body += "<p>Chance of precipitation: $hourPrecip% / $popText</p>" $body += "<p>Current Temp: $hourTemp`F Wind Chill: $hourChilltxt Feels Like: $hourFeels`F</p>" $body += "<p>Dew Point: $dewPoint</p>" $body += "<p>Wind Speed: $hourWind`mph Direction: $windDir</p>" $body += "<p>Humidity: $hourHum%</p>" $body += "<p>UV Index: $hourUV" } foreach ($email in $weatherList) {Send-WeatherEmail -To $email -Subject "Your hourly forecast for $city" -body $body} } } {$_ -eq 'forecast'} { if ($sendEmail) { $todayForecast = $weatherForecast.forecast.simpleforecast.forecastday $body = "<p></p>" $body += "<p>Here is your 4 day forecast!</p>" $selCam = Get-Random $weatherForecast.webcams.count $camImg = $weatherforecast.webcams[$selCam].CURRENTIMAGEURL $camName = $weatherForecast.webcams[$selCam].linktext $camLink = $weatherForecast.webcams[$selCam].link $body += "<p>Random webcam shot from: <a href=`"$camLink`">$camName</a></p>" $body += "<p><img src=`"$camImg`"></p>" $body += "<p>$city Radar:</p>" $body += "<p><img src=`"$radarURL`"></p>" $curAlerts = $weatherForecast.alerts if ($curAlerts) { $body += "<p><b><font color=`"red`">Weather Alert! ($typeName)</font></b></p>" $body += "<p>Date: $alertDate Expires: $alertExp</p>" $body += "<p>$alertMsg</p>" } foreach ($day in $todayForecast) { $body += "<p></p>" $body += "<p></p>" $dayImg = $day.icon_url $dayMonth = $day.date.monthname $dayDay = $day.date.day $dayName = $day.date.weekday $dayHigh = $day.high.fahrenheit $dayLow = $day.low.fahrenheit $maxWind = $day.maxwind.mph $aveWind = $day.avewind.mph $aveHum = $day.avehumidity $conditions = $day.conditions [int]$dayPrecip = $day.pop $popText = Get-WeatherFunction -Weather 'preciptext' -value $dayPrecip $body += "<p><b>$dayName, $dayMonth $dayDay</b></p>" $body += "<p><img src=`"$dayImg`">$conditions</p>" $body += "<p>Chance of precipitation: $dayPrecip% / $popText</p>" $body += "<p>High: $dayHigh`F Low: $dayLow`F</p>" $body += "<p>Ave Winds: $aveWind`mph Max Winds: $maxWind`mph</p>" $body += "<p>Humidity: $aveHum%</p>" } foreach ($email in $weatherList) {Send-WeatherEmail -To $email -Subject "Your 4 day forecast for $city" -body $body} } } {$_ -eq 'camera'} { $selCam = Get-Random $weatherForecast.webcams.count $camImg = $weatherforecast.webcams[$selCam].CURRENTIMAGEURL $camName = $weatherForecast.webcams[$selCam].linktext $camLink = $weatherForecast.webcams[$selCam].link $fileExt = $camImg.SubString($camImg.LastIndexOf("."),4) $cityShort = $city.substring(0,$city.lastindexof(",")) $fileName = $cityShort + $fileExt $location = (Get-Location).Path $camFile = Invoke-WebRequest -Uri $camImg -OutFile "$location\$fileName" $file = $location + "\" + $fileName $gallery = 'YourSquareSpaceGalleryOrEmailToSendAttachmentTo' $SMTPClient = New-Object Net.Mail.SmtpClient($SmtpServer, 587) $SMTPMessage = New-Object System.Net.Mail.MailMessage($emailFrom,$gallery,"$city cam","$city cam") $SMTPClient.Credentials = New-Object System.Net.NetworkCredential($emailUser,$emailPass) $att = New-Object Net.Mail.Attachment($file) $SMTPClient.EnableSsl = $true Switch (($fileName.substring($fileName.LastIndexOf(".")+1)).tolower()) { {$_ -like "*png*"} { $typeExt = 'png' } {$_ -like "*jpg*"} { $typeExt = 'jpg' } {$_ -like "*gif*"} { $typeExt = 'gif' } } $SMTPMessage.Attachments.Add($att) ($smtpmessage.Attachments).contenttype.mediatype = "image/$typeExt" $SMTPClient.Send($SMTPMessage) $att.Dispose() $SMTPMessage.Dispose() Remove-Item $file } } Return $weatherForecast }
Let's take a look at some of the code
The 'camera' option uses its own email sending commands since I had to change the MIME type of the attachment. There is also a variable used to specify where to send the email to. This variable is within the switch for now (until I clean it up!) as $gallery. You'll want to change that to an email address you want to receive the attached webcam shot.
For some reason when sending an image file PowerShell sends it with type information stating it is an application. The following code is what I used to fix that. I noticed this issue when it wouldn't post to SquareSpace unless I first sent it to my Gmail, and then forwarded it to SquareSpace. Once I forwarded it I noticed it would post. It was then I decided to take a look at the full email details and saw the difference in attachment types.
That led me to write the following section of code so I could send the random webcam shot to the gallery and have it post.
{$_ -eq 'camera'} { $selCam = Get-Random $weatherForecast.webcams.count $camImg = $weatherforecast.webcams[$selCam].CURRENTIMAGEURL $camName = $weatherForecast.webcams[$selCam].linktext $camLink = $weatherForecast.webcams[$selCam].link $fileExt = $camImg.SubString($camImg.LastIndexOf("."),4) $cityShort = $city.substring(0,$city.lastindexof(",")) $fileName = $cityShort + $fileExt $location = (Get-Location).Path $camFile = Invoke-WebRequest -Uri $camImg -OutFile "$location\$fileName" $file = $location + "\" + $fileName $gallery = 'your SquareSpace gallery URL or Email address to send pictures to' $SMTPClient = New-Object Net.Mail.SmtpClient($SmtpServer, 587) $SMTPMessage = New-Object System.Net.Mail.MailMessage($emailFrom,$gallery,"$city cam","$city cam") $SMTPClient.Credentials = New-Object System.Net.NetworkCredential($emailUser,$emailPass) $att = New-Object Net.Mail.Attachment($file) $SMTPClient.EnableSsl = $true Switch (($fileName.substring($fileName.LastIndexOf(".")+1)).tolower()) { {$_ -like "*png*"} { $typeExt = 'png' } {$_ -like "*jpg*"} { $typeExt = 'jpg' } {$_ -like "*gif*"} { $typeExt = 'gif' } } $SMTPMessage.Attachments.Add($att) ($smtpmessage.Attachments).contenttype.mediatype = "image/$typeExt" $SMTPClient.Send($SMTPMessage) $att.Dispose() $SMTPMessage.Dispose() Remove-Item $file }
The above code downloads the file, looks at what type of image it is, and then changes the type accordingly. After that it constructs an email object, attaches the file, send the email, and finally removes the file.
The next function is Send-WeatherEmail
function Send-WeatherEmail { [cmdletbinding()] param( [Parameter(Mandatory=$true,ValueFromPipeline=$true)] [string] $To, [string] [Parameter(Mandatory=$true,ValueFromPipeline=$true)] $Subject, [Parameter(Mandatory=$true,ValueFromPipeline=$true)] $Body, [string] $attachment ) if (!$to) {Write-Error "No recipient specified";break} if (!$subject) {Write-Error "No subject specified";break} if (!$body) {Write-Error "No body specified";break} $gmailCredential = New-Object System.Management.Automation.PSCredential($emailUser,$emailPass) Send-MailMessage -To $to -From $emailFrom -Body $body -BodyAsHtml:$true -Subject $Subject -SmtpServer $smtpServer -Port $smtpPort -UseSsl -Credential $gmailCredential }
This function is a short and sweet function that wraps up Send-MailMessage so you can use it throughout the script easily.
Now onto the function Get-WeatherFunction
function Get-WeatherFunction { [cmdletbinding()] param( [string] $weather, $value ) Switch ($weather) { {$_ -eq 'preciptext'} { Switch ($value) { {$_ -lt 20} { $popText = 'No mention' } {$_ -eq 20} { $popText = 'Slight Chance' } {($_ -lt 50 -and $_ -gt 20)} { $popText = 'Chance' } {$_ -eq 50} { $popText = 'Good chance' } {($_ -lt 70 -and $_ -gt 50)} { $popText = 'Likely' } {$_ -ge 70} { $popText = 'Extremely likely' } } Return $popText } {$_ -eq 'alert'} { Switch ($curAlerts.type) { 'HEA' {$typeName = 'Heat Advisory'} 'TOR' {$typeName = 'Tornado Warning'} 'TOW' {$typeName = 'Tornado Watch'} 'WRN' {$typeName = 'Severe Thunderstorm Warning'} 'SEW' {$typeName = 'Severe Thunderstorm Watch'} 'WIN' {$typeName = 'Winter Weather Advisory'} 'FLO' {$typeName = 'Flood Warning'} 'WAT' {$typeName = 'Flood Watch / Statement'} 'WND' {$typeName = 'High Wind Advisory'} 'SVR' {$typeName = 'Severe Weather Statement'} 'HEA' {$typeName = 'Heat Advisory'} 'FOG' {$typeName = 'Dense Fog Advisory'} 'SPE' {$typeName = 'Special Weather Statement'} 'FIR' {$typeName = 'Fire Weather Advisory'} 'VOL' {$typeName = 'Volcanic Activity Statement'} 'HWW' {$typeName = 'High Wind Warning'} 'REC' {$typeName = 'Record Set'} 'REP' {$typeName = 'Public Reports'} 'PUB' {$typeName = 'Public Information Statement'} } Return $typeName } } }
This is perhaps one of the gnarliest functions I've ever named. I'll have to rethink the name, perhaps. For now, let's look at what it does.
Currently it will take the parameter $weather and take different actions based on the input. If you specify 'preciptext' it will return the precipitation chance in text format based on the number it is given. For example if you run:
Get-WeatherFunction -Weather 'preciptext' -value 71
It will return 'Extremely Likely'.
If you specify 'alert' then you'll be able to provide the 3 letter alert code Weather Underground returns via the API and return the full alert text. For example:
Get-WeatherFunction -Weather 'alert' -value 'HEA'
This would return 'Heat Advisory'.
The final function: Get-LIFXApi
function Get-LIFXApi { [cmdletbinding()] param( [string] $action = 'setstate', [string] $state = 'on', [string] $color = 'white', [double] $brightness = '0.4' ) $apiKey = 'YourLifxApiKey' $base64Key = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(($apiKey))) $headers = @{Authorization=("Basic {0}" -f $base64Key)} $allURL = 'https://api.lifx.com/v1/lights/all' $acceptheader = 'application/json' $baseURL = 'https://api.lifx.com/v1/lights/' $foregroundColor = 'white' [array]$colors = @('white','red','orange','yellow','cyan','green','blue','purple','pink') $ninjaLights = Invoke-RestMethod -headers $headers -Uri $allURL function Change-LightState { [cmdletbinding()] param( [string] $state, [string] $color, [double] $brightness, [string] $selector ) $ninjaID = $ninjaLights.id $selector = 'id:' + $ninjaID $fullURL = $baseurl + $selector + '/state' $payloadBuilder = "{ `"power`": `"$state`", `"color`": `"$color`", `"brightness`" : $brightness }" Write-Host "Changing light to:" -ForegroundColor $foregroundcolor Write-Host `t"State :" $state Write-Host `t"Color :" $color Write-Host `t"Brightness:" ($brightness * 100)"%" `n $stateReturn = Invoke-RestMethod -headers $headers -uri $fullURL -Method Put -Body $payloadBuilder Write-Host "API status:" -ForegroundColor $foregroundcolor Write-Host `t"Light :" $stateReturn.results.label Write-Host `t"Status:" $stateReturn.results.status `n } Switch ($action) { 'setstate' { Change-LightState -state $state -color $color -brightness $brightness -selector $selector } 'fluxtest' { $originalBrightness = $ninjaLights.Brightness $originalColor = $ninjalights.Color $originalState = $ninjalights.Power $colorString = "hue:" + $originalcolor.hue + " saturation:" + $originalcolor.saturation + " kelvin:" + $originalColor.Kelvin $i = 0 While ($i -le 20) { $color = Get-Random $colors [double]$brightness = "{0:n2}" -f (Get-Random 0.99) Change-LightState -color $color -brightness $brightness -selector $selector -state $state Start-Sleep -seconds 1 $i++ } Change-LightState -state $originalState -color $colorString -brightness $originalBrightness -selector $selector } 'flashcolor' { $originalBrightness = $ninjaLights.Brightness $originalColor = $ninjalights.Color $originalState = $ninjalights.Power $colorString = "hue:" + $originalcolor.hue + " saturation:" + $originalcolor.saturation + " kelvin:" + $originalColor.Kelvin Change-LightState -state $state -color $color -brightness $brightness -selector $selector Start-Sleep -Seconds 1 Change-LightState -state $originalState -color $colorString -brightness $originalBrightness -selector $selector Start-Sleep -Seconds 1 Change-LightState -state $state -color $color -brightness $brightness -selector $selector Start-Sleep -Seconds 1 Change-LightState -state $originalState -color $colorString -brightness $originalBrightness -selector $selector Start-Sleep -Seconds 1 Change-LightState -state $state -color $color -brightness $brightness -selector $selector Start-Sleep -Seconds 1 Change-LightState -state $originalState -color $colorString -brightness $originalBrightness -selector $selector } } }
I wrote the above function as a script initially when I learned that LIFX provides and API you can use to control your light! It is currently called if the parameter lifx is $true, and if there is a current alert.
if ($lifx) { Get-LIFXAPI -action flashcolor -brightness 1 -color Red -state on }
It then performs the switch option 'flashcolor' which flashes the light red at 100%. It then returns the light to whatever setting it was set to before it started flashing. To get this function to work you'll need to change $apiKey to your LIFX Cloud API key. For more details on working with the LIFX API, check out my post here.
Full code
[cmdletbinding()] param( [string] $city = (Read-Host "City?"), [string] $forecast = 'forecast', [boolean] $sendEmail, [boolean] $lifx, [boolean] $sendAlertEmail ) if (!$foregroundColor) {$foregroundColor = 'green'} $baseURL = 'http://api.wunderground.com/api/' $apiKey = 'yourAPIKey' $acceptHeader = 'application/json' #"password" | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString | Out-File .\emlpassword.txt $emailPass = Get-Content .\emlpassword.txt | ConvertTo-SecureString $weatherList = Get-Content .\emails.txt $alertList = Get-Content .\alertEmails.txt $emailUser = 'yourEmailUsername' $emailFrom = 'youremail@service.com' $smtpServer = 'smtp.gmail.com' $smtpPort = '587' function Get-Weather { param() $findMe = $city $find = Invoke-RestMethod -Uri "http://autocomplete.wunderground.com/aq?query=$findMe" if ($find) { $cityAPI = $find.Results[0].l $city = $find.Results[0].name $fullURL = $baseURL + $apiKey + "/features/conditions/hourly/forecast/webcams/alerts" + "$cityAPI.json" $radarURL = "http://api.wunderground.com/api/$apiKey/animatedradar/animatedsatellite" + "$cityAPI.gif?num=6&delay=50&interval=30" Write-Host `n"API URLS for $city" -foregroundcolor $foregroundColor Write-Host `t$fullURL Write-Host `t$radarURL $weatherForecast = Invoke-RestMethod -Uri $fullURL -ContentType $acceptHeader $currentCond = $weatherForecast.current_observation Write-Host `n"Current Conditions for: $city" -foregroundColor $foregroundColor Write-Host $currentCond.Weather Write-Host "Temperature:" $currentCond.temp_f"F" Write-Host "Winds:" $currentCond.wind_string $curAlerts = $weatherForecast.alerts if ($curAlerts) { if ($lifx) { Get-LIFXAPI -action flashcolor -brightness 1 -color Red -state on } $typeName = Get-WeatherFunction -Weather 'alert' -value $weatherForecast.alerts $alertDate = $curAlerts.date $alertExp = $curAlerts.expires $alertMsg = $curAlerts.message Write-Host `n"Weather Alert! ($typeName)" -foregroundcolor Red Write-Host "Date: $alertDate Expires: $alertExp" Write-Host "$alertMsg" if ($sendAlertEmail) { Foreach ($email in $alertList) { Send-WeatherEmail -to $email -Subject "Weather Alert!" -Body "Alert Type: $typeName City: $city Message: $alertMsg" } } } } Switch ($forecast) { {$_ -eq 'hourly'} { if ($sendEmail) { $hourlyForecast = $weatherForecast.hourly_forecast $body = "<p></p>" $body += "<p>Here is your hourly forecast!</p>" $selCam = Get-Random $weatherForecast.webcams.count $camImg = $weatherforecast.webcams[$selCam].CURRENTIMAGEURL $camName = $weatherForecast.webcams[$selCam].linktext $camLink = $weatherForecast.webcams[$selCam].link $body += "<p>Random webcam shot from: <a href=`"$camLink`">$camName</a></p>" $body += "<p><img src=`"$camImg`"></p>" $body += "<p>$city Radar:</p>" $body += "<p><img src=`"$radarURL`"></p>" if ($curAlerts) { $body += "<p><b><font color=`"red`">Weather Alert! ($typeName)</font></b></p>" $body += "<p>Date: $alertDate Expires: $alertExp</p>" $body += "<p>$alertMsg</p>" } foreach ($hour in $hourlyForecast) { $body += "<p></p>" $body += "<p></p>" $prettyTime = $hour.fcttime.pretty $hourTemp = $hour.temp.english $hourImg = $hour.icon_url [int]$hourChill = $hour.windchill.english if ($hourChill -eq -9999) { $hourChilltxt = 'N/A' } else { $hourChilltxt = $hourChill.ToString() + 'F' } $hourWind = $hour.wspd.english $windDir = $hour.wdir.dir $hourUV = $hour.uvi $dewPoint = $hour.dewpoint.english $hourFeels = $hour.feelslike.english $hourHum = $hour.humidity $conditions = $hour.condition [int]$hourPrecip = $hour.pop $popText = Get-WeatherFunction -Weather 'preciptext' -value $hourPrecip $body += "<p><b>$prettyTime</b></p>" $body += "<p><img src=`"$hourImg`">$conditions</p>" $body += "<p>Chance of precipitation: $hourPrecip% / $popText</p>" $body += "<p>Current Temp: $hourTemp`F Wind Chill: $hourChilltxt Feels Like: $hourFeels`F</p>" $body += "<p>Dew Point: $dewPoint</p>" $body += "<p>Wind Speed: $hourWind`mph Direction: $windDir</p>" $body += "<p>Humidity: $hourHum%</p>" $body += "<p>UV Index: $hourUV" } foreach ($email in $weatherList) {Send-WeatherEmail -To $email -Subject "Your hourly forecast for $city" -body $body} } } {$_ -eq 'forecast'} { if ($sendEmail) { $todayForecast = $weatherForecast.forecast.simpleforecast.forecastday $body = "<p></p>" $body += "<p>Here is your 4 day forecast!</p>" $selCam = Get-Random $weatherForecast.webcams.count $camImg = $weatherforecast.webcams[$selCam].CURRENTIMAGEURL $camName = $weatherForecast.webcams[$selCam].linktext $camLink = $weatherForecast.webcams[$selCam].link $body += "<p>Random webcam shot from: <a href=`"$camLink`">$camName</a></p>" $body += "<p><img src=`"$camImg`"></p>" $body += "<p>$city Radar:</p>" $body += "<p><img src=`"$radarURL`"></p>" $curAlerts = $weatherForecast.alerts if ($curAlerts) { $body += "<p><b><font color=`"red`">Weather Alert! ($typeName)</font></b></p>" $body += "<p>Date: $alertDate Expires: $alertExp</p>" $body += "<p>$alertMsg</p>" } foreach ($day in $todayForecast) { $body += "<p></p>" $body += "<p></p>" $dayImg = $day.icon_url $dayMonth = $day.date.monthname $dayDay = $day.date.day $dayName = $day.date.weekday $dayHigh = $day.high.fahrenheit $dayLow = $day.low.fahrenheit $maxWind = $day.maxwind.mph $aveWind = $day.avewind.mph $aveHum = $day.avehumidity $conditions = $day.conditions [int]$dayPrecip = $day.pop $popText = Get-WeatherFunction -Weather 'preciptext' -value $dayPrecip $body += "<p><b>$dayName, $dayMonth $dayDay</b></p>" $body += "<p><img src=`"$dayImg`">$conditions</p>" $body += "<p>Chance of precipitation: $dayPrecip% / $popText</p>" $body += "<p>High: $dayHigh`F Low: $dayLow`F</p>" $body += "<p>Ave Winds: $aveWind`mph Max Winds: $maxWind`mph</p>" $body += "<p>Humidity: $aveHum%</p>" } foreach ($email in $weatherList) {Send-WeatherEmail -To $email -Subject "Your 4 day forecast for $city" -body $body} } } {$_ -eq 'camera'} { $selCam = Get-Random $weatherForecast.webcams.count $camImg = $weatherforecast.webcams[$selCam].CURRENTIMAGEURL $camName = $weatherForecast.webcams[$selCam].linktext $camLink = $weatherForecast.webcams[$selCam].link $fileExt = $camImg.SubString($camImg.LastIndexOf("."),4) $cityShort = $city.substring(0,$city.lastindexof(",")) $fileName = $cityShort + $fileExt $location = (Get-Location).Path $camFile = Invoke-WebRequest -Uri $camImg -OutFile "$location\$fileName" $file = $location + "\" + $fileName $gallery = 'YourSquareSpaceGalleryOrEmailToSendAttachmentTo' $SMTPClient = New-Object Net.Mail.SmtpClient($SmtpServer, 587) $SMTPMessage = New-Object System.Net.Mail.MailMessage($emailFrom,$gallery,"$city cam","$city cam") $SMTPClient.Credentials = New-Object System.Net.NetworkCredential($emailUser,$emailPass) $att = New-Object Net.Mail.Attachment($file) $SMTPClient.EnableSsl = $true Switch (($fileName.substring($fileName.LastIndexOf(".")+1)).tolower()) { {$_ -like "*png*"} { $typeExt = 'png' } {$_ -like "*jpg*"} { $typeExt = 'jpg' } {$_ -like "*gif*"} { $typeExt = 'gif' } } $SMTPMessage.Attachments.Add($att) ($smtpmessage.Attachments).contenttype.mediatype = "image/$typeExt" $SMTPClient.Send($SMTPMessage) $att.Dispose() $SMTPMessage.Dispose() Remove-Item $file } } Return $weatherForecast } function Send-WeatherEmail { [cmdletbinding()] param( [Parameter(Mandatory=$true,ValueFromPipeline=$true)] [string] $To, [string] [Parameter(Mandatory=$true,ValueFromPipeline=$true)] $Subject, [Parameter(Mandatory=$true,ValueFromPipeline=$true)] $Body, [string] $attachment ) if (!$to) {Write-Error "No recipient specified";break} if (!$subject) {Write-Error "No subject specified";break} if (!$body) {Write-Error "No body specified";break} $gmailCredential = New-Object System.Management.Automation.PSCredential($emailUser,$emailPass) Send-MailMessage -To $to -From $emailFrom -Body $body -BodyAsHtml:$true -Subject $Subject -SmtpServer $smtpServer -Port $smtpPort -UseSsl -Credential $gmailCredential } function Get-WeatherFunction { [cmdletbinding()] param( [string] $weather, $value ) Switch ($weather) { {$_ -eq 'preciptext'} { Switch ($value) { {$_ -lt 20} { $popText = 'No mention' } {$_ -eq 20} { $popText = 'Slight Chance' } {($_ -lt 50 -and $_ -gt 20)} { $popText = 'Chance' } {$_ -eq 50} { $popText = 'Good chance' } {($_ -lt 70 -and $_ -gt 50)} { $popText = 'Likely' } {$_ -ge 70} { $popText = 'Extremely likely' } } Return $popText } {$_ -eq 'alert'} { Switch ($curAlerts.type) { 'HEA' {$typeName = 'Heat Advisory'} 'TOR' {$typeName = 'Tornado Warning'} 'TOW' {$typeName = 'Tornado Watch'} 'WRN' {$typeName = 'Severe Thunderstorm Warning'} 'SEW' {$typeName = 'Severe Thunderstorm Watch'} 'WIN' {$typeName = 'Winter Weather Advisory'} 'FLO' {$typeName = 'Flood Warning'} 'WAT' {$typeName = 'Flood Watch / Statement'} 'WND' {$typeName = 'High Wind Advisory'} 'SVR' {$typeName = 'Severe Weather Statement'} 'HEA' {$typeName = 'Heat Advisory'} 'FOG' {$typeName = 'Dense Fog Advisory'} 'SPE' {$typeName = 'Special Weather Statement'} 'FIR' {$typeName = 'Fire Weather Advisory'} 'VOL' {$typeName = 'Volcanic Activity Statement'} 'HWW' {$typeName = 'High Wind Warning'} 'REC' {$typeName = 'Record Set'} 'REP' {$typeName = 'Public Reports'} 'PUB' {$typeName = 'Public Information Statement'} } Return $typeName } } } function Get-LIFXApi { [cmdletbinding()] param( [string] $action = 'setstate', [string] $state = 'on', [string] $color = 'white', [double] $brightness = '0.4' ) $apiKey = 'YourLifxApiKey' $base64Key = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(($apiKey))) $headers = @{Authorization=("Basic {0}" -f $base64Key)} $allURL = 'https://api.lifx.com/v1/lights/all' $acceptheader = 'application/json' $baseURL = 'https://api.lifx.com/v1/lights/' $foregroundColor = 'white' [array]$colors = @('white','red','orange','yellow','cyan','green','blue','purple','pink') $ninjaLights = Invoke-RestMethod -headers $headers -Uri $allURL function Change-LightState { [cmdletbinding()] param( [string] $state, [string] $color, [double] $brightness, [string] $selector ) $ninjaID = $ninjaLights.id $selector = 'id:' + $ninjaID $fullURL = $baseurl + $selector + '/state' $payloadBuilder = "{ `"power`": `"$state`", `"color`": `"$color`", `"brightness`" : $brightness }" Write-Host "Changing light to:" -ForegroundColor $foregroundcolor Write-Host `t"State :" $state Write-Host `t"Color :" $color Write-Host `t"Brightness:" ($brightness * 100)"%" `n $stateReturn = Invoke-RestMethod -headers $headers -uri $fullURL -Method Put -Body $payloadBuilder Write-Host "API status:" -ForegroundColor $foregroundcolor Write-Host `t"Light :" $stateReturn.results.label Write-Host `t"Status:" $stateReturn.results.status `n } Switch ($action) { 'setstate' { Change-LightState -state $state -color $color -brightness $brightness -selector $selector } 'fluxtest' { $originalBrightness = $ninjaLights.Brightness $originalColor = $ninjalights.Color $originalState = $ninjalights.Power $colorString = "hue:" + $originalcolor.hue + " saturation:" + $originalcolor.saturation + " kelvin:" + $originalColor.Kelvin $i = 0 While ($i -le 20) { $color = Get-Random $colors [double]$brightness = "{0:n2}" -f (Get-Random 0.99) Change-LightState -color $color -brightness $brightness -selector $selector -state $state Start-Sleep -seconds 1 $i++ } Change-LightState -state $originalState -color $colorString -brightness $originalBrightness -selector $selector } 'flashcolor' { $originalBrightness = $ninjaLights.Brightness $originalColor = $ninjalights.Color $originalState = $ninjalights.Power $colorString = "hue:" + $originalcolor.hue + " saturation:" + $originalcolor.saturation + " kelvin:" + $originalColor.Kelvin Change-LightState -state $state -color $color -brightness $brightness -selector $selector Start-Sleep -Seconds 1 Change-LightState -state $originalState -color $colorString -brightness $originalBrightness -selector $selector Start-Sleep -Seconds 1 Change-LightState -state $state -color $color -brightness $brightness -selector $selector Start-Sleep -Seconds 1 Change-LightState -state $originalState -color $colorString -brightness $originalBrightness -selector $selector Start-Sleep -Seconds 1 Change-LightState -state $state -color $color -brightness $brightness -selector $selector Start-Sleep -Seconds 1 Change-LightState -state $originalState -color $colorString -brightness $originalBrightness -selector $selector } } } Get-Weather
Examples:
.\Get-Weather.ps1 -city houston -forecast hourly -sendEmail $true
And here is the email:
$weather = .\Get-Weather.ps1 -city Chicago -lifx $true
As you can see in the above example, the light will flash red. Then it begins alternating with the state it was in beforehand, and finally it returns the light to the original state it was in.
More to come!
I've been writing this Get-Weather script for a little while now, and there's more I want to do with it. For now that list is:
- Clean up the code a bit.
- Add help nodes.
- Add more error checking.
- Find out more ways to utilize both the Weather Underground API and the LIFX API.
In the next post I will detail how I use this script in a scheduled task to send out daily weather emails.
As always, let me know if you have any questions, feedback, or ideas!
-Ginger Ninja