summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorawy <awy@awy.one>2025-08-24 04:55:06 +0300
committerawy <awy@awy.one>2025-08-24 04:55:06 +0300
commitf0f0a3dacc0e6894e83d42ab7788771b33b701b5 (patch)
treed53bd0891d11650c299314509f9a6678501b7857
parentd660f875554a25bad0f107ee8ca694a8ad9472ef (diff)
stweath
-rw-r--r--Makefile3
-rwxr-xr-xsb-weather82
-rw-r--r--src/stweath.c215
3 files changed, 218 insertions, 82 deletions
diff --git a/Makefile b/Makefile
index 7dfea64..834b60c 100644
--- a/Makefile
+++ b/Makefile
@@ -17,6 +17,9 @@ $(BINDIR)/stmusic: $(SRCDIR)/stmusic.c
$(BINDIR)/sttorrent: $(SRCDIR)/sttorrent.c
$(CC) $(CFLAGS) -o $@ $(SRCDIR)/cjson/cJSON.c $< -lcurl
+$(BINDIR)/stweath: $(SRCDIR)/stweath.c
+ $(CC) $(CFLAGS) -o $@ $(SRCDIR)/cjson/cJSON.c $< -lcurl
+
$(BINDIR)/%: $(SRCDIR)/%.c
$(CC) $(CFLAGS) -o $@ $<
diff --git a/sb-weather b/sb-weather
deleted file mode 100755
index f3a5aca..0000000
--- a/sb-weather
+++ /dev/null
@@ -1,82 +0,0 @@
-#!/bin/sh
-arg=$1
-report=~/.cache/weather_report.json
-
-ifinstalled jq
-
-# Get a weather report from 'wttr.in' and save it locally.
-getforecast() { { rg -q -m1 '^up$' /sys/class/net/w*/operstate || rg -q -m1 '^up$' /sys/class/net/e*/operstate; } &&
- curl -sf "wttr.in/?format=j1" --output "$report" && touch "$report"
-}
-
-# Forecast should be updated only once a day.
-checkforecast() {
- [ "$(stat -c %y "$report" 2>/dev/null |
- cut -d' ' -f1)" = "$(date '+%Y-%m-%d')" ]
-}
-
-# Function to get weather emoji from code
-get_weather_emoji() {
- local code="$1"
- grep "^$code " ~/.local/share/extras/weather_codes | awk '{print $2}'
-}
-
-showweather(){
- temp=$(jq -r '.current_condition[0].temp_C' $report)
- code=$(jq -r '.current_condition[0].weatherCode' $report)
- rainchance=$(jq '.weather[0].hourly[0].chanceofrain | tonumber' $report)
-
- emoji=$(get_weather_emoji "$code")
- case "$arg" in
- weather)
- case 1 in
- $((temp >= 30)) ) class="very_hot" ;;
- $((temp >= 26)) ) class="hot" ;;
- $((temp >= 21)) ) class="warm" ;;
- $((temp >= 16)) ) class="mild" ;;
- $((temp >= 11)) ) class="cool" ;;
- $((temp >= 6)) ) class="chilly" ;;
- $((temp >= 0)) ) class="cold" ;;
- $((temp < 0)) ) class="below_zero" ;;
- * ) echo unavailable && exit ;;
- esac
- output="$emoji$temp°C"
- ;;
- rain)
- case 1 in
- $((rainchance >= 70)) ) class="high" ;;
- $((rainchance >= 45)) ) class="mid" ;;
- $((rainchance > 0)) ) class="low" ;;
- $((rainchance <= 0)) ) printf '' && exit ;;
- * ) echo unavailable && exit ;;
- esac
- output="$rainchance%"
- ;;
- *)
- echo "Usage: $0 {weather|rain}"
- exit 1
- ;;
- esac
- printf '{"text": "%s", "class": "%s" }' "$output" "$class"
-}
-
-case $BLOCK_BUTTON in
- 2) getforecast && showweather ;;
- 3) notify-send " Weather module" "\- Middle click to update forecast.
-: Chance of rain/snow
-: Current temperature" ;;
- 8) setsid -f "$TERMINAL" -e "$EDITOR" "$0" >/dev/null 2>&1 ;;
-esac
-
-# shellcheck disable=SC2015
-checkforecast && showweather ||
- ( flock -n 9 &&
- ( tries=0; while [ $tries -ne 100 ]; do
- getforecast && break ||
- { tries=$((tries+1)); sleep .1; }
- done
- ! checkforecast &&
- until getforecast; do sleep 60; done
- pkill -RTMIN+5 "${STATUSBAR:-waybar}"
- ) &
- echo ) 9>"${XDG_RUNTIME_DIR}/sb-forecast.lock"
diff --git a/src/stweath.c b/src/stweath.c
new file mode 100644
index 0000000..6e5bef3
--- /dev/null
+++ b/src/stweath.c
@@ -0,0 +1,215 @@
+/* Copyright (C) 2025 awy <awy@awy.one>
+
+ stweath is free software: you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation,
+ either version 3 of the License, or (at your option) any later version.
+
+ stweath is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with stweath. If not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <curl/curl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <unistd.h>
+#include "cjson/cJSON.h"
+
+int
+getforecast(char *path)
+{
+ FILE *report;
+ CURL* handle;
+ CURLcode res;
+
+ report = fopen(path, "w");
+ if (!report) { return 1; }
+
+ handle = curl_easy_init();
+
+ if (!handle) {
+ fclose(report);
+ return 1;
+ }
+
+ curl_easy_setopt(handle, CURLOPT_URL, "https://wttr.in/?format=j1");
+ curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, NULL);
+ curl_easy_setopt(handle, CURLOPT_WRITEDATA, report);
+
+ res = curl_easy_perform(handle);
+
+ if (res != CURLE_OK) {
+ fclose(report);
+ return 1;
+ }
+
+ curl_easy_cleanup(handle);
+
+ fclose(report);
+ return 0;
+}
+
+int
+showforecast(char *path)
+{
+ long size;
+ char *data;
+ char *icon;
+ cJSON *root, *temp, *wcode, *rain;
+ FILE *report;
+
+ report = fopen(path, "rb");
+ if (!report) { return 1; }
+
+ fseek(report, 0, SEEK_END);
+ size = ftell(report);
+ fseek(report, 0, SEEK_SET);
+
+ data = malloc(size + 1);
+ if (!data) {
+ perror("malloc");
+ fclose(report);
+ return 1;
+ }
+ fread(data, 1, size, report);
+ data[size] = '\0';
+ fclose(report);
+
+ root = cJSON_Parse(data);
+ free(data);
+ if (!root) { return 1; }
+
+ rain = cJSON_GetArrayItem(cJSON_GetObjectItem(root, "weather"), 0);
+ rain = cJSON_GetArrayItem(cJSON_GetObjectItem(rain, "hourly"), 0);
+ rain = cJSON_GetObjectItem(rain, "chanceofrain");
+ temp = cJSON_GetObjectItem(root, "current_condition");
+ temp = cJSON_GetArrayItem(temp, 0);
+ wcode = cJSON_GetObjectItem(temp, "weatherCode");
+ temp = cJSON_GetObjectItem(temp, "temp_C");
+
+ switch (atoi(wcode->valuestring)) {
+ case 113: icon = ""; break;
+ case 116: icon = ""; break;
+ case 119:
+ case 122:
+ case 143:
+ case 248:
+ case 260:
+ icon = ""; break;
+ case 176:
+ case 179:
+ case 182:
+ case 185:
+ case 263:
+ case 266:
+ case 281:
+ case 284:
+ case 293:
+ case 296:
+ case 299:
+ case 302:
+ case 305:
+ case 308:
+ case 311:
+ case 314:
+ case 317:
+ case 350:
+ case 353:
+ case 356:
+ case 359:
+ case 362:
+ case 365:
+ case 368:
+ case 392:
+ icon = ""; break;
+ case 200: icon = ""; break;
+ case 227:
+ case 230:
+ case 320:
+ case 323:
+ case 326:
+ case 374:
+ case 377:
+ case 386:
+ case 389:
+ icon = ""; break;
+ case 329:
+ case 332:
+ case 335:
+ case 338:
+ case 371:
+ case 395:
+ icon = ""; break;
+ default: icon = "❓"; break;
+ }
+
+ if (atoi(rain->valuestring) != 0) {
+ printf(" %s%% ", rain->valuestring);
+ }
+ printf("%s %s°C", icon, temp->valuestring);
+
+ cJSON_Delete(root);
+ return 0;
+}
+
+int
+checkforecast(char *path)
+{
+ time_t now;
+ char *buff;
+ struct stat st;
+ int tries, force;
+
+ /* Check if there is WEATHERFORCEUPDATE
+ environment variable */
+ force = 0;
+ buff = getenv("WEATHERFORCEUPDATE");
+ if (buff && buff[0] == '1') { force = 1; }
+
+ if (stat(path, &st) != 0) { return 1; }
+
+ now = time(NULL);
+ if (now == ((time_t)-1)) { return 1; }
+
+ /* Check if 30 mins passed
+ then get new forecast */
+ if (((now - st.st_mtime) >= 1800) || force) {
+ tries = 0;
+ /* Try 100 fast retries */
+ while (tries < 100) {
+ if(getforecast(path) == 0) {
+ break;
+ }
+ tries++;
+ sleep(1);
+ }
+ }
+
+ showforecast(path);
+ return 0;
+}
+
+int
+main(void)
+{
+ char *path;
+
+ path = getenv("XDG_CACHE_HOME");
+ if (!path) { puts("XDG_CACHE_HOME is not set"); return 1; }
+ strcat(path, "/weatherreport.json");
+
+ curl_global_init(CURL_GLOBAL_ALL);
+
+ checkforecast(path);
+
+ curl_global_cleanup();
+ return 0;
+}