# $Id: owfss.py 756 2014-01-01 20:02:58Z mwall $
# Copyright 2013 Matthew Wall
"""weewx module to collect data from one-wire devices via owfs

Installation

Put this file in the bin/user directory.


Configuration

Add the following to weewx.conf:

[OWFSService]
    interface = u
    [[sensor_map]]
        extraTemp1 = /uncached/28.8A071E050000/temperature
        UV = /uncached/EE.1F20CB020800/UVI/UVI
        radiation = /26.FB67E1000000/S3-R1-A/current

[Engines]
    [[WxEngine]]
        service_list = ... , weewx.wxengine.StdQC, user.owfss.OWFSService, weewx.wxengine.StdArchive, ...
"""

import syslog
import time
import ow
import weewx
import weewx.units
from weewx.wxengine import StdService

SERVICE_VERSION = 0.1

def logmsg(level, msg):
    syslog.syslog(level, 'owfss: %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 OWFSService(StdService):
    """Collect data from one-wire devices via owfs."""

    def __init__(self, engine, config_dict):
        """
        interface: Where to find the one-wire sensors.  Options include
        u, /dev/ttyS0
        [Required. Default is u (usb)]

        sensor_map: Associate sensor values with database fields.
        [Required]
        """
        super(OWFSService, self).__init__(engine, config_dict)

        d = config_dict.get('OWFSService', {})
        self.sensor_map = d['sensor_map']
        self.interface  = d.get('interface', 'u')
        self.max_tries  = int(d.get('max_tries', 3))
        self.retry_wait = int(d.get('retry_wait', 5))
        ow.init(self.interface)
        self.bind(weewx.NEW_ARCHIVE_RECORD, self.getData)
        loginf('sensor map is %s' % self.sensor_map)

    def getData(self, event):
        ntries = 0
        while ntries < self.max_tries:
            ntries += 1
            try:
                p = {}
                p['usUnits'] = weewx.METRIC
                for s in self.sensor_map:
                    p[s] = float(ow.owfs_get(self.sensor_map[s]))
                ntries = 0
                if event.record['usUnits'] != weewx.METRIC:
                    converter = weewx.units.StdUnitConverters[event.record['usUnits']]
                    p = converter.convertDict(p)
                event.record.update(p)
                break
            except ow.exError, e:
                logerr("Failed attempt %d of %d to get LOOP data: %s" %
                       (ntries, self.max_tries, e))
                logdbg("Waiting %d seconds before retry" % self.retry_wait)
                time.sleep(self.retry_wait)
        else:
            msg = "Max retries (%d) exceeded for LOOP data" % self.max_tries
            logerr(msg)
            raise weewx.RetriesExceeded(msg)


# define a main entry point for basic testing without weewx overhead.
# invoke this as follows from the weewx root dir:
#
# PYTHONPATH=bin python bin/user/owfss.py

usage = """%prog [options] [--debug] [--help]"""

def main():
    import optparse
    syslog.openlog('wee_owfss', syslog.LOG_PID | syslog.LOG_CONS)
    parser = optparse.OptionParser(usage=usage)
    parser.add_option('--version', dest='version', action='store_true',
                      help='display driver version')
    parser.add_option('--debug', dest='debug', action='store_true',
                      help='display diagnostic information while running')
    parser.add_option("--iface", dest="iface", type=str, metavar="IFACE",
                      help="specify the interface, e.g., u or /dev/ttyS0")
    parser.add_option('--sensors', dest='sensors', action='store_true',
                      help='display list attached sensors')
    parser.add_option('--readings', dest='readings', action='store_true',
                      help='display sensor readings')
    (options, args) = parser.parse_args()

    if options.version:
        print "owfss version %s" % SERVICE_VERSION
        exit(1)

    # default to usb for the interface
    iface = options.iface if options.iface is not None else 'u'

    if options.debug is not None:
        syslog.setlogmask(syslog.LOG_UPTO(syslog.LOG_DEBUG))
    else:
        syslog.setlogmask(syslog.LOG_UPTO(syslog.LOG_INFO))

    if options.sensors:
        ow.init(iface)
        traverse(ow.Sensor('/'), identify_sensor)
    elif options.readings:
        ow.init(iface)
        traverse(ow.Sensor('/'), display_sensor_info)

def identify_sensor(s):
    print '%s: %s %s' % (s.id, s._path, s._type)

def display_sensor_info(s):
    print s.id
    display_dict(s.__dict__)

def display_dict(d, level=0):
    for k in d:
        if isinstance(d[k], dict):
            display_dict(d[k], level=level+1)
        else:
            if k == 'alias':
                pass
            elif k.startswith('_'):
                print '%s%s: %s' % ('  '*level, k, d[k])
            else:
                print '%s%s: %s' % ('  '*level, d[k], ow.owfs_get(d[k]))

def traverse(device, func):
    for s in device.sensors():
        if s._type in ['DS2409']:
            traverse(s, func)
        else:
            func(s)

if __name__ == '__main__':
    main()
