diff --git a/ags/weather/Weather.js b/ags/weather/Weather.js new file mode 100644 index 0000000..a229526 --- /dev/null +++ b/ags/weather/Weather.js @@ -0,0 +1,23 @@ +import { PopupContent } from '../bar/buttons/Weather.js'; +import Widget from 'resource:///com/github/Aylur/ags/widget.js'; +import PopupWindow from '../misc/PopupWindow.js'; +import options from '../options.js'; + +export default () => PopupWindow({ + name: 'weather', + anchor: ['top'], + setup: self => self.hook(options.bar.position, () => { + self.anchor = [options.bar.position.value]; + if (options.bar.position.value === 'top') + self.transition = 'slide_down'; + + if (options.bar.position.value === 'bottom') + self.transition = 'slide_up'; + }), + child: Widget.Box({ + class_name: 'dashboard', + children: [ + PopupContent(), + ], + }), +}); \ No newline at end of file diff --git a/ags/weather/openweathermap.sh b/ags/weather/openweathermap.sh new file mode 100644 index 0000000..a3664c4 --- /dev/null +++ b/ags/weather/openweathermap.sh @@ -0,0 +1,433 @@ +#!/bin/sh + +apiKey="" +defaultLocation="" +Ags=0 +Debug=0 +output="" +WindSpeedUnit="kph" +DegreeCharacter="Β°C" +HumidityUnit="%" +CloudCoverUnit="%" +RainUnit=" mm" + +function debug_break() { + echo "-----" +} + +function get_script_argument_options() { + if [[ $firstArgLetter == "a" ]]; then + Ags=1 + fi + + if [[ $firstArgLetter == "d" ]]; then + Debug=1 + fi +} + +function get_config_file_data() { + if [ -f "$ConfigFile" ]; then + readarray -t line < "$ConfigFile" + apiKey=${line[0]} + defaultLocation=${line[1]} + fi + + if [ "$Debug" = "1" ]; then + # echo "apiKey: $apiKey" + echo "defaultLocation: $defaultLocation" + debug_break + fi + + if [[ "$apiKey" == "" || "$defaultLocation" == "" ]]; then + echo "Wrong Config file." + exit 1 + fi +} + +function check_api_status() { + check=$(echo "$response" | grep -c -e '"cod":"40"' || true) + check2=$(echo "$response" | grep -c -e '"cod":"30"' || true) + sum=$(( check + check2 )) + + if [ $sum -gt 0 ];then + exit 99 + fi +} + +function get_api_response() { + if [ "$Debug" = "1" ]; then + echo "API REQUEST TO GET DATA. URL: $URL" + fi + + # Make the API request using curl + # Check if the request was successful + if ! response=$(curl -s "$URL"); then + echo "Error making API request" + exit 1 + fi + + # Save response to cache file + echo "$response" > "$CacheFile" + + if [ "$Debug" = "1" ]; then + echo "$response" + debug_break + fi + + check_api_status +} + +function get_api_response_from_cache_file() { + if [ "$Debug" = "1" ]; then + echo 'DATA FROM CACHE' + fi + + response=$(cat "$CacheFile") + + if [ "$Debug" = "1" ]; then + echo "$response" + debug_break + fi + + check_api_status +} + +function get_response() { + # Check if cache file exists and is less than 5 minutes old + if [ -f "$CacheFile" ] && [ $(($(date +%s) - $(date -r "$CacheFile" +%s))) -lt 300 ]; then + get_api_response_from_cache_file + else + get_api_response + fi +} + +function print_weather_output() { + if [ "$Debug" = "1" ]; then + echo "NOW: $nowTemperature" + fi + + if [ -z "$output" ]; then + echo "No weather data to output." + exit 1 + fi + + if [ "${output: -1}" = "," ]; then + output="${output%,}" + fi + + output="[$output]" + + if [ "$Ags" == "1" ]; then + ags -r "weather.setTooltip($output)" + ags -r "weather.setTemperatureWeather(\"$nowTemperature\")" + else + echo "$output" + fi +} + +function get_debug_info_sunset_and_sunrise_hours() { + if [ "$Debug" = "1" ]; then + echo "Now: $CurrentTime" + CurrentHour=$(date +"%-H" -d @"$CurrentTime") + echo "Current: $CurrentHour" + + echo "SunsetHour: $SunsetHour" + echo "Sunrise: $SunriseHour" + debug_break + fi +} + +#sunset information from the Current weather response +function get_current_sunset_and_sunrise_hours() { + SunsetTime=$(echo "$response" | jq -r .sys.sunset) + SunriseTime=$(echo "$response" | jq -r .sys.sunrise) + SunsetHour=$(date +"%-H" -d @"$SunsetTime") + SunriseHour=$(date +"%-H" -d @"$SunriseTime") + + #current time + CurrentTime=$(date +%s) + + get_debug_info_sunset_and_sunrise_hours +} + +#sunset information from the weather Forecast response +function get_forecast_sunset_and_sunrise_hours() { + SunsetTime=$(echo "$response" | jq -r .city.sunset) + SunriseTime=$(echo "$response" | jq -r .city.sunrise) + SunsetHour=$(date +"%-H" -d @"$SunsetTime") + SunriseHour=$(date +"%-H" -d @"$SunriseTime") + + #current time + CurrentTime=$(date +%s) + + get_debug_info_sunset_and_sunrise_hours +} + +#Updates provided weather icon with night icons and sunset/sunrise icons +function get_weather_icon() { + icon=$1 + + checkNewIcon="True" + + if [[ "$icon" == "β˜€οΈ" || "$icon" == "🌀" || "$icon" == "β›…" ]]; then + if [[ $WeatherHour -eq $SunriseHour ]]; then + checkNewIcon="False" + icon="πŸŒ„" + fi + + if [[ $checkNewIcon == "True" && $WeatherHour -eq $SunsetHour ]]; then + checkNewIcon="False" + icon="πŸŒ‡" + fi + fi + + if [[ $checkNewIcon == "True" && ($WeatherHour -ge $SunsetHour || $WeatherHour -le $SunriseHour) ]]; then + case $1 in + "β˜€οΈ") icon="πŸŒ‘" ;; + "🌀") icon="πŸŒ‘" ;; + "β›…") icon="" ;; + "☁") icon="" ;; + "🌦") icon="" ;; + "🌧️") icon="" ;; + "🌨") icon="" ;; + "ξŒ“") icon="ξŽ•" ;; + esac + + if [[ "$icon" == "πŸŒ‘" ]]; then + # Generate a random number between 1 and 5 + random_number=$(( RANDOM % 5 + 1 )) + + # Check the random number and provide different outputs + if [ $random_number -eq 1 ]; then + icon="πŸŒ•" + fi + fi + fi +} + +function get_min_and_max_weather() { + # MinTemperature=$(echo "$response" | jq -r .list["$i"].main.temp_min | tr '\n' ' ') + # MaxTemperature=$(echo "$response" | jq -r .list["$i"].main.temp_max | tr '\n' ' ') + + # Check if a new day has started + Day=$(date +"%Y-%m-%d" -d @"${WeatherDate}") + if [[ "$Day" != "$CurrentDay" ]]; then + # Reset variables for the new day + CurrentDay="$Day" + MinTemperature="$Temperature" + MaxTemperature="$Temperature" + fi + + # Update min and max temperatures + if (( $(echo "$Temperature < $MinTemperature" | bc -l) )); then + MinTemperature="$Temperature" + fi + + if (( $(echo "$Temperature > $MaxTemperature" | bc -l) )); then + MaxTemperature="$Temperature" + fi + + MinTemp=$(printf '%.*f\n' 0 "$MinTemperature") + MaxTemp=$(printf '%.*f\n' 0 "$MaxTemperature") +} + +function clear_defaults() { + WeatherDate="" + WeatherHour="" + FormattedDate="" + Temperature="" + Temperature="" + # ShortWeather="" + LongWeather="" + Humidity="" + CloudCover="" + WindSpeed="" + MinTemperature="" + MaxTemperature="" + MinTemp="" + MaxTemp="" + icons="" + Rain1h="null" + Rain3h="null" +} + +function weather_data() { + forecast=$1 + + if [ "$forecast" = "f" ]; then + FormattedDate=$(date -d @"${WeatherDate}" +"%A, %B %d, %Y") + # ShortWeather=$(echo "$response" | jq -r .list["$i"].weather[] | jq -r .main | tr '\n' ' '| awk '{$1=$1};1' ) + LongWeather=$(echo "$response" | jq -r .list["$i"].weather[] | jq -r .description | sed -E 's/\S+/\u&/g' | tr '\n' ' '| awk '{$1=$1};1' ) + Humidity=$(echo "$response" | jq -r .list["$i"].main.humidity | tr '\n' ' '| awk '{$1=$1};1' ) + CloudCover=$(echo "$response" | jq -r .list["$i"].clouds.all | tr '\n' ' '| awk '{$1=$1};1' ) + WindSpeed=$(echo "$response" | jq -r .list["$i"].wind.speed | tr '\n' ' ' | awk '{$1=$1};1' ) + WindSpeed=$(printf '%.*f\n' 1 "$WindSpeed") + + Rain1h=$(echo "$response" | jq -r .list["$i"] | jq -r '.rain."1h"') + Rain3h=$(echo "$response" | jq -r .list["$i"] | jq -r '.rain."3h"') + + icons=$(echo "$response" | jq -r .list["$i"].weather[] | jq -r .icon | tr '\n' ' ' ) + else + #current weather + WeatherDate=$(echo "$response" | jq -r .dt | tr '\n' ' ') + WeatherHour=$(date +"%-H" -d @"${WeatherDate}") + FormattedDate=$(date -d @"${WeatherDate}" +"%A, %B %d, %Y") + + # Extracting and formatting the temperature + Temperature=$(echo "$response" | jq -r .main.temp | tr '\n' ' ') + # Use bc to format the temperature to 0 decimal places + Temperature=$(echo "$Temperature" | bc -l | awk '{printf "%.0f", $1}') + + # ShortWeather=$(echo "$response" | jq -r .weather[0].main | tr '\n' ' '| awk '{$1=$1};1' ) + LongWeather=$(echo "$response" | jq -r .weather[0].description | sed -E 's/\S+/\u&/g' | tr '\n' ' '| awk '{$1=$1};1' ) + + Humidity=$(echo "$response" | jq -r .main.humidity | tr '\n' ' '| awk '{$1=$1};1' ) + CloudCover=$(echo "$response" | jq -r .clouds.all | tr '\n' ' '| awk '{$1=$1};1' ) + WindSpeed=$(echo "$response" | jq -r .wind.speed | tr '\n' ' ' | awk '{$1=$1};1' ) + WindSpeed=$(printf '%.*f\n' 1 "$WindSpeed") + + Rain1h=$(echo "$response" | jq -r '.rain."1h"') + Rain3h=$(echo "$response" | jq -r '.rain."3h"') + + MinTemperature=$(echo "$response" | jq -r .main.temp_min | tr '\n' ' ') + MaxTemperature=$(echo "$response" | jq -r .main.temp_max | tr '\n' ' ') + MinTemp=$(echo "$MinTemperature" | bc -l | awk '{printf "%.0f", $1}') + MaxTemp=$(echo "$MaxTemperature" | bc -l | awk '{printf "%.0f", $1}') + + icons=$(echo "$response" | jq -r .weather[0].icon | tr '\n' ' ') + fi + + Rain="0" + + if [[ "$Rain3h" != "null" ]]; then + Rain="$Rain3h" + fi + + if [[ "$Rain1h" != "null" ]]; then + Rain="$Rain1h" + fi + + iconval=${icons%?} + case $iconval in + 01*) get_weather_icon "β˜€οΈ";; + 02*) get_weather_icon "🌀";; + 03*) get_weather_icon "β›…";; + 04*) get_weather_icon "☁";; + 09*) get_weather_icon "🌦";; + 10*) get_weather_icon "🌧️";; + 11*) get_weather_icon "🌩";; + 13*) get_weather_icon "🌨";; + 50*) get_weather_icon "ξŒ“";; + esac + + if [ "$Debug" = "1" ]; then + echo "WeatherDate: $FormattedDate, Hour: $WeatherHour" + # echo "ShortWeather: $ShortWeather" + echo "LongWeather: $LongWeather" + echo "Humidity: $Humidity$HumidityUnit" + echo "CloudCover: $CloudCover$CloudCoverUnit" + echo "WindSpeed: $WindSpeed $WindSpeedUnit" + echo "Temperature: $Temperature$DegreeCharacter" + echo "icon: $icon" + echo "MinTemp: $MinTemp$DegreeCharacter" + echo "MaxTemp: $MaxTemp$DegreeCharacter" + echo "rain_1h: $Rain1h" + echo "rain_3h: $Rain3h" + debug_break + fi + + if [ "$forecast" != "f" ]; then + nowTemperature="$icon $Temperature$DegreeCharacter" + fi + + output+="{\"date\":\"$FormattedDate\",\"hour\":\"$WeatherHour\",\"temperature\":\"${Temperature}Β°C\",\"icon\":\"$icon\",\"wind\":\"$WindSpeed$WindSpeedUnit\",\"rain\":\"$Rain$RainUnit\",\"humidity\":\"$Humidity$HumidityUnit\",\"minTemp\":\"$MinTemp$DegreeCharacter\",\"maxTemp\":\"$MaxTemp$DegreeCharacter\"}," +} + +function get_weather_forecast_data() { + CurrentDay="" + # PreviousDay="" + TomorrowDate=$(date -d '+1 day' +"%s") + + clear_defaults + + i=0 + # NumEntries=$(echo "$response" |jq -r .cnt) + # while [ $i -lt $NumEntries ]; do + while [ $i -lt 40 ]; do + WeatherDate=$(echo "$response" | jq -r .list[$i].dt | tr '\n' ' ') + WeatherHour=$(date +"%-H" -d @"${WeatherDate}") + CastDate=$(date +"%s" -d @"${WeatherDate}") + + Temperature=$(echo "$response" | jq -r .list[$i].main.temp | tr '\n' ' ') + Temperature=$(echo "$Temperature" | bc -l | awk '{printf "%.0f", $1}') + + get_min_and_max_weather + + #show the weather forecast hourly until it reaches tomorrow date, then only 1 hour + if [ "$CastDate" -le "$TomorrowDate" ]; then + weather_data "f" + else + if [ "$WeatherHour" -ge "17" ] && [ "$WeatherHour" -le "18" ] || + [ "$WeatherHour" -ge "18" ] && [ "$WeatherHour" -le "19" ]; then + + # if [[ "$CurrentDay" != "$PreviousDay" ]]; then + weather_data "f" + # fi + + # PreviousDay=$CurrentDay + fi + fi + + i=$((i + 1)) + done +} + +function call_weather_current_api() { + # API endpoint URL Current Weather + URL="http://api.openweathermap.org/data/2.5/weather?id=$defaultLocation&units=metric&appid=$apiKey" + + # Current Weather API cache file path based on defaultLocation + CacheFile="/tmp/weather_cache_${defaultLocation}_current.json" + + get_response + # get_api_response_from_cache_file + + get_current_sunset_and_sunrise_hours + weather_data "c" +} + +function call_weather_forecast_api() { + # API endpoint URL Forecast + URL="http://api.openweathermap.org/data/2.5/forecast?id=$defaultLocation&units=metric&appid=$apiKey" + + # Forecast API cache file path based on defaultLocation + CacheFile="/tmp/weather_cache_${defaultLocation}_forecast.json" + + get_response + # get_api_response_from_cache_file + + #used to retrieve forecast data + get_forecast_sunset_and_sunrise_hours + get_weather_forecast_data +} + +#get this scripts arguments => (ags, debug or none) +#checks the first letter of the argument provided to the script +if [[ $# -ge 1 ]]; then + firstArgLetter="$(echo "$1" | head -c 1)" +else + firstArgLetter= +fi + +get_script_argument_options + +#call to get config file data => (apiKey and defaultLocation) +ConfigFile="$HOME/.config/scripts/weather.rc" +get_config_file_data + +call_weather_current_api + +call_weather_forecast_api + +#prints out final output / sends over the data +print_weather_output \ No newline at end of file diff --git a/ags/weather/weather.scss b/ags/weather/weather.scss new file mode 100644 index 0000000..cb2a0b7 --- /dev/null +++ b/ags/weather/weather.scss @@ -0,0 +1,417 @@ +@mixin calendar { + @include widget; + padding: $padding*2 $padding*2 0; + + calendar{ + // all: unset; + .view { + all:unset; + } + + &.button{ + @include button($flat: true); + } + + &:selected{ + box-shadow: inset 0 -8px 0 0 transparentize($accent, 0.5), + inset 0 0 0 1px $accent; + border-radius: $radii*0.6; + } + + &.header{ + background-color: transparent; + border: none; + color: transparentize($fg-color, 0.5); + } + + &.highlight{ + background-color: transparent; + color: transparentize($accent, 0.5); + } + + &:indeterminate { + color: transparentize($fg-color, 0.9); + } + font-size: 1.1em; + padding: .2em; + color: $fg_color; + } +} + +window#notification .dashboard, +window#weather .weather, +window#dashboard .window-content { + @include floating-widget; + + .notifications { + min-width: $notifications-width; + + .header { + margin-bottom: $spacing; + margin-right: $spacing; + + > label { margin-left: $radii / 2; } + + button { + @include button; + padding: $padding/2 $padding; + } + } + + .notification-scrollable { + @include scrollable; + } + + .notification-list { + //fixes weird spacing bug on notification panel - release4 + // margin-right: $spacing; + } + + .notification { + @include notification; + + > box { + @include widget; + padding: $padding; + margin-bottom: $spacing; + } + } + + .placeholder { + image { font-size: 7em; } + label { font-size: 1.2em; } + } + } + + separator { + background-color: $popover-border-color; + min-width: 2px; + border-radius: $radii; + margin-right: $spacing; + } + + .datemenu, .system-info { + @include spacing; + } + + .clock-box { + padding: $padding; + + .clock { + font-size: 5em; + } + + .uptime { + color: transparentize($fg-color, 0.2); + } + } + + .calendar { + @include calendar; + } + + //weather popup + .weather-info { + .weather-icon{ + font-size: 1.5em; + margin-top: 0.3em; + margin-bottom: 0.15em; + } + + .weather-temperature { + all: unset; + font-size: 1.2em; + margin-left: 0.5em; + } + } +} + +// height of the notification popup +window#notification .dashboard { + min-height: 32em; +} + +// weather popup +.weather-refresh { + all: unset; + border: none; + margin: 0; + padding: 0; +} + +.weather-refresh:hover { + color: gray; +} + +.weather-refresh:active { + color: black; +} + +.weather-refresh:focus { + color: $accent; +} + +.weather-info { + background-color: rgba(81, 164, 231, 0.76); + border-radius: 16px; + padding: 0.55em; + // margin: 0 5px; + + min-width: 5em; + min-height: 10em; +} + +.weather-temperature { + margin-bottom: 0.6em; +} + +.weather-day { + padding: 2px; +} + +.weather-day:first-child { + padding: 0; +} + +window#dashboard .dashboard, +window#quicksettings .window-content { + .circular-progress-box { + @include widget; + padding: $padding; + + .circular-progress { + min-height: $sys-info-size; + min-width: $sys-info-size; + margin: $padding/2; + font-size: $padding; + background-color: $bg-color; + color: $accent; + + image { + font-size: 1.8em; + } + } + } +} + +// weather window width of the weather widget +window#weather .weather .tooltip { + .weather-spacing { + padding-left: $spacing; + } + + .qsweather-widget { + margin: 0; + } + + .qs-weather-box-child { + min-width: 5em; + + .weather-icon { + font-size: 1.5em; + } + } + + .qs-weather-box-main { + min-width: 16.5em; + } +} + +// shared logic for weather widget, first one only goes to qs +window#quicksettings .window-content, +window#weather .tooltip { + .qsweather-widget { + margin: 0; + padding: 0; + margin-top: $padding / 2; + border-radius: $radii; + } + + .qs-weather-box-main { + margin: 0.5em; + + .weather-icon { + font-size: 2.42em; + margin-left: 0.5em; + } + + .weather-temperature { + all: unset; + font-size: 1.6em; + } + + .weather-rain { + margin-left: 1.2em; + } + + .weather-hour { + margin-right: 1em; + } + } + + .qs-weather-box-child { + padding-bottom: 0.7em; + + .weather-icon { + margin-top: 0.3em; + font-size: 24px; + } + + .weather-temperature { + all: unset; + margin-bottom: 0.2em; + font-size: 20px; + } + } + + // .weather-max { + // color: #FF8C00; + // } + + // .weather-min { + // color: #87ceeb; + // } +} + +// fix to keep topbar on the correct height - 29 (hyprctl layers), new on release4 +.weather.panel-button label{ + font-size: 16px; +} + +$sliderVolumeMinHeight: 2em; +$sliderTroughMinWidth: 2em; + +window#dashboard .datemenu .sub-menu { + all:unset; + + &:focus, + &:hover{ + color: $accent; + } +} + +.led-light-color-switcher { + .on, + .off { + min-width: 1.8em; + min-height: 3.8em; + background-color: #fff; + border: 1px solid black; + border-radius: 5px; + color: black; + margin-top: 0.5em; + + &:focus, + &:hover{ + box-shadow: inset 0 0 4px 2px $accent, + inset 0 0 0 99px $widget_bg; + } + } + + .on { + box-shadow: inset 0px -20px 2px 2px rgba(0, 0, 0, 0.2); + margin-right: 0.5em; + } + + .off { + box-shadow: inset 0px 20px 2px 2px rgba(0, 0, 0, 0.2); + } + + .slider { + min-width: 21.3em; + margin: 0 0.5em; + padding: 0; + margin-top: 1em; + + slider { + all: unset; + } + + trough{ + min-height: $sliderVolumeMinHeight; + border-radius: $radii; + } + + highlight, + progress { + background: transparent; + border-radius: $radii; + min-width: $sliderTroughMinWidth; + } + } + + .color-temp.slider, + .hue.slider, + .brightness.slider { + highlight, + progress { + &:focus, + &:hover{ + box-shadow: inset 0 0 3px 3px $accent, + inset 0 0 0 99px $widget_bg; + + border: $accent; + } + } + } + + .color-temp.slider { + trough{ + box-shadow: + inset 700px 0 10px rgba(98, 159, 255, 0.8), /* 6500K */ + inset 600px 0 10px rgba(144, 196, 255, 0.8), /* 6000K */ + inset 500px 0 10px rgba(200, 232, 255, 0.8), /* 5000K */ + inset 400px 0 10px rgba(255, 255, 255, 0.8), /* 4000K */ + inset 300px 0 10px rgba(255, 249, 200, 0.8), /* 3000K */ + inset 200px 0 10px rgba(255, 232, 156, 0.8), /* 2000K */ + inset 100px 0 10px rgba(255, 184, 104, 0.8), /* 1700K */ + ; + } + + highlight, + progress { + box-shadow: inset 0px 0px 2px 1px black; + } + } + + .hue.slider { + trough{ + box-shadow: + inset 350px 0 10px rgba(255, 0, 0, 0.8), /* Red */ + inset 300px 0 10px rgba(255, 0, 255, 0.8), /* Magenta */ + inset 250px 0 10px rgba(0, 0, 255, 0.8), /* Blue */ + inset 200px 0 10px rgba(0, 255, 255, 0.8), /* Cyan */ + inset 150px 0 10px rgba(0, 255, 0, 0.8), /* Green */ + inset 100px 0 10px rgba(255, 255, 0, 0.8), /* Yellow */ + inset 50px 0 10px rgba(255, 0, 0, 0.8), /* Red */ + ; + } + + highlight, + progress { + box-shadow: inset 0px 0px 2px 1px black; + } + } + + .brightness.slider { + trough{ + box-shadow: + inset 500px 0 10px rgba(255, 255, 255, 0.5), + inset 450px 0 10px rgba(220, 220, 220, 0.5), + inset 400px 0 10px rgba(190, 190, 190, 0.5), + inset 350px 0 10px rgba(160, 160, 160, 0.5), + inset 300px 0 10px rgba(130, 130, 130, 0.5), + inset 250px 0 10px rgba(100, 100, 100, 0.5), + inset 200px 0 10px rgba(70, 70, 70, 0.5), + inset 150px 0 10px rgba(40, 40, 40, 0.5), + inset 100px 0 10px rgba(10, 10, 10, 0.5), + inset 50px 0 10px rgba(0, 0, 0, 0.5) + ; + + highlight, + progress { + box-shadow: inset 0px 0px 2px 1px white; + } + } + } +} \ No newline at end of file diff --git a/ags/weather/weather_logic.js b/ags/weather/weather_logic.js new file mode 100644 index 0000000..9005eb7 --- /dev/null +++ b/ags/weather/weather_logic.js @@ -0,0 +1,486 @@ +import Weather from '../../services/weather.js'; +import Widget from 'resource:///com/github/Aylur/ags/widget.js'; +import App from 'resource:///com/github/Aylur/ags/app.js'; + +export const TemperatureIndicator = () => Widget.Stack({ + children: { + true: PannelButton(), + false: Widget.Label(), + }, + setup: self => self.hook(Weather, () => { + self.shown = Weather.temperatureWeather ? true : false; + }), +}); + +export const PannelButton = props => Widget.Button({ + ...props, + class_name: 'weather panel-button', + child: Temperature(), + on_clicked: () => App.toggleWindow('weather'), + on_secondary_click: () => Weather.weatherData, + setup: self => self + .hook(App, (_, win, visible) => { + self.toggleClassName('active', win === 'weather' && visible); + }), +}); + +export const Temperature = props => Widget.Label({ + ...props, + setup: self => self.hook(Weather, () => { + if (Weather.temperatureWeather && self.label !== Weather.temperatureWeather) { + self.label = Weather.temperatureWeather.toString() + } + }), +}); + +function weatherBackgroundStyle(icon, box) { + switch (icon) { + case "πŸŒ‡": {//sunset + box.setCss(` + background: linear-gradient(to bottom, #ff6f61, #ffca58, #f0e68c); + color: #000000; + `); + break; + } + case "πŸŒ„": {//sunrise + box.setCss(` + background: linear-gradient(to bottom, #ffcc00, #ff6f61, #ff5e62, #d55f74); + color: #000000; + `); + break; + } + case "🌀": {//few clouds + box.setCss(` + background: linear-gradient(to bottom, #80b3ff, #ffffff, #ffdb4d); + color: #000000; + `); + break; + } + case "🌩": {//thunderstorm + box.setCss(` + background: linear-gradient(to bottom, #0c0e23, #1a1c38, #121320, #0c0e23); + color: #ffffff; + `); + break; + } + case "πŸŒ‘": { + box.setCss(` + background: linear-gradient(to bottom, #2c3e50, #1a2533); + color: #ffffff; + `); + break; + } + case "πŸŒ•": { + box.setCss(` + background: linear-gradient(to bottom, #001f3f, #002f4f, #003f5f, #004f6f, #005f7f); + color: #ffffff; + `); + break; + } + case "β˜€οΈ": { + box.setCss(` + background: linear-gradient(to bottom, #ffeb99, #ffe580, #ffd866, #ffcf4c, #ffc333); + color: #000000; + `); + break; + } + case "☁": + case "☁️": {//cloudy + box.setCss(` + background: linear-gradient(to bottom, #c4c4c4, #d1d1d1, #dedede, #ebebeb, #f8f8f8); + color: #000000; + `); + break; + } + case "": { + box.setCss(` + background: linear-gradient(to bottom, #1c2331, #212a38, #263141, #2b3749, #303d51); + color: #ffffff; + `); + break; + } + case "ξŒ“": {//fog + box.setCss(` + background: linear-gradient(to bottom, #d8d8d8, #e2e2e2, #ececec, #f6f6f6, #ffffff); + color: #000000; + `); + break; + } + case "ξŽ•": { + box.setCss(` + background: linear-gradient(to bottom, #1c2331, #1c2331, #1c2331, #293547, #38475f); + color: #ffffff; + `); + break; + } + case "β›ˆοΈ": {//heavy rain + box.setCss(` + background: linear-gradient(to bottom, #2c3e50, #34495e, #2c3e50, #34495e, #2c3e50); + color: #000000; + `); + break; + } + case "σ°™Ύ": { + box.setCss(` + background: linear-gradient(to bottom, #050818, #070b1d, #0a0e22, #0d1126, #10152b); + color: #ffffff; + `); + break; + } + case "🌦️": {//light rain + box.setCss(` + background: linear-gradient(to bottom, #547aad, #6692b8, #78a9c3, #8abfd0, #9cd7dd); + color: #000000; + `); + break; + } + case "": { + box.setCss(` + background: linear-gradient(to bottom, #0e1620, #121c2a, #162133, #18273c, #1c2c46); + color: #ffffff; + `); + break; + } + case "β›…": {//partly cloudy + box.setCss(` + background: linear-gradient(to bottom, #a8c9f0, #c4dfea, #f0f0cc, #ffd700, #f0f0cc, #c4dfea, #a8c9f0); + color: #000000; + `); + break; + } + case "": { + box.setCss(` + background: linear-gradient(to bottom, #040d1c, #081427, #0c1a32, #101f3d, #142348); + color: #ffffff; + `); + break; + } + case "🌧️": {//rain showers + box.setCss(` + background: linear-gradient(to bottom, #5e7d99, #6a8ba6, #7698b3, #87a5bf, #97b3cb, #a6c0d8, #b4cedf); + color: #000000; + `); + break; + } + case "": { + box.setCss(` + background: linear-gradient(to bottom, #050818, #070b1d, #0a0e22, #0d1126, #10152b); + color: #ffffff; + `); + break; + } + case "🌨": {//snow + box.setCss(` + background: linear-gradient(to bottom, #d0e6ec, #e0f0f5, #f0f5f9, #f5fafd, #ffffff); + color: #000000; + `); + break; + } + case "": { + box.setCss(` + background: linear-gradient(to bottom, #0e1620, #162133, #1c263f, #232c4c, #293259); + color: #ffffff; + `); + break; + } + case "🌨️": {//sleet + box.setCss(` + background: linear-gradient(to bottom, #d0e6ec, #e0f0f5, #f0f5f9, #ffffff, #f0f5f9, #e0f0f5, #d0e6ec); + color: #000000; + `); + break; + } + case "": { + box.setCss(` + background: linear-gradient(to bottom, #050818, #08142a, #0c1d3b, #101f47, #142556); + color: #ffffff; + `); + break; + } + default: { + box.setCss(` + background: red; + color: blue; + `); + break; + } + } +} + +export const WeatherInfo = (weatherData) => Widget.Box({ + class_name: 'weather-info', + vertical: true, + children: [ + Widget.Label({ label: weatherData.date.substring(0, 3).toUpperCase(), }), + Widget.Label({ label: weatherData.hour + 'h', }), + Widget.Label({ label: weatherData.icon, class_name: 'weather-icon', }), + Widget.Label({ label: weatherData.temperature, class_name: 'weather-temperature' }), + Widget.Box({ vexpand: true }), + weatherData.rain !== '0 mm' ? Widget.Label({ label: weatherData.rain, }) : null, + // Widget.Label({ label: "ξ‰Ύ " + weatherData.wind, }), + // Widget.Label({ label: weatherData.humidity, }), + Widget.Label({ label: '↑ ' + weatherData.maxTemp, class_name: 'weather-max', }), + Widget.Label({ label: '↓ ' + weatherData.minTemp, class_name: 'weather-min', }), + ], + setup: self => self.hook(Weather, () => { + weatherBackgroundStyle(weatherData.icon, self) + }), +}); + +function getMostCommon(arr){ + return arr.sort((a,b) => + arr.filter(v => v===a).length + - arr.filter(v => v===b).length + ).pop(); +} + +export const WeatherBoxChild = (w) => Widget.Box({ + class_name: 'qs-weather-box-child', + vertical: true, + hexpand: true, + children: [ + Widget.Label({ label: w.hour + 'h', class_name: 'weather-hour', }), + Widget.Label({ label: w.icon, class_name: 'weather-icon', }), + Widget.Label({ label: w.temperature, class_name: 'weather-temperature' }), + Widget.Box({ vexpand: true }), + w.rain !== '0 mm' ? Widget.Label({ label: w.rain, class_name: 'weather-rain', }) : null, + Widget.Label({ label: "ξ‰Ύ " + Math.round(w.wind.replace(/kph$/, '')) + ' kph', class_name: 'weather-wind', }), + ], +}); + +export const WeatherBoxChildWrapper = (w, temperatureDataPerDay, totalWeatherForecastDataArray) => Widget.Box({ + class_name: 'qs-weather-box-child-wrapper', + hexpand: true, + setup: self => self.hook(Weather, () => { + let useTotalInstead = false; + + if (totalWeatherForecastDataArray.length) { + useTotalInstead = true; + + totalWeatherForecastDataArray.forEach(totalEl => { + self.add( + WeatherBoxChild(totalEl), + ) + }) + } + + if (false === useTotalInstead && w && temperatureDataPerDay && temperatureDataPerDay[w.date.substring(0, 3).toUpperCase()].data.length) { + temperatureDataPerDay[w.date.substring(0, 3).toUpperCase()].data.forEach(el => { + self.add( + WeatherBoxChild(el), + ) + }) + } + }), +}); + +export const WeatherMainWidget = (widgetIcon, widgetDate, rain, temperatureDataPerDay, w, totalWeatherForecastDataArray = []) => Widget.Box({ + class_name: 'qsweather-widget', + vertical: true, + hexpand: true, + children: [ + Widget.Box({ + class_name: 'qs-weather-box-main', + children: [ + Widget.Label({ label: widgetIcon, class_name: 'weather-icon', }), + rain != 0 ? Widget.Label({ label: rain + 'mm', class_name: 'weather-rain', }): null, + Widget.Box({ hexpand: true }), + Widget.Label({ label: '↑ ' + temperatureDataPerDay[widgetDate.substring(0, 3).toUpperCase()].maxTemp + ' ', class_name: 'weather-max', }), + Widget.Label({ label: '↓ ' + temperatureDataPerDay[widgetDate.substring(0, 3).toUpperCase()].minTemp + ' ', class_name: 'weather-min', }), + Widget.Label({ label: widgetDate.substring(0, 3).toUpperCase(), class_name: 'weather-hour', }), + ] + }), + WeatherBoxChildWrapper(w, temperatureDataPerDay, totalWeatherForecastDataArray) + + ], + setup: self => self.hook(Weather, () => { + weatherBackgroundStyle(widgetIcon, self); + }), +}); + +export const Tooltip = (total = null) => Widget.Box({ + setup: self => self.hook(Weather, () => { + let tooltip = Weather.tooltip; + + if (tooltip) { + self.get_children().forEach(ch => ch.destroy()); + + let prevDayName = null; + let temperatureDataPerDay = {}; + let weatherStatusIconArray = []; + let weatherForecastDataArray = []; + let totalWeatherForecastDataArray = []; + + let totalWeatherForecastsCounter = total; + let forecastWidgetsNumber = 0; + + tooltip.forEach(w => { + if (w.date !== prevDayName) { + weatherStatusIconArray = []; + prevDayName = w.date; + forecastWidgetsNumber = 0; + weatherForecastDataArray = []; + } + + // used to retrieve min/max temp per day + const date = w.date.substring(0, 3).toUpperCase(); + const temperature = parseInt(w.temperature); + + const rain = w.rain.replace(/ mm$/, ''); + forecastWidgetsNumber = forecastWidgetsNumber + 1; + + weatherForecastDataArray.push(w); + + if (total && totalWeatherForecastsCounter && totalWeatherForecastsCounter > 0) { + totalWeatherForecastDataArray.push(w); + totalWeatherForecastsCounter = totalWeatherForecastsCounter - 1; + } + + // If the date is not already in the object, initialize it + if (!temperatureDataPerDay[date]) { + temperatureDataPerDay[date] = { + minTemp: temperature, + maxTemp: temperature, + rain: rain, + icons: [], + widgetsNumber: forecastWidgetsNumber, + data: weatherForecastDataArray, + }; + } else { + // Update min and max temperatures if necessary + temperatureDataPerDay[date].minTemp = Math.min(temperatureDataPerDay[date].minTemp, temperature); + temperatureDataPerDay[date].maxTemp = Math.max(temperatureDataPerDay[date].maxTemp, temperature); + temperatureDataPerDay[date].rain = Math.max(temperatureDataPerDay[date].rain, rain); + temperatureDataPerDay[date].icons = weatherStatusIconArray; + temperatureDataPerDay[date].widgetsNumber = forecastWidgetsNumber; + temperatureDataPerDay[date].data = weatherForecastDataArray; + } + + // add icon to the array in between somewhat sunny hours, used to later get most common icon for main widget days + if (w.hour >= 7 && w.hour <= 19) { + weatherStatusIconArray.push(w.icon); + } + }); + + // clear for next loop + prevDayName = null; + + let widgetIcon = null; + let widgetDate = null; + + let w = null; + for (let i = 0; i < tooltip.length; i++) { + w = tooltip[i]; + + // console.log('loop ' + w.date + ' h ' + w.hour + ' i ' + w.icon ) + + // used to limit forecast to specified amount (if total variable is provided, it will display that amount of forecast widgets on a main one) + if (total && total >= 0) { + + if (!widgetIcon) { + widgetIcon = w.icon; + } + + if (!widgetDate) { + widgetDate = w.date; + } + + const rain = temperatureDataPerDay[widgetDate.substring(0, 3).toUpperCase()].rain; + + self.add( + WeatherMainWidget(widgetIcon, widgetDate, rain, temperatureDataPerDay, w, totalWeatherForecastDataArray) + ); + break; + } + + // if provided date differs to previous day name, by default prevDayName is null + if (w.date !== prevDayName && i > 0) { + // adds spacing to the widgets + self.add( + Widget.Box({ + children: [ + Widget.Label({ label: ' ', class_name: 'weather-spacing' }), + ], + }), + ); + } + + // logic that determines if the widget contains just 1 forecast widget, if so it will display it in a smaller widget - like the one for the forecast days + if (temperatureDataPerDay[w.date.substring(0, 3).toUpperCase()].widgetsNumber === 1) { + prevDayName = w.date; + + self.add(WeatherInfo(w)); + continue; + } + + // this one creates main one - wide weather widget that contains smaller forecast widgets + if (w.date !== prevDayName) { + const rain = temperatureDataPerDay[w.date.substring(0, 3).toUpperCase()].rain; + let icon = getMostCommon(temperatureDataPerDay[w.date.substring(0, 3).toUpperCase()].icons); + widgetDate = w.date; + + if (!icon) { + icon = w.icon; + } + + widgetIcon = icon; + + self.add( + WeatherMainWidget(widgetIcon, widgetDate, rain, temperatureDataPerDay, w) + ); + } + + prevDayName = w.date; + }; + } + }), +}); + +export const ResetTimer = props => Widget.Label({ + ...props, + setup: self => { + self.poll(600000, (self) => { + Weather.weatherData + }); + }, +}); + +export const Forecast = () => Widget.Box({ + class_name: 'datemenu', + vertical: true, + children: [ + Widget.Box({ + class_name: 'tooltip', + children: [ + ResetTimer(), + Tooltip(), + ] + }) + ] +}); + +export const PopupContent = () => Widget.Box({ + class_name: 'weather', + vexpand: false, + children: [ + Widget.Box({ + vertical: true, + children: [ + Forecast(), + // RefreshButton({ class_name: 'header panel-button', }), + ] + }) + ] +}); + +export const RefreshButton = props => Widget.Button({ + ...props, + class_name: 'weather-refresh', + child: Widget.Icon({ + icon: 'view-refresh-symbolic', + hpack: 'end', + }), + on_clicked: () => { + Weather.weatherData + }, +}); \ No newline at end of file diff --git a/ags/weather/weather_service.js b/ags/weather/weather_service.js new file mode 100644 index 0000000..77d7d86 --- /dev/null +++ b/ags/weather/weather_service.js @@ -0,0 +1,31 @@ +import Service from 'resource:///com/github/Aylur/ags/service.js'; +import * as Utils from 'resource:///com/github/Aylur/ags/utils.js'; + +class Weather extends Service { + static { Service.register(this); } + + constructor() { + super(); + this._temperatureWeather = null; + this._tooltip = null; + } + + get weatherData() { + Utils.execAsync(['bash', '-c', "openweathermap ags"]).catch(print); + } + + get temperatureWeather() { + return this._temperatureWeather; + } + setTemperatureWeather(temp) { + this._temperatureWeather = temp; + this.emit('changed'); + } + + get tooltip() { + return this._tooltip; + } + setTooltip(text) { this._tooltip = text } +} + +export default new Weather(); \ No newline at end of file diff --git a/eww/eww.scss b/eww/eww.scss index 51e434b..23841ba 100644 --- a/eww/eww.scss +++ b/eww/eww.scss @@ -72,4 +72,4 @@ min-height: 20px; min-width: 310px; margin : 10px 0px 0px 0px; -} \ No newline at end of file +} diff --git a/eww/eww.yuck b/eww/eww.yuck index 7fd6918..a6e29a2 100644 --- a/eww/eww.yuck +++ b/eww/eww.yuck @@ -3,55 +3,35 @@ (deflisten music_position "scripts/music listen-position") (deflisten music_status "scripts/music listen-status") -(defwidget music_progress_widget [] - (box - :class "music_progress_box" - :halign "center" - :space-evenly false - (box - :state "progress" - (scale - :onscroll "playerctl position {}" - :min 0 - :max "${music_metadata.length}" - :active true - :hexpand true - :vexpand true - :value "${music_position.position}" - ) - ) - ) -) - ; Music -(defwidget music [] - (box :class "genwin" :orientation "h" :space-evenly "false" :vexpand "false" :hexpand "false" - (box :class "album_art" :vexpand "false" :hexpand "false" :style "background-image: url('${music_metadata.arturl}');") - (box :orientation "v" :spacing 20 :space-evenly "false" :vexpand "false" :hexpand "false" - (label :halign "center" :class "song" :wrap "true" :limit-width 20 :text "${music_metadata.title}") - (label :halign "center" :class "artist" :wrap "true" :limit-width 15 :text "${music_metadata.artist}") - (box :orientation "h" :spacing 20 :halign "center" :space-evenly "true" :vexpand "false" :hexpand "false" - (button :class "btn_prev" :onclick "playerctl previous" "") - (button :class "btn_play" :onclick "playerctl play-pause" {"${music_status.status}" == "Playing" ? "" : ""}) - (button :class "btn_next" :onclick "playerctl next" "") - ) - (box :class "music_bar" :halign "center" :state "progress" - (scale - :onscroll "playerctl position {}" - :onclick "playerctl position {}" - :min 0 - :max "${music_metadata.length}" - :active true - :hexpand true - :vexpand true - :value "${music_position.position}" - ) - ) - ) - ) +(defwidget music [] + (box :class "genwin" :orientation "h" :space-evenly "false" :vexpand "false" :hexpand "false" + (box :class "album_art" :vexpand "false" :hexpand "false" :style "background-image: url('${music_metadata.arturl}');") + (box :orientation "v" :spacing 20 :space-evenly "false" :vexpand "false" :hexpand "false" + (label :halign "center" :class "song" :wrap "true" :limit-width 20 :text "${music_metadata.title}") + (label :halign "center" :class "artist" :wrap "true" :limit-width 15 :text "${music_metadata.artist}") + (box :orientation "h" :spacing 20 :halign "center" :space-evenly "true" :vexpand "false" :hexpand "false" + (button :class "btn_prev" :onclick "playerctl previous" "") + (button :class "btn_play" :onclick "playerctl play-pause" {"${music_status.status}" == "Playing" ? "" : ""}) + (button :class "btn_next" :onclick "playerctl next" "") + ) + (box :class "music_bar" :halign "center" :state "progress" + (scale + :onscroll "playerctl position {}" + :onclick "playerctl position {}" + :min 0 + :max "${music_metadata.length}" + :active true + :hexpand true + :vexpand true + :value "${music_position.position}" + ) + ) + ) + ) ) ; music (defwindow music :stacking "fg" :focusable "false" :screen 1 :monitor 0 - :geometry (geometry :x 100 :y 100 :width 610 :height 280) - (music)) + :geometry (geometry :x 100 :y 100 :width 610 :height 280) + (music)) diff --git a/eww/weather/eww.scss b/eww/weather/eww.scss new file mode 100644 index 0000000..2e36366 --- /dev/null +++ b/eww/weather/eww.scss @@ -0,0 +1,28 @@ +/** Global *******************************************/ +*{ + all: unset; + font-family: feather; + font-family: Iosevka; +} + +/** Background ***************************************/ +.bg { + // background-image: url("images/bg.png"); + background-color: #474D59; + opacity: 1; +} + +/** Generic window ***********************************/ +.genwin { + background-color: #2E3440; + border-radius: 16px; + padding: 20px; +} + +.label_temp { + font-size: 100; +} + +.label_stat { + font-size: 50; +} diff --git a/eww/weather/eww.yuck b/eww/weather/eww.yuck new file mode 100644 index 0000000..c0c7f06 --- /dev/null +++ b/eww/weather/eww.yuck @@ -0,0 +1,26 @@ +; Weather data +(defpoll weatherdata :interval "15m" `wttr/wttr.py`) + +(defwidget weather [] + (box :class "genwin" :style "background-image: url('images/bg.png');background-size: cover;" + (box :orientation "v" :spacing 10 :space-evenly false :vexpand false :hexpand false + (box :orientation "h" :spacing 0 :vexpand false :hexpand false + (label :class "label_temp" :halign "center" :style "color: white;" :text "${weatherdata.current_condition[0].temp_C}") + ; (label :class "iconweather" :halign "center" :text "${weatherdata.current_condition[0].weatherCode}") + ) + (box :orientation "v" :spacing 10 :halign "center" :space-evenly false :vexpand false :hexpand false + (label :class "label_stat" :text "${weatherdata.current_condition[0].weatherDesc[0].value}") + (box :orientation "h" :spacing 10 :space-evenly false :vexpand false :hexpand false + (label :class "label_quote" :text "${weatherdata.nearest_area[0].country[0].value}") + (label :class "label_quote" :text "${weatherdata.nearest_area[0].region[0].value}") + ) + ) + ) + ) +) + +; Weather window +(defwindow weather :stacking "fg" :focusable true :exclusive true :screen 1 :windowtype "dock" + :geometry (geometry :x 100 :y 100 :width 610 :height 280) + (weather) +) diff --git a/eww/weather/images/bg.png b/eww/weather/images/bg.png new file mode 100644 index 0000000..4a9b899 Binary files /dev/null and b/eww/weather/images/bg.png differ diff --git a/eww/weather/wttr.json b/eww/weather/wttr.json new file mode 100644 index 0000000..4fb9a29 --- /dev/null +++ b/eww/weather/wttr.json @@ -0,0 +1,1146 @@ +{ + "current_condition": [ + { + "FeelsLikeC": "8", + "FeelsLikeF": "47", + "cloudcover": "77", + "humidity": "88", + "localObsDateTime": "2024-09-03 09:00 AM", + "observation_time": "02:00 AM", + "precipInches": "0.0", + "precipMM": "0.1", + "pressure": "1008", + "pressureInches": "30", + "temp_C": "10", + "temp_F": "50", + "uvIndex": "3", + "visibility": "10", + "visibilityMiles": "6", + "weatherCode": "176", + "weatherDesc": [{ "value": "Patchy rain nearby" }], + "weatherIconUrl": [{ "value": "" }], + "winddir16Point": "W", + "winddirDegree": "268", + "windspeedKmph": "14", + "windspeedMiles": "9" + } + ], + "nearest_area": [ + { + "areaName": [{ "value": "Вомск" }], + "country": [{ "value": "Russia" }], + "latitude": "56.500", + "longitude": "84.967", + "population": "485519", + "region": [{ "value": "Tomsk" }], + "weatherUrl": [{ "value": "" }] + } + ], + "request": [{ "query": "Lat 56.49 and Lon 84.95", "type": "LatLon" }], + "weather": [ + { + "astronomy": [ + { + "moon_illumination": "0", + "moon_phase": "Waxing Crescent", + "moonrise": "06:17 AM", + "moonset": "08:22 PM", + "sunrise": "06:27 AM", + "sunset": "08:10 PM" + } + ], + "avgtempC": "9", + "avgtempF": "48", + "date": "2024-09-03", + "hourly": [ + { + "DewPointC": "7", + "DewPointF": "45", + "FeelsLikeC": "5", + "FeelsLikeF": "41", + "HeatIndexC": "8", + "HeatIndexF": "46", + "WindChillC": "5", + "WindChillF": "41", + "WindGustKmph": "31", + "WindGustMiles": "19", + "chanceoffog": "0", + "chanceoffrost": "0", + "chanceofhightemp": "0", + "chanceofovercast": "90", + "chanceofrain": "100", + "chanceofremdry": "0", + "chanceofsnow": "0", + "chanceofsunshine": "0", + "chanceofthunder": "0", + "chanceofwindy": "0", + "cloudcover": "100", + "diffRad": "0.0", + "humidity": "97", + "precipInches": "0.0", + "precipMM": "0.6", + "pressure": "1005", + "pressureInches": "30", + "shortRad": "0.0", + "tempC": "8", + "tempF": "46", + "time": "0", + "uvIndex": "1", + "visibility": "2", + "visibilityMiles": "1", + "weatherCode": "266", + "weatherDesc": [{ "value": "Light drizzle" }], + "weatherIconUrl": [{ "value": "" }], + "winddir16Point": "SSW", + "winddirDegree": "192", + "windspeedKmph": "20", + "windspeedMiles": "12" + }, + { + "DewPointC": "9", + "DewPointF": "48", + "FeelsLikeC": "7", + "FeelsLikeF": "44", + "HeatIndexC": "9", + "HeatIndexF": "48", + "WindChillC": "7", + "WindChillF": "44", + "WindGustKmph": "22", + "WindGustMiles": "14", + "chanceoffog": "0", + "chanceoffrost": "0", + "chanceofhightemp": "0", + "chanceofovercast": "92", + "chanceofrain": "100", + "chanceofremdry": "0", + "chanceofsnow": "0", + "chanceofsunshine": "0", + "chanceofthunder": "0", + "chanceofwindy": "0", + "cloudcover": "100", + "diffRad": "1.5", + "humidity": "99", + "precipInches": "0.0", + "precipMM": "0.3", + "pressure": "1004", + "pressureInches": "30", + "shortRad": "1.9", + "tempC": "9", + "tempF": "48", + "time": "300", + "uvIndex": "1", + "visibility": "2", + "visibilityMiles": "1", + "weatherCode": "266", + "weatherDesc": [{ "value": "Light drizzle" }], + "weatherIconUrl": [{ "value": "" }], + "winddir16Point": "SW", + "winddirDegree": "233", + "windspeedKmph": "14", + "windspeedMiles": "9" + }, + { + "DewPointC": "9", + "DewPointF": "48", + "FeelsLikeC": "8", + "FeelsLikeF": "47", + "HeatIndexC": "10", + "HeatIndexF": "51", + "WindChillC": "8", + "WindChillF": "47", + "WindGustKmph": "20", + "WindGustMiles": "13", + "chanceoffog": "7", + "chanceoffrost": "0", + "chanceofhightemp": "0", + "chanceofovercast": "88", + "chanceofrain": "100", + "chanceofremdry": "0", + "chanceofsnow": "0", + "chanceofsunshine": "0", + "chanceofthunder": "0", + "chanceofwindy": "0", + "cloudcover": "100", + "diffRad": "22.1", + "humidity": "92", + "precipInches": "0.0", + "precipMM": "0.2", + "pressure": "1006", + "pressureInches": "30", + "shortRad": "38.6", + "tempC": "10", + "tempF": "51", + "time": "600", + "uvIndex": "1", + "visibility": "10", + "visibilityMiles": "6", + "weatherCode": "353", + "weatherDesc": [{ "value": "Light rain shower" }], + "weatherIconUrl": [{ "value": "" }], + "winddir16Point": "W", + "winddirDegree": "280", + "windspeedKmph": "15", + "windspeedMiles": "9" + }, + { + "DewPointC": "9", + "DewPointF": "47", + "FeelsLikeC": "9", + "FeelsLikeF": "48", + "HeatIndexC": "11", + "HeatIndexF": "51", + "WindChillC": "9", + "WindChillF": "48", + "WindGustKmph": "22", + "WindGustMiles": "13", + "chanceoffog": "0", + "chanceoffrost": "0", + "chanceofhightemp": "0", + "chanceofovercast": "81", + "chanceofrain": "100", + "chanceofremdry": "0", + "chanceofsnow": "0", + "chanceofsunshine": "0", + "chanceofthunder": "0", + "chanceofwindy": "0", + "cloudcover": "84", + "diffRad": "100.4", + "humidity": "86", + "precipInches": "0.0", + "precipMM": "0.1", + "pressure": "1008", + "pressureInches": "30", + "shortRad": "176.4", + "tempC": "11", + "tempF": "51", + "time": "900", + "uvIndex": "3", + "visibility": "10", + "visibilityMiles": "6", + "weatherCode": "176", + "weatherDesc": [{ "value": "Patchy rain nearby" }], + "weatherIconUrl": [{ "value": "" }], + "winddir16Point": "W", + "winddirDegree": "276", + "windspeedKmph": "15", + "windspeedMiles": "10" + }, + { + "DewPointC": "8", + "DewPointF": "47", + "FeelsLikeC": "9", + "FeelsLikeF": "49", + "HeatIndexC": "11", + "HeatIndexF": "51", + "WindChillC": "9", + "WindChillF": "49", + "WindGustKmph": "17", + "WindGustMiles": "11", + "chanceoffog": "0", + "chanceoffrost": "0", + "chanceofhightemp": "0", + "chanceofovercast": "86", + "chanceofrain": "100", + "chanceofremdry": "0", + "chanceofsnow": "0", + "chanceofsunshine": "0", + "chanceofthunder": "0", + "chanceofwindy": "0", + "cloudcover": "84", + "diffRad": "90.4", + "humidity": "85", + "precipInches": "0.0", + "precipMM": "0.2", + "pressure": "1009", + "pressureInches": "30", + "shortRad": "160.4", + "tempC": "11", + "tempF": "51", + "time": "1200", + "uvIndex": "3", + "visibility": "10", + "visibilityMiles": "6", + "weatherCode": "353", + "weatherDesc": [{ "value": "Light rain shower" }], + "weatherIconUrl": [{ "value": "" }], + "winddir16Point": "W", + "winddirDegree": "262", + "windspeedKmph": "12", + "windspeedMiles": "7" + }, + { + "DewPointC": "8", + "DewPointF": "47", + "FeelsLikeC": "8", + "FeelsLikeF": "47", + "HeatIndexC": "9", + "HeatIndexF": "49", + "WindChillC": "8", + "WindChillF": "47", + "WindGustKmph": "15", + "WindGustMiles": "9", + "chanceoffog": "0", + "chanceoffrost": "0", + "chanceofhightemp": "0", + "chanceofovercast": "81", + "chanceofrain": "71", + "chanceofremdry": "0", + "chanceofsnow": "0", + "chanceofsunshine": "0", + "chanceofthunder": "0", + "chanceofwindy": "0", + "cloudcover": "100", + "diffRad": "7.1", + "humidity": "92", + "precipInches": "0.0", + "precipMM": "0.0", + "pressure": "1011", + "pressureInches": "30", + "shortRad": "13.3", + "tempC": "9", + "tempF": "49", + "time": "1500", + "uvIndex": "2", + "visibility": "10", + "visibilityMiles": "6", + "weatherCode": "176", + "weatherDesc": [{ "value": "Patchy rain nearby" }], + "weatherIconUrl": [{ "value": "" }], + "winddir16Point": "SW", + "winddirDegree": "225", + "windspeedKmph": "9", + "windspeedMiles": "5" + }, + { + "DewPointC": "7", + "DewPointF": "45", + "FeelsLikeC": "7", + "FeelsLikeF": "44", + "HeatIndexC": "8", + "HeatIndexF": "46", + "WindChillC": "7", + "WindChillF": "44", + "WindGustKmph": "12", + "WindGustMiles": "7", + "chanceoffog": "2", + "chanceoffrost": "0", + "chanceofhightemp": "0", + "chanceofovercast": "90", + "chanceofrain": "0", + "chanceofremdry": "93", + "chanceofsnow": "0", + "chanceofsunshine": "17", + "chanceofthunder": "0", + "chanceofwindy": "0", + "cloudcover": "100", + "diffRad": "1.8", + "humidity": "97", + "precipInches": "0.0", + "precipMM": "0.0", + "pressure": "1011", + "pressureInches": "30", + "shortRad": "3.3", + "tempC": "8", + "tempF": "46", + "time": "1800", + "uvIndex": "2", + "visibility": "2", + "visibilityMiles": "1", + "weatherCode": "143", + "weatherDesc": [{ "value": "Mist" }], + "weatherIconUrl": [{ "value": "" }], + "winddir16Point": "SW", + "winddirDegree": "226", + "windspeedKmph": "6", + "windspeedMiles": "4" + }, + { + "DewPointC": "7", + "DewPointF": "45", + "FeelsLikeC": "8", + "FeelsLikeF": "46", + "HeatIndexC": "8", + "HeatIndexF": "46", + "WindChillC": "8", + "WindChillF": "46", + "WindGustKmph": "3", + "WindGustMiles": "2", + "chanceoffog": "41", + "chanceoffrost": "0", + "chanceofhightemp": "0", + "chanceofovercast": "94", + "chanceofrain": "0", + "chanceofremdry": "92", + "chanceofsnow": "0", + "chanceofsunshine": "13", + "chanceofthunder": "0", + "chanceofwindy": "0", + "cloudcover": "90", + "diffRad": "0.0", + "humidity": "96", + "precipInches": "0.0", + "precipMM": "0.0", + "pressure": "1011", + "pressureInches": "30", + "shortRad": "0.0", + "tempC": "8", + "tempF": "46", + "time": "2100", + "uvIndex": "1", + "visibility": "2", + "visibilityMiles": "1", + "weatherCode": "143", + "weatherDesc": [{ "value": "Mist" }], + "weatherIconUrl": [{ "value": "" }], + "winddir16Point": "WSW", + "winddirDegree": "244", + "windspeedKmph": "1", + "windspeedMiles": "1" + } + ], + "maxtempC": "11", + "maxtempF": "52", + "mintempC": "7", + "mintempF": "44", + "sunHour": "10.5", + "totalSnow_cm": "0.0", + "uvIndex": "1" + }, + { + "astronomy": [ + { + "moon_illumination": "1", + "moon_phase": "Waxing Crescent", + "moonrise": "07:35 AM", + "moonset": "08:26 PM", + "sunrise": "06:29 AM", + "sunset": "08:07 PM" + } + ], + "avgtempC": "9", + "avgtempF": "49", + "date": "2024-09-04", + "hourly": [ + { + "DewPointC": "6", + "DewPointF": "43", + "FeelsLikeC": "6", + "FeelsLikeF": "43", + "HeatIndexC": "6", + "HeatIndexF": "44", + "WindChillC": "6", + "WindChillF": "43", + "WindGustKmph": "9", + "WindGustMiles": "6", + "chanceoffog": "16", + "chanceoffrost": "0", + "chanceofhightemp": "0", + "chanceofovercast": "86", + "chanceofrain": "0", + "chanceofremdry": "86", + "chanceofsnow": "0", + "chanceofsunshine": "17", + "chanceofthunder": "0", + "chanceofwindy": "0", + "cloudcover": "66", + "diffRad": "0.0", + "humidity": "97", + "precipInches": "0.0", + "precipMM": "0.0", + "pressure": "1009", + "pressureInches": "30", + "shortRad": "0.0", + "tempC": "6", + "tempF": "44", + "time": "0", + "uvIndex": "1", + "visibility": "2", + "visibilityMiles": "1", + "weatherCode": "143", + "weatherDesc": [{ "value": "Mist" }], + "weatherIconUrl": [{ "value": "" }], + "winddir16Point": "ENE", + "winddirDegree": "60", + "windspeedKmph": "5", + "windspeedMiles": "3" + }, + { + "DewPointC": "6", + "DewPointF": "44", + "FeelsLikeC": "6", + "FeelsLikeF": "43", + "HeatIndexC": "7", + "HeatIndexF": "45", + "WindChillC": "6", + "WindChillF": "43", + "WindGustKmph": "11", + "WindGustMiles": "7", + "chanceoffog": "0", + "chanceoffrost": "0", + "chanceofhightemp": "0", + "chanceofovercast": "81", + "chanceofrain": "0", + "chanceofremdry": "91", + "chanceofsnow": "0", + "chanceofsunshine": "19", + "chanceofthunder": "0", + "chanceofwindy": "0", + "cloudcover": "50", + "diffRad": "28.3", + "humidity": "95", + "precipInches": "0.0", + "precipMM": "0.0", + "pressure": "1009", + "pressureInches": "30", + "shortRad": "57.6", + "tempC": "7", + "tempF": "45", + "time": "300", + "uvIndex": "1", + "visibility": "2", + "visibilityMiles": "1", + "weatherCode": "143", + "weatherDesc": [{ "value": "Mist" }], + "weatherIconUrl": [{ "value": "" }], + "winddir16Point": "NE", + "winddirDegree": "37", + "windspeedKmph": "6", + "windspeedMiles": "4" + }, + { + "DewPointC": "7", + "DewPointF": "45", + "FeelsLikeC": "10", + "FeelsLikeF": "51", + "HeatIndexC": "12", + "HeatIndexF": "53", + "WindChillC": "10", + "WindChillF": "51", + "WindGustKmph": "12", + "WindGustMiles": "7", + "chanceoffog": "0", + "chanceoffrost": "0", + "chanceofhightemp": "0", + "chanceofovercast": "85", + "chanceofrain": "0", + "chanceofremdry": "85", + "chanceofsnow": "0", + "chanceofsunshine": "10", + "chanceofthunder": "0", + "chanceofwindy": "0", + "cloudcover": "74", + "diffRad": "94.1", + "humidity": "74", + "precipInches": "0.0", + "precipMM": "0.0", + "pressure": "1009", + "pressureInches": "30", + "shortRad": "242.7", + "tempC": "12", + "tempF": "53", + "time": "600", + "uvIndex": "1", + "visibility": "10", + "visibilityMiles": "6", + "weatherCode": "119", + "weatherDesc": [{ "value": "Cloudy " }], + "weatherIconUrl": [{ "value": "" }], + "winddir16Point": "NNE", + "winddirDegree": "30", + "windspeedKmph": "10", + "windspeedMiles": "6" + }, + { + "DewPointC": "6", + "DewPointF": "43", + "FeelsLikeC": "13", + "FeelsLikeF": "55", + "HeatIndexC": "14", + "HeatIndexF": "57", + "WindChillC": "13", + "WindChillF": "55", + "WindGustKmph": "14", + "WindGustMiles": "9", + "chanceoffog": "0", + "chanceoffrost": "0", + "chanceofhightemp": "0", + "chanceofovercast": "81", + "chanceofrain": "100", + "chanceofremdry": "0", + "chanceofsnow": "0", + "chanceofsunshine": "0", + "chanceofthunder": "0", + "chanceofwindy": "0", + "cloudcover": "100", + "diffRad": "181.7", + "humidity": "59", + "precipInches": "0.0", + "precipMM": "0.1", + "pressure": "1009", + "pressureInches": "30", + "shortRad": "463.6", + "tempC": "14", + "tempF": "57", + "time": "900", + "uvIndex": "3", + "visibility": "10", + "visibilityMiles": "6", + "weatherCode": "176", + "weatherDesc": [{ "value": "Patchy rain nearby" }], + "weatherIconUrl": [{ "value": "" }], + "winddir16Point": "N", + "winddirDegree": "358", + "windspeedKmph": "12", + "windspeedMiles": "7" + }, + { + "DewPointC": "5", + "DewPointF": "41", + "FeelsLikeC": "11", + "FeelsLikeF": "52", + "HeatIndexC": "13", + "HeatIndexF": "55", + "WindChillC": "11", + "WindChillF": "52", + "WindGustKmph": "17", + "WindGustMiles": "11", + "chanceoffog": "0", + "chanceoffrost": "0", + "chanceofhightemp": "0", + "chanceofovercast": "80", + "chanceofrain": "0", + "chanceofremdry": "83", + "chanceofsnow": "0", + "chanceofsunshine": "19", + "chanceofthunder": "0", + "chanceofwindy": "0", + "cloudcover": "92", + "diffRad": "153.0", + "humidity": "60", + "precipInches": "0.0", + "precipMM": "0.0", + "pressure": "1011", + "pressureInches": "30", + "shortRad": "327.8", + "tempC": "13", + "tempF": "55", + "time": "1200", + "uvIndex": "3", + "visibility": "10", + "visibilityMiles": "6", + "weatherCode": "122", + "weatherDesc": [{ "value": "Overcast " }], + "weatherIconUrl": [{ "value": "" }], + "winddir16Point": "N", + "winddirDegree": "352", + "windspeedKmph": "13", + "windspeedMiles": "8" + }, + { + "DewPointC": "6", + "DewPointF": "43", + "FeelsLikeC": "8", + "FeelsLikeF": "46", + "HeatIndexC": "9", + "HeatIndexF": "48", + "WindChillC": "8", + "WindChillF": "46", + "WindGustKmph": "15", + "WindGustMiles": "9", + "chanceoffog": "0", + "chanceoffrost": "0", + "chanceofhightemp": "0", + "chanceofovercast": "82", + "chanceofrain": "62", + "chanceofremdry": "0", + "chanceofsnow": "0", + "chanceofsunshine": "0", + "chanceofthunder": "0", + "chanceofwindy": "0", + "cloudcover": "82", + "diffRad": "14.3", + "humidity": "81", + "precipInches": "0.0", + "precipMM": "0.0", + "pressure": "1012", + "pressureInches": "30", + "shortRad": "29.1", + "tempC": "9", + "tempF": "48", + "time": "1500", + "uvIndex": "2", + "visibility": "10", + "visibilityMiles": "6", + "weatherCode": "176", + "weatherDesc": [{ "value": "Patchy rain nearby" }], + "weatherIconUrl": [{ "value": "" }], + "winddir16Point": "N", + "winddirDegree": "357", + "windspeedKmph": "8", + "windspeedMiles": "5" + }, + { + "DewPointC": "6", + "DewPointF": "42", + "FeelsLikeC": "5", + "FeelsLikeF": "41", + "HeatIndexC": "6", + "HeatIndexF": "43", + "WindChillC": "5", + "WindChillF": "41", + "WindGustKmph": "13", + "WindGustMiles": "8", + "chanceoffog": "7", + "chanceoffrost": "0", + "chanceofhightemp": "0", + "chanceofovercast": "83", + "chanceofrain": "0", + "chanceofremdry": "93", + "chanceofsnow": "0", + "chanceofsunshine": "6", + "chanceofthunder": "0", + "chanceofwindy": "0", + "cloudcover": "34", + "diffRad": "3.6", + "humidity": "96", + "precipInches": "0.0", + "precipMM": "0.0", + "pressure": "1014", + "pressureInches": "30", + "shortRad": "7.3", + "tempC": "6", + "tempF": "43", + "time": "1800", + "uvIndex": "2", + "visibility": "2", + "visibilityMiles": "1", + "weatherCode": "143", + "weatherDesc": [{ "value": "Mist" }], + "weatherIconUrl": [{ "value": "" }], + "winddir16Point": "N", + "winddirDegree": "4", + "windspeedKmph": "6", + "windspeedMiles": "4" + }, + { + "DewPointC": "6", + "DewPointF": "42", + "FeelsLikeC": "5", + "FeelsLikeF": "42", + "HeatIndexC": "6", + "HeatIndexF": "43", + "WindChillC": "5", + "WindChillF": "42", + "WindGustKmph": "10", + "WindGustMiles": "6", + "chanceoffog": "20", + "chanceoffrost": "0", + "chanceofhightemp": "0", + "chanceofovercast": "82", + "chanceofrain": "0", + "chanceofremdry": "93", + "chanceofsnow": "0", + "chanceofsunshine": "10", + "chanceofthunder": "0", + "chanceofwindy": "0", + "cloudcover": "68", + "diffRad": "0.0", + "humidity": "96", + "precipInches": "0.0", + "precipMM": "0.0", + "pressure": "1014", + "pressureInches": "30", + "shortRad": "0.0", + "tempC": "6", + "tempF": "43", + "time": "2100", + "uvIndex": "1", + "visibility": "2", + "visibilityMiles": "1", + "weatherCode": "143", + "weatherDesc": [{ "value": "Mist" }], + "weatherIconUrl": [{ "value": "" }], + "winddir16Point": "N", + "winddirDegree": "3", + "windspeedKmph": "5", + "windspeedMiles": "3" + } + ], + "maxtempC": "14", + "maxtempF": "57", + "mintempC": "6", + "mintempF": "43", + "sunHour": "11.3", + "totalSnow_cm": "0.0", + "uvIndex": "3" + }, + { + "astronomy": [ + { + "moon_illumination": "3", + "moon_phase": "Waxing Crescent", + "moonrise": "08:50 AM", + "moonset": "08:29 PM", + "sunrise": "06:31 AM", + "sunset": "08:04 PM" + } + ], + "avgtempC": "10", + "avgtempF": "49", + "date": "2024-09-05", + "hourly": [ + { + "DewPointC": "6", + "DewPointF": "43", + "FeelsLikeC": "6", + "FeelsLikeF": "43", + "HeatIndexC": "7", + "HeatIndexF": "44", + "WindChillC": "6", + "WindChillF": "43", + "WindGustKmph": "10", + "WindGustMiles": "6", + "chanceoffog": "22", + "chanceoffrost": "0", + "chanceofhightemp": "0", + "chanceofovercast": "89", + "chanceofrain": "0", + "chanceofremdry": "84", + "chanceofsnow": "0", + "chanceofsunshine": "18", + "chanceofthunder": "0", + "chanceofwindy": "0", + "cloudcover": "67", + "diffRad": "0.0", + "humidity": "96", + "precipInches": "0.0", + "precipMM": "0.0", + "pressure": "1014", + "pressureInches": "30", + "shortRad": "0.0", + "tempC": "7", + "tempF": "44", + "time": "0", + "uvIndex": "1", + "visibility": "2", + "visibilityMiles": "1", + "weatherCode": "143", + "weatherDesc": [{ "value": "Mist" }], + "weatherIconUrl": [{ "value": "" }], + "winddir16Point": "NNE", + "winddirDegree": "14", + "windspeedKmph": "6", + "windspeedMiles": "4" + }, + { + "DewPointC": "6", + "DewPointF": "43", + "FeelsLikeC": "6", + "FeelsLikeF": "43", + "HeatIndexC": "8", + "HeatIndexF": "46", + "WindChillC": "6", + "WindChillF": "43", + "WindGustKmph": "11", + "WindGustMiles": "7", + "chanceoffog": "0", + "chanceoffrost": "0", + "chanceofhightemp": "0", + "chanceofovercast": "44", + "chanceofrain": "0", + "chanceofremdry": "90", + "chanceofsnow": "0", + "chanceofsunshine": "71", + "chanceofthunder": "0", + "chanceofwindy": "0", + "cloudcover": "55", + "diffRad": "26.2", + "humidity": "92", + "precipInches": "0.0", + "precipMM": "0.0", + "pressure": "1014", + "pressureInches": "30", + "shortRad": "55.5", + "tempC": "8", + "tempF": "46", + "time": "300", + "uvIndex": "1", + "visibility": "10", + "visibilityMiles": "6", + "weatherCode": "116", + "weatherDesc": [{ "value": "Partly Cloudy " }], + "weatherIconUrl": [{ "value": "" }], + "winddir16Point": "NNE", + "winddirDegree": "23", + "windspeedKmph": "8", + "windspeedMiles": "5" + }, + { + "DewPointC": "6", + "DewPointF": "43", + "FeelsLikeC": "11", + "FeelsLikeF": "51", + "HeatIndexC": "12", + "HeatIndexF": "53", + "WindChillC": "11", + "WindChillF": "51", + "WindGustKmph": "13", + "WindGustMiles": "8", + "chanceoffog": "0", + "chanceoffrost": "0", + "chanceofhightemp": "0", + "chanceofovercast": "86", + "chanceofrain": "68", + "chanceofremdry": "0", + "chanceofsnow": "0", + "chanceofsunshine": "0", + "chanceofthunder": "0", + "chanceofwindy": "0", + "cloudcover": "82", + "diffRad": "82.4", + "humidity": "67", + "precipInches": "0.0", + "precipMM": "0.0", + "pressure": "1013", + "pressureInches": "30", + "shortRad": "256.5", + "tempC": "12", + "tempF": "53", + "time": "600", + "uvIndex": "1", + "visibility": "10", + "visibilityMiles": "6", + "weatherCode": "176", + "weatherDesc": [{ "value": "Patchy rain nearby" }], + "weatherIconUrl": [{ "value": "" }], + "winddir16Point": "NE", + "winddirDegree": "37", + "windspeedKmph": "12", + "windspeedMiles": "7" + }, + { + "DewPointC": "5", + "DewPointF": "41", + "FeelsLikeC": "13", + "FeelsLikeF": "55", + "HeatIndexC": "14", + "HeatIndexF": "57", + "WindChillC": "13", + "WindChillF": "55", + "WindGustKmph": "15", + "WindGustMiles": "9", + "chanceoffog": "0", + "chanceoffrost": "0", + "chanceofhightemp": "0", + "chanceofovercast": "89", + "chanceofrain": "83", + "chanceofremdry": "0", + "chanceofsnow": "0", + "chanceofsunshine": "0", + "chanceofthunder": "0", + "chanceofwindy": "0", + "cloudcover": "93", + "diffRad": "163.1", + "humidity": "54", + "precipInches": "0.0", + "precipMM": "0.0", + "pressure": "1012", + "pressureInches": "30", + "shortRad": "453.8", + "tempC": "14", + "tempF": "57", + "time": "900", + "uvIndex": "3", + "visibility": "10", + "visibilityMiles": "6", + "weatherCode": "176", + "weatherDesc": [{ "value": "Patchy rain nearby" }], + "weatherIconUrl": [{ "value": "" }], + "winddir16Point": "NE", + "winddirDegree": "49", + "windspeedKmph": "13", + "windspeedMiles": "8" + }, + { + "DewPointC": "6", + "DewPointF": "44", + "FeelsLikeC": "12", + "FeelsLikeF": "54", + "HeatIndexC": "13", + "HeatIndexF": "55", + "WindChillC": "12", + "WindChillF": "54", + "WindGustKmph": "14", + "WindGustMiles": "8", + "chanceoffog": "0", + "chanceoffrost": "0", + "chanceofhightemp": "0", + "chanceofovercast": "93", + "chanceofrain": "67", + "chanceofremdry": "0", + "chanceofsnow": "0", + "chanceofsunshine": "0", + "chanceofthunder": "0", + "chanceofwindy": "0", + "cloudcover": "69", + "diffRad": "100.0", + "humidity": "64", + "precipInches": "0.0", + "precipMM": "0.0", + "pressure": "1012", + "pressureInches": "30", + "shortRad": "219.3", + "tempC": "13", + "tempF": "55", + "time": "1200", + "uvIndex": "3", + "visibility": "10", + "visibilityMiles": "6", + "weatherCode": "176", + "weatherDesc": [{ "value": "Patchy rain nearby" }], + "weatherIconUrl": [{ "value": "" }], + "winddir16Point": "NE", + "winddirDegree": "42", + "windspeedKmph": "9", + "windspeedMiles": "6" + }, + { + "DewPointC": "7", + "DewPointF": "44", + "FeelsLikeC": "9", + "FeelsLikeF": "49", + "HeatIndexC": "11", + "HeatIndexF": "51", + "WindChillC": "9", + "WindChillF": "49", + "WindGustKmph": "19", + "WindGustMiles": "12", + "chanceoffog": "0", + "chanceoffrost": "0", + "chanceofhightemp": "0", + "chanceofovercast": "91", + "chanceofrain": "81", + "chanceofremdry": "0", + "chanceofsnow": "0", + "chanceofsunshine": "0", + "chanceofthunder": "0", + "chanceofwindy": "0", + "cloudcover": "51", + "diffRad": "8.8", + "humidity": "76", + "precipInches": "0.0", + "precipMM": "0.0", + "pressure": "1012", + "pressureInches": "30", + "shortRad": "16.7", + "tempC": "11", + "tempF": "51", + "time": "1500", + "uvIndex": "3", + "visibility": "10", + "visibilityMiles": "6", + "weatherCode": "176", + "weatherDesc": [{ "value": "Patchy rain nearby" }], + "weatherIconUrl": [{ "value": "" }], + "winddir16Point": "NNE", + "winddirDegree": "33", + "windspeedKmph": "11", + "windspeedMiles": "7" + }, + { + "DewPointC": "5", + "DewPointF": "42", + "FeelsLikeC": "5", + "FeelsLikeF": "41", + "HeatIndexC": "7", + "HeatIndexF": "44", + "WindChillC": "5", + "WindChillF": "41", + "WindGustKmph": "20", + "WindGustMiles": "13", + "chanceoffog": "0", + "chanceoffrost": "0", + "chanceofhightemp": "0", + "chanceofovercast": "37", + "chanceofrain": "0", + "chanceofremdry": "94", + "chanceofsnow": "0", + "chanceofsunshine": "88", + "chanceofthunder": "0", + "chanceofwindy": "0", + "cloudcover": "36", + "diffRad": "2.2", + "humidity": "90", + "precipInches": "0.0", + "precipMM": "0.0", + "pressure": "1012", + "pressureInches": "30", + "shortRad": "4.2", + "tempC": "7", + "tempF": "44", + "time": "1800", + "uvIndex": "3", + "visibility": "10", + "visibilityMiles": "6", + "weatherCode": "116", + "weatherDesc": [{ "value": "Partly Cloudy " }], + "weatherIconUrl": [{ "value": "" }], + "winddir16Point": "NE", + "winddirDegree": "38", + "windspeedKmph": "10", + "windspeedMiles": "6" + }, + { + "DewPointC": "5", + "DewPointF": "41", + "FeelsLikeC": "3", + "FeelsLikeF": "38", + "HeatIndexC": "6", + "HeatIndexF": "42", + "WindChillC": "3", + "WindChillF": "38", + "WindGustKmph": "22", + "WindGustMiles": "14", + "chanceoffog": "0", + "chanceoffrost": "0", + "chanceofhightemp": "0", + "chanceofovercast": "89", + "chanceofrain": "0", + "chanceofremdry": "82", + "chanceofsnow": "0", + "chanceofsunshine": "11", + "chanceofthunder": "0", + "chanceofwindy": "0", + "cloudcover": "16", + "diffRad": "0.0", + "humidity": "95", + "precipInches": "0.0", + "precipMM": "0.0", + "pressure": "1011", + "pressureInches": "30", + "shortRad": "0.0", + "tempC": "6", + "tempF": "42", + "time": "2100", + "uvIndex": "1", + "visibility": "2", + "visibilityMiles": "1", + "weatherCode": "143", + "weatherDesc": [{ "value": "Mist" }], + "weatherIconUrl": [{ "value": "" }], + "winddir16Point": "NE", + "winddirDegree": "35", + "windspeedKmph": "10", + "windspeedMiles": "6" + } + ], + "maxtempC": "14", + "maxtempF": "57", + "mintempC": "6", + "mintempF": "42", + "sunHour": "12.0", + "totalSnow_cm": "0.0", + "uvIndex": "3" + } + ] +} diff --git a/eww/weather/wttr/wttr.py b/eww/weather/wttr/wttr.py new file mode 100755 index 0000000..6497b35 --- /dev/null +++ b/eww/weather/wttr/wttr.py @@ -0,0 +1,67 @@ +#!/usr/bin/python + +import json +import requests +from datetime import datetime + +WEATHER_CODES = { + '113': 'β˜€οΈ', + '116': '⛅️', + '119': '☁️', + '122': '☁️', + '143': '🌫', + '176': '🌦', + '179': '🌧', + '182': '🌧', + '185': '🌧', + '200': 'β›ˆ', + '227': '🌨', + '230': '❄️', + '248': '🌫', + '260': '🌫', + '263': '🌦', + '266': '🌦', + '281': '🌧', + '284': '🌧', + '293': '🌦', + '296': '🌦', + '299': '🌧', + '302': '🌧', + '305': '🌧', + '308': '🌧', + '311': '🌧', + '314': '🌧', + '317': '🌧', + '320': '🌨', + '323': '🌨', + '326': '🌨', + '329': '❄️', + '332': '❄️', + '335': '❄️', + '338': '❄️', + '350': '🌧', + '353': '🌦', + '356': '🌧', + '359': '🌧', + '362': '🌧', + '365': '🌧', + '368': '🌨', + '371': '❄️', + '374': '🌧', + '377': '🌧', + '386': 'β›ˆ', + '389': '🌩', + '392': 'β›ˆ', + '395': '❄️' +} + +data = {} + +weather = requests.get("https://wttr.in/Tomsk+Russia?format=j1").json() + +# data['text'] = WEATHER_CODES[weather['current_condition'][0]['weatherCode']] + \ +# " "+weather['current_condition'][0]['FeelsLikeC']+"Β°" + +weather['current_condition'][0]['weatherCode'] = WEATHER_CODES[weather['current_condition'][0]['weatherCode']] + +print(json.dumps(weather)) diff --git a/waybar/config b/waybar/config index 5f9bd82..f16148e 100644 --- a/waybar/config +++ b/waybar/config @@ -135,7 +135,7 @@ "custom/weather": { "format": "{}", "tooltip": true, - "interval": 3600, + "interval": 900, "exec": "wttrbar --location tomsk", "return-type": "json" }, diff --git a/waybar/scripts/get-weather.sh b/waybar/scripts/get-weather.sh index f78645f..debf2e4 100755 --- a/waybar/scripts/get-weather.sh +++ b/waybar/scripts/get-weather.sh @@ -1,4 +1,5 @@ -#!/usr/bin/env bash +#!/bin/sh + for i in {1..5} do text=$(curl -s "https://wttr.in/$1?format=1")