| 1 | <?php |
|---|
| 2 | // $Id: openweather.module,v 1.195 2006/04/14 09:05:19 killes Exp $ |
|---|
| 3 | |
|---|
| 4 | /** |
|---|
| 5 | * @file |
|---|
| 6 | * Enables your site to capture votes on different topics in the form of multiple |
|---|
| 7 | * choice questions. |
|---|
| 8 | */ |
|---|
| 9 | |
|---|
| 10 | |
|---|
| 11 | /** |
|---|
| 12 | * Implementation of hook_help(). |
|---|
| 13 | */ |
|---|
| 14 | function openweather_help($section) { |
|---|
| 15 | switch ($section) { |
|---|
| 16 | case 'admin/help#openweather': |
|---|
| 17 | $output = '<p>'. t('Openweather logs and graphs weather station readings from open2300 compatible stations.').'</p>'; |
|---|
| 18 | return $output; |
|---|
| 19 | case 'admin/modules#description': |
|---|
| 20 | return t("Open2300 weather station logger."); |
|---|
| 21 | case 'node/add#openweather': |
|---|
| 22 | return t("Add a new weather station to monitor."); |
|---|
| 23 | } |
|---|
| 24 | } |
|---|
| 25 | |
|---|
| 26 | /** |
|---|
| 27 | * Implementation of hook_perm(). |
|---|
| 28 | */ |
|---|
| 29 | function openweather_perm() { |
|---|
| 30 | return array('create weather station'); |
|---|
| 31 | } |
|---|
| 32 | |
|---|
| 33 | /** |
|---|
| 34 | * Implementation of hook_access(). |
|---|
| 35 | */ |
|---|
| 36 | function openweather_access($op, $node) { |
|---|
| 37 | if ($op == 'create') { |
|---|
| 38 | return user_access('create weather station'); |
|---|
| 39 | } |
|---|
| 40 | } |
|---|
| 41 | |
|---|
| 42 | /** |
|---|
| 43 | * Implementation of hook_block(). |
|---|
| 44 | * |
|---|
| 45 | * Generates a block containing the latest poll. |
|---|
| 46 | */ |
|---|
| 47 | function openweather_block($op = 'list', $delta = 0) { |
|---|
| 48 | } |
|---|
| 49 | |
|---|
| 50 | /** |
|---|
| 51 | * Implementation of hook_delete(). |
|---|
| 52 | */ |
|---|
| 53 | function openweather_delete($node) { |
|---|
| 54 | db_query("DELETE FROM {openweather_readings} WHERE nid = %d", $node->nid); |
|---|
| 55 | } |
|---|
| 56 | |
|---|
| 57 | /** |
|---|
| 58 | * Implementation of hook_form(). |
|---|
| 59 | */ |
|---|
| 60 | function openweather_form(&$node) { |
|---|
| 61 | |
|---|
| 62 | $admin = user_access('administer nodes'); |
|---|
| 63 | |
|---|
| 64 | $form['title'] = array('#type' => 'textfield', '#title' => t('Station name'), '#required' => TRUE, '#default_value' => $node->title, '#weight' => -5); |
|---|
| 65 | /* $form['suburb'] = array('#type' => 'textfield', '#title' => t('Suburb'), '#required' => TRUE, '#default_value' => "", '#weight' => -5); |
|---|
| 66 | $form['lat'] = array('#type' => 'textfield', '#title' => t('Latitude'), '#required' => TRUE, '#default_value' => $node->title, '#weight' => -5); |
|---|
| 67 | $form['long'] = array('#type' => 'textfield', '#title' => t('Longitude'), '#required' => TRUE, '#default_value' => $node->title, '#weight' => -5); |
|---|
| 68 | */ |
|---|
| 69 | return $form; |
|---|
| 70 | } |
|---|
| 71 | |
|---|
| 72 | /** |
|---|
| 73 | * Implementation of hook_menu(). |
|---|
| 74 | */ |
|---|
| 75 | function openweather_menu($may_cache) { |
|---|
| 76 | $items = array(); |
|---|
| 77 | |
|---|
| 78 | if ($may_cache) { |
|---|
| 79 | $items[] = array('path' => 'node/add/openweather', 'title' => t('weather station'), |
|---|
| 80 | 'access' => user_access('create weather station')); |
|---|
| 81 | $items[] = array('path' => 'openweather', 'title' => t('openweather stations'), |
|---|
| 82 | 'callback' => 'openweather_page', |
|---|
| 83 | 'access' => user_access('access content'), |
|---|
| 84 | 'type' => MENU_SUGGESTED_ITEM); |
|---|
| 85 | |
|---|
| 86 | $items[] = array('path' => 'admin/settings/openweather', |
|---|
| 87 | 'title' => t('openweather'), |
|---|
| 88 | 'callback' => 'drupal_get_form', |
|---|
| 89 | 'callback arguments' => 'openweather_admin_settings', |
|---|
| 90 | 'access' => user_access('administer site configuration'), |
|---|
| 91 | 'type' => MENU_CALLBACK); |
|---|
| 92 | |
|---|
| 93 | } |
|---|
| 94 | else { |
|---|
| 95 | if (arg(0) == 'node' && is_numeric(arg(1))) { |
|---|
| 96 | $node = node_load(arg(1)); |
|---|
| 97 | if ($node->type == 'openweather'){ |
|---|
| 98 | $items[] = array('path' => 'node/'. arg(1) .'/csv_readings', |
|---|
| 99 | 'title' => t('readings'), |
|---|
| 100 | 'callback' => 'openweather_csv_readings', |
|---|
| 101 | 'access' => user_access('access content'), |
|---|
| 102 | 'weight' => 3, |
|---|
| 103 | 'type' => MENU_LOCAL_TASK); |
|---|
| 104 | |
|---|
| 105 | $items[] = array('path' => 'node/'. arg(1) .'/graph', |
|---|
| 106 | 'title' => t('graph'), |
|---|
| 107 | 'callback' => 'openweather_graph', |
|---|
| 108 | 'access' => user_access('access content'), |
|---|
| 109 | 'weight' => 3, |
|---|
| 110 | 'type' => MENU_CALLBACK); |
|---|
| 111 | |
|---|
| 112 | } |
|---|
| 113 | } |
|---|
| 114 | } |
|---|
| 115 | |
|---|
| 116 | return $items; |
|---|
| 117 | } |
|---|
| 118 | |
|---|
| 119 | function openweather_link($type, $node=0, $main=0){ |
|---|
| 120 | if ($type == 'node' && $node->type == 'openweather'){ |
|---|
| 121 | $links = module_invoke_all('openweather', 'node', $node, 1); |
|---|
| 122 | $links[] = array('title'=>t('export readings'), 'href'=>'node/'.$node->nid.'/csv_readings','attributes'=>array('title'=>'Export the openweather readings as CSV')); |
|---|
| 123 | } |
|---|
| 124 | return $links; |
|---|
| 125 | } |
|---|
| 126 | |
|---|
| 127 | |
|---|
| 128 | /** |
|---|
| 129 | * Implementation of hook_node_info(). |
|---|
| 130 | */ |
|---|
| 131 | function openweather_node_info() { |
|---|
| 132 | return array( |
|---|
| 133 | 'openweather' => array( |
|---|
| 134 | 'name' => t("openweather"), |
|---|
| 135 | 'base' => 'openweather', |
|---|
| 136 | 'module' => 'openweather', |
|---|
| 137 | )); |
|---|
| 138 | } |
|---|
| 139 | |
|---|
| 140 | /** |
|---|
| 141 | * _page hook |
|---|
| 142 | * Display official stats, and a list of weather stations w/ stats |
|---|
| 143 | */ |
|---|
| 144 | function openweather_page() { |
|---|
| 145 | global $user; |
|---|
| 146 | |
|---|
| 147 | $output = ''; |
|---|
| 148 | |
|---|
| 149 | // If there is official weather get it |
|---|
| 150 | $radarURL = variable_get('openweather_radar', null); |
|---|
| 151 | if ($radarURL != null) $output .= "<img src='$radarURL'/>"; |
|---|
| 152 | |
|---|
| 153 | if (module_exists("weather")){ |
|---|
| 154 | $b = weather_block('view'); |
|---|
| 155 | $output .= $b['content']; |
|---|
| 156 | } |
|---|
| 157 | |
|---|
| 158 | // List all of my openweathers by category |
|---|
| 159 | $sql = "SELECT n.nid, n.title FROM {node} n WHERE n.type='openweather'"; |
|---|
| 160 | |
|---|
| 161 | $result = db_query($sql); |
|---|
| 162 | $output .= '<ul>'; |
|---|
| 163 | while ($node = db_fetch_object($result)) { |
|---|
| 164 | $output .= '<li>'. l($node->title, "node/$node->nid").openweather_summary($node->nid) .'</li>'; |
|---|
| 165 | } |
|---|
| 166 | $output .= '</ul>'; |
|---|
| 167 | $output .= theme("pager", NULL, 15); |
|---|
| 168 | return $output; |
|---|
| 169 | } |
|---|
| 170 | |
|---|
| 171 | function openweather_admin_settings() { |
|---|
| 172 | $form['openweather_radar'] = array( |
|---|
| 173 | '#type'=>'textfield', |
|---|
| 174 | '#title'=>t('URL for weather radar'), |
|---|
| 175 | '#default_value'=>variable_get('openweather_radar', '')); |
|---|
| 176 | |
|---|
| 177 | return system_settings_form($form); |
|---|
| 178 | } |
|---|
| 179 | |
|---|
| 180 | |
|---|
| 181 | /** |
|---|
| 182 | * Creates a simple teaser that lists all the choices. |
|---|
| 183 | */ |
|---|
| 184 | function openweather_teaser($node) { |
|---|
| 185 | $teaser = NULL; |
|---|
| 186 | if (is_array($node->choice)) { |
|---|
| 187 | foreach ($node->choice as $k => $choice) { |
|---|
| 188 | $teaser .= '* '. $choice['chtext'] .'\n'; |
|---|
| 189 | } |
|---|
| 190 | } |
|---|
| 191 | return $teaser; |
|---|
| 192 | } |
|---|
| 193 | |
|---|
| 194 | function openweather_to_direction($angle){ |
|---|
| 195 | if ($angle < 11.25) |
|---|
| 196 | return 'N'; |
|---|
| 197 | else if ($angle < 33.75) |
|---|
| 198 | return 'NNE'; |
|---|
| 199 | else if ($angle < 56.25) |
|---|
| 200 | return 'NE'; |
|---|
| 201 | else if ($angle < 78.75) |
|---|
| 202 | return 'ENE'; |
|---|
| 203 | else if ($angle < 101.25) |
|---|
| 204 | return 'E'; |
|---|
| 205 | else if ($angle < 123.75) |
|---|
| 206 | return 'ESE'; |
|---|
| 207 | else if ($angle < 146.25) |
|---|
| 208 | return 'SE'; |
|---|
| 209 | else if ($angle < 168.75) |
|---|
| 210 | return 'SSE'; |
|---|
| 211 | else if ($angle < 191.25) |
|---|
| 212 | return 'S'; |
|---|
| 213 | else if ($angle < 213.75) |
|---|
| 214 | return 'SSW'; |
|---|
| 215 | else if ($angle < 236.25) |
|---|
| 216 | return 'SW'; |
|---|
| 217 | else if ($angle < 258.75) |
|---|
| 218 | return 'WSW'; |
|---|
| 219 | else if ($angle < 281.25) |
|---|
| 220 | return 'W'; |
|---|
| 221 | else if ($angle < 303.75) |
|---|
| 222 | return 'WNW'; |
|---|
| 223 | else if ($angle < 326.25) |
|---|
| 224 | return 'NW'; |
|---|
| 225 | else if ($angle < 348.75) |
|---|
| 226 | return 'NNW'; |
|---|
| 227 | else |
|---|
| 228 | return 'N'; |
|---|
| 229 | } |
|---|
| 230 | |
|---|
| 231 | /** |
|---|
| 232 | * Gets (and caches) the latest readings |
|---|
| 233 | */ |
|---|
| 234 | function openweather_get_latest($nid){ |
|---|
| 235 | static $cache = array(); |
|---|
| 236 | if (array_key_exists($nid,$cache)) |
|---|
| 237 | return $cache[$nid]; |
|---|
| 238 | else { |
|---|
| 239 | $data = db_query("SELECT * FROM {openweather_readings} WHERE nid = $nid ORDER BY `timestamp` DESC LIMIT 1"); |
|---|
| 240 | $data = db_fetch_object($data); |
|---|
| 241 | $cache[$nid] = $data; |
|---|
| 242 | return $data; |
|---|
| 243 | } |
|---|
| 244 | } |
|---|
| 245 | |
|---|
| 246 | |
|---|
| 247 | /** |
|---|
| 248 | * Summary table of weather station feed |
|---|
| 249 | */ |
|---|
| 250 | function openweather_summary($nid){ |
|---|
| 251 | $data = openweather_get_latest($nid); |
|---|
| 252 | |
|---|
| 253 | $output .= "<table>"; |
|---|
| 254 | $output .= "<tr><th>Temperature</th><td>$data->temp_out °C</td><th>Humidity</th><td>$data->rel_hum_out %</td></tr>"; |
|---|
| 255 | $output .= "<tr><th>Wind speed</th><td>$data->windspeed kmh</td><th>Wind direction</th><td>".openweather_to_direction($data->wind_angle)."($data->wind_angle °)</td></tr>"; |
|---|
| 256 | $output .= "<tr><th>Relative pressure</th><td>$data->rel_pressure</td><th>Timestamp</th><td>$data->timestamp</td></tr>"; |
|---|
| 257 | $output .= "</table>"; |
|---|
| 258 | return $output; |
|---|
| 259 | } |
|---|
| 260 | |
|---|
| 261 | /** |
|---|
| 262 | * Implementation of hook_view(). |
|---|
| 263 | * |
|---|
| 264 | * @param $block |
|---|
| 265 | */ |
|---|
| 266 | function openweather_view($node, $teaser = FALSE, $page = FALSE, $block = FALSE) { |
|---|
| 267 | global $user; |
|---|
| 268 | $node->content['summary'] = array('#weight'=>0, |
|---|
| 269 | '#value'=>openweather_summary($node->nid)); |
|---|
| 270 | |
|---|
| 271 | $node->content['graphs'] = array('#weight'=>1, |
|---|
| 272 | '#value'=>"<img src='?q=node/".$node->nid."/graph/temp_out' />". |
|---|
| 273 | "<img src='?q=node/".$node->nid."/graph/rel_hum_out' />". |
|---|
| 274 | "<img src='?q=node/".$node->nid."/graph/rel_pressure' />". |
|---|
| 275 | "<img src='?q=node/".$node->nid."/graph/windspeed' />"); |
|---|
| 276 | |
|---|
| 277 | return $node; |
|---|
| 278 | } |
|---|
| 279 | |
|---|
| 280 | /** |
|---|
| 281 | * Export the openweather readings as a CSV file |
|---|
| 282 | */ |
|---|
| 283 | function openweather_csv_readings() { |
|---|
| 284 | $nid = arg(1); |
|---|
| 285 | $node = node_load($nid); |
|---|
| 286 | drupal_set_header('Content-Type: text/csv; charset=utf-8'); |
|---|
| 287 | drupal_set_header('Content-Disposition: attachment; filename="openweather.csv";'); |
|---|
| 288 | print "Time, temp_out, rel_hum_out, windspeed, wind_angle, wind_chill, rain_1h, rel_pressure, dewpoint\n"; |
|---|
| 289 | |
|---|
| 290 | $results = db_query("SELECT * FROM {openweather_readings} WHERE nid = %d ORDER BY timestamp DESC", $node->nid); |
|---|
| 291 | while ($row = db_fetch_object($results)){ |
|---|
| 292 | print "$row->timestamp, $row->temp_out, $row->rel_hum_out, $row->windspeed, $row->wind_angle, $row->wind_chill, $row->rain_1h, $row->rel_pressure, $row->dewpoint\n"; |
|---|
| 293 | } |
|---|
| 294 | } |
|---|
| 295 | |
|---|
| 296 | /** |
|---|
| 297 | * Query the DB for the reading values |
|---|
| 298 | */ |
|---|
| 299 | function openweather_get_values($node){ |
|---|
| 300 | $res = array(); |
|---|
| 301 | $results = db_query("SELECT * FROM {openweather_readings} WHERE nid = %d ORDER BY timestamp DESC", $node->nid); |
|---|
| 302 | |
|---|
| 303 | while ($row = db_fetch_object($results)){ |
|---|
| 304 | $res[] = $row; |
|---|
| 305 | } |
|---|
| 306 | return $res; |
|---|
| 307 | } |
|---|
| 308 | |
|---|
| 309 | /** |
|---|
| 310 | * Implementation of hook_user(). |
|---|
| 311 | */ |
|---|
| 312 | function openweather_user($op, &$edit, &$user) { |
|---|
| 313 | if ($op == 'delete') { |
|---|
| 314 | db_query('UPDATE {openweather_readings} SET uid = 0 WHERE uid = %d', $user->uid); |
|---|
| 315 | } |
|---|
| 316 | } |
|---|
| 317 | |
|---|
| 318 | function openweather_xmlrpc(){ |
|---|
| 319 | return array( |
|---|
| 320 | array( |
|---|
| 321 | 'openweather.addEntry', |
|---|
| 322 | 'openweather_add_entry', |
|---|
| 323 | array('boolean','string','string', 'string', 'struct'), |
|---|
| 324 | t('Add data entry from remote device'), |
|---|
| 325 | ), |
|---|
| 326 | ); |
|---|
| 327 | } |
|---|
| 328 | |
|---|
| 329 | /* |
|---|
| 330 | * timestamp |
|---|
| 331 | * temp_in |
|---|
| 332 | * temp_out |
|---|
| 333 | * rel_hum_in |
|---|
| 334 | * rel_hum_out |
|---|
| 335 | * windspeed |
|---|
| 336 | * wind_angle |
|---|
| 337 | * wind_chill |
|---|
| 338 | * rain_1h |
|---|
| 339 | * rel_pressure |
|---|
| 340 | * dewpoint |
|---|
| 341 | */ |
|---|
| 342 | function openweather_add_entry($nid, $username, $password, $data){ |
|---|
| 343 | $user = user_authenticate($username, $password); |
|---|
| 344 | |
|---|
| 345 | if ($user->uid) { |
|---|
| 346 | $node = node_load($nid); |
|---|
| 347 | if ($node->type != 'openweather') |
|---|
| 348 | return false; |
|---|
| 349 | // Check person owns or can write to entry |
|---|
| 350 | |
|---|
| 351 | return db_query("INSERT INTO {openweather_readings} (nid, `timestamp`, temp_out, rel_hum_out, windspeed, wind_angle, wind_chill, rain_1h, rel_pressure, dewpoint, rain_total) VALUES (%d, '%s', %s, %s, %s, %s, %s, %s, %s, %s, %s)", $nid, $data['timestamp'], $data['temp'], $data['rel_hum'], $data['windspeed'], $data['wind_angle'], $data['wind_chill'], $data['rain_1h'], $data['rel_pressure'], $data['dewpoint'], $data['rain_total']); |
|---|
| 352 | } else { |
|---|
| 353 | return false; |
|---|
| 354 | } |
|---|
| 355 | } |
|---|
| 356 | |
|---|
| 357 | /** |
|---|
| 358 | * Produce a PNG graph of the $type data for the last 24 hours |
|---|
| 359 | */ |
|---|
| 360 | function openweather_graph() { |
|---|
| 361 | $nid = arg(1); |
|---|
| 362 | $type = arg(3); |
|---|
| 363 | $node = node_load($nid); |
|---|
| 364 | $title = ''; |
|---|
| 365 | $units = ''; |
|---|
| 366 | $autoScale = false; |
|---|
| 367 | $decimals = 0; |
|---|
| 368 | |
|---|
| 369 | switch ($type){ |
|---|
| 370 | case null: |
|---|
| 371 | case 'temp_out': |
|---|
| 372 | $type = 'temp_out'; |
|---|
| 373 | $title = t("Outdoor temperature"); |
|---|
| 374 | $autoScale = true; |
|---|
| 375 | $units = 'deg C'; |
|---|
| 376 | $decimals = 1; |
|---|
| 377 | break; |
|---|
| 378 | |
|---|
| 379 | case 'rel_pressure': |
|---|
| 380 | $title = t("Relative pressure"); |
|---|
| 381 | $units = 'hpa'; |
|---|
| 382 | $autoScale = true; |
|---|
| 383 | break; |
|---|
| 384 | |
|---|
| 385 | case 'rel_hum_out': |
|---|
| 386 | $title = t("Outdoor relative humidity"); |
|---|
| 387 | $autoScale = true; |
|---|
| 388 | $units = '%'; |
|---|
| 389 | break; |
|---|
| 390 | |
|---|
| 391 | case 'windspeed': |
|---|
| 392 | $title = t("Windspeed"); |
|---|
| 393 | $autoScale = false; |
|---|
| 394 | $units = 'kmh'; |
|---|
| 395 | $decimals = 1; |
|---|
| 396 | break; |
|---|
| 397 | |
|---|
| 398 | case 'rain_1h': |
|---|
| 399 | $title = t("Rainfall in previous hour"); |
|---|
| 400 | $autoScale = false; |
|---|
| 401 | $units = 'mm'; |
|---|
| 402 | $decimals = 1; |
|---|
| 403 | break; |
|---|
| 404 | |
|---|
| 405 | case 'rain_total': |
|---|
| 406 | $title = t("Rainfall total"); |
|---|
| 407 | $autoScale = false; |
|---|
| 408 | $units = 'mm'; |
|---|
| 409 | $decimals = 1; |
|---|
| 410 | break; |
|---|
| 411 | |
|---|
| 412 | default: |
|---|
| 413 | return; |
|---|
| 414 | } |
|---|
| 415 | |
|---|
| 416 | $data = openweather_get_latest($nid); |
|---|
| 417 | |
|---|
| 418 | $r = db_query("SELECT time(`timestamp`) AS `timevalue`, unix_timestamp(`timestamp`) as `utimestamp`, $type FROM {openweather_readings} WHERE nid = $nid AND unix_timestamp(timestamp) > ".(strtotime($data->timestamp)-24*60*60)." ORDER BY `timestamp` DESC LIMIT 500" ); |
|---|
| 419 | |
|---|
| 420 | drupal_set_header('Content-Type: image/png'); |
|---|
| 421 | include 'graph.php'; |
|---|
| 422 | $g = new graph(400, 240); |
|---|
| 423 | $g->parameter['path_to_fonts'] = 'modules/openweather/fonts/'; |
|---|
| 424 | $g->parameter['title'] = $title; |
|---|
| 425 | $g->parameter['x_label'] = 'Time'; |
|---|
| 426 | $g->parameter['y_label_left'] = $units; |
|---|
| 427 | $g->parameter['y_decimal_left'] = $decimals; |
|---|
| 428 | |
|---|
| 429 | $g->parameter['x_axis_text'] = 12; |
|---|
| 430 | |
|---|
| 431 | $g->x_data = array(); |
|---|
| 432 | $g->y_data = array( $type=>array() ); |
|---|
| 433 | $minY = null; |
|---|
| 434 | $count = 0; |
|---|
| 435 | while ($e = db_fetch_object($r)) { |
|---|
| 436 | $count++; |
|---|
| 437 | $g->x_data = array_merge($g->x_data, array($e->timevalue=>$e->utimestamp)); |
|---|
| 438 | $g->y_data[$type][] = $e->$type; |
|---|
| 439 | if ($minY == null || $e->$type < $minY) $minY = $e->$type; |
|---|
| 440 | } |
|---|
| 441 | $g->x_data = array_reverse($g->x_data); |
|---|
| 442 | $g->y_data[$type] = array_reverse($g->y_data[$type]); |
|---|
| 443 | |
|---|
| 444 | $g->y_format[$type] = array('colour'=>'red', 'line'=>'brush', 'brush_size'=>2); |
|---|
| 445 | $g->y_order = array($type); |
|---|
| 446 | |
|---|
| 447 | if ($autoScale) $g->parameter['y_min_left'] = $minY; |
|---|
| 448 | |
|---|
| 449 | $g->parameter['x_axis_text'] = $count/12; |
|---|
| 450 | |
|---|
| 451 | $g->draw(); |
|---|
| 452 | } |
|---|