Compare commits
2 commits
d8cdf07279
...
f43fc2430b
| Author | SHA1 | Date | |
|---|---|---|---|
| f43fc2430b | |||
| 6b8993703c |
5 changed files with 175 additions and 33 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -19,6 +19,10 @@ transient/
|
||||||
url/
|
url/
|
||||||
.org-id-locations
|
.org-id-locations
|
||||||
bookmarks
|
bookmarks
|
||||||
|
request/
|
||||||
|
|
||||||
|
# Weather forecast
|
||||||
|
forecast
|
||||||
|
|
||||||
# Custom file
|
# Custom file
|
||||||
custom.el
|
custom.el
|
||||||
|
|
|
||||||
|
|
@ -113,7 +113,7 @@
|
||||||
("RSS" elfeed "r")
|
("RSS" elfeed "r")
|
||||||
("Terminal" theurgy-bottom-shell "t")
|
("Terminal" theurgy-bottom-shell "t")
|
||||||
("Gomuks" theurgy-gomuks-workspace "G")
|
("Gomuks" theurgy-gomuks-workspace "G")
|
||||||
("Weather" theurgy-show-weather "w")))))
|
("Weather" theurgy-weather "w")))))
|
||||||
:align center
|
:align center
|
||||||
:width 20))
|
:width 20))
|
||||||
(grid-make-box `(:content ,(concat
|
(grid-make-box `(:content ,(concat
|
||||||
|
|
@ -144,7 +144,7 @@
|
||||||
(grid-make-column (list (grid-make-box `(:content ,(concat
|
(grid-make-column (list (grid-make-box `(:content ,(concat
|
||||||
(enlight-menu
|
(enlight-menu
|
||||||
'(("Userland"
|
'(("Userland"
|
||||||
("Dired" (dired "~") "d")
|
("Weather" theurgy-weather "w")
|
||||||
("RSS" elfeed "r")
|
("RSS" elfeed "r")
|
||||||
("Terminal" theurgy-bottom-shell "t")
|
("Terminal" theurgy-bottom-shell "t")
|
||||||
))))
|
))))
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,9 @@
|
||||||
;; Transient
|
;; Transient
|
||||||
(use-package transient)
|
(use-package transient)
|
||||||
|
|
||||||
|
;; Request is generally useful and a dependency for other packages
|
||||||
|
(use-package request)
|
||||||
|
|
||||||
(provide 'shared-packages)
|
(provide 'shared-packages)
|
||||||
|
|
||||||
;;; shared-packages.el ends here
|
;;; shared-packages.el ends here
|
||||||
|
|
|
||||||
|
|
@ -22,37 +22,10 @@
|
||||||
|
|
||||||
;;; Code:
|
;;; Code:
|
||||||
|
|
||||||
(defcustom theurgy-city
|
(defun rename-eww-hook ()
|
||||||
"Perth"
|
"Rename eww browser's buffer so sites open in new page."
|
||||||
"The city used for fetching weather from wttr.in."
|
(rename-buffer "eww" t))
|
||||||
:type 'string
|
(add-hook 'eww-mode-hook #'rename-eww-hook)
|
||||||
:group 'theurgy
|
|
||||||
:group 'theurgy-weather)
|
|
||||||
|
|
||||||
(defun theurgy-eww-rename-buffer ()
|
|
||||||
"Rename the eww buffer intelligently."
|
|
||||||
(when (eq major-mode 'eww-mode)
|
|
||||||
(let ((url (plist-get eww-data :url)))
|
|
||||||
(cond
|
|
||||||
((string-match-p "wttr.in" url) "*weather*")
|
|
||||||
(t "*eww*")))))
|
|
||||||
|
|
||||||
(setq eww-auto-rename-buffer #'theurgy-eww-rename-buffer)
|
|
||||||
|
|
||||||
(defun theurgy-show-weather ()
|
|
||||||
"Show wttr.in in an eww buffer."
|
|
||||||
(interactive)
|
|
||||||
(if (get-buffer "*weather*")
|
|
||||||
(switch-to-buffer "*weather*")
|
|
||||||
(eww (concat "wttr.in/" theurgy-city "?0"))))
|
|
||||||
|
|
||||||
(unless (equal system-type 'android)
|
|
||||||
(add-to-list 'display-buffer-alist
|
|
||||||
'("\\*weather\\*"
|
|
||||||
(display-buffer-in-side-window)
|
|
||||||
(side . left)
|
|
||||||
(slot . 4)
|
|
||||||
(window-width . 0.15))))
|
|
||||||
|
|
||||||
(provide 'browser)
|
(provide 'browser)
|
||||||
|
|
||||||
|
|
|
||||||
162
userland/weather.el
Normal file
162
userland/weather.el
Normal file
|
|
@ -0,0 +1,162 @@
|
||||||
|
;;; weather.el --- Fetch and display the weather from bom.gov.au -*- lexical-binding: t -*-
|
||||||
|
|
||||||
|
|
||||||
|
;; This file is not part of GNU Emacs
|
||||||
|
|
||||||
|
;; This program 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.
|
||||||
|
|
||||||
|
;; This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
;;; Commentary:
|
||||||
|
|
||||||
|
;; Reverse-engineered API docs are available here https://trickypr.github.io/bom-weather-docs/
|
||||||
|
;; This code depends on request.el
|
||||||
|
|
||||||
|
;;; Code:
|
||||||
|
|
||||||
|
(defcustom theurgy-geohash
|
||||||
|
""
|
||||||
|
"The geohash used for fetching forecast data from BOM. You can manually find this by going to https://api.weather.bom.gov.au/v1/locations?search=<suburb-name>, or you can get a nicer interface for it with \\[theurgy-weather-find-geohash]."
|
||||||
|
:type 'string
|
||||||
|
:group 'theurgy
|
||||||
|
:group 'theurgy-weather)
|
||||||
|
|
||||||
|
(defvar bom-api "https://api.weather.bom.gov.au/v1/")
|
||||||
|
|
||||||
|
(defun theurgy-weather-find-geohash (search-term)
|
||||||
|
"Search for SEARCH-TERM and then return possible geohash candidates."
|
||||||
|
(interactive "sSearch term (Suburb or Postcode): ")
|
||||||
|
(when (> 4 (length search-term))
|
||||||
|
(error "Search term must be greater than 3 characters (don't ask me)"))
|
||||||
|
(request (concat bom-api "locations?search=" search-term)
|
||||||
|
:parser 'json-read
|
||||||
|
:success (cl-function
|
||||||
|
(lambda (&key data &allow-other-keys)
|
||||||
|
(let* ((res (cdr (assoc 'data data)))
|
||||||
|
(candidates (mapcar (lambda (alst)
|
||||||
|
(cons (concat
|
||||||
|
(cdr (assoc 'name alst)) ", "
|
||||||
|
(cdr (assoc 'state alst)) ", "
|
||||||
|
(cdr (assoc 'postcode alst)))
|
||||||
|
(cdr (assoc 'geohash alst))))
|
||||||
|
res)))
|
||||||
|
(customize-set-variable 'theurgy-geohash (cdr (assoc (completing-read "Select Location: " candidates) candidates))))))))
|
||||||
|
|
||||||
|
(defvar forecast-timer nil)
|
||||||
|
(defvar forecast-location "forecast") ;; The location of the cached forecast, related to the emacs user directory
|
||||||
|
|
||||||
|
(defun theurgy-weather-fetch-forecast ()
|
||||||
|
"Fetch and save the weather forecast."
|
||||||
|
(interactive)
|
||||||
|
(request
|
||||||
|
(concat bom-api "locations/" theurgy-geohash "/forecasts/daily")
|
||||||
|
:parser 'json-read
|
||||||
|
:success (cl-function
|
||||||
|
(lambda (&key data &allow-other-keys)
|
||||||
|
(save-excursion
|
||||||
|
(find-file (concat user-emacs-directory forecast-location))
|
||||||
|
(delete-region (point-min) (point-max))
|
||||||
|
(insert (format "%S" (mapcar (lambda (alst)
|
||||||
|
(cons (let ((d (parse-time-string (cdr (assoc 'date alst)))))
|
||||||
|
(list (nth 3 d) (nth 4 d) (nth 5 d)))
|
||||||
|
(assq-delete-all 'date alst)))
|
||||||
|
(cdr (assoc 'data data)))))
|
||||||
|
(save-buffer)
|
||||||
|
(kill-buffer))))))
|
||||||
|
|
||||||
|
(defun current-date ()
|
||||||
|
"Get the current date as a list."
|
||||||
|
(let ((d (decode-time (current-time))))
|
||||||
|
(list (nth 3 d) (nth 4 d) (nth 5 d))))
|
||||||
|
|
||||||
|
(defun today+ (days)
|
||||||
|
"Date list for today + DAYS."
|
||||||
|
(let* ((sec (+ (* days 86400) (time-convert (current-time) 'integer)))
|
||||||
|
(d (decode-time sec)))
|
||||||
|
(list (nth 3 d) (nth 4 d) (nth 5 d))))
|
||||||
|
|
||||||
|
(defun prompt-date ()
|
||||||
|
"Prompt the user for a date."
|
||||||
|
(let ((d (parse-time-string (org-read-date))))
|
||||||
|
(list (nth 3 d) (nth 4 d) (nth 5 d))))
|
||||||
|
|
||||||
|
(defun theurgy-weather-get-for-date (date)
|
||||||
|
"Get weather for a particular DATE."
|
||||||
|
(let* ((forecast-data (read (with-temp-buffer
|
||||||
|
(insert-file-contents (concat user-emacs-directory forecast-location))
|
||||||
|
(buffer-string))))
|
||||||
|
(day (assoc date
|
||||||
|
forecast-data)))
|
||||||
|
day))
|
||||||
|
|
||||||
|
(defun theurgy-quick-forecast (date)
|
||||||
|
"Quick forecast for DATE."
|
||||||
|
(let ((forecast (theurgy-weather-get-for-date date)))
|
||||||
|
(message (format "%s - %s degrees. %s"
|
||||||
|
(cdr (assoc 'temp_min forecast))
|
||||||
|
(cdr (assoc 'temp_max forecast))
|
||||||
|
(cdr (assoc 'short_text forecast))))))
|
||||||
|
|
||||||
|
(defun theurgy-weather-quick ()
|
||||||
|
"Show the forecast as a message."
|
||||||
|
(interactive)
|
||||||
|
(theurgy-quick-forecast (prompt-date)))
|
||||||
|
|
||||||
|
(defun theurgy-start-forecast-timer ()
|
||||||
|
"Start the timer for periodically retrieving the weather forecast."
|
||||||
|
(interactive)
|
||||||
|
(theurgy-weather-fetch-forecast)
|
||||||
|
(unless forecast-timer
|
||||||
|
(when (timerp forecast-timer)
|
||||||
|
(cancel-timer forecast-timer))
|
||||||
|
(setq forecast-timer (run-at-time t (* 60 30) (theurgy-weather-fetch-forecast)))))
|
||||||
|
|
||||||
|
(theurgy-start-forecast-timer)
|
||||||
|
|
||||||
|
(defun theurgy-weather-insert-forecast (fc)
|
||||||
|
"Insert provided forecast FC in the current buffer, as org markup."
|
||||||
|
(let ((date (car fc)))
|
||||||
|
(insert (format "* %s/%s/%s - %s\n" (nth 0 date) (nth 1 date) (nth 2 date) (cdr (assoc 'short_text fc))))
|
||||||
|
(insert (format "%s\n" (cdr (assoc 'extended_text fc))))
|
||||||
|
(insert (format "- %s-%s°C\n"
|
||||||
|
(cdr (assoc 'temp_min fc))
|
||||||
|
(cdr (assoc 'temp_max fc))))
|
||||||
|
(insert (format "- %s%% Chance of Rain (%s-%smm)\n"
|
||||||
|
(cdr (assoc 'chance (assoc 'rain fc)))
|
||||||
|
(cdr (assoc 'lower_range (assoc 'amount (assoc 'rain fc))))
|
||||||
|
(cdr (assoc 'upper_range (assoc 'amount (assoc 'rain fc))))))
|
||||||
|
(insert (format "- %s UV\n"
|
||||||
|
(cdr (assoc 'category (assoc 'uv fc)))))
|
||||||
|
(insert (format "- %s Fire Danger\n"
|
||||||
|
(cdr (assoc 'fire_danger fc))))
|
||||||
|
(insert "\n")))
|
||||||
|
|
||||||
|
(defun theurgy-weather ()
|
||||||
|
"Show weather information in a new buffer."
|
||||||
|
(interactive)
|
||||||
|
(let ((buf (generate-new-buffer "*Weather*")))
|
||||||
|
(with-current-buffer buf
|
||||||
|
(delete-region (point-min) (point-max))
|
||||||
|
(org-mode)
|
||||||
|
(let* ((today (theurgy-weather-get-for-date (current-date)))
|
||||||
|
(tomorrow (theurgy-weather-get-for-date (today+ 1)))
|
||||||
|
(after-tomorrow (theurgy-weather-get-for-date (today+ 2))))
|
||||||
|
(theurgy-weather-insert-forecast today)
|
||||||
|
(theurgy-weather-insert-forecast tomorrow)
|
||||||
|
(theurgy-weather-insert-forecast after-tomorrow))
|
||||||
|
(setq buffer-read-only t))
|
||||||
|
(switch-to-buffer buf)))
|
||||||
|
|
||||||
|
(provide 'weather)
|
||||||
|
|
||||||
|
;;; weather.el ends here
|
||||||
Loading…
Add table
Add a link
Reference in a new issue