summaryrefslogtreecommitdiffhomepage
path: root/net/jao-weather.el
blob: 04a852352d25a2cac1ee633e7e15f4a86b89a5d6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
;;  Based on code by Thierry Volpiatto
;;  (http://mercurial.intuxication.org/hg/xml-weather)

(require 'xml)
(require 'derived)


;;; config:
(defvar jao-weather-format-id-url
  "http://xoap.weather.com/search/search?where=%s")

(defvar jao-weather-format-xml-from-id-url ; id, unit=m,day-forecast=5,login,key
  "http://xoap.weather.com/weather/local/%s?cc=*&unit=%s&dayf=%s&prod=xoap&par=%s&key=%s")

(defvar jao-weather-unit "m"
  "*m mean metric, you will have wind speed in km/h, temperature in °C and so on.")

(defvar jao-weather-login nil)
(defvar jao-weather-key nil)

(defvar jao-weather-day-forecast-num 5
  "*Number of days for forecast; Maximum 5.")

(defvar jao-weather-default-id "SPXX0015")

(defvar jao-weather-timer-delay 3600)

(defvar jao-weather-last-data nil)


;;; access:
(defun jao-weather-authentify ()
  "Authentify user from .authinfo file.
You have to setup correctly `auth-sources' to make this function
finding the path of your .authinfo file that is normally ~/.authinfo.
Entry in .authinfo should be:
machine xoap.weather.com port http login xxxxx password xxxxxx"
  (let ((auth (auth-source-user-or-password  '("login" "password")
                                             "xoap.weather.com"
                                             "http")))
    (setq jao-weather-login (car auth)
          jao-weather-key (cadr auth))))

(defun jao-weather--url (id)
  (unless (and jao-weather-login jao-weather-key)
    (jao-weather-authentify))
  (format jao-weather-format-xml-from-id-url
          (or id jao-weather-default-id)
          jao-weather-unit
          (min jao-weather-day-forecast-num 5)
          jao-weather-login
          jao-weather-key))

(defvar jao-weather-hook nil)

;; http://xoap.weather.com/weather/local/[locid]
;; Replace the [locid], of course, with the location ID obtained in the previous step.
;; Appended to this URL is a mix of other parameters,
;; some required and some optional. A typical example might be:
;; http://xoap.weather.com/weather/local/NLXX0002?cc=*&dayf=5&prod=xoap&par=[partner id]&key=[license key]
(defun jao-weather--get-info-async (&optional id)
  (let ((url (jao-weather--url id))
        (url-show-status nil))
    (url-retrieve url (lambda (res)
                        (when (not res)
                          (let ((data (jao-weather-get-alist)))
                            (when data
                              (setq jao-weather-last-data data)
                              (run-hooks 'jao-weather-hook))))
                        (kill-buffer (current-buffer))))))

(defun jao-weather--get-info-now (&optional id)
  (let* ((url (jao-weather--url id))
         (buffer (url-retrieve-synchronously url))
         (data (and buffer
                    (with-current-buffer buffer
                      (jao-weather-get-alist)))))
    (when buffer (kill-buffer buffer))
    (when data
      (setq jao-weather-last-data data)
      (run-hooks 'jao-weather-hook))
    data))


;;; formatting:
(defun jao-weather--flist (c fs)
  (when c
    (let (result)
      (dolist (f fs result)
        (let ((v (caddr (assoc (cadr f) c))))
          (when (and (stringp v) (not (string-equal v "N/A")))
            (push (cons (car f) v) result)))))))

(defun jao-weather--parse-cc (cc)
  (append (jao-weather--flist cc '((:date lsup)
                                   (:observatory obst)
                                   (:temperature tmp)
                                   (:condition t)
                                   (:pressure r)))
          (jao-weather--flist (assoc 'wind cc) '((:windir d)
                                                 (:wind-tilt t)
                                                 (:gust gust)))))

(defun jao-weather--parse-location (loc)
  (jao-weather--flist loc '((:city dnam)
                            (:time tm)
                            (:latitude lat)
                            (:longitude lon)
                            (:sunrise sunr)
                            (:sunset suns))))

(defun jao-weather--parse-day (d)
  (let ((p2 (assoc 'part
                   (remove (assoc 'part (cdr d))
                           (cdr d))))
        (wday (or (cdr (assoc 't (cadr d))) "day")))
    `(,(cdr (assoc 'dt (cadr d)))
      (:weekday . ,wday)
      (:weekday-abbrev . ,(substring wday 0 3))
      ,@(jao-weather--flist (cdr d) '((:max hi)
                                      (:min low)
                                      (:sunrise sunr)
                                      (:sunset suns)
                                      (:humidity hmid)))
      ,@(jao-weather--flist (assoc 'wind (assoc 'part (cdr d)))
                            '((:wind-dir 't) (:wind-speed 's)))
      ,@(jao-weather--flist (assoc 'wind p2) '((:night-wind-dir wea)
                                               (:night-wind-speed s)))
      ,@(jao-weather--flist p2
                            '((:night-condition t) (:night-humidity hmid))))))

(defun jao-weather-get-alist ()
  (let* ((pxml (car (xml-parse-region (point-min) (point-max))))
         (loc (car (xml-get-children pxml 'loc)))
         (cc (car (xml-get-children pxml 'cc)))
         (dayf (xml-get-children pxml 'dayf))
         (dayfs (xml-get-children (car dayf) 'day))
         (today (append (jao-weather--parse-cc cc)
                        (jao-weather--parse-location loc)))
         (forecast (mapcar 'jao-weather--parse-day dayfs)))
    `((today ,@today) (forecast ,@forecast))))

(defun jao-weather--format-fields (data fields sep)
  (if data
      (mapconcat (lambda (kv)
                   (let ((v (cdr (assoc (car kv) data))))
                     (if (not v) ""
                       (format (or (cdr kv) "%s") v))))
             fields
             sep)
    ""))

(defsubst jao-weather--today-string (fields sep)
  (jao-weather--format-fields (cdr (assoc 'today jao-weather-last-data))
                              fields sep))

(defun jao-weather--forecast-string (n fields sep)
  (jao-weather--format-fields (nth n (cdr (assoc 'forecast
                                                 jao-weather-last-data)))
                              fields sep))


;;; update daemon:
(defvar jao-weather--timer nil)
(defun jao-weather-start (&optional delay)
  (interactive)
  (jao-weather-stop)
  (setq jao-weather--timer
        (run-with-timer (or delay 0)
                        jao-weather-timer-delay
                        'jao-weather--get-info-async)))

(defun jao-weather-stop ()
  (interactive)
  (when jao-weather--timer
    (cancel-timer jao-weather--timer)
    (setq jao-weather--timer nil)))


;;; today
(defun jao-weather-today-msg (&optional arg)
  (interactive "p")
  (when (> arg 4) (jao-weather--get-info-now))
  (if (= 4 arg) (jao-weather-forecast-msg)
    (message "%s" (jao-weather--today-string '((:temperature . " %s°C")
                                               (:condition . "(%s)")
                                               (:sunrise . "↑ %s")
                                               (:sunset . "↓ %s")
                                               (:date . "[%s]"))
                                             " "))))

(defun jao-weather-forecast-msg (&optional arg)
  (interactive "P")
  (when arg (jao-weather--get-info-now))
  (message " %s" (mapconcat
                 (lambda (n)
                   (jao-weather--forecast-string n
                                                 '((:weekday-abbrev . "%s ")
                                                   (:max . "%s°/")
                                                   (:min . "%s°")
                                                   (:condition . ", %s")
                                                   (:night-condition . ", %s"))
                                                 ""))
                 '(1 2 3 4) " | ")))

(defun jao-weather-temperature ()
  (string-to-number (jao-weather--today-string '((:temperature)) "")))


(defun jao-weather-temperature* (&optional sep)
  (concat (jao-weather--today-string '((:temperature . "%s°")) "")
          (or sep " ")
          (jao-weather--forecast-string 1
                                        '((:max . "%s°/") (:min . "%s°")
                                          (:night-condition . " %s"))
                                        "")))

;; Provide
(provide 'jao-weather)