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

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