from machine import Pin, PWM
from network import WLAN, STA_IF
from time import sleep_ms, localtime
from json import loads, dumps
from umqtt.robust import MQTTClient

# Link do dashboard público
# https://demo.thingsboard.io/dashboard/58b46e60-7b4e-11ef-95bb-f3c5773bc8e3?publicId=b1523ef0-4533-11ee-9079-75f587c23e37

DUTY_MOTOR = 25000
FECHADO = 0
ABERTO = 1
FECHANDO = 2
ABRINDO = 3
PARADOF = 4
PARADOA = 5

sensor_fechado = Pin(14, Pin.IN, Pin.PULL_UP)
sensor_aberto = Pin(27, Pin.IN, Pin.PULL_UP)
botao         = Pin(26, Pin.IN, Pin.PULL_UP)
motor_in1 = PWM(Pin(13), freq=500, duty=0)
motor_in2 = PWM(Pin(12), freq=500, duty=0)

net = 'Wokwi-GUEST'
pas = ''
#net = 'Labs-AUTO-02'
#pas = 'ifrsrgautomacao'

clientId = 'crrifrsrg'
broker = 'demo.thingsboard.io'
usuario= 'OiQg8FhqDtpk4QW5NRee'

topico = b'v1/devices/me/telemetry'
topRPCGeral = 'v1/devices/me/rpc/request/'
topRPCResp = 'v1/devices/me/rpc/response/'


def ativaWifi(rede, senha):
    # Retorna True se consegue conectar à rede, ou False do contrário
    # Faz 10 tentativas, separadas por 1s de tempo
    wifi = WLAN(STA_IF)
    wifi.active(True)
    if not wifi.isconnected():
        wifi.connect(rede, senha)
        tentativas = 0
        while not wifi.isconnected() and tentativas < 10:
            sleep_ms(1000)
            tentativas += 1
    if wifi.isconnected():
        return (wifi, True)
    else:
        return (wifi, False)


def nomeEstado (e):
    if e == FECHADO:
        return 'Fechado'
    elif e == ABRINDO:
        return 'Abrindo'
    elif e == ABERTO:
        return 'Aberto'
    elif e == FECHANDO:
        return 'Fechando'
    elif e in (PARADOF, PARADOA):
        return 'Parado'


def mudaEstado(e):
    if estado == FECHADO:
        return ABRINDO
    elif estado == ABERTO:
        return FECHANDO
    elif estado == FECHANDO:
        return PARADOF
    elif estado == ABRINDO:
        return PARADOA
    elif estado == PARADOF:
        return ABRINDO
    elif estado == PARADOA:
        return FECHANDO


def rpc(t, p):
    global estado, novoEstado, cliente, inicial
    reqid = t.decode().replace(topRPCGeral,'')
    try:
        acao = loads(p.decode())
        print (acao)
        if acao['method'] == "getState":
            if inicial:
                cliente.publish((topRPCResp+reqid).encode(),
                                b'1' if novoEstado in (ABRINDO, FECHANDO) else b'0', retain=True)
                inicial = False
            else:
                cliente.publish((topRPCResp+reqid).encode(),
                                b'1' if estado in (ABRINDO, FECHANDO) else b'0', retain=True)

        elif acao['method'] == "setState":
            novoEstado = mudaEstado(estado)
            cliente.publish((topRPCResp+reqid).encode(),
                            b'1' if novoEstado in (ABRINDO, FECHANDO) else b'0', retain=True)
        '''elif acao['method'] == "getAuto":
            mqtt.publish((topRPCResp+reqid).encode(),
                          b'1' if automatico else b'0', retain=True)
            #incluir retained
        elif acao['method'] == "setAuto":
            automatico = acao['params']
            print (acao['params'])'''
    except Exception as e:
        print (p)
        print (e)

#-------------------------------------------------------------
# Inicialização de fato
inicial = True
estado = ABERTO
novoEstado = FECHANDO # Inicia o portão como fechado
b = bAnt = botao.value()

dic = {'nomeEstado':''}
# Conectar à rede
net, conectado = ativaWifi(net,pas)

if conectado:
    cliente = MQTTClient(clientId, broker, user = usuario, password='')
    cliente.set_callback(rpc)
    cliente.connect()
    cliente.subscribe(b"v1/devices/me/rpc/request/+")


# Loop 
while True:
    if conectado:
        cliente.check_msg()

    b = botao.value()
    if b != bAnt:
        if b == 0:
            if estado == FECHADO:
                novoEstado = ABRINDO
            elif estado == ABERTO:
                novoEstado = FECHANDO
            elif estado == FECHANDO:
                novoEstado = PARADOF
            elif estado == ABRINDO:
                novoEstado = PARADOA
            elif estado == PARADOF:
                novoEstado = ABRINDO
            elif estado == PARADOA:
                novoEstado = FECHANDO
        sleep_ms(200)
        bAnt = b
            
        
    if estado != novoEstado:
        print (estado, novoEstado)
        #fechado
        if estado == FECHADO:
            if novoEstado == ABRINDO:
                motor_in1.duty_u16(0)
                motor_in2.duty_u16(DUTY_MOTOR)
                sleep_ms(1000)
        #aberto
        elif estado == ABERTO: 
            if novoEstado == FECHANDO:
                motor_in1.duty_u16(DUTY_MOTOR)
                motor_in2.duty_u16(0)
                sleep_ms(1000)
        #abrindo
        elif estado == ABRINDO:
            if novoEstado in (ABERTO, PARADOA):
                motor_in1.duty_u16(0)
                motor_in2.duty_u16(0)
        #fechando
        elif estado == FECHANDO:
            if novoEstado in (FECHADO, PARADOF):
                motor_in1.duty_u16(0)
                motor_in2.duty_u16(0)
        #parado
        elif estado in (PARADOA, PARADOF):
            if novoEstado == ABRINDO:
                motor_in1.duty_u16(0)
                motor_in2.duty_u16(DUTY_MOTOR)
            elif novoEstado == FECHANDO:
                motor_in1.duty_u16(DUTY_MOTOR)
                motor_in2.duty_u16(0)
        else: #parada
            print ('Exceção')
            if estado == ABRINDO:
                novoEstado = PARADOA
            else:
                novoEstado = PARADOF
        
        estado = novoEstado

        if conectado:
            # Publicar
            dic['nomeEstado'] = nomeEstado(estado)
            cliente.publish(topico, dumps(dic).encode(), retain=True)        
        
    else:
        if estado == ABRINDO:
            if sensor_aberto.value() == 0:
                novoEstado = ABERTO
        elif estado == FECHANDO:
            if sensor_fechado.value() == 0:
                novoEstado = FECHADO