#!/usr/bin/python
# encoding: utf-8
#
# Handles devices reporting themselves as USB VID/PID 0C45:7401 (mine also says RDing TEMPerV1.2).
#
# Copyright 2012, 2013 Philipp Adelt <info@philipp.adelt.net>
#
# This code is licensed under the GNU public license (GPL). See LICENSE.md for details.

# originally from: https://github.com/padelt/temper-python

# minor updates (calibration, bus/device access) by:
# Maximilian Engelhardt <maxi@stusta.de>
# Johannes Naab <jn@stusta.de>

import usb
import sys
import struct
import time

VIDPIDs = [(0x0c45L,0x7401L)]
REQ_INT_LEN = 8
REQ_BULK_LEN = 8
TIMEOUT = 2000

class TemperDevice():
    def __init__(self, bus, device):
        self._device = device
        self._bus = bus
        self._handle = None

    def get_id(self):
        return self._bus.dirname, self._device.filename

    def get_temperature(self, calibration=0, format='celsius'):
        try:
            if not self._handle:
                self._handle = self._device.open()
                try:
                    self._handle.detachKernelDriver(0)
                except usb.USBError:
                    pass
                try:
                    self._handle.detachKernelDriver(1)
                except usb.USBError:
                    pass
                self._handle.setConfiguration(1)
                self._handle.claimInterface(0)
                self._handle.claimInterface(1)
                self._handle.controlMsg(requestType=0x21, request=0x09, value=0x0201, index=0x00, buffer="\x01\x01", timeout=TIMEOUT) # ini_control_transfer

            self._control_transfer(self._handle, "\x01\x80\x33\x01\x00\x00\x00\x00") # uTemperatura
            self._interrupt_read(self._handle)
            self._control_transfer(self._handle, "\x01\x82\x77\x01\x00\x00\x00\x00") # uIni1
            self._interrupt_read(self._handle)
            self._control_transfer(self._handle, "\x01\x86\xff\x01\x00\x00\x00\x00") # uIni2
            self._interrupt_read(self._handle)
            self._interrupt_read(self._handle)
            self._control_transfer(self._handle, "\x01\x80\x33\x01\x00\x00\x00\x00") # uTemperatura
            data = self._interrupt_read(self._handle)
            data_s = "".join([chr(byte) for byte in data])
            temp_c = (struct.unpack('>h', data_s[2:4])[0])
            temp_c += calibration
            temp_c *= 125.0/32000.0
            if format == 'celsius':
                return temp_c
            elif format == 'fahrenheit':
                return temp_c*1.8+32.0
            elif format == 'millicelsius':
                return int(temp_c*1000)
            else:
                raise ValueError("Unknown format")
        except usb.USBError, e:
            self.close()
            if "not permitted" in str(e):
                raise Exception("Permission problem accessing USB. Maybe I need to run as root?")
            else:
                raise

    def close(self):
        if self._handle:
            try:
                self._handle.releaseInterface()
            except ValueError:
                pass
            self._handle = None

    def _control_transfer(self, handle, data):
        handle.controlMsg(requestType=0x21, request=0x09, value=0x0200, index=0x01, buffer=data, timeout=TIMEOUT)

    def _interrupt_read(self, handle):
        return handle.interruptRead(0x82, REQ_INT_LEN)


class TemperHandler():
    def __init__(self):
        busses = usb.busses()
        # key: (bus.dirname, dev.filename), value: TemperDevice
        self._devices = {}
        for bus in busses:
            for x in bus.devices:
                if (x.idVendor,x.idProduct) in VIDPIDs:
                    tdev = TemperDevice(bus, x)
                    self._devices[tdev.get_id()] = tdev


    def get_devices(self):
        return self._devices

if __name__ == '__main__':
    th = TemperHandler()
    for i, dev in th._devices.iteritems():
        try:
            print "Device %s: %0.1f°C %0.1f°F" % (i[0]+"/"+i[1], dev.get_temperature(calibration=0), dev.get_temperature(format="fahrenheit"))
        except Exception as e:
            print e