bytespider / Meross

Investigating the Meross/Refoss MSS310 Smart Plug and getting these devices to communicate with our private MQTT brokers
112 stars 19 forks source link

Struggle with Meross light switch disconnecting every few seconds from MQTT broker #47

Closed agrabren closed 2 years ago

agrabren commented 2 years ago

I'm not sure what I'm doing wrong, but watching the Mosquitto log, I keep seeing the device connect, subscribe, publish a few times, and disconnect. I have over 60 different Meross switches that I will eventually all pair to my home assistant, but only after I can reliably control one.

Mosquitto version:

  mosquitto version 2.0.14 starting
Config:
port 8883

require_certificate false
use_identity_as_username false
allow_anonymous true

capath /etc/mosquitto/certs

# replace with your CA Root and server certificate and key paths
cafile /etc/mosquitto/certs/mqtt_ca.crt
certfile /etc/mosquitto/certs/mqtt.crt
keyfile /etc/mosquitto/certs/mqtt.key

persistence true
persistence_location /var/lib/mosquitto/

log_dest file /var/log/mosquitto/mosquitto.log
log_type all

include_dir /etc/mosquitto/conf.d

Log:

1650919713: New connection from 192.168.1.171:54012 on port 8883.
1650919713: New client connected from 192.168.1.171:54012 as fmware:2012317778118925583748e1e943e9bc_bVUplGdeu4lGBUtb (p1, c1, k120, u'48:e1:e9:43:e9:bc').
1650919713: No will message specified.
1650919713: Sending CONNACK to fmware:2012317778118925583748e1e943e9bc_bVUplGdeu4lGBUtb (0, 0)
1650919713: Received SUBSCRIBE from fmware:2012317778118925583748e1e943e9bc_bVUplGdeu4lGBUtb
1650919713:     /appliance/2012317778118925583748e1e943e9bc/subscribe (QoS 1)
1650919713: fmware:2012317778118925583748e1e943e9bc_bVUplGdeu4lGBUtb 1 /appliance/2012317778118925583748e1e943e9bc/subscribe
1650919713: Sending SUBACK to fmware:2012317778118925583748e1e943e9bc_bVUplGdeu4lGBUtb
1650919713: Received PUBLISH from fmware:2012317778118925583748e1e943e9bc_bVUplGdeu4lGBUtb (d0, q1, r0, m3, '/appliance/2012317778118925583748e1e943e9bc/publish', ... (347 bytes))
1650919713: Sending PUBACK to fmware:2012317778118925583748e1e943e9bc_bVUplGdeu4lGBUtb (m3, rc0)
1650919713: Received PUBLISH from fmware:2012317778118925583748e1e943e9bc_bVUplGdeu4lGBUtb (d0, q1, r0, m4, '/appliance/2012317778118925583748e1e943e9bc/publish', ... (731 bytes))
1650919713: Sending PUBACK to fmware:2012317778118925583748e1e943e9bc_bVUplGdeu4lGBUtb (m4, rc0)
1650919720: Received PUBLISH from fmware:2012317778118925583748e1e943e9bc_bVUplGdeu4lGBUtb (d0, q1, r0, m5, '/appliance/2012317778118925583748e1e943e9bc/publish', ... (731 bytes))
1650919720: Sending PUBACK to fmware:2012317778118925583748e1e943e9bc_bVUplGdeu4lGBUtb (m5, rc0)
1650919726: Received PUBLISH from fmware:2012317778118925583748e1e943e9bc_bVUplGdeu4lGBUtb (d0, q1, r0, m6, '/appliance/2012317778118925583748e1e943e9bc/publish', ... (731 bytes))
1650919726: Sending PUBACK to fmware:2012317778118925583748e1e943e9bc_bVUplGdeu4lGBUtb (m6, rc0)
1650919732: Received DISCONNECT from fmware:2012317778118925583748e1e943e9bc_bVUplGdeu4lGBUtb
1650919732: Client fmware:2012317778118925583748e1e943e9bc_bVUplGdeu4lGBUtb disconnected.
1650919736: New connection from 192.168.1.171:54013 on port 8883.
bytespider commented 2 years ago

I've not tested against Mosquito 2.

This is interesting behaviour, it's possible the device is expecting a response.

We know that Meross devices are hard coded to contact one of several NTP servers, are your devices able to do so?

bytespider commented 2 years ago

Update: I tested with a basic Mosquitto 2.0.14 setup and my MSS310 connected fine.

lechercheur123 commented 2 years ago

Hello,

What is the model of your switch ?

For my Meross MSS310, I have to configure Appliance.System.Clock and Appliance.System.Time for them to work.

This is the code I use :

import paho.mqtt.client as mqtt
import json
import time as t
import hashlib
from datetime import *
from dateutil import tz
from dateutil.relativedelta import *
import dateutil
import threading

uuid_tab = ["211028369287905189876876","2110280560197851807867687","2110283878849551867867","21102869179984518075486876757","2110284045264867687654","687654564567","211028286904815168754687"]
key_tab = ["yrteytr","rezttytr","zregdvh","iyourrrte","jhgjkjkh","tryed","nbvbnr"]
run_thread = True

# The callback for when the client receives a CONNACK response from the server.
def on_connect(client, userdata, flags, rc):
    print("Connected with result code "+str(rc))

    # Subscribing in on_connect() means that if we lose the connection and
    # reconnect then subscriptions will be renewed.
    for i in range(0, len(uuid_tab)):
        client.subscribe("/appliance/" + uuid_tab[i] + "/publish")
        client.subscribe("/app/0-" + uuid_tab[i] + "/subscribe")
        message=generateMqttMessage(uuid_tab[i], key_tab[i], "PUSH", "Appliance.System.Clock")
        client.publish("/appliance/" + uuid_tab[i] + "/subscribe", payload=message)
        message2=generateMqttMessage(uuid_tab[i], key_tab[i], "SET", "Appliance.System.Time")
        client.publish("/appliance/" + uuid_tab[i] + "/subscribe", payload=message2)
    #message=generateMqttMessage(uuid_tab[0], key_tab[0], "GET", "Appliance.Control.Electricity")
    #client.publish("/appliance/" + uuid_tab[0] + "/subscribe", payload=message)
    run_thread = True
#   x.start()

def on_disconnect(client, userdata, rc):
    run_thread = False
#   x.join()

# The callback for when a PUBLISH message is received from the server.
def on_message(client, userdata, msg):
    #print(msg.topic+" "+str(msg.payload))
    for i in range(0, len(uuid_tab)):
        if(msg.topic == "/app/0-"+uuid_tab[i]+"/subscribe"):
            #print("Topic OK")
            reponse=json.loads(msg.payload)
            if("namespace" in reponse["header"]):
                #print("Namespace trouve")
                if(reponse["header"]["namespace"]=="Appliance.Control.Electricity" and reponse["header"]["method"]=="GETACK"):
                    #print("Envoi a Emoncms")
                    client.publish("emon/prise"+str(i+1)+"/power1", payload=str(int((reponse["payload"]["electricity"]["power"])/1000)))
            break
        elif(msg.topic == "/appliance/"+uuid_tab[i]+"/publish"):
            reponse=json.loads(msg.payload)
            if("namespace" in reponse["header"]):
                if(reponse["header"]["namespace"]=="Appliance.System.Clock" and reponse["header"]["method"]=="PUSH"):
                    timeupdate=generateMqttMessage(uuid_tab[i], key_tab[i], "PUSH", "Appliance.System.Clock")
                    client.publish("/appliance/" + uuid_tab[i] + "/subscribe", payload=timeupdate)
            break

def on_publish(client, userdata, mid):
        print("MID : "+str(mid))

def generateMqttMessage(uuid, key, messageType, namespace, parameters=None):
    data={}
    data["header"]={}
    data["header"]["from"]="/app/0-"+uuid+"/subscribe"
    data["header"]["messageId"]="47e87acd7ced4519517df8916942f1a1"
    data["header"]["method"]=messageType
    data["header"]["namespace"]=namespace
    data["header"]["payloadVersion"]=1
    data["header"]["sign"]="0"
    data["header"]["timestamp"]=int(datetime.now(timezone.utc).timestamp())
    stringToHash = data["header"]["messageId"] + key + str(data["header"]["timestamp"])
    hash=hashlib.md5(stringToHash.encode())
    data["header"]["sign"]=hash.hexdigest()

    data["payload"]={}
    if(namespace == "Appliance.System.Clock"):
        data["payload"]["clock"]={}
        data["payload"]["clock"]["timestamp"]=data["header"]["timestamp"]
    elif(namespace == "Appliance.System.Time"):
        data["payload"]["time"]={}
        data["payload"]["time"]["timestamp"]=data["header"]["timestamp"]
        data["payload"]["time"]["timezone"] = "Europe/Paris"
        data["payload"]["time"]["timeRule"] = []

        tm = tz.gettz(data["payload"]["time"]["timezone"])
        maintenant=datetime.now(tm)
        #maintenant = maintenant + relativedelta(months=10)
        #print(maintenant)
        # Previous end or debut of DST
        if(datetime.dst(maintenant) != timedelta()):
            dst1 = maintenant + relativedelta(month=3, day=31, weekday=SU(-1), second=0, minute=0, hour=3, microsecond=0)
            dst2 = maintenant + relativedelta(month=10, day=31, weekday=SU(-1), second=59, minute=59, hour=2, microsecond=0)
            dst3 = maintenant + relativedelta(years=1, month=3, day=31, weekday=SU(-1), second=0, minute=0, hour=3, microsecond=0)
            dst1on = 1
            dst2on = 0
            dst3on = 1
        else:
            dst2 = maintenant + relativedelta(month=3, day=31,  weekday=SU(-1), second=0, minute=0, hour=3)
            if(maintenant < dst2):
                dst1 = maintenant + relativedelta(years=-1, month=10, day=31, weekday=SU(-1), second=59, minute=59, hour=2)
                dst3 = maintenant + relativedelta(month=10, day=31, weekday=SU(-1), second=59, minute=59, hour=2)
            else:
                dst1 = maintenant + relativedelta(month=10, day=31, weekday=SU(-1), second=59, minute=59, hour=2, microsecond=0)
                dst2 = maintenant + relativedelta(years=1, month=3, day=31, weekday=SU(-1), second=0, minute=0, hour=3, microsecond=0)
                dst3 = maintenant + relativedelta(years=1, month=10, day=31, weekday=SU(-1), second=59, minute=59, hour=2, microsecond=0)
            dst1on = 0
            dst2on = 1
            dst3on = 0

        data["payload"]["time"]["timeRule"].append([int(dst1.timestamp()) + dst2on, 3600 + (dst1on*3600), dst1on])
        data["payload"]["time"]["timeRule"].append([int(dst2.timestamp()) + dst1on, 3600 + (dst2on*3600), dst2on])
        data["payload"]["time"]["timeRule"].append([int(dst3.timestamp()) + dst2on, 3600 + (dst3on*3600), dst3on])
        print(dst1)
        print(dst2)
        print(dst3)
    elif(namespace == "Appliance.Control.Electricity"):
        data["payload"]["channel"] = 0
    else:
        return None

    #print(json.dumps(data, indent=4))
    return(json.dumps(data))

def sendElectricityRequest(name):
    while(run_thread):
        for i in range(0, len(uuid_tab)):
            requete=generateMqttMessage(uuid_tab[i], key_tab[i], "GET", "Appliance.Control.Electricity")
            client.publish("/appliance/" + uuid_tab[i] + "/subscribe", payload=requete)
        t.sleep(10)

client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
#client.on_publish = on_publish
client.on_disconnect = on_disconnect

client.tls_set()
client.reconnect_delay_set(min_delay=1, max_delay=120)
client.username_pw_set("gfdhghg", "4578746197987")
client.connect("mqtts.domain.fr", 8883, 60)

#x = threading.Thread(target=sendElectricityRequest, args=(1,))
client.loop_start()
sendElectricityRequest("test")
client.loop_stop()
#t.sleep(30)

#message=generateMqttMessage(uuid_tab[0], key_tab[0], "GET", "Appliance.Control.Electricity")
#client.publish("/appliance/" + uuid_tab[0] + "/publish", payload=message)
#while(True):

# Blocking call that processes network traffic, dispatches callbacks and
# handles reconnecting.
# Other loop*() functions are available that give a threaded interface and a
# mnual interface.
#client.loop_forever()
lechercheur123 commented 2 years ago

I don't use Home Assistant, that's why I have my own script to handle the MQTT messages. The script listen to the MQTT messages sent by the MSS310 and send the power consumption to an other topic.

agrabren commented 2 years ago

I'm really sorry I can't get test all this stuff tonight, my work day got the best of me and I haven't had much luck getting an easier-to-debug MQTT server setup. So I lost a little ground on this. I can provide these details about the device I'm using: Type: mss510x Version: 3.0.0 Chip: mt7682 Firmware ver: 3.2.2 Hopefully tomorrow I'll have better luck with a clean MQTT installation and configuration with new certs and such. Then I will update with my latest, and hopefully some additional debug information.

bytespider commented 2 years ago

@agrabren Make sure you try the setup on a network with no restriction on out going traffic. Then as @lechercheur123 explains, try configuring Appliance.System.Clock and Appliance.System.Time.

Another option is to use Home Assistant and the Meross LAN integration. Be aware that you must use version <= 5.1 of the MQTT integration as in later versions compatibility with Meross devices broke and they refuse to fix it stating that they will not support usernames that don't conform with the "Unix password file" convention (Meross devices use MAC addresses as usernames and the colon breaks the password file).

agrabren commented 2 years ago

Thanks to both of your insights and comments, I've made progress! Thank you so much (although I may update the Custom Pairer app as it could really use some TLC and documentation for us newbies). The problem I was encountering was that I didn't have the correct key. Since I did a full factory reset of the switch and fumbled around with the custom pairing app, I hadn't noticed that the MQTT key was both required and something that could be programmed in via the app. Once I got MQTT Hub going with the correct key, I started getting to register the switch, which led to everything appearing to work. So I'm going to close this with comment, and I'll suggest some edits and make a pull request on some fixes for the custom pairer (I'm an Android dev by day, home automation newbie by night)