# libShelly.py
"""
Version: 221227

Diverse Shelly-Geraete setzen und abfragen

Shelly
- Geraet
-- Relais
-- ShellyMotion
-- ShellyUni
-- ShellyPlus1PM
"""
import http.client as httplib
import logging
from mylib2 import *
import json
import socket
import base64
import http.client as httplib
from mysql.connector import errorcode


# GLOBALE VARIABLEN
# global logger 

# TODO
class Shelly():
    # Konstruktor
    def __init__(self, base_ip, logger=None) -> None:
        self.base_ip = base_ip # 192.168.168.
        self.logger = logger
        return None
    # Shelly.__init__()

    # Formatter
    def __str__(self):
        return ('.base_ip:{}'.format(self.base_ip))
    # Shelly.__str__()

    # IP-Adresse
    def set(self, base_ip = None) -> str:
        if base_ip != None:
            self.base_ip = base_ip
        return self.base_ip
    # ShellyUni.set

    # http_get()
    def http_get(self, id, url, username=None, password=None, log_error=False):
        """Send HTTP GET request"""
        res = ""
        success = False
        conn = None
        host = '{}{}'.format(self.base_ip, id)
        try:
            #self.logger.debug("http_get: http://%s%s", host, url)
            conn = httplib.HTTPConnection(host, timeout=5)
            headers = {"Connection": "close"}
            conn.request("GET", url, None, headers)
            resp = conn.getresponse()

            if resp.status == 401 and username is not None and password is not None:
                combo = '%s:%s' % (username, password)
                auth = str(base64.b64encode(combo.encode()))  # .replace('\n', '')
                headers["Authorization"] = "Basic %s" % auth
                conn.request("GET", url, None, headers)
                resp = conn.getresponse()

            if resp.status == 200: #ok
                body = resp.read()
                # print("Body: %s", body)
                res = json.loads(body)
                success = True
                #self.logger.debug("http_get: http://%s%s - Ok %s", host, url, res)
            else:
                res = "Shelly.http_get: Error, {} {} http://{}{}".format(resp.status, resp.reason, host, url)
                if self.logger != None:
                    self.logger.warning(res)
        except Exception as ex:
            if (type(ex) == socket.timeout):
                res = "Shelly.http_get: Timeout connecting to http://" + host + url
                #try:
                #    res = socket.gethostbyaddr(host)[0]
                #    msg += " [" + res + "]"
                #except Exception as ex:
                #    pass
                if self.logger != None:
                    self.logger.error(res)
            else:
                res = str(ex)
                if log_error:
                    if self.logger != None:
                        self.logger.debug("Error http GET: %s %s %s", host, url, res)
                #else:
                #    self.logger.error("Fail http GET: %s %s %s", host, url, res)
        finally:
            if conn:
                conn.close()

        return success, res
    # shelly.http_get()
# class Shelly

# Geraet
class Geraet():

    # Konstruktor
    def __init__(self, id, typ, name='', raum='', shelly=None):
        self.id = id
        self.typ = typ
        self.name = name
        self.raum = raum
        self.shelly = shelly
        self.logger = None
        if shelly != None:
            self.logger = shelly.logger
        return None
    # Geraet.__init__()

    # Formatter
    def __str__(self):
        return ('.shelly:{} .id:{} .typ:{} .name:{} .raum:{}'.format(self.shelly, self.id, self.typ, self.name, self.raum))
    # Geraet.str()

    #
    def dict(self):
        d = {str(self.id): self} # {Geräte-ID : Gerät}
        # logger.debug('d: {}'.format(d))
        return d
    # Geraet.dict()

    # Kommando an Gerät übertragen
    def kommando(self, kdos):
        # kdos = {'EIN': None, 'TIMER':30}

        CKDOS = {
            'SHSW-1': {
                'uri': '/relay/0', 
                'EIN': 'turn=on',
                'AUS': 'turn=off',
                'UMS': 'turn=toggle',
                'TIMER': 'timer=',
                'ISON': 'ison'},
            'SHBDUO-1': {
                'uri': '/light/0', 
                'EIN': 'turn=on',
                'AUS': 'turn=off',
                'HELL': 'helligkeit=',
                'FARBE': 'white=',
                'UMS': 'turn=toggle',
                'TIMER': 'timer='},
            'SHMOTION-1': {
                'STATUS': 'status',},
            }
        # self.logger.debug('KDOS: {}'.format(KDOS['SWSS']))

        uri = CKDOS.get(self.typ).get('uri', None)
        if uri is None:
            self.logger.error('{}: Kommando uri für {} nicht definiert'.format(self.id, self.typ))
            return -1
        c = '?'
        #print(kdos)
        for kdo, par in kdos.items():
            k = CKDOS.get(self.typ).get(kdo)
            if k is None:
                self.logger.error('{}: Kommando {} für {} nicht definiert'.format(self.id, kdo, self.typ))
                return -1
            uri += c + k
            if par != None:
                uri += str(par)
            c ='&'
        success, res = self.shelly.http_get(self.id, uri)
        return success, res
    # Geraet.kommando()

    def read(self, attr):
        res = ""
        uri = "/status"
        success = False
        conn = None
        # Attribut auslesen
        success, res = self.shelly.http_get(self.id, uri)
        pass # TODO
    # Geraet.read()
# class Geraet

# Class Relais
class Relais(Geraet):

    # Konstruktor
    def __init__(self, id, lfd=0, name=None, raum='Lager', shelly=None) -> None:
        if name == None:
            name = 'ShellyRelais{}.{}'.format(id, lfd)
        super().__init__(id, 'SHSW-1', name, raum, shelly)
        self.lfd = lfd
        return None
    # Relais.__init__()

    # Zustand Relais
    def status(self) -> int:
        ret, res = self.shelly.http_get(self.id, "/status")
        ison = -1
        if ret:
            ison = res.get('relays', -2)[self.lfd].get('ison', -3)
        #slef.logger.debug('relais.ison: {}'.format(ison))
        return ison
    # Relais.status()

    # Relais an
    def turn_on(self) -> None:
        ret, res = self.shelly.http_get(self.id, "/relay/{}?turn=on".format(self.lfd))
        return None
    # Relais.turn_on()

    # Relais aus
    def turn_off(self, logger=None) -> None:
        ret, res = self.shelly.http_get(self.id, "/relay/{}?turn=off".format(self.lfd))
        return None
    # Relais.turn_of()

# class Relais

class ShellyMotion(Geraet):

    # Konstruktor
    def __init__(self, id, name=None, raum='Lager', shelly=None):
        if name == None:
            name = 'ShellyMotion{}'.format(id)
        super().__init__(id, 'SHMOTION-2', name, raum, shelly)
        self.state = None
    # Relais.__init__()

    # Gesamtstatus
    def status(self, attr=None): # attr: lux, bat, tmp, sensor
        self.logger.debug('> ShellyMotion.status({})'.format(attr))
        ret, res = self.shelly.http_get(self.id, "/status")
        if res != None:
            if (attr != None):
                if self.logger != None:
                    self.logger.debug('status {}: {} ({})'.format(attr, res, type(res)))
                try:
                    res = res.get(attr).get('value', -1)
                except:
                    res = -2
        else:
            self.logger.error('ret: {}'.format(ret))
        return ret, res
    # ShellyMotion.status()

# class ShellyMotion

class ShellyUni(Geraet):
    '''ShellyUni'''

    # Konstruktor
    def __init__(self, id, name=None, raum='Lager', shelly=None):
        if name == None:
            name = 'ShellyUni{}'.format(id)
        super().__init__(id, 'SHUNI-1', name, raum, shelly)
        self.state = None
    # Relais.__init__()

    # Gesamtstatus
    def status(self):
        self.logger.debug('> ShellyUni.status()')
        ret, res = self.shelly.http_get(self.id, "/status")
        if ret:
            #self.logger.debug('result: {}'.format(res))
            #return res['ison']
            pass
        else:
            self.logger.error('ShellUni.status.ret: {}'.format(ret))
        return ret, res
    # ShellyUni.status()

    # Spannungsmessung ADC
    def voltage(self):
        self.logger.debug('> ShellyUni.voltage()')
        ret, res = self.status()
        #self.logger.debug('result: {}'.format(res))
        volt = -1
        if ret:
            try:
                volt = res['adcs'][0]['voltage']
            except:
                volt = -2
            #self.logger.debug('voltage: {} V'.format(volt))
        return volt
    # ShellyUni.voltage

# class ShellyUni


class ShellyPlus1PM(Geraet):
    '''ShellyPlus1PM'''

    # Konstruktor
    def __init__(self, id, name=None, raum='Lager', shelly=None):
        if name == None:
            name = 'ShellyPlus1PM{}'.format(id)
        super().__init__(id, 'SHPLUS-1PM', name, raum, shelly)
        self.state = None
    # Relais.__init__()

    # Gesamtstatus
    def status(self):
        '''
        ret: returncode
        res: {}
            on      :   an?
            aktP    :   aktuelle Leistung [W]
            aktU    :   aktuelle Spannug [V]
            actI    :   aktueller Strom [I]
            minW    :   Arbeit vorletzte Minute [mWh/min] -> W
            devT    :   Gerätetemperatur [°C]
        '''
        #self.logger.debug('> ShellyPlus1PM.status(): {}'.format(self.name))
        ret, res = self.shelly.http_get(self.id, "/rpc/Switch.GetStatus?id=0")
        result = {}
        if ret:
        # {'id': 0, 'source': 'init', 'output': True, 'apower': 0.0, 'voltage': 226.7, 'current': 0.02, 
        # 'aenergy': {'total': 6.748, 'by_minute': [2.89, 2.89, 3.372], 'minute_ts': 1676309987}, 
        # 'temperature': {'tC': 39.1, 'tF': 102.3}}
            result['on']  =res.get('output') 
            result['aktP']=res.get('apower') 
            result['aktU']=res.get('voltage') 
            result['aktI']=res.get('current') 
            result['minW']=res.get('aenergy').get('by_minute')[1]*60/1000 #letzte Minute unvollstaendig
            result['devT']=res.get('temperature').get('tC')
            self.logger.debug('> ShellyPlus1PM.status(): {} {}'.format(self.name, result))
        else:
            self.logger.error('  Sh+1PM: {}, status(), ret: {}'.format(self.name, ret))
        return ret, result
    # ShellyPlus1PM.status()

    # Einschalten
    def on(self):
        #self.logger.debug('> ShellyPlus1PM.on(): {}'.format(self.name))
        ret, res = self.shelly.http_get(self.id, "/rpc/Switch.Set?id=0&on=true")
        if ret:
            self.logger.error('Sh+1PM: {}, on(), ret: {}'.format(self.name, ret))
            pass
        else:
            self.logger.error('Sh+1PM: {}, on(), ret: {}'.format(self.name, ret))
        return ret, res
    # ShellyPlus1PM.on()

    # Ausschalten
    def off(self):
        #elf.logger.debug('> ShellyPlus1PM.off(): {}'.format(self.name))
        ret, res = self.shelly.http_get(self.id, "/rpc/Switch.Set?id=0&on=false")
        if ret:
            pass
        else:
            self.logger.error('Sh+1PM: {}, off(), ret: {}'.format(self.name, ret))
        return ret, res
    # ShellyPlus1PM.off()

    # An/Aus?
    def ison(self):
        #self.logger.debug('> ShellyPlus1PM.ison(): {}'.format(self.name))
        ret, res = self.status()
        # self.logger.debug('result: {}'.format(res))
        if ret:
            on = res['on']
        else:
            on ='?'
        return ret, on 
    # ShellyPlus1PM.on()

    # Leistung (W) (letzte min)
    def leistung(self):
        #self.logger.debug('> ShellyPlus1PM.leistung(): {}'.format(self.name))
        ret, res = self.status()
        #self.logger.debug('result: {} ({})'.format(res, ret))
        watt = -1
        if ret:
            try:
                watt = res.get('minW')
            except:
                watt = -2
        return watt
    # ShellyPlus1PM.leistung()


# class ShellyPlus1PM