weather widget

This commit is contained in:
wt committed 2024-09-03 12:22:09 +07:00
1 parent 44afecd7df
commit 54278273cb
14 files changed
+2688 -50

No files matched your search

+23
View File
@@ -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(),
],
}),
});
+433
View File
@@ -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
+417
View File
@@ -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;
}
}
}
}
+486
View File
@@ -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
},
});
+31
View File
@@ -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();