# ==============================================================================
# WetternetzSachsen
# ==============================================================================
class WetternetzSachsen(StdRESTful):
"""Upload data to the Wetternetz Sachsen (Saxony - Germany) using protocol WNS V2.1
To enable this module, add the following to weewx.conf:
[StdRESTful]
[[WetternetzSachsen]]
enable = True
username = StationsID [WNSxxx]
password = Stationskennung [DirectPost Password]
The WetternetzSachsen-server expects a single string of values delimited by
semicolons. The position of each value matters, for example position 1
is the StationsID and position 2 is the Stationskennung [DirectPost Password].
For details of the Wetternetz Sachsen V2.1 protocol and further position of values for the string see
http://www.wetternetz-sachsen.de/download/parameterliste_%20wns_21.txt
# The URL used by Wetternetz Sachsen:
archive_url = "http://www.wetternetz-sachsen.de/get_daten_21.php"
"""
def __init__(self, engine, config_dict):
super(StdWetternetzSachsen, self).__init__(engine, config_dict)
site_dict = get_site_dict(
config_dict, 'WetternetzSachsen', 'station', 'password')
if site_dict is None:
return
site_dict.setdefault('language', 'de')
# Get the manager dictionary:
site_dict['manager_dict'] = weewx.manager.get_manager_dict_from_config(
config_dict, 'wx_binding')
self.archive_queue = Queue.Queue()
self.archive_thread = WetternetzSachsenThread(self.archive_queue, **site_dict)
self.archive_thread.start()
self.bind(weewx.NEW_ARCHIVE_RECORD, self.new_archive_record)
syslog.syslog(syslog.LOG_INFO, "restx: WetternetzSachsen: "
"Data will be uploaded for user %s" %
site_dict['username'])
def new_archive_record(self, event):
self.archive_queue.put(event.record)
class WetternetzSachsenThread(RESTThread):
"""Concrete class for threads posting from the archive queue,
using the WetternetzSachsen V2.1 protocol."""
_SERVER_URL = 'http://www.wetternetz-sachsen.de/get_daten_21.php'
def __init__(self, queue, username, password,
manager_dict,
language='de', server_url=_SERVER_URL,
post_interval=300, max_backlog=sys.maxint, stale=None,
log_success=True, log_failure=True,
timeout=60, max_tries=3, retry_wait=5, retry_login=1800, skip_upload=False):
"""Initialize an instances of WetternetzSachsenThread.
Parameters specific to this class:
username: StationsID [WNSxxx]
password: Stationskennung [DirectPost Password]
manager_dict: A dictionary holding the database manager
information. It will be used to open a connection to the archive
database.
server_url: URL of the server
No need to change, Default is the WetternetzSachsen getdaten-php-file
Parameters customized for this class:
post_interval: The interval in seconds between posts. The usual upload interval is 300 seconds. So default is set to 300. Intervals between 60 and 3600 seconds are possible but useful are only intervals less than 900 seconds. """
super(WetternetzSachsenThread, self).__init__(queue,
protocol_name='WetternetzSachsen',
manager_dict=manager_dict,
post_interval=post_interval,
max_backlog=max_backlog,
stale=stale,
log_success=log_success,
log_failure=log_failure,
timeout=timeout,
max_tries=max_tries,
retry_wait=retry_wait,
retry_login=retry_login,
skip_upload=skip_upload)
self.username = username
self.password = password
self.server_url = server_url
def get_record(self, record, dbmanager):
# Get the record from my superclass
r = super(WetternetzSachsenThread, self).get_record(record, dbmanager)
def process_record(self, record, dbmanager):
r = self.get_record(record, dbmanager)
url = self.get_url(r)
if self.skip_upload:
raise AbortedPost("Skip post")
req = urllib2.Request(url)
req.add_header("User-Agent", "weewx/%s" % weewx.__version__)
self.post_with_retries(req)
def check_response(self, response):
"""Check the HTTP response code for an Ambient related error."""
for line in response:
if line.startswith("Benutzer/Passwort Fehler"):
raise BadLogin(line)
elif not line.startswith('OK'):
raise FailedPost("server returned '%s'" % line)
def get_url(self, in_record):
# Convert to units required by WetternetzSachsen
record = weewx.units.to_METRIC(in_record)
# assemble an array of values in the proper order
values = [self.username]
values = [self.password]
values.append('WNS V2.1') # TMPVER
values.append('WeeWx') # WSOVER
time_tt = time.gmtime(record['dateTime'])
values.append(time.strftime("%H:%M", time_tt)) # ZEIT__
values.append(time.strftime("%d.%m.%Y", time_tt)) # DATUM_
values.append('-1') # UTCDIF
values.append('$current.outTemp') # T2AKT_
values.append('$day.outTemp.min') # T2MIN_
values.append('$day.outTemp.max') # T2MAX_
values.append('$span($hour_delta=1).outTemp') # T2D1H_
values.append('$current.extraTemp1') # T5AKT_
values.append('$current.extraTemp1') # T5MIN_
values.append('$current.outHumidity')) # LFAKT_
values.append('$span($time_delta=300).rain.sum') # RRD05_
values.append('$span($time_delta=600).rain.sum') # RRD10_
values.append('$span($hour_delta=1).rain.sum') # RRD1H_
values.append('$span($hour_delta=3).rain.sum') # RRD3H_
values.append('$span($hour_delta=24).rain.sum') # RRD24H
values.append('$day.rain.sum') # RRD1D_
values.append('$current.wind') # WSAKT_
values.append('$current.windDir') # WRAKT_
values.append('$current.windGust') # WBAKT_
values.append('$span($time_delta=600).wind.avg') # WSM10_
values.append('$span($time_delta=600).windDir.avg') # WRM10_
values.append('$span($hour_delta=1).wind.max') # WSMX1H
values.append('$day.wind.max') # WSMX1D
values.append('$day.windGust.max') # WBMX1D
values.append('$current.chill') # WCAKT_ ????????????
values.append('$span($hour_delta=1).chill.min') # WCMN1H ??????????
values.append('$day.chill.min') # WCMN1D ??????????
values.append('$current.barometer') # LDAKT_
values.append('$current.pressure') # LDABS_
values.append('$span($hour_delta=1).barometer') # LDD1H_
values.append('$span($hour_delta=3).barometer') # LDD3H_
values.append('$span($hour_delta=24).barometer') # LDD24H
values.append('$day.ET.sum') # EVA1D_
values.append('--') # SOD1H_
values.append('--') # SOD1D_
values.append('--') # BEDGRA
values.append('$current.radiation') # SSAKT_
values.append('$span($hour_delta=1).radiation') # SSMX1H
values.append('$day.radiation.max') # SSMX1D
values.append('$current.UV') # UVINDX
values.append('$day.UV.max') # UVMX1D
values.append('--') # WOLKUG
values.append('--') # SIWEIT
values.append('--') # SNEHOE
values.append('--') # SNEDAT
values.append('--') # SNEFGR
values.append('$span($month_delta=1).outTemp.avg') # T2M1M_
values.append('--') # T2M1MA
values.append('--') # RRDATU
values.append('$yesterday.rain.sum') # RRGEST
values.append('$month.rain.sum') # RRD1M_
values.append('--') # RRD1MR
values.append('$year.rain.sum') # RRD1A_
values.append('--') # RRD1AR
values.append('$month.ET.sum') # EVAD1M
values.append('$year.ET.sum') # EVAD1A
values.append('--') # SOD1M_
values.append('--') # SOD1A_
values.append('--') # SOD1AR
values.append('--') # KLTSUM
values.append('--') # WRMSUM
values.append('--') # GRASUM
values.append('--') # GRADAT
valstr = ';'.join(values)
url = self.server_url + '?val=' + valstr
# show the url in the logs for debug, but mask any credentials
if weewx.debug >= 2:
syslog.syslog(syslog.LOG_DEBUG, 'restx: WetternetzSachsen: url: %s' %
re.sub(m.hexdigest(), "XXX", url))
return url
# ==============================================================================
# Station Registry
# ==============================================================================
class StdStationRegistry(StdRESTful):
"""Class for phoning home to register a weewx station.
To enable this module, add the following to weewx.conf:
[StdRESTful]
[[StationRegistry]]
register_this_station = True
This will periodically do a http GET with the following information:
station_url Should be world-accessible. Used as key.
description Brief synopsis of the station
latitude Station latitude in decimal
longitude Station longitude in decimal
station_type The driver name, for example Vantage, FineOffsetUSB
station_model The hardware_name property from the driver
weewx_info weewx version
python_info
platform_info
The station_url is the unique key by which a station is identified.
"""
archive_url = 'http://weewx.com/register/register.cgi'
def __init__(self, engine, config_dict):
super(StdStationRegistry, self).__init__(engine, config_dict)
# Extract a copy of the dictionary with the registry options:
_registry_dict = accumulateLeaves(config_dict['StdRESTful']['StationRegistry'], max_level=1)
# Should the service be run?
if not to_bool(_registry_dict.pop('register_this_station', False)):
syslog.syslog(syslog.LOG_INFO, "restx: StationRegistry: "
"Registration not requested.")
return
# Registry requires a valid station url
_registry_dict.setdefault('station_url',
self.engine.stn_info.station_url)
if _registry_dict['station_url'] is None:
syslog.syslog(syslog.LOG_INFO, "restx: StationRegistry: "
"Station will not be registered: no station_url specified.")
return
_registry_dict.setdefault('station_type', config_dict['Station'].get('station_type', 'Unknown'))
_registry_dict.setdefault('description', self.engine.stn_info.location)
_registry_dict.setdefault('latitude', self.engine.stn_info.latitude_f)
_registry_dict.setdefault('longitude', self.engine.stn_info.longitude_f)
_registry_dict.setdefault('station_model', self.engine.stn_info.hardware)
self.archive_queue = Queue.Queue()
self.archive_thread = StationRegistryThread(self.archive_queue,
**_registry_dict)
self.archive_thread.start()
self.bind(weewx.NEW_ARCHIVE_RECORD, self.new_archive_record)
syslog.syslog(syslog.LOG_INFO, "restx: StationRegistry: "
"Station will be registered.")
def new_archive_record(self, event):
self.archive_queue.put(event.record)
class StationRegistryThread(RESTThread):
"""Concrete threaded class for posting to the weewx station registry."""
def __init__(self, queue, station_url, latitude, longitude,
server_url=StdStationRegistry.archive_url,
description="Unknown",
station_type="Unknown", station_model="Unknown",
post_interval=604800, max_backlog=0, stale=None,
log_success=True, log_failure=True,
timeout=60, max_tries=3, retry_wait=5):
"""Initialize an instance of StationRegistryThread.
Parameters specific to this class:
station_url: An URL used to identify the station. This will be
used as the unique key in the registry to identify each station.
latitude: Latitude of the staion
longitude: Longitude of the station
server_url: The URL of the registry server.
Default is 'http://weewx.com/register/register.cgi'
description: A brief description of the station.
Default is 'Unknown'
station_type: The type of station. Generally, this is the name of
the driver used by the station.
Default is 'Unknown'
station_model: The hardware model, typically the hardware_name
property provided by the driver.
Default is 'Unknown'.
Parameters customized for this class:
post_interval: How long to wait between posts.
Default is 604800 seconds (1 week).
"""
super(StationRegistryThread, self).__init__(
queue,
protocol_name='StationRegistry',
post_interval=post_interval,
max_backlog=max_backlog,
stale=stale,
log_success=log_success,
log_failure=log_failure,
timeout=timeout,
max_tries=max_tries,
retry_wait=retry_wait)
self.station_url = station_url
self.latitude = to_float(latitude)
self.longitude = to_float(longitude)
self.server_url = server_url
self.description = weeutil.weeutil.list_as_string(description)
self.station_type = station_type
self.station_model = station_model
def get_record(self, dummy_record, dummy_archive):
_record = dict()
_record['station_url'] = self.station_url
_record['description'] = self.description
_record['latitude'] = self.latitude
_record['longitude'] = self.longitude
_record['station_type'] = self.station_type
_record['station_model'] = self.station_model
_record['python_info'] = platform.python_version()
_record['platform_info'] = platform.platform()
_record['weewx_info'] = weewx.__version__
_record['usUnits'] = weewx.US
return _record
_FORMATS = {'station_url' : 'station_url=%s',
'description' : 'description=%s',
'latitude' : 'latitude=%.4f',
'longitude' : 'longitude=%.4f',
'station_type' : 'station_type=%s',
'station_model': 'station_model=%s',
'python_info' : 'python_info=%s',
'platform_info': 'platform_info=%s',
'weewx_info' : 'weewx_info=%s'}
def format_url(self, record):
"""Return an URL for posting using the StationRegistry protocol."""
_liststr = []
for _key in StationRegistryThread._FORMATS:
v = record[_key]
if v is not None:
_liststr.append(urllib.quote_plus(
StationRegistryThread._FORMATS[_key] % v, '='))
_urlquery = '&'.join(_liststr)
_url = "%s?%s" % (self.server_url, _urlquery)
return _url
def check_response(self, response):
"""Check the response from a Station Registry post."""
for line in response:
# the server replies to a bad post with a line starting with "FAIL"
if line.startswith('FAIL'):
raise FailedPost(line)