# $Id: pobwx.py 2766 2014-12-02 02:45:36Z tkeffer $ # Copyright (c) 2012 Tom Keffer # See the file LICENSE.txt for your full rights. """ weewx Driver for the Ambient Weather ObserverIP, based on scraping Pat O'Brien https://github.com/poblabs/weewx-ObserverIP """ from __future__ import with_statement from lxml import html import math, time, syslog, requests import weedb import weewx.drivers import weeutil.weeutil import weewx.wxformulas DRIVER_NAME = 'ObserverIP_scraper' DRIVER_VERSION = '1.0' def loader(config_dict, engine): station = ObserverIP_scraper(**config_dict['ObserverIP_scraper']) return station # Convert stuff like "---" to None, like weewx expects. def dataToFloat(value): try: return float(value) except: return None class ObserverIP_scraper(weewx.drivers.AbstractDevice): """Custom driver for Ambient Weather ObserverIP, based on scraping Mostly based off Simulator """ def __init__(self, **stn_dict): self.loop_interval = float(stn_dict.get('loop_interval')) # This is how many reads should be done between each reboot, # to get a reboot every 24 hours. self.reboot_interval = 24 * 60 * 60 / self.loop_interval # Seconds to wait after sending reboot command. self.reboot_delay = 30 self.reads_since_reboot = 0 self.success_count = 0 self.station_ip = stn_dict.get('ip_address') self.station_url = "http://%s/livedata.htm" % self.station_ip self.reboot_url = "http://%s/msgreboot.htm" % self.station_ip self.station_hardware = stn_dict.get('hardware') self.last_rain = None def getTime(self): # The ObserverIP doesn't do seconds, so using the time from the ObserverIP # with a loop packet of every 15 seconds is useless. All measurements will be archived # from the same minute timestamp, even though the values could be different within the same minute. # This method uses mwall's method from his fork of ObserverIP epoch = int(time.time() + 0.5 ) return epoch def hardware_name(self): return self.station_hardware def get_battery_status(self, data): if (data == "Normal"): return 0 else: return 1 def rain_delta(self, rain_total): # Handle the rain accum by taking the accumulated rain reading and only submitting the increments delta = weewx.wxformulas.calculate_rain(rain_total, self.last_rain) self.last_rain = rain_total return delta def rebootObserverIP(self): syslog.syslog("Rebooting ObserverIP") # Touch the ObserverIP reboot URL to cause it to reboot. try: page = requests.get(self.reboot_url, timeout=15.0) page.raise_for_status() except Exception as e: syslog.syslog("ObserverIP_scraper driver couldn't access %s." % self.reboot_url) syslog.syslog("Error caught was: %s" % e) syslog.syslog("Sleeping for %s" % self.reboot_delay) time.sleep(self.reboot_delay) def genLoopPackets(self): while True: # Screen scrape the ObserverIP to get sensor readings. try: page = requests.get(self.station_url, timeout=15.0) page.raise_for_status() tree = html.fromstring(page.content) # Can weewx take this value? #uvi = tree.xpath('//input[@name="uvi"]')[0].value inBattery = tree.xpath('//input[@name="inBattSta"]')[0].value outBattery = tree.xpath('//input[@name="outBattSta1"]')[0].value inTemp = tree.xpath('//input[@name="inTemp"]')[0].value inHumid = tree.xpath('//input[@name="inHumi"]')[0].value outTemp = tree.xpath('//input[@name="outTemp"]')[0].value outHumid = tree.xpath('//input[@name="outHumi"]')[0].value absPressure = tree.xpath('//input[@name="AbsPress"]')[0].value relPressure = tree.xpath('//input[@name="RelPress"]')[0].value windDir = tree.xpath('//input[@name="windir"]')[0].value windSpeed = tree.xpath('//input[@name="avgwind"]')[0].value windGust = tree.xpath('//input[@name="gustspeed"]')[0].value solarRadiation = tree.xpath('//input[@name="solarrad"]')[0].value uv = tree.xpath('//input[@name="uv"]')[0].value dayRain = tree.xpath('//input[@name="rainofdaily"]')[0].value monthRain = tree.xpath('//input[@name="rainofmonthly"]')[0].value yearRain = tree.xpath('//input[@name="rainofyearly"]')[0].value self.success_count += 1 except Exception as e: syslog.syslog("ObserverIP_scraper driver couldn't access %s." % self.station_url) syslog.syslog("Error caught was: %s" % e) syslog.syslog("%d successful reads since last error" % self.success_count) self.success_count = 0 pass # Continue without exiting. TODO: Better error handling and error sleeping # Build the packet data try: _packet = { 'dateTime' : self.getTime(), 'usUnits' : weewx.US, 'outTemp' : dataToFloat(outTemp), 'outHumidity' : dataToFloat(outHumid), 'inTemp' : dataToFloat(inTemp), 'inHumidity' : dataToFloat(inHumid), 'pressure' : dataToFloat(absPressure), 'barometer' : dataToFloat(relPressure), 'rain': self.rain_delta(dataToFloat(yearRain)), # weewx can calculate the per-period rain totals #'dayRain' : dataToFloat(dayRain), #'monthRain' : dataToFloat(monthRain), #'yearRain' : dataToFloat(yearRain), 'windDir' : dataToFloat(windDir), 'windSpeed' : dataToFloat(windSpeed), 'windGust' : dataToFloat(windGust), # WS-0900 does not support these. The fields are "----.-" #'radiation' : float(solarRadiation), #'UV' : float(uv), 'outTempBatteryStatus' : self.get_battery_status(outBattery), 'inTempBatteryStatus' : self.get_battery_status(inBattery) } yield _packet except Exception as e: syslog.syslog("ObserverIP_scraper driver had an error yielding data packet to weewx.") syslog.syslog("Error caught was: %s" % e) pass # Continue without exiting. TODO: Better error handling and error sleeping # If the time is right, reboot the ObserverIP self.reads_since_reboot += 1 if (self.reads_since_reboot > self.reboot_interval): self.rebootObserverIP() self.reads_since_reboot = 0 # Sleep time #syslog.syslog("Sleeping for %s" % self.loop_interval) time.sleep(self.loop_interval)