;;; xwem-weather.el --- Display weather information in XWEM dock. ;; Copyright (C) 2004,2005 by XWEM Org. ;; Author: Steve Youngs ;; Created: 2004-06-22 ;; Keywords: xwem ;; X-CVS: $Id: xwem-weather.el,v 1.5 2005-04-04 19:54:17 lg Exp $ ;; This file is part of XWEM. ;; XWEM 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 2, or (at your option) ;; any later version. ;; XWEM 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 XEmacs; see the file COPYING. If not, write to the Free ;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA ;; 02111-1307, USA. ;;; Synched up with: Not in FSF ;;; Commentary: ;; Display weather information (current local temp) in the dock area. ;; Optionally display a frame with more detailed weather information. ;; Set this up by adding the following code to your ~/.xwem/xwemrc.el ;; (require 'xwem-weather) ;; (customize-set-variable 'xwem-weather-update-frequency 3600) ;; (add-hook 'xwem-after-init-hook 'xwem-weather-init) ;;; Code: (require 'itimer) (require 'xwem-osd) (require 'xwem-interactive) (require 'xwem-help) (require 'xwem-compat) (defgroup xwem-weather nil "XWEM Weather options." :prefix "xwem-weather-" :group 'xwem) (defcustom xwem-weather-station-id "ybbn" "*The four letter weather station ID. The default setting is the author's local station, Brisbane, Australia. So you can all be jealous of the wonderful weather we have in Australia. :-P You should be able to find out what the code is for your nearest weather station at http://weather.noaa.gov/" :type 'string :link '(url-link "http://weather.noaa.gov/") :group 'xwem-weather) (defcustom xwem-weather-data-directory xwem-dir "*The directory to story weather data files." :type '(directory :must-match t) :group 'xwem-weather) (defcustom xwem-weather-data-file (expand-file-name xwem-weather-station-id xwem-weather-data-directory) "*File to hold the weather data." :type 'file :group 'xwem-weather) (defcustom xwem-weather-temperature-format 'celsius "*Display temperature in Celsius or Fahrenheit." :type '(choice (const :tag "Celsius" celsius) (const :tag "Fahrenheit" fahrenheit)) :group 'xwem-weather) (defface xwem-weather-osd-face '((((class color)) (:foreground "cyan" :family "fixed" :size "12pt")) (t (:family "fixed" :size "12pt"))) "*Face for the weather OSD." :group 'xwem-weather) (defvar xwem-weather-menu '(["Show details" xwem-weather-show-details] ["Update" xwem-weather-update] "---" ["Destroy" xwem-weather-remove]) "Menu to popup on `xwem-weather-popup-menu' command. NOTE: title for this menu is formated dynamically.") ;;;###autoload(autoload 'xwem-weather-prefix "xwem-weather" nil nil 'keymap) (xwem-define-prefix-command 'xwem-weather-prefix t) (defvar xwem-weather-keymap (symbol-function 'xwem-weather-prefix) "Keymap for weather commands. Bindings: \\{xwem-weather-keymap}") (define-key xwem-weather-keymap ?d 'xwem-weather-show-details) (define-key xwem-weather-keymap ?u 'xwem-weather-update) ;;; Internal variables (defvar xwem-weather-osd nil) (defvar xwem-weather-frequency 0) (defun xwem-weather-alter-update-frequency (value) "Alters the update frequency of the weather updates. DO NOT CALL THIS FUNCTION DIRECTLY. Change the update frequency by customising `xwem-weather-update-frequency'. You MUST use `customize-set-variable' to do so. Argument SYM is the symbol name of what is changing the frequency. It will always be `xwem-weather-update-frequency'. Argument VALUE is an integer determining how often, in seconds, to update the weather data." (let ((itimer (get-itimer "xwem-weather-itimer"))) (cond ((and (xwem-osd-p xwem-weather-osd) value itimer) (set-itimer-value itimer value) (set-itimer-restart itimer value)) ((and (xwem-osd-p xwem-weather-osd) (eq value 0) itimer) (delete-itimer itimer)) ((and (xwem-osd-p xwem-weather-osd) (> value 0) (not itimer)) (start-itimer "xwem-weather-itimer" 'xwem-weather-update value value)) (t nil)) (setq xwem-weather-frequency value) (xwem-message 'info "Weather update frequency set to: %d seconds" value))) ;; This is below the internal variable marker so bug reports aren't ;; gigabytes long. I think we can live without this var being in bug ;; reports. --SY. (defcustom xwem-weather-update-frequency 0 "*The number of seconds between updates of the weather info. Most weather stations only update once/hour so it might not be very beneficial to set this to lower than an hour. If this is set to 0 updates will not happen. If you want to set this outside of the custom interface, you MUST use `customize-set-variable'." :type '(choice (integer :tag "Do not update" 0) (integer :tag "Every 10 minutes" 600) (integer :tag "Every 15 minutes" 900) (integer :tag "Every 30 minutes" 1800) (integer :tag "Every 60 minutes" 3600) (integer :tag "Every 2 hours" 7200) (integer :tag "Every 4 hours" 14400) (integer :tag "Every 12 hours" 43200) (integer :tag "Every 24 hours" 86400) (integer :tag "Other")) :set '(lambda (sym value) (xwem-weather-alter-update-frequency value)) :initialize 'custom-initialize-default :group 'xwem-weather) (defconst xwem-weather-url-fqdn "weather.noaa.gov" "FQDN part of the weather URL.") (defconst xwem-weather-url-dir "pub/data/observations/metar/decoded" "Directory part of the weather URL.") (defun xwem-weather-retrieve-update () "Retrieve weather info." (let* ((host xwem-weather-url-fqdn) (dir xwem-weather-url-dir) (file (upcase (concat xwem-weather-station-id ".txt"))) (path (concat dir "/" file)) (user-agent (concat "XEmacs " emacs-program-version)) (http (open-network-stream "xwem-weather-update" " *xwem-weather-update-buf*" host 80)) (pbuf (process-buffer http))) (process-send-string http (concat "GET /" path " HTTP/1.1\r\n" "MIME-Version: 1.0\r\n" "Connection: close\r\n" "Extension: Security/Digest Security/SSL\r\n" "Host: " host "\r\n" "Accept: */*\r\n" "User-Agent: " user-agent "\r\n\r\n")) (while (eq (process-status http) 'open) (dispatch-event (next-event))) (with-temp-buffer (erase-buffer) (insert-buffer pbuf) (goto-char (point-min)) (re-search-forward "^Content-Length: \\([0-9]+.*$\\)" nil t) (let* ((file-length (string-to-int (match-string 1))) (file-begin (progn (goto-char (point-min)) (re-search-forward "^Content-Type:" nil t) (forward-line 2) (point-at-bol)))) (goto-char file-begin) (forward-char file-length) (narrow-to-region file-begin (point)) (write-region (point-min) (point-max) xwem-weather-data-file))) (kill-buffer pbuf))) (defun xwem-weather-get-temp () "Return the temperature as a string from the weather data file." (with-temp-buffer (erase-buffer) (insert-file-contents-literally xwem-weather-data-file) (goto-char (point-min)) (when (re-search-forward "^Temperature: \\(-?[0-9]+ F\\) (\\(-?[0-9]+ C\\))" nil t) (let ((temp-f (match-string 1)) (temp-c (match-string 2))) (if (eq xwem-weather-temperature-format 'celsius) temp-c temp-f))))) (defun xwem-weather-osd-text-width (text) "Return TEXT width." (X-Text-width (xwem-dpy) (or (and (xwem-osd-p xwem-weather-osd) (X-Gc-font (xwem-osd-mask-gc xwem-weather-osd))) (X-Font-get (xwem-dpy) (face-font-name 'xwem-weather-osd-face))) text)) (defun xwem-weather-osd-text-height (text) "Return TEXT width." (X-Text-height (xwem-dpy) (or (and (xwem-osd-p xwem-weather-osd) (X-Gc-font (xwem-osd-mask-gc xwem-weather-osd))) (X-Font-get (xwem-dpy) (face-font-name 'xwem-weather-osd-face))) text)) (defun xwem-weather-display-text (text) "Display TEXT in weather dockapp." (let ((we-width (xwem-weather-osd-text-width text)) (height (xwem-weather-osd-text-height text)) (goff (xwem-weather-osd-text-width (substring text 0 (- (length text) 2))))) (xwem-osd-set-width xwem-weather-osd we-width) (xwem-osd-set-height xwem-weather-osd height) (xwem-osd-text xwem-weather-osd text) ;; Display degree (xwem-osd-arc-add xwem-weather-osd (make-X-Arc :x goff :y 0 :width 3 :height 3 :angle1 0 :angle2 360)))) ;;;###autoload(autoload 'xwem-weather-update "xwem-weather" nil t) (define-xwem-command xwem-weather-update () "*Update the weather OSD." (xwem-interactive "_") (xwem-weather-retrieve-update) (when (xwem-osd-p xwem-weather-osd) (xwem-weather-display-text (xwem-weather-get-temp)))) ;;;###autoload(autoload 'xwem-weather-show-details "xwem-weather" nil t) (define-xwem-command xwem-weather-show-details () "*Show the details of the current weather information." (xwem-interactive) (xwem-help-display "weather" (insert-file-contents xwem-weather-data-file))) ;;;###autoload(autoload 'xwem-weather-popup-remove "xwem-weather" nil t) (define-xwem-command xwem-weather-remove () "*Remove the weather OSD." (xwem-interactive) (when (xwem-osd-p xwem-weather-osd) (xwem-osd-destroy xwem-weather-osd)) (when (itimerp (get-itimer "xwem-weather-itimer")) (delete-itimer (get-itimer "xwem-weather-itimer")))) ;;;###autoload(autoload 'xwem-weather-popup-menu "xwem-weather" nil t) (define-xwem-command xwem-weather-popup-menu (ev) "Popup weather menu." (xwem-interactive (list xwem-last-event)) (unless (button-event-p ev) (error 'xwem-error "`xwem-weather-popup-menu' must be bound to mouse event")) (xwem-popup-menu (cons (format "Weather (%s)" (upcase xwem-weather-station-id)) xwem-weather-menu))) (defvar xwem-weather-osd-keymap (let ((map (make-sparse-keymap 'xwem-weather-osd-keymap))) (define-key map [button1] 'xwem-weather-show-details) (define-key map [button2] 'xwem-weather-update) (define-key map [button3] 'xwem-weather-popup-menu) map) "Keymap for weather OSD.") (define-xwem-command xwem-weather-display-temp (&optional force) "*Display the current temperature using OSD." (xwem-interactive "P") (when force (xwem-weather-retrieve-update)) (if (xwem-osd-p xwem-weather-osd) (xwem-weather-display-text (xwem-weather-get-temp)) ;; Create and setup weather osd than display current weather (let ((text (xwem-weather-get-temp))) (setq xwem-weather-osd (xwem-osd-create-dock (xwem-dpy) (xwem-weather-osd-text-width text) (xwem-weather-osd-text-height text) (list 'keymap xwem-weather-osd-keymap))) (xwem-osd-set-color xwem-weather-osd (face-foreground-name 'xwem-weather-osd-face)) (xwem-osd-set-font xwem-weather-osd (face-font-name 'xwem-weather-osd-face)) ;; Do it so, `xwem-weather-osd-text-height' and ;; `xwem-weather-osd-text-height' will use mask's gc. (xwem-osd-create-mask xwem-weather-osd) (xwem-weather-display-text text)))) ;;;###autoload (defun xwem-weather-init (&optional dockid dockgroup dockalign) "Initialise the weather dock." (when (xwem-osd-p xwem-weather-osd) (xwem-osd-destroy xwem-weather-osd)) (when (itimerp (get-itimer "xwem-weather-itimer")) (delete-itimer (get-itimer "xwem-weather-itimer"))) (xwem-weather-display-temp 'force) (unless (zerop xwem-weather-frequency) (start-itimer "xwem-weather-itimer" 'xwem-weather-update xwem-weather-frequency xwem-weather-frequency))) (provide 'xwem-weather) ;;; On-load actions (xwem-global-set-key (xwem-kbd "H-c W") 'xwem-weather-prefix) ;;; xwem-weather.el ends here