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!