# $Id: emoncms.py 737 2013-12-14 14:27:30Z mwall $
# Copyright 2013 Matthew Wall

#==============================================================================
# EmonCMS
#==============================================================================
# Upload data to EmonCMS
# http://emoncms.org
#
# Installation:
# 1) put this file in bin/user
# 2) add the following configuration stanza to weewx.conf
# 3) restart weewx
#
# [StdRESTful]
#     ...
#     [[EmonCMS]]
#         token = TOKEN
#         driver = user.emoncms.EmonCMS

import httplib
import socket
import syslog
import urllib
import urllib2

import weewx
import weewx.restful
from weeutil.weeutil import tobool

def logmsg(level, msg):
    syslog.syslog(level, 'emoncms: %s' % msg)

def logdbg(msg):
    logmsg(syslog.LOG_DEBUG, msg)

def loginf(msg):
    logmsg(syslog.LOG_INFO, msg)

def logerr(msg):
    logmsg(syslog.LOG_ERR, msg)

class EmonCMS(weewx.restful.REST):
    """Upload to an emoncms server."""

    _VERSION = 0.2
    _SERVER_URL = 'http://emoncms.org/input/post'

    # Types and formats of the data to be published:
    _formats = {'barometer'   : 'barometer_inHg:%.3f',
                'outTemp'     : 'outTemp_F:%.1f',
                'outHumidity' : 'outHumidity:%03.0f',
#                'inTemp'      : 'inTemp_F:%.1f',
#                'inHumidity'  : 'inHumidity:%03.0f',
                'windSpeed'   : 'windSpeed_mph:%.2f',
                'windDir'     : 'windDir:%03.0f',
                'windGust'    : 'windGust_mph:%.2f',
                'dewpoint'    : 'dewpoint_F:%.1f',
                'rain24'      : 'rain24_in:%.2f',
                'hourRain'    : 'hourRain_in:%.2f',
                'dayRain'     : 'dayRain_in:%.2f',
                'radiation'   : 'radiation:%.2f',
                'UV'          : 'UV:%.2f'}

    def __init__(self, site, **kwargs):
        """Initialize for a given upload site.

        token: unique token
        [Required]
        
        station: station identifier
        [Optional. Default is None]

        url: URL of the server
        [Optional. Default is the emoncms.org site]
        
        max_tries: maximum number of tries before giving up
        [Optional. Default is 3]

        timeout: timeout in seconds
        [Optional. Default is 60]

        skip_upload: debugging option to display data but do not upload
        [Optional. Default is False]
        """
        
        self.site       = site
        self.station    = kwargs.get('station', None)
        self.server_url = kwargs.get('url', self._SERVER_URL)
        self.token      = kwargs['token']
        self.max_tries  = int(kwargs.get('max_tries', 3))
        self.timeout    = int(kwargs.get('timeout', 60))
        self.skip_upload= tobool(kwargs.get('skip_upload', False))

    def postData(self, archive, time_ts):
        """Post using the EmonCMS protocol.

        archive: An instance of weewx.archive.Archive
        
        time_ts: The record desired as a unix epoch time.
        """

        _url = self.getURL(archive, time_ts)
        logdbg('url: %s' % _url)
        if self.skip_upload:
            raise weewx.restful.SkippedPost, 'skip_upload=True'

        for _count in range(self.max_tries):
            # Now use an HTTP GET to post the data.
            try:
                _request = urllib2.Request(_url)
                _request.add_header("User-Agent",
                                    "weewx/%s" % weewx.__version__)
                _response = urllib2.urlopen(_request, None, self.timeout)
            except (urllib2.URLError, socket.error, httplib.BadStatusLine), e:
                # Unsuccessful. Log it and go around again for another try
                logerr('Failed upload attempt %d to %s: %s' %
                       (_count+1, self.site, e,))
            else:
                # No exception thrown, but must still check for errors.
                # if you call read() on the response twice, the second is empty
                txt = _response.read()
                logdbg("code: %s" % _response.getcode())
                logdbg("read: '%s'" % txt)
                if txt != 'ok' :
                    logerr('Failed upload to %s: %s' % (self.site, txt))
                    raise weewx.restful.FailedPost, txt
                return
        else:
            # This is executed only if the loop terminates normally, meaning
            # the upload failed max_tries times. Log it.
            logerr('Failed upload to %s' % self.site)
            raise IOError, 'Failed upload to %s after %d tries' % (
                self.site, self.max_tries)

    def getURL(self, archive, time_ts):
        """Format a single archive record for posting to EmonCMS.
        
        archive: An instance of weewx.archive.Archive
        
        time_ts: The record desired as a unix epoch time.
        """

        record = self.extractRecordFrom(archive, time_ts)
        station = urllib.quote_plus(self.station) \
            if self.station is not None else None
        data = []
        for k in EmonCMS._formats:
            v = record[k]
            if v is not None:
                s = EmonCMS._formats[k] % v
                if station is not None:
                    s = '%s_%s' % (station, s)
                data.append(s)
        _url = '%s?apikey=%s&time=%s&json={%s}' % (
            self.server_url, self.token, time_ts, ','.join(data))
        return _url
