Meross: python library with mqtt

Hi,
I am using two Meross MSS425e, which is a Wifi enabled power strip with 3 fullsized sockets individually switchable and 4 usbports, which can only be switched together. The powerstrips by meross appear to be mostly based on a MediaTek chipset. Thus they cannot be flashed with the tuya firmware. There is an option to use IFTTT to connect them with openhab here, but I rather do not want introduce another online service. There also appears to be no binding as of now. However, there is a python library on github.
As recommended to me by vzorglub I used this library and mqtt to expose status and control to openhab. I recently installed a mqtt broker because of zigbee2mqtt (many good tutorials available), which is why I won’t describe it here. Furthermore, I tried to mimic way how zigbee2mqtt publishes information using JSON.
For checking if the routes and information are ‘set’ and published correctly I can recommend the tool mqtt.fx.
As I only have two power strips without energy consumption sensor, that’s all I could test for now.

I am running openhab2.5M3 on a raspberrypi3.

I basically followed the tutorial for installing the Paho MQTT Python Client here. Please mind that the meross library is only tested for python3.5+.

pip3 install paho-mqtt

Then the meross library

pip3 install meross_iot==0.3.1.5 --upgrade

[Accroding to comment by xavip, see below: pip3 install meross-iot==0.3.1.5 --upgrade]

I added a script meross2mqtt.py in /etc/openhab2/scripts

import paho.mqtt.client as mqtt
from meross_iot.manager import MerossManager
from meross_iot.meross_event import MerossEventType
from meross_iot.cloud.devices.light_bulbs import GenericBulb
from meross_iot.cloud.devices.power_plugs import GenericPlug
from meross_iot.cloud.devices.door_openers import GenericGarageDoorOpener
from random import randint
import time
import os
import json


EMAIL = "<your email used for registration with meross>"
PASSWORD = "<your password used for registration with meross>"
connectMsg = json.dumps({"state":True})

ident = "meross"
client = mqtt.Client(ident)
client.connect("<ip address of openhab server>")
client.publish(ident, payload=connectMsg, qos=0, retain=False)

def on_message(client, userdata, message):
    print("message received " ,str(message.payload.decode("utf-8")))
    print("message topic=",message.topic)
    print("message qos=",message.qos)
    print("message retain flag=",message.retain)
    
    msg_split = message.topic.split("/")
    if len(msg_split)>2:
        if msg_split[0] == ident and msg_split[-1]=="set":
            handle_message("/".join(msg_split[1:-1]), str(message.payload.decode("utf-8")))
    
client.on_message=on_message


manager = None

def event_handler(eventobj):
    if eventobj.event_type == MerossEventType.DEVICE_ONLINE_STATUS:
        print("Device online status changed: %s went %s" % (eventobj.device.name, eventobj.status))
        client.publish("meross/%s" %(eventobj.device.name),getJsonMsg(eventobj.device)) 
        pass

    elif eventobj.event_type == MerossEventType.DEVICE_SWITCH_STATUS:
        print("Switch state changed: Device %s (channel %d) went %s" % (eventobj.device.name, eventobj.channel_id,
                                                                        eventobj.switch_state))
        client.publish("meross/%s/channel_%s" %(eventobj.device.name,eventobj.channel_id),getJsonMsg(eventobj.device,eventobj.channel_id))
        
        channel = eventobj.device.get_channels()[eventobj.channel_id]
        if 'devName' in channel:
            client.publish("meross/%s/%s" %(eventobj.device.name,channel['devName']),getJsonMsg(eventobj.device,eventobj.channel_id))                                                                           
    elif eventobj.event_type == MerossEventType.CLIENT_CONNECTION:
        print("MQTT connection state changed: client went %s" % eventobj.status)

        # TODO: Give example of reconnection?

    elif eventobj.event_type == MerossEventType.GARAGE_DOOR_STATUS:
        print("Garage door is now %s" % eventobj.door_state)
        

    else:
        print("Unknown event!")

def initialize_meross():
    global manager
    # Initiates the Meross Cloud Manager. This is in charge of handling the communication with the remote endpoint
    manager = MerossManager(meross_email=EMAIL, meross_password=PASSWORD)

    # Register event handlers for the manager...
    manager.register_event_handler(event_handler)

    # Starts the manager
    manager.start()
    
    subscribe_broker(manager)
    
    return manager
    
def subscribe_broker(manager):
    #print("All the supported devices I found:")
    all_devices = manager.get_supported_devices()
    for d in all_devices:
        
        if hasattr(d, 'get_channels'):
            channels = d.get_channels()
            for i in range(len(channels)):
                if 'devName' in channels[i]:
                    client.subscribe("meross/%s/%s/set" %(d.name,channels[i]['devName']))
                print(client.subscribe("meross/%s/channel_%i/set" %(d.name,i)))
        
        print(client.subscribe("meross/%s/set" %(d.name)))
        
def getJsonMsg(d, cNo = None):

    info = {"state":getState(d, cNo), "type": d.type, "friendlyName":d.name, "online": d.online}  
    
    if d.online:
        if cNo is None: 
            info.update(d.get_sys_data())
        else:
            info.update(d.get_channels()[cNo])

    return json.dumps(info)
        
def getState(d, cNo=None):
    status = False
    
    if d.online:
        if cNo is None:
            status = d.get_status()
        else:
            status = d.get_status(cNo)
    
    if status:
        return "ON"
    return "OFF"
    
    
def getChannelName(d, cNo):
    return d.get_channels()[cNo]['devName']
    
def handle_message(topic, messageStr):
    print("topic %s -> handling message: %s" %(topic, messageStr)) 
    msg = json.loads(messageStr)
    topic_split = topic.split("/")
    device_name = topic_split[0]
    
    if manager is not None:
        device = manager.get_device_by_name(device_name)
        if device is not None:
            if len(topic_split)==1:
                if 'state' in msg:
                    if msg['state']=='ON':
                        device.turn_on()
                    elif msg['state']=='OFF':
                        device.turn_off()
                
            
            elif len(topic_split)==2:
                channel_name = topic_split[1]
                cNo = -1
                if channel_name.startswith("channel_"):
                    cNo = int(channel_name.replace("channel_",""))
                else:
                    channels = device.get_channels()
                    for i in range(len(channels)):
                        if 'devName' in channels[i] and channels[i]['devName']==channel_name:
                            cNo = i
                if cNo>-1:
                    if 'state' in msg:
                        if msg['state']=='ON':
                            device.turn_on_channel(cNo)
                        elif msg['state']=='OFF':
                            device.turn_off_channel(cNo)
                else:
                    print("Channel '%s' not found for device '%s'!" %(topic_split[1],device_name))
            
    
if __name__ == '__main__':
    manager = initialize_meross()
    client.loop_forever() 
else:
    client.loop_start() 
    

Please change the three entries with “<…>” accordingly.

Running this script your devices will publish their state in topics like meross/device name and eventually meross/device name/channel_x and meross/device name/channel name. The device name is the one used in the meross app. The channel topics appear if the device has several channels like a power strip with a channel for each switch. Please note that channel_0 switches the whole power strip on and off, while the last channel switches USB. Additionally to channel_x the state is also published under the name specified in the meross app, for example meross/device name/light01. The devices also subscribed to the topics meross/device name/set and meross/device name/channel name/set.

These topics can be used to set up devices in openhab

Bridge mqtt:broker:broker "MQTT Broker" [ host="localhost", port=1883, secure=false, clientID="openHAB2" ]
{
    Thing topic merossPlug01 "MEROSS Plug 01" @ "Bedroom" {
    Channels:
        Type switch : PowerStrip "MEROSS Plug 01" [ stateTopic="meross/Schlafz. Leiste", commandTopic="meross/Schlafz. Leiste/set", transformationPattern="JSONPATH:$.state", transformationPatternOut="JS:setZigbeeState.js", on="ON", off="OFF"]
	Type switch : PowerSwitch1 "MEROSS Plug 01 Switch 01" [ stateTopic="meross/Schlafz. Leiste/channel_1", commandTopic="meross/Schlafz. Leiste/channel_1/set", transformationPattern="JSONPATH:$.state", transformationPatternOut="JS:setZigbeeState.js", on="ON", off="OFF"]
	Type switch : PowerSwitch2 "MEROSS Plug 01 Switch 02" [ stateTopic="meross/Schlafz. Leiste/channel_2", commandTopic="meross/Schlafz. Leiste/channel_2/set", transformationPattern="JSONPATH:$.state", transformationPatternOut="JS:setZigbeeState.js", on="ON", off="OFF"]
	Type switch : PowerSwitch3 "MEROSS Plug 01 Switch 03" [ stateTopic="meross/Schlafz. Leiste/channel_3", commandTopic="meross/Schlafz. Leiste/channel_3/set", transformationPattern="JSONPATH:$.state", transformationPatternOut="JS:setZigbeeState.js", on="ON", off="OFF"]
	Type switch : USB "MEROSS Plug 01 USB" [ stateTopic="meross/Schlafz. Leiste/channel_4", commandTopic="meross/Schlafz. Leiste/channel_4/set", transformationPattern="JSONPATH:$.state", transformationPatternOut="JS:setZigbeeState.js", on="ON", off="OFF"]

    }
}

In order to create the correct JSON for the transformationPatternOut (ony available in openhab2.5) I reused a script for zigbee2mqtt setZigbeeState.js (placed in openHab-conf -> transform):

(function(x){

    var result = new Object();
    result.state = x;

    return JSON.stringify(result);
    
})(input)

In order to run the script in the background I created a service
sudo vim /etc/systemd/system/meross2mqtt.service

Type=simple
ExecStart=/usr/bin/python3 /etc/openhab2/scripts/meross2mqtt.py

[Install]
WantedBy=multi-user.target

[If you have problems see comment below by xavip regarding the service script]

Then enable (sudo systemctl enable meross2mqtt.service), start (sudo systemctl start meross2mqtt.service) and checking its status (sudo systemctl status meross2mqtt.service).
I found that I had to install paho-mqtt and meross_iot for root again

sudo pip3 install paho-mqtt
sudo pip3 install meross_iot==0.3.1.5 --upgrade

In the PaperUI you can create items as usual and to enable Google Home functionality add an item manually like this:

Switch MEROSSPlug01_Switch02 "Nachttisch" <light> ["Lighting"] { channel="mqtt:topic:broker:merossPlug01:PowerSwitch2" }

In general it should work like this, but there is quite some room for improvement not only for the other device types but also for the setup. I would like to hear about it from the more experienced users and improve this solution.
Thanks, good luck and have fun!

3 Likes

Hello,

i tried to install “meross_iot” according to https://github.com/albertogeniola/MerossIot

pip install meross_iot==0.3.1.9 --upgrade

I got the following message:

[19:32:31] openhabian@openhab:~$ pip install meross_iot==0.3.1.9 --upgrade
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Collecting meross_iot==0.3.1.9
  Could not find a version that satisfies the requirement meross_iot==0.3.1.9 (from versions: )
No matching distribution found for meross_iot==0.3.1.9

Could someone help me please? I don´t know what I´m doing wrong.

Many thanks!!

Regards

it’s meross-iot not meross_iot

Hello. Thank you for your answer but that´s not the solution. See message below.

[15:34:48] openhabian@openhab:~$ pip install meross-iot==0.3.1.11 --upgrade
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Collecting meross-iot==0.3.1.11
Could not find a version that satisfies the requirement meross-iot==0.3.1.11 (from versions: )
No matching distribution found for meross-iot==0.3.1.11
[15:36:00] openhabian@openhab:~$ pip install meross_iot==0.3.1.11 --upgrade
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Collecting meross_iot==0.3.1.11
Could not find a version that satisfies the requirement meross_iot==0.3.1.11 (from versions: )
No matching distribution found for meross_iot==0.3.1.11

Also on the official github page they write it “meross_iot”.

Yes, but in pip is meross-iot (https://pypi.org/project/meross-iot/)
I’m installed it in a hasbian just few minutes ago with
pip3 install meross-iot (i’m usign python 3)

try with

pip install meross-iot

I’ve had trouble registering the python script as a service.
In my case (openHABian), this fixed my problem

[Unit]
Description=Meross2MQTT
After=multi-user.target

[Service]
Type=simple
ExecStart=/usr/bin/python3 /etc/openhab2/scripts/meross2mqtt.py

[Install]
WantedBy=multi-user.target

Continuing the discussion from Meross: python library with mqtt:

Hi,
first of all I would say thank you to Johanness for the excellent work and for sharing this tutorial with us.
However, I went crazy to register the service.
I solved it by activating the service:

sudo chmod 644 /usr/systemd/system/meross2mqtt.service
chmod + x /etc/openhab2/scripts/meross2mqtt.py
sudo systemctl daemon-reload
sudo systemctl enables meross2mqtt.py
sudo systemctl start meross2mqtt.py

Anyway, I have a lot of thermostatic valves that I would like to control in On / Off and whose temperature I would like to read.

Could anyone help me with the setup?
Thank in advance

Hi, I have the ms310 plug, does anyone know if there’s any way to get the power consumption value?
Thanks

Hi angelengy, I am sorry, but I don’t have access to a plug with the ability to measure used power …
On the page of the github page of the python library https://github.com/albertogeniola/MerossIot, it is mentioned as possible:

if p.supports_electricity_reading():
            print("Awesome! This device also supports power consumption reading.")
            print("Current consumption is: %s" % str(p.get_electricity())) 

Have you tried something like that?

I had found it but I don’t know how to implement it, unfortunately I don’t know java and python languages.

Sorry, I had to recap a little bit on how it worked. How far are you able to follow the tutorial? Are you able to monitor the messages in mqtt using MQTT.fx? If that is the case, feel free to send me examples of messages from your plug and I can adjust the scripts above to support power consumption.

Thank you so much!

Yes, I receive messages on MQTT.fx, this is the message that appears only when the service is started

{“friendlyName”: “asciugatrice”, “online”: true, “type”: “mss310”, “all”: {“system”: {“firmware”: {“port”: 2001, “compileTime”: “2018-12-03 11:15:36”, “server”: “iot.meross.com”, “wifiMac”: “xxx”, “version”: “1.1.18”, “secondPort”: 2001, “userId”: xxx, “secondServer”: “smart.meross.com”, “innerIp”: “192.168.3.114”}, “hardware”: {“subType”: “us”, “uuid”: “xxx”, “macAddress”: 000", “version”: “1.0.0”, “chipType”: “MT7688”, “type”: “mss310”}, “time”: {“timezone”: “Europe/Rome”, “timeRule”: [[1521939600, 7200, 1], [1540688400, 3600, 0], [1553994000, 7200, 1], [1572138000, 3600, 0], [1585443600, 7200, 1], [1603587600, 3600, 0], [1616893200, 7200, 1], [1635642000, 3600, 0], [1648342800, 7200, 1], [1667091600, 3600, 0], [1679792400, 7200, 1], [1698541200, 3600, 0], [1711846800, 7200, 1], [1729990800, 3600, 0], [1743296400, 7200, 1], [1761440400, 3600, 0], [1774746000, 7200, 1], [1792890000, 3600, 0], [1806195600, 7200, 1], [1824944400, 3600, 0]], “timestamp”: 1584981241}, “online”: {“status”: 1}}, “control”: {“trigger”: [], “toggle”: {“onoff”: 1, “lmTime”: 1584918002}, “timer”: []}}, “state”: “ON”}

Instead on the state change I receive this

{“friendlyName”: “asciugatrice”, “online”: true, “type”: “mss310”, “state”: “OFF”}

Thanks for the two snippets. Hmmm, neither of them contain something which would relate to power consumption.
I am only guessing. When its state is “ON”, is it broadcasting a different message, hopefully also containing power? If it is not sending by itself, one probably has to request power consumption reading every minute. Not such a nice solution :unamused:

I imagined it, I also looked at the addon for Home Assistant since everything works there but obviously I didn’t understand anything. :sweat_smile:
Thanks anyway

Admittedly, the solution for HomeAssistant looks completely fleshed out. Sorry, this is sadly not that.

1 Like