Source code for pywws.service.metoffice

# pywws - Python software for USB Wireless Weather Stations
# http://github.com/jim-easterbrook/pywws
# Copyright (C) 2018-21  pywws contributors

# This program 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
# of the License, or (at your option) any later version.

# This program 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 this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

"""Upload weather data to UK Met Office "WOW".

The UK Met Office runs a `Weather Observations Website`_ (WOW) that
displays readings from amateur and official weather stations. This
module uploads data to it from pywws. You can upload "logged" or "live"
data (or both). The module ensures there is at least 5 minutes between
each reading as required by the API.

* Create account: https://register.metoffice.gov.uk/WaveRegistrationClient/public/newaccount.do?service=weatherobservations
* API: http://wow.metoffice.gov.uk/support/dataformats#automatic
* Additional dependency: http://docs.python-requests.org/
* Example ``weather.ini`` configuration::

    [metoffice]
    site id = 12345678
    aws pin = 987654

    [logged]
    services = ['metoffice', 'underground']

    [live]
    services = ['metoffice', 'underground']

Note that a ``site id`` allocated since June 2016 will probably look
like ``6a571450-df53-e611-9401-0003ff5987fd``.

.. _Weather Observations Website: http://wow.metoffice.gov.uk/home

"""

from __future__ import absolute_import, unicode_literals

from contextlib import contextmanager
from datetime import timedelta
import logging
import os
import sys

import requests

import pywws
from pywws.conversions import rain_inch
from pywws.process import get_day_end_hour
import pywws.service
from pywws.timezone import time_zone

__docformat__ = "restructuredtext en"
service_name = os.path.splitext(os.path.basename(__file__))[0]
logger = logging.getLogger(__name__)


[docs]class ToService(pywws.service.CatchupDataService): config = { 'site id': ('', True, 'siteid'), 'aws pin': ('', True, 'siteAuthenticationKey'), } fixed_data = {'softwaretype': 'pywws-' + pywws.__version__} interval = timedelta(seconds=300) logger = logger service_name = service_name template = """ #live# #idx "'dateutc' : '%Y-%m-%d %H:%M:%S',"# #wind_dir "'winddir' : '%.0f'," "" "winddir_degrees(x)"# #wind_ave "'windspeedmph': '%.2f'," "" "wind_mph(x)"# #wind_gust "'windgustmph' : '%.2f'," "" "wind_mph(x)"# #hum_out "'humidity' : '%.d',"# #temp_out "'tempf' : '%.1f'," "" "temp_f(x)"# #rel_pressure "'baromin' : '%.4f'," "" "pressure_inhg(x)"# #calc "rain_inch(self.rain_rate(data))" "'rainin' : '%.4f',"# #calc "rain_inch(self.rain_day_local(data))" "'dailyrainin' : '%.4f',"# #calc "temp_f(dew_point(data['temp_out'], data['hum_out']))" "'dewptf' : '%.1f',"# """ def __init__(self, context, check_params=True): super(ToService, self).__init__(context, check_params) # initialise rain history last_update = context.calib_data.nearest(self.last_update) self.last_rain = context.calib_data[last_update]['rain'] # add rain functions to templater self.templater.rain_rate = self.rain_rate self.templater.rain_day_local = self.rain_day_local
[docs] @contextmanager def session(self): with requests.Session() as session: yield session
[docs] def rain_rate(self, data): # compute rain since last upload result = max(data['rain'] - self.last_rain, 0.0) self.last_rain = data['rain'] return result
[docs] def rain_day_local(self, data): # compute rain since day start day_end_hour, use_dst = get_day_end_hour(self.context.params) day_start = time_zone.day_start( data['idx'], day_end_hour, use_dst=use_dst) day_start = self.context.calib_data.nearest(day_start) return max( data['rain'] - self.context.calib_data[day_start]['rain'], 0.0)
[docs] def valid_data(self, data): return any([data[x] is not None for x in ( 'wind_dir', 'wind_ave', 'wind_gust', 'hum_out', 'temp_out', 'rel_pressure')])
[docs] def upload_data(self, session, prepared_data={}): try: rsp = session.get('http://wow.metoffice.gov.uk/automaticreading', params=prepared_data, timeout=60) except Exception as ex: return False, repr(ex) if rsp.status_code == 429: # UK Met Office server uses 429 to signal duplicate data return True, 'repeated data {:s}'.format(prepared_data['dateutc']) if rsp.status_code != 200: return False, 'http status: {:d}'.format(rsp.status_code) rsp = rsp.json() if rsp: return True, 'server response "{!r}"'.format(rsp) return True, 'OK'
if __name__ == "__main__": sys.exit(pywws.service.main(ToService))