PowerShell console weather script
I wanted a little more detail than some other terminal weather programs provide so I wrote one in PowerShell. I do not consider it complete as I will eventually rewrite error handling, how I handle the data, and add better documentation. It works and can be used to learn or rework into a better script.
This script uses all free APIs. Only the GEO Location API needs an account and key. Enter at the top of the script or simply add your info. manually.
Other APIs used:
#!/usr/bin/env pwsh <# API services used: https://ipinfo.io https://www.ip2location.io # Requires account and API key. https://www.weather.gov/documentation/services-web-api https://open-meteo.com In the script folder a folder named geo will be created, which should contain two files. * Manual config: config.txt should contain: latitude longitude full_state_name The second file is station.txt and contains the weather station closet to your location. This file gets created the first time you fetch the weather and will limit hits to the API. On Linux you can create a bash script to start the app and symlink to /usr/local/bin as any name you like to call it whenever you need it in the terminal. I used psw for ease on the memory. * Contents of my bash script: #!/usr/bin/env bash pwsh /home/username/Documents/Powershell/ps-weather/psw.ps1 * The desktop file can be created in ~/.local/share/applications to add as an app menu item for your DE. Contents of my desktop file: [Desktop Entry] Version=1.0 Name=Powershell Weather GenericName=Weather Comment=Weather Keywords=weather Exec=pwsh /home/username/Documents/Powershell/ps-weather/psw.ps1 Icon=/home/username/Documents/Powershell/ps-weather/psw.png Terminal=true Type=Application Categories=Application;X-psw; Notes: Could have better error handling and likely better routine to parse the API data, but it works for my needs. #> $IPLocationKey = "your key" # enter your api key from: https://www.ip2location.io/ip2location-documentation $STATES = @{ "Alabama"="AL" "Alaska"="AK" "Arizona"="AZ" "Arkansas"="AR" "California"="CA" "Colorado"="CO" "Connecticut"="CT" "Delaware"="DE" "Florida"="FL" "Georgia"="GA" "Hawaii"="HI" "Idaho"="ID" "Illinois"="IL" "Indiana"="IN" "Iowa"="IA" "Kansas"="KS" "Kentucky"="KY" "Louisiana"="LA" "Maine"="ME" "Maryland"="MD" "Massachusetts"="MA" "Michigan"="MI" "Minnesota"="MN" "Mississippi"="MS" "Missouri"="MO" "Montana"="MT" "Nebraska"="NE" "Nevada"="NV" "New Hampshire"="NH" "New Jersey"="NJ" "New Mexico"="NM" "New York"="NY" "North Carolina"="NC" "North Dakota"="ND" "Ohio"="OH" "Oklahoma"="OK" "Oregon"="OR" "Pennsylvania"="PA" "Rhode Island"="RI" "South Carolina"="SC" "South Dakota"="SD" "Tennessee"="TN" "Texas"="TX" "Utah"="UT" "Vermont"="VT" "Virginia"="VA" "Washington"="WA" "West Virginia"="WV" "Wisconsin"="WI" "Wyoming"="WY" "District of Columbia" = "DC" "Guam"="GU" "Marshall Islands"="MH" "Northern Mariana Island"="MP" "Puerto Rico"="PR" "Virgin Islands"="VI" } function Get-WindRoseDirection() { param ( $degree ) if ($degree -ge 0 -and $degree -lt 22.5) { $WindDirection = "N" } elseif ($degree -ge 22.5 -and $degree -lt 45) { $WindDirection = "NNE" } elseif ($degree -ge 45 -and $degree -lt 67.5) { $WindDirection = "NE" } elseif ($degree -ge 67.5 -and $degree -lt 90) { $WindDirection = "ENE" } elseif ($degree -ge 90 -and $degree -lt 112.5) { $WindDirection = "E" } elseif ($degree -ge 112.5 -and $degree -lt 135) { $WindDirection = "ESE" } elseif ($degree -ge 135 -and $degree -lt 157.5) { $WindDirection = "SE" } elseif ($degree -ge 157.5 -and $degree -lt 180) { $WindDirection = "SSE" } elseif ($degree -ge 180 -and $degree -lt 202.5) { $WindDirection = "S" } elseif ($degree -ge 202.5 -and $degree -lt 225) { $WindDirection = "SSW" } elseif ($degree -ge 225 -and $degree -lt 247.5) { $WindDirection = "SW" } elseif ($degree -ge 247.5 -and $degree -lt 270) { $WindDirection = "WSW" } elseif ($degree -ge 270 -and $degree -lt 292.5) { $WindDirection = "W" } elseif ($degree -ge 292.5 -and $degree -lt 315) { $WindDirection = "WNW" } elseif ($degree -ge 315 -and $degree -lt 337.5) { $WindDirection = "NW" } elseif ($degree -ge 337.5 -and $degree -lt 360) { $WindDirection = "NNW" } elseif ($degree -eq 360) { $WindDirection = "N" } return $WindDirection } function Get-FrontFacingIP() { $StorePath = Join-Path -Path $PSScriptRoot -ChildPath "geo" $StoreFile = Join-Path -Path $StorePath -ChildPath "config.txt" if (Test-Path -path $StoreFile) { $UserIP = Get-Content -Path $StoreFile $UserIP = $UserIP.Trim() } return $UserIP.Trim() } function Get-Geo() { param ( $ip, $GEOKEY ) $infoService = "https://api.ip2location.io/?key=$GEOKEY&ip=$ip" $geotable = Invoke-RestMethod -Method Get -Uri $infoService return $GeoTable.latitude, $GeoTable.longitude, $GeoTable.region_name } function Set-Location() { param ( $UserIP = $null, $GeoKey ) $StorePath = Join-Path -Path $PSScriptRoot -ChildPath "geo" $StoreFile = Join-Path -Path $StorePath -ChildPath "config.txt" if (-not(Test-Path -Path $StorePath)) { New-Item -ItemType "directory" -Path $StorePath -Force } if ($UserSupplied) { Set-Content -Path $StoreFile -Value $UserSupplied -Force } else { $IPService = "https://ipinfo.io/ip" $UserIP = Invoke-RestMethod -Method Get -URI $IPService Set-Content -Path $StoreFile -Value $UserIP -Force } $Lat = "" $Long = "" $State = "" if ($GeoKey.Trim().Length -gt 0) { $Lat, $Long, $State = Get-Geo -ip $UserIP -GEOKEY $GeoKey Add-Content -Path $StoreFile -Value $Lat -Force Add-Content -Path $StoreFile -Value $Long -Force Add-Content -Path $StoreFile -Value $State -Force } else { Write-Host "Please provide an API key or supply latitude, longitude, and state full name manually by saving each on their own line in that order in /geo/config.txt in the script folder." } return $Lat, $Long, $State } function Get-Location() { $StorePath = Join-Path -Path $PSScriptRoot -ChildPath "geo" $StoreFile = Join-Path -Path $StorePath -ChildPath "config.txt" if (Test-Path -path $StoreFile) { $Location = Get-Content -Path $StoreFile } return $Location } function Get-Alerts() { param ( $STATE ) $endpoint = "https://api.weather.gov/alerts/active?area=$STATE" $AlertTable = Invoke-RestMethod -Method Get -Uri $endpoint return $AlertTable } function Get-Forecast() { param ( $LATITUDE, $LONGITUDE ) $StorePath = Join-Path -Path $PSScriptRoot -ChildPath "geo" $StoreFile = Join-Path -Path $StorePath -ChildPath "station.txt" if (Test-Path -path $StoreFile) { $fcendpoint = Get-Content -Path $StoreFile } else { $endpoint = "https://api.weather.gov/points/$LATITUDE,$LONGITUDE" $DataTable = Invoke-RestMethod -Method Get -Uri $endpoint $fcendpoint = $DataTable.properties.forecast Add-Content -Path $StoreFile -Value $fcendpoint -Force } $ForecastTable = Invoke-RestMethod -Method Get -Uri $fcendpoint.Trim() return $ForecastTable.properties.periods } function Get-Current() { param ( $LATITUDE, $LONGITUDE ) $endpoint = "https://api.open-meteo.com/v1/gfs?latitude=$LATITUDE&longitude=$LONGITUDE¤t=temperature_2m,relative_humidity_2m,precipitation,cloud_cover,wind_speed_10m,wind_direction_10m,wind_gusts_10m&temperature_unit=fahrenheit&wind_speed_unit=mph&precipitation_unit=inch" $DataTable = Invoke-RestMethod -Method Get -Uri $endpoint return $DataTable } clear $UserInput = "" While($UserInput.ToLower().Trim() -ne "q") { $UserInput = "" $UserInput = Read-Host -Prompt "> l (save location) n (now) f (forcast today) e (forcast extended) a (alerts) c (clear) q (quit)" if ($null -eq $UserInput) { $UserInput = "" } if ($UserInput.ToLower().Trim() -eq "q") { clear } if ($UserInput.ToLower().Trim() -eq "l") { $UserIP = Read-Host -Prompt "Enter your front facing IP. Leave empty to attempt automatic lookup" if ($UserIP) { $WData = Set-Location -UserIP $IPInput.Trim() -GeoKey $IPLocationKey } else { $WData = Set-Location } if ($WData[0]) { Write-Host "Saved." } else { Write-Host "Unable to get lat,long data." } } if ($UserInput.ToLower().Trim() -eq "c") { clear } if ($UserInput.ToLower().Trim() -eq "a") { $WData = Get-Location $ip,$lat,$long,$state = $WData.Split(" ") if ($state) { $abv = $STATES[$state] $Alert = Get-Alerts -STATE $abv.Trim() $Alerts = $Alert.features if ($Alerts.Length -gt 0) { $Msg = "" ForEach ($a in $Alerts) { $headline = $a.properties.headline $effdate = $a.properties.effective $enddate = $a.properties.expires $locales = $a.properties.areaDesc $desc = $a.properties.description $Msg += "`n*** {0}`n`nEffective: {1}`nEnds: {2}`n`nAffects: {3}`n`nAlert: {4}`n" -f $headline,$effdate,$enddate,$locales,$desc } } else { $Msg = "No alerts."} Write-Host $Msg -ForegroundColor Red } else { Write-Host "Unable to get location data." -ForegroundColor Red } } if ($UserInput.ToLower().Trim() -eq "e") { $WData = Get-Location $ip,$lat,$long,$state = $WData.Split(" ") $Forecasts = Get-Forecast -LATITUDE $lat.Trim() -LONGITUDE $long.Trim() $Msg = "" if ($lat) { ForEach ($periods in $Forecasts) { $Count = 0 #if ($Count -eq 3) { break } ForEach ($period in $periods) { $name = $period.name $temp = "{0} {1}" -f $period.temperature, $period.temperatureUnit if ($null -eq $period.dewpoint.value) { $dewp = "n/a" } else { $dewp = "{0} {1}" -f $period.dewpoint.value.ToString("#.##"), $period.dewpoint.unitCode.Split(":")[1] } if ($null -eq $period.relativeHumidity.value) { $relhm = "0%" } else { $relhm = "{0}%" -f $period.relativeHumidity.value } $wind = "0" $direction = "n/a" if ($period.windSpeed) { $wind = $period.windSpeed $direction = $period.windDirection } $forecast = $period.detailedForecast $Msg += "`n*** {0}`n`nTemp: {1}`nDewPoint: {2}`nRel. Humidity: {3}`nWindSpeed: {4}`nDirection: {5}`n`nForecast:`n`n{6}`n" -f $name,$temp,$dewp,$relhm,$wind,$direction,$forecast $Count += 1 } } Write-Host $Msg -ForegroundColor Cyan } else { Write-Host "Unable to get location data." -ForegroundColor Red } } if ($UserInput.ToLower().Trim() -eq "f") { $WData = Get-Location $ip,$lat,$long,$state = $WData.Split(" ") if ($lat) { $Current = Get-Current -LATITUDE $lat.Trim() -LONGITUDE $long.Trim() $Ctime = Get-Date $CTemp = $Current.current.temperature_2m $CHmd = $Current.current.relative_humidity_2m $CPRC = $Current.current.precipitation $CCld = $Current.current.cloud_cover $CWinds = $Current.current.wind_speed_10m $CWindd = $Current.current.wind_direction_10m $WindDir = Get-WindRoseDirection -degree $CWindd #$Gusts = $Current.current.wind_gusts_10m $CurrMsg = "`n*** Current: {0}`n`nTemp: {1}°F`nHumidity: {2}%`nPrecipitation: {3}`"`nCloud Cover: {4}%`nWind Speed: {5} mph`nDirection: {6}`nRADAR: https://radar.weather.gov/" -f $Ctime,$CTemp,$CHmd,$CPRC,$CCld,$CWinds,$WindDir $Forecasts = Get-Forecast -LATITUDE $lat.Trim() -LONGITUDE $long.Trim() $Msg = "" $count = 0 ForEach ($periods in $Forecasts) { ForEach ($period in $periods) { if ($Count -eq 2) { break } $name = $period.name $temp = "{0} {1}" -f $period.temperature, $period.temperatureUnit if ($null -eq $period.dewpoint.value) { $dewp = "n/a" } else { $dewp = "{0} {1}" -f $period.dewpoint.value.ToString("#.##"), $period.dewpoint.unitCode.Split(":")[1] } if ($null -eq $period.relativeHumidity.value) { $relhm = "0%" } else { $relhm = "{0}%" -f $period.relativeHumidity.value } $wind = "0" $direction = "n/a" if ($period.windSpeed) { $wind = $period.windSpeed $direction = $period.windDirection } $forecast = $period.detailedForecast $Msg += "`n*** {0}`n`nTemp: {1}`nDewPoint: {2}`nRel. Humidity: {3}`nWindSpeed: {4}`nDirection: {5}`n`nForecast:`n`n{6}`n" -f $name,$temp,$dewp,$relhm,$wind,$direction,$forecast $Count += 1 } } Write-Host $CurrMsg -ForegroundColor Green Write-Host $Msg -ForegroundColor Cyan } else { Write-Host "Unable to get location data." -ForegroundColor Red } } if ($UserInput.ToLower().Trim() -eq "n") { $WData = Get-Location $ip,$lat,$long,$state = $WData.Split(" ") if ($lat) { $Current = Get-Current -LATITUDE $lat.Trim() -LONGITUDE $long.Trim() $Ctime = Get-Date $CTemp = $Current.current.temperature_2m $CHmd = $Current.current.relative_humidity_2m $CPRC = $Current.current.precipitation $CCld = $Current.current.cloud_cover $CWinds = $Current.current.wind_speed_10m $CWindd = $Current.current.wind_direction_10m $WindDir = Get-WindRoseDirection -degree $CWindd #$Gusts = $Current.current.wind_gusts_10m $CurrMsg = "`n*** Current: {0}`n`nTemp: {1}°F`nHumidity: {2}%`nPrecipitation: {3}`"`nCloud Cover: {4}%`nWind Speed: {5} mph`nDirection: {6}`nRADAR: https://radar.weather.gov/" -f $Ctime,$CTemp,$CHmd,$CPRC,$CCld,$CWinds,$WindDir Write-Host $CurrMsg -ForegroundColor Green } else { Write-Host "Unable to get location data." -ForegroundColor Red } } }
Screenshot