def __init__(self, parent, app):
        super(QWidget, self).__init__()

        layout1 = QHBoxLayout()
        layout2 = QVBoxLayout()
        
        layout1.addStretch()
        layout1.addLayout(layout2)
        layout1.addStretch()

        label = QLabel(self)
        label.setText("Simple Project: <b>Display Environment Measurements on LCD</b>. Simply displays all measured values on LCD. Sources in all programming languages can be found <a href=\"http://www.tinkerforge.com/en/doc/Kits/WeatherStation/WeatherStation.html#display-environment-measurements-on-lcd\">here</a>.<br>")
        label.setTextFormat(Qt.RichText)
        label.setTextInteractionFlags(Qt.TextBrowserInteraction)
        label.setOpenExternalLinks(True)
        label.setWordWrap(True)
        label.setAlignment(Qt.AlignJustify)

        layout2.addSpacing(10)
        layout2.addWidget(label)
        layout2.addSpacing(10)

        self.lcdwidget = LCDWidget(self, app)

        layout2.addWidget(self.lcdwidget)
        layout2.addStretch()

        self.setLayout(layout1)

        self.qtcb_update_illuminance.connect(self.update_illuminance_data_slot)
        self.qtcb_update_air_pressure.connect(self.update_air_pressure_data_slot)
        self.qtcb_update_temperature.connect(self.update_temperature_data_slot)
        self.qtcb_update_humidity.connect(self.update_humidity_data_slot)
        self.qtcb_button_pressed.connect(self.button_pressed_slot)
class ProjectEnvDisplay(QWidget):

    qtcb_update_illuminance = pyqtSignal(float)
    qtcb_update_air_pressure = pyqtSignal(float)
    qtcb_update_temperature = pyqtSignal(float)
    qtcb_update_humidity = pyqtSignal(float)
    qtcb_button_pressed = pyqtSignal(int)

    lcdwidget = None

    def __init__(self, parent, app):
        super(QWidget, self).__init__()

        layout1 = QHBoxLayout()
        layout2 = QVBoxLayout()
        
        layout1.addStretch()
        layout1.addLayout(layout2)
        layout1.addStretch()

        label = QLabel(self)
        label.setText("Simple Project: <b>Display Environment Measurements on LCD</b>. Simply displays all measured values on LCD. Sources in all programming languages can be found <a href=\"http://www.tinkerforge.com/en/doc/Kits/WeatherStation/WeatherStation.html#display-environment-measurements-on-lcd\">here</a>.<br>")
        label.setTextFormat(Qt.RichText)
        label.setTextInteractionFlags(Qt.TextBrowserInteraction)
        label.setOpenExternalLinks(True)
        label.setWordWrap(True)
        label.setAlignment(Qt.AlignJustify)

        layout2.addSpacing(10)
        layout2.addWidget(label)
        layout2.addSpacing(10)

        self.lcdwidget = LCDWidget(self, app)

        layout2.addWidget(self.lcdwidget)
        layout2.addStretch()

        self.setLayout(layout1)

        self.qtcb_update_illuminance.connect(self.update_illuminance_data_slot)
        self.qtcb_update_air_pressure.connect(self.update_air_pressure_data_slot)
        self.qtcb_update_temperature.connect(self.update_temperature_data_slot)
        self.qtcb_update_humidity.connect(self.update_humidity_data_slot)
        self.qtcb_button_pressed.connect(self.button_pressed_slot)


    def update_illuminance_data_slot(self, illuminance):

        text = 'Illuminanc %6.2f lx' % (illuminance/10.0)
        self.lcdwidget.write_line(0, 0, text, self)

    def update_illuminance(self, illuminance):
        self.qtcb_update_illuminance.emit(illuminance)

    def update_humidity_data_slot(self, humidity):
        text = 'Humidity   %6.2f %%' % (humidity/10.0)
        self.lcdwidget.write_line(1, 0, text, self)
    
    def update_humidity(self, humidity):
        self.qtcb_update_humidity.emit(humidity)

    def update_air_pressure_data_slot(self, air_pressure):

        text = 'Air Press %7.2f mb' % (air_pressure/1000.0)
        self.lcdwidget.write_line(2, 0, text, self)
    
    def update_air_pressure(self, air_pressure):
        self.qtcb_update_air_pressure.emit(air_pressure)

    def update_temperature_data_slot(self, temperature):

        # \xDF == ° on LCD 20x4 charset
        text = 'Temperature %5.2f \xDFC' % (temperature/100.0)
        self.lcdwidget.write_line(3, 0, text, self)

    def update_temperature(self, temperature):
        self.qtcb_update_temperature.emit(temperature)

    def button_pressed_slot(self, button):
        pass

    def button_pressed(self, button):
        self.qtcb_button_pressed.emit(button)
    def __init__(self, parent, app):
        super(QWidget, self).__init__(parent)

        layout1 = QHBoxLayout()
        layout2 = QVBoxLayout()
        
        layout1.addStretch()
        layout1.addLayout(layout2)
        layout1.addStretch()

        label = QLabel(self)
        label.setText("Project: <b>Show Statistics with Button Control</b>. Shows different statistics. You can select different modi by pressing the buttons. Some modi will allow display change by pressing the button multiple times. Sources in C# can be found <a href=\"http://www.tinkerforge.com/en/doc/Kits/WeatherStation/WeatherStation.html#show-statistics-with-button-control\">here</a>.")
        label.setTextFormat(Qt.RichText)
        label.setTextInteractionFlags(Qt.TextBrowserInteraction)
        label.setOpenExternalLinks(True)
        label.setWordWrap(True)
        label.setAlignment(Qt.AlignJustify)

        layout2.addSpacing(10)
        layout2.addWidget(label)
        layout2.addSpacing(10)

        self.lcdwidget = LCDWidget(self, app)

        layout2.addWidget(self.lcdwidget)
        layout2.addSpacing(10)

        self.buttons[0] = QPushButton(self)
        self.buttons[0].setFixedSize(100, 30)
        self.buttons[0].setText("BTN0")
        self.buttons[0].clicked.connect(lambda: self.button_pressed_slot(4))

        self.buttons[1] = QPushButton(self)
        self.buttons[1].setFixedSize(100, 30)
        self.buttons[1].setText("BTN1")
        self.buttons[1].clicked.connect(lambda: self.button_pressed_slot(5))

        self.buttons[2] = QPushButton(self)
        self.buttons[2].setFixedSize(100, 30)
        self.buttons[2].setText("BTN2")
        self.buttons[2].clicked.connect(lambda: self.button_pressed_slot(6))

        self.buttons[3] = QPushButton(self)
        self.buttons[3].setFixedSize(100, 30)
        self.buttons[3].setText("BTN3")
        self.buttons[3].clicked.connect(lambda: self.button_pressed_slot(7))
        
        layout3 = QHBoxLayout()
        layout3.addWidget(self.buttons[0])
        layout3.addWidget(self.buttons[1])
        layout3.addWidget(self.buttons[2])
        layout3.addWidget(self.buttons[3])

        layout2.addLayout(layout3)
        layout2.addStretch()

        self.setLayout(layout1)

        self.qtcb_update_illuminance.connect(self.update_illuminance_data_slot)
        self.qtcb_update_air_pressure.connect(self.update_air_pressure_data_slot)
        self.qtcb_update_temperature.connect(self.update_temperature_data_slot)
        self.qtcb_update_humidity.connect(self.update_humidity_data_slot)
        self.qtcb_button_pressed.connect(self.button_pressed_slot)

        self.timer = QTimer(self)
        self.timer.timeout.connect(self.Update)
        self.timer.start(1000)
class ProjectStatistics(QWidget):

    UPDATE_TYPE_STANDARD = 0
    UPDATE_TYPE_GRAPH = 1
    UPDATE_TYPE_MIN_MAX_AVG = 2
    UPDATE_TYPE_TIME = 3

    LINE_LENGTH = 20
    CUSTOM_CHAR_START = 8
    CUSTOM_CHAR_END = 15
    BAR_HEIGHT = 24

    MODE_ILLUMINANCE = 0
    MODE_HUMIDITY = 1
    MODE_AIR_PRESSURE = 2
    MODE_TEMPERATURE = 3

    latestIlluminance = None
    latestHumidity = None
    latestAirPressure = None
    latestTemperature = None

    illuminanceQueue = []
    humidityQueue = []
    airPressureQueue = []
    temperatureQueue = []

    buttonPressed = 0
    buttonPressedCounter = [0, 0, 0, 0]

    timer = None

    buttons = [None, None, None, None]

    qtcb_update_illuminance = pyqtSignal(float)
    qtcb_update_air_pressure = pyqtSignal(float)
    qtcb_update_temperature = pyqtSignal(float)
    qtcb_update_humidity = pyqtSignal(float)
    qtcb_button_pressed = pyqtSignal(int)

    lcdwidget = None

    def __init__(self, parent, app):
        super(QWidget, self).__init__(parent)

        layout1 = QHBoxLayout()
        layout2 = QVBoxLayout()
        
        layout1.addStretch()
        layout1.addLayout(layout2)
        layout1.addStretch()

        label = QLabel(self)
        label.setText("Project: <b>Show Statistics with Button Control</b>. Shows different statistics. You can select different modi by pressing the buttons. Some modi will allow display change by pressing the button multiple times. Sources in C# can be found <a href=\"http://www.tinkerforge.com/en/doc/Kits/WeatherStation/WeatherStation.html#show-statistics-with-button-control\">here</a>.")
        label.setTextFormat(Qt.RichText)
        label.setTextInteractionFlags(Qt.TextBrowserInteraction)
        label.setOpenExternalLinks(True)
        label.setWordWrap(True)
        label.setAlignment(Qt.AlignJustify)

        layout2.addSpacing(10)
        layout2.addWidget(label)
        layout2.addSpacing(10)

        self.lcdwidget = LCDWidget(self, app)

        layout2.addWidget(self.lcdwidget)
        layout2.addSpacing(10)

        self.buttons[0] = QPushButton(self)
        self.buttons[0].setFixedSize(100, 30)
        self.buttons[0].setText("BTN0")
        self.buttons[0].clicked.connect(lambda: self.button_pressed_slot(4))

        self.buttons[1] = QPushButton(self)
        self.buttons[1].setFixedSize(100, 30)
        self.buttons[1].setText("BTN1")
        self.buttons[1].clicked.connect(lambda: self.button_pressed_slot(5))

        self.buttons[2] = QPushButton(self)
        self.buttons[2].setFixedSize(100, 30)
        self.buttons[2].setText("BTN2")
        self.buttons[2].clicked.connect(lambda: self.button_pressed_slot(6))

        self.buttons[3] = QPushButton(self)
        self.buttons[3].setFixedSize(100, 30)
        self.buttons[3].setText("BTN3")
        self.buttons[3].clicked.connect(lambda: self.button_pressed_slot(7))
        
        layout3 = QHBoxLayout()
        layout3.addWidget(self.buttons[0])
        layout3.addWidget(self.buttons[1])
        layout3.addWidget(self.buttons[2])
        layout3.addWidget(self.buttons[3])

        layout2.addLayout(layout3)
        layout2.addStretch()

        self.setLayout(layout1)

        self.qtcb_update_illuminance.connect(self.update_illuminance_data_slot)
        self.qtcb_update_air_pressure.connect(self.update_air_pressure_data_slot)
        self.qtcb_update_temperature.connect(self.update_temperature_data_slot)
        self.qtcb_update_humidity.connect(self.update_humidity_data_slot)
        self.qtcb_button_pressed.connect(self.button_pressed_slot)

        self.timer = QTimer(self)
        self.timer.timeout.connect(self.Update)
        self.timer.start(1000)

    def update_illuminance_data_slot(self, illuminance):
        self.latestIlluminance = illuminance/10.0

    def update_illuminance(self, illuminance):
        self.qtcb_update_illuminance.emit(illuminance)

    def update_humidity_data_slot(self, humidity):
        self.latestHumidity = humidity/10.0
    
    def update_humidity(self, humidity):
        self.qtcb_update_humidity.emit(humidity)

    def update_air_pressure_data_slot(self, air_pressure):
        self.latestAirPressure = air_pressure/1000.0
    
    def update_air_pressure(self, air_pressure):
        self.qtcb_update_air_pressure.emit(air_pressure)

    def update_temperature_data_slot(self, temperature):
        self.latestTemperature = temperature/100.0

    def update_temperature(self, temperature):
        self.qtcb_update_temperature.emit(temperature)

    def button_pressed_slot(self, button):
        if button < 4:
            self.buttons[button % 4].animateClick()
        else:

            if button % 4 == self.buttonPressed:
                self.buttonPressedCounter[button % 4] += 1
            else:
                self.buttonPressed = button % 4

            self.lcdwidget.clear(self)
            self.UpdateSwitch()

    def button_pressed(self, button):
        self.qtcb_button_pressed.emit(button)

    def TimeFromSeconds(self, s):
        string = "("
        m = s/60
        h = m/60

        if h > 0:
            string += str(h) + "h)"
        else:
            if m == 0:
                m = 1
            string += str(m) + "m)"

        if len(string) == 4:
            return " " + string

        return string

    def GetMinMaxAvg(self, q):
        if len(q) == 0:
            return [0.0, 0.0, 0.0]

        vmin = 10000.0
        vmax = -10000.0
        vavg = 0.0

        for v in q:
            if v < vmin:
                vmin = v
            if v > vmax:
                vmax = v
            vavg += v

        vavg = vavg / len(q)

        return [vmin, vmax, vavg]

    def UpdateStandard(self):

        if not self.latestIlluminance == None:
            text = 'Illuminanc %6.2f lx' % (self.latestIlluminance)
            self.lcdwidget.write_line(0, 0, text, self)

        if not self.latestHumidity == None:
            text = 'Humidity   %6.2f %%' % (self.latestHumidity)
            self.lcdwidget.write_line(1, 0, text, self)

        if not self.latestAirPressure == None:
            text = 'Air Press %7.2f mb' % (self.latestAirPressure)
            self.lcdwidget.write_line(2, 0, text, self)
        
        if not self.latestTemperature == None:
            text = 'Temperature %5.2f \xDFC' % (self.latestTemperature)
            self.lcdwidget.write_line(3, 0, text, self)


    def UpdateGraph(self):
        barSumMin = 0.0
        barSumMax = 0.0
    

        if self.buttonPressedCounter[1] % 4 == self.MODE_ILLUMINANCE:
            [barSumMin, barSumMax] = self.UpdateGraphWriteBars(self.illuminanceQueue)
            self.UpdateGraphWriteTitle("I: ", barSumMin, barSumMax, len(self.illuminanceQueue))

        if self.buttonPressedCounter[1] % 4 == self.MODE_HUMIDITY:
            [barSumMin, barSumMax] = self.UpdateGraphWriteBars(self.humidityQueue)
            self.UpdateGraphWriteTitle("H: ", barSumMin, barSumMax, len(self.humidityQueue))

        if self.buttonPressedCounter[1] % 4 == self.MODE_AIR_PRESSURE:
            [barSumMin, barSumMax] = self.UpdateGraphWriteBars(self.airPressureQueue)
            self.UpdateGraphWriteTitle("A: ", barSumMin, barSumMax, len(self.airPressureQueue))

        if self.buttonPressedCounter[1] % 4 == self.MODE_TEMPERATURE:
            [barSumMin, barSumMax] = self.UpdateGraphWriteBars(self.temperatureQueue)
            self.UpdateGraphWriteTitle("T: ", barSumMin, barSumMax, len(self.temperatureQueue))

    def UpdateGraphWriteTitle(self, string, barSumMin, barSumMax, count):
        line0 = string + str(int(barSumMin)) + " - " + str(int(barSumMax+1.0))
        time = self.TimeFromSeconds(count)

        numSpaces = self.LINE_LENGTH - len(line0) - len(time)

        for i in range(numSpaces):
            line0 += " "

        self.lcdwidget.write_line(0,0, line0 + time, self)

    def UpdateGraphWriteBars(self, q):
        barSumMin = 10000.0
        barSumMax = -10000.0

        count = len(q)

        countBars = count/self.LINE_LENGTH
        if countBars == 0:
            countBars = 1

        barSum = [  0.0, 0.0, 0.0, 0.0, 0.0,
                    0.0, 0.0, 0.0, 0.0, 0.0,
                    0.0, 0.0, 0.0, 0.0, 0.0,
                    0.0, 0.0, 0.0, 0.0, 0.0 ]
        i = 0
        for v in q:
            barSum[i/countBars] += v
            i += 1
            if i == self.LINE_LENGTH*countBars:
                break

        for i in range(len(barSum)):
            barSum[i] /= countBars
            if barSum[i] < barSumMin:
                barSumMin = barSum[i]
            if barSum[i] > barSumMax:
                barSumMax = barSum[i]

        if (barSumMax - barSumMin) == 0:
            scale = 1
        else:
            scale = (self.BAR_HEIGHT-1)/(barSumMax - barSumMin)

        offset = barSumMin * scale - 1

        barHeight = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
        for i in range(len(barSum)):
            barHeight[i] = int(round(barSum[i] * scale - offset, 0))

        lines = [[' ' for x in range(20)] for y in range(4)]

        for i in range(len(barHeight)):
            barLenght = barHeight[i]/8
            j = 0
            for j in range(barLenght):
                lines[j+1][i] = chr(self.CUSTOM_CHAR_END)
            if j < 3:
                lines[j+1][i] = chr((barHeight[i]-1)/3 + self.CUSTOM_CHAR_START)

        for line in range(4):
            string = ''
            for s in lines[line]:
                string += s
            self.lcdwidget.write_line(4-line, 0, string, self)

        return [barSumMin, barSumMax]


    def UpdateMinMaxAvg(self):
        if self.buttonPressedCounter[2] % 4 == self.MODE_ILLUMINANCE:
            self.UpdateMinMaxAvgWrite("Illuminance    " + self.TimeFromSeconds(len(self.illuminanceQueue)), "Lux", self.GetMinMaxAvg(self.illuminanceQueue))
        if self.buttonPressedCounter[2] % 4 == self.MODE_HUMIDITY:
            self.UpdateMinMaxAvgWrite("Humidity       " + self.TimeFromSeconds(len(self.humidityQueue)), "%RH", self.GetMinMaxAvg(self.humidityQueue))
        if self.buttonPressedCounter[2] % 4 == self.MODE_AIR_PRESSURE:
            self.UpdateMinMaxAvgWrite("Air Pressure   " + self.TimeFromSeconds(len(self.airPressureQueue)), "Lux", self.GetMinMaxAvg(self.airPressureQueue))
        if self.buttonPressedCounter[2] % 4 == self.MODE_TEMPERATURE:
            self.UpdateMinMaxAvgWrite("Temperature    " + self.TimeFromSeconds(len(self.temperatureQueue)), "\xDFC", self.GetMinMaxAvg(self.temperatureQueue))

    def UpdateMinMaxAvgWrite(self, title, unit, values):
        vmin = "Min: %6.2f" % values[0] + unit 
        vavg = "Avg: %6.2f" % values[2] + unit 
        vmax = "Max: %6.2f" % values[1] + unit 

        self.lcdwidget.write_line(0, 0, title, self)
        self.lcdwidget.write_line(1, 0, vmin, self)
        self.lcdwidget.write_line(2, 0, vavg, self)
        self.lcdwidget.write_line(3, 0, vmax, self)
        
    
    def UpdateTime(self): 
        line0 = time.strftime("%H:%M:%S")
        line1 = time.strftime("%A")
        line2 = time.strftime("%d. %b %Y")

        self.lcdwidget.write_line(0, (self.LINE_LENGTH-len(line0))/2, line0, self)
        self.lcdwidget.write_line(1, (self.LINE_LENGTH-len(line1))/2, line1, self)
        self.lcdwidget.write_line(2, (self.LINE_LENGTH-len(line2))/2, line2, self)

    def UpdateSwitch(self):
        if self.buttonPressed == self.UPDATE_TYPE_STANDARD:
            self.UpdateStandard()
        if self.buttonPressed == self.UPDATE_TYPE_GRAPH:
            self.UpdateGraph()
        if self.buttonPressed == self.UPDATE_TYPE_MIN_MAX_AVG:
            self.UpdateMinMaxAvg()
        if self.buttonPressed == self.UPDATE_TYPE_TIME:
            self.UpdateTime()

    def Update(self):

        if not self.latestIlluminance == None:
            self.illuminanceQueue.append(self.latestIlluminance)
            if len(self.illuminanceQueue) > 60*60*24:
                self.illuminanceQueue.pop()

        if not self.latestHumidity == None:
            self.humidityQueue.append(self.latestHumidity)
            if len(self.humidityQueue) > 60*60*24:
                self.humidityQueue.pop()
        
        if not self.latestAirPressure == None:
            self.airPressureQueue.append(self.latestAirPressure)
            if len(self.airPressureQueue) > 60*60*24:
                self.airPressureQueue.pop()
        
        if not self.latestTemperature == None:
            self.temperatureQueue.append(self.latestTemperature)
            if len(self.temperatureQueue) > 60*60*24:
                self.temperatureQueue.pop()

        self.UpdateSwitch()
    def __init__(self, parent, app):
        super(QWidget, self).__init__()

        self.lcdwidget = LCDWidget(self, app)
        self.lcdwidget.hide()

        self.text_agent = QLineEdit(self)
        self.text_agent.setText(self.xively_agent)
        
        self.text_channel = QLineEdit(self)
        self.text_channel.setText(self.xively_channel)
        
        self.text_api_key = QLineEdit(self)
        self.text_api_key.setText(self.xively_api_key)
        
        self.number_update_rate = QSpinBox(self)
        self.number_update_rate.setRange(1, 1440)
        self.number_update_rate.setSuffix(' min')
        self.number_update_rate.setValue(self.xively_update_rate)

        layout1 = QHBoxLayout()
        layout2 = QVBoxLayout()
        
        layout1.addStretch()
        layout1.addLayout(layout2)
        layout1.addStretch()
        
        layout2.addSpacerItem(QSpacerItem(LCDWidget.FIXED_WIDGTH, 0))

        label = QLabel(self)
        label.setText("Project: <b>Connect to Xively</b>. This project uploads the measured values to Xively. Please find documentation how to configure it and program sources in Python <a href=\"http://www.tinkerforge.com/en/doc/Kits/WeatherStation/WeatherStation.html#connect-to-xively\">here</a>.<br>")
        label.setTextFormat(Qt.RichText)
        label.setTextInteractionFlags(Qt.TextBrowserInteraction)
        label.setOpenExternalLinks(True)
        label.setWordWrap(True)
        label.setAlignment(Qt.AlignJustify)

        layout2.addSpacing(10)
        layout2.addWidget(label)
        layout2.addSpacing(10)

        layout3a = QHBoxLayout()
        label = QLabel("Agent Description:")
        label.setMinimumWidth(150)
        layout3a.addWidget(label)
        layout3a.addWidget(self.text_agent, 1)

        layout2.addLayout(layout3a)
        layout2.addSpacing(10)

        layout3b = QHBoxLayout()
        label = QLabel("Feed:")
        label.setMinimumWidth(150)
        layout3b.addWidget(label)
        layout3b.addWidget(self.text_channel, 1)

        layout2.addLayout(layout3b)
        layout2.addSpacing(10)

        layout3c = QHBoxLayout()
        label = QLabel("API Key:")
        label.setMinimumWidth(150)
        layout3c.addWidget(label)
        layout3c.addWidget(self.text_api_key, 1)

        layout2.addLayout(layout3c)
        layout2.addSpacing(10)

        layout3d = QHBoxLayout()
        label = QLabel("Update Rate:")
        label.setMinimumWidth(150)
        layout3d.addWidget(label)
        layout3d.addWidget(self.number_update_rate, 1)

        layout2.addLayout(layout3d)
        layout2.addSpacing(10)

        self.label_upload_active = QLabel("Not Active", self)
        self.label_upload_active.setMinimumWidth(150)
        font = QFont()
        font.setPixelSize(20)
        self.label_upload_active.setFont(font)
        self.set_active_label(False)

        self.save_button = QPushButton("Save/Activate")

        layout4 = QHBoxLayout()
        layout4.addWidget(self.label_upload_active)
        layout4.addWidget(self.save_button, 1)

        layout2.addLayout(layout4)
        layout2.addStretch()

        self.setLayout(layout1)

        self.qtcb_update_illuminance.connect(self.update_illuminance_data_slot)
        self.qtcb_update_air_pressure.connect(self.update_air_pressure_data_slot)
        self.qtcb_update_temperature.connect(self.update_temperature_data_slot)
        self.qtcb_update_humidity.connect(self.update_humidity_data_slot)
        self.qtcb_button_pressed.connect(self.button_pressed_slot)
        self.save_button.clicked.connect(self.save_configuration)

        self.lcdwidget.clear(self)

        self.error_message = QErrorMessage(self)
class ProjectXively(QWidget):

    qtcb_update_illuminance = pyqtSignal(float)
    qtcb_update_air_pressure = pyqtSignal(float)
    qtcb_update_temperature = pyqtSignal(float)
    qtcb_update_humidity = pyqtSignal(float)
    qtcb_button_pressed = pyqtSignal(int)

    lcdwidget = None

    xively_host = "api.xively.com"
    xively_agent = "Tinkerforge Starter Kit Weather Station Demo"
    xively_channel = "Enter Feed ID here"
    xively_api_key = "Enter API Key here"

    xively_items = {}
    xively_headers = None
    xively_params = ""
    xively_update_rate = 5 # in minutes

    text_agent = None
    text_channel = None
    text_api_key = None
    number_update_rate = None

    save_button = None

    xively_timer = None

    error_message = None

    label_upload_active = None

    last_upload = None

    def __init__(self, parent, app):
        super(QWidget, self).__init__()

        self.lcdwidget = LCDWidget(self, app)
        self.lcdwidget.hide()

        self.text_agent = QLineEdit(self)
        self.text_agent.setText(self.xively_agent)
        
        self.text_channel = QLineEdit(self)
        self.text_channel.setText(self.xively_channel)
        
        self.text_api_key = QLineEdit(self)
        self.text_api_key.setText(self.xively_api_key)
        
        self.number_update_rate = QSpinBox(self)
        self.number_update_rate.setRange(1, 1440)
        self.number_update_rate.setSuffix(' min')
        self.number_update_rate.setValue(self.xively_update_rate)

        layout1 = QHBoxLayout()
        layout2 = QVBoxLayout()
        
        layout1.addStretch()
        layout1.addLayout(layout2)
        layout1.addStretch()
        
        layout2.addSpacerItem(QSpacerItem(LCDWidget.FIXED_WIDGTH, 0))

        label = QLabel(self)
        label.setText("Project: <b>Connect to Xively</b>. This project uploads the measured values to Xively. Please find documentation how to configure it and program sources in Python <a href=\"http://www.tinkerforge.com/en/doc/Kits/WeatherStation/WeatherStation.html#connect-to-xively\">here</a>.<br>")
        label.setTextFormat(Qt.RichText)
        label.setTextInteractionFlags(Qt.TextBrowserInteraction)
        label.setOpenExternalLinks(True)
        label.setWordWrap(True)
        label.setAlignment(Qt.AlignJustify)

        layout2.addSpacing(10)
        layout2.addWidget(label)
        layout2.addSpacing(10)

        layout3a = QHBoxLayout()
        label = QLabel("Agent Description:")
        label.setMinimumWidth(150)
        layout3a.addWidget(label)
        layout3a.addWidget(self.text_agent, 1)

        layout2.addLayout(layout3a)
        layout2.addSpacing(10)

        layout3b = QHBoxLayout()
        label = QLabel("Feed:")
        label.setMinimumWidth(150)
        layout3b.addWidget(label)
        layout3b.addWidget(self.text_channel, 1)

        layout2.addLayout(layout3b)
        layout2.addSpacing(10)

        layout3c = QHBoxLayout()
        label = QLabel("API Key:")
        label.setMinimumWidth(150)
        layout3c.addWidget(label)
        layout3c.addWidget(self.text_api_key, 1)

        layout2.addLayout(layout3c)
        layout2.addSpacing(10)

        layout3d = QHBoxLayout()
        label = QLabel("Update Rate:")
        label.setMinimumWidth(150)
        layout3d.addWidget(label)
        layout3d.addWidget(self.number_update_rate, 1)

        layout2.addLayout(layout3d)
        layout2.addSpacing(10)

        self.label_upload_active = QLabel("Not Active", self)
        self.label_upload_active.setMinimumWidth(150)
        font = QFont()
        font.setPixelSize(20)
        self.label_upload_active.setFont(font)
        self.set_active_label(False)

        self.save_button = QPushButton("Save/Activate")

        layout4 = QHBoxLayout()
        layout4.addWidget(self.label_upload_active)
        layout4.addWidget(self.save_button, 1)

        layout2.addLayout(layout4)
        layout2.addStretch()

        self.setLayout(layout1)

        self.qtcb_update_illuminance.connect(self.update_illuminance_data_slot)
        self.qtcb_update_air_pressure.connect(self.update_air_pressure_data_slot)
        self.qtcb_update_temperature.connect(self.update_temperature_data_slot)
        self.qtcb_update_humidity.connect(self.update_humidity_data_slot)
        self.qtcb_button_pressed.connect(self.button_pressed_slot)
        self.save_button.clicked.connect(self.save_configuration)

        self.lcdwidget.clear(self)

        self.error_message = QErrorMessage(self)

    def set_active_label(self, value):
        palette = self.label_upload_active.palette()
        if value:
            palette.setColor(self.foregroundRole(), Qt.darkGreen)
            self.label_upload_active.setText("Active")
        else:
            palette.setColor(self.foregroundRole(), Qt.red)
            self.label_upload_active.setText("Not Active")

        self.label_upload_active.setPalette(palette)

    def save_configuration(self):
        try:
            self.xively_agent = str(self.text_agent.text()).decode('ascii')
            self.xively_channel = str(self.text_channel.text()).decode('ascii')
            self.xively_api_key = str(self.text_api_key.text()).decode('ascii')
        except:
            self.error_message.showMessage('Agent, Feed and API Key can only contain ASCII characters')
            return

        self.xively_update_rate = self.number_update_rate.value()

        self.xively_headers = {
            "Content-Type"  : "application/x-www-form-urlencoded",
            "X-ApiKey"      : self.xively_api_key,
            "User-Agent"    : self.xively_agent,
        }
        self.xively_params = "/v2/feeds/" + self.xively_channel

        if self.xively_timer is None:
            self.xively_timer = QTimer(self)
            self.xively_timer.timeout.connect(self.update_xively)
            self.xively_timer.start(self.xively_update_rate*60*1000)

        self.set_active_label(True)
        self.update_xively()

    def write_lcd(self):
        if self.last_upload == None:
            tmp = "Last: Never"
        else:
            tmp = "Last: " + self.last_upload

        self.lcdwidget.write_line(0, 0, "Xively Upload", self)
        self.lcdwidget.write_line(2, 0, tmp, self)

    def update_xively(self):
        if len(self.xively_items) == 0:
            return

        stream_items = []
        for identifier, value in self.xively_items.items():
            stream_items.append({'id': identifier,
                                 'current_value': value[0],
                                 'min_value': value[1],
                                 'max_value': value[2]})

        data = {'version': '1.0.0',
                    'datastreams': stream_items}
        self.xively_items = {}
        body = json.dumps(data)

        try:
            http = httplib.HTTPSConnection(self.xively_host)
            http.request('PUT', self.xively_params, body, self.xively_headers)
            response = http.getresponse()
            http.close()

            if response.status != 200:
                self.error_message.showMessage('Could not upload to xively -> Response:' +
                          str(response.status) + ': ' + response.reason + '. Check your configuration.')
                self.xively_timer.stop()
                self.xively_timer = None
                self.set_active_label(False)
                return
        except Exception as e:
            self.error_message.showMessage('HTTP error: ' + str(e))
            self.xively_timer.stop()
            self.xively_timer = None
            self.set_active_label(False)
            return

        # set upload time if upload was a success
        self.last_upload = time.strftime("%H:%M:%S")


    def put(self, identifier, value):
        self.write_lcd()
        try:
            _, min_value, max_value = self.xively_items[identifier]
            if value < min_value:
                min_value = value
            if value > max_value:
                max_value = value
            self.xively_items[identifier] = (value, min_value, max_value)
        except:
            self.xively_items[identifier] = (value, value, value)

    def update_illuminance_data_slot(self, illuminance):
        self.put('AmbientLight', illuminance/10.0)

    def update_illuminance(self, illuminance):
        self.qtcb_update_illuminance.emit(illuminance)

    def update_humidity_data_slot(self, humidity):
        self.put('Humidity', humidity/10.0)
    
    def update_humidity(self, humidity):
        self.qtcb_update_humidity.emit(humidity)

    def update_air_pressure_data_slot(self, air_pressure):
        self.put('AirPressure', air_pressure/1000.0)
    
    def update_air_pressure(self, air_pressure):
        self.qtcb_update_air_pressure.emit(air_pressure)

    def update_temperature_data_slot(self, temperature):
        self.put('Temperature', temperature/100.0)

    def update_temperature(self, temperature):
        self.qtcb_update_temperature.emit(temperature)

    def button_pressed_slot(self, button):
        pass

    def button_pressed(self, button):
        self.qtcb_button_pressed.emit(button)