Meross Garage Door Opener - MSG200

There are other discussions about the Meross Garage Door Opener and guides to install the opener but I wanted to highlight the process in case it gets confusing since there are bugs in the original driver code.

  1. Install the app by adding the app in App Code on your Hubitat. Here is the link: https://github.com/ithinkdancan/hubitat-meross/blob/main/apps/meross_app.groovy
  2. Install the driver code for the garage doors. Copy the code below and add it as a new driver. This code addresses a bug where the firmware version is checked to be 0 or 323. Since the MSG200's latest firmware is at 2.1.3, this causes errors once the device is added. I have changed it to check for firmware above 200 so it resolves the issue.
/**
 * Meross Smart WiFi Garage Door Opener
 *
 * Author: Daniel Tijerina
 * Last updated: 2021-09-26
 *
 *
 * Licensed under the Apache License, Version 2.0 (the 'License'); you may not
 * use this file except in compliance with the License. You may obtain a copy
 * of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

import java.security.MessageDigest

metadata {
    definition(
        name: 'Meross Smart WiFi Garage Door Opener',
        namespace: 'ithinkdancan',
        author: 'Daniel Tijerina'
    ) {
        capability 'DoorControl'
        capability 'GarageDoorControl'
        capability 'Actuator'
        capability 'ContactSensor'
        capability 'Refresh'

        attribute 'model', 'string'
        attribute 'version', 'string'
    }
    preferences {
        section('Device Selection') {
            input('deviceIp', 'text', title: 'Device IP Address', description: '', required: true, defaultValue: '')
            input('key', 'text', title: 'Key', description: 'Required for firmware version 3.2.3 and greater', required: false, defaultValue: '')
            input('messageId', 'text', title: 'Message ID', description: '', required: true, defaultValue: '')
            input('timestamp', 'number', title: 'Timestamp', description: '', required: true, defaultValue: '')
            input('sign', 'text', title: 'Sign', description: '', required: true, defaultValue: '')
            input('uuid', 'text', title: 'UUID', description: '', required: true, defaultValue: '')
            input('channel', 'number', title: 'Garage Door Port', description: '', required: true, defaultValue: 1)
            input('garageOpenCloseTime','number',title: 'Garage Open/Close time (in seconds)', description:'', required: true, defaultValue: 5)
            input('DebugLogging', 'bool', title: 'Enable debug logging', defaultValue: true)
        }
    }
}

def getDriverVersion() {
    1
}

def initialize() {
    log 'Initializing Device'
    refresh()

    unschedule(refresh)
    runEvery5Minutes(refresh)
}

def sendCommand(int open) {
    
    def currentVersion = device.currentState('version')?.value ? device.currentState('version')?.value.replace(".","").toInteger() : 200

    // Firmware version 3.2.3 and greater require different data for request
    if (!settings.deviceIp || !settings.uuid || (currentVersion >= 200 && !settings.key) || (currentVersion < 200 && (!settings.messageId || !settings.sign || !settings.timestamp))) {
        sendEvent(name: 'door', value: 'unknown', isStateChange: false)
        log.warn('missing setting configuration')
        return
    }
    sendEvent(name: 'door', value: open ? 'opening' : 'closing', isStateChange: true)

    try {
        def payloadData = currentVersion >= 200 ? getSign() : [MessageId: settings.messageId, Sign: settings.sign, CurrentTime: settings.timestamp]
        
        def hubAction = new hubitat.device.HubAction([
        method: 'POST',
        path: '/config',
        headers: [
            'HOST': settings.deviceIp,
            'Content-Type': 'application/json',
        ],
        body: '{"payload":{"state":{"open":' + open + ',"channel":' + settings.channel + ',"uuid":"' + settings.uuid + '"}},"header":{"messageId":"'+payloadData.get('MessageId')+'","method":"SET","from":"http://'+settings.deviceIp+'/config","sign":"'+payloadData.get('Sign')+'","namespace":"Appliance.GarageDoor.State","triggerSrc":"AndroidLocal","timestamp":' + payloadData.get('CurrentTime') + ',"payloadVersion":1' + ',"uuid":"' + settings.uuid + '"}}'
    ])
        runIn(settings.garageOpenCloseTime, "refresh")
        return hubAction
    } catch (e) {
        log.error("runCmd hit exception ${e} on ${hubAction}")
    }
}


def refresh() {
    def currentVersion = device.currentState('version')?.value ? device.currentState('version')?.value.replace(".","").toInteger() : 200

    // Firmware version 3.2.3 and greater require different data for request
    if (!settings.deviceIp || !settings.uuid || (currentVersion >= 200 && !settings.key) || (currentVersion < 200 && (!settings.messageId || !settings.sign || !settings.timestamp))) {
        sendEvent(name: 'door', value: 'unknown', isStateChange: false)
        log.warn('missing setting configuration')
        return
    }
    try {
        def payloadData = currentVersion >= 200 ? getSign() : [MessageId: settings.messageId, Sign: settings.sign, CurrentTime: settings.timestamp]

        log.info('Refreshing')
        
        def hubAction = new hubitat.device.HubAction([
            method: 'POST',
            path: '/config',
            headers: [
                'HOST': settings.deviceIp,
                'Content-Type': 'application/json',
            ],
            body: '{"payload":{},"header":{"messageId":"'+payloadData.get('MessageId')+'","method":"GET","from":"http://'+settings.deviceIp+'/subscribe","sign":"'+ payloadData.get('Sign') +'","namespace": "Appliance.System.All","triggerSrc":"AndroidLocal","timestamp":' + payloadData.get('CurrentTime') + ',"payloadVersion":1}}'
        ])
        log hubAction
        return hubAction
    } catch (Exception e) {
        log.debug "runCmd hit exception ${e} on ${hubAction}"
    }
}

def open() {
    log.info('Opening Garage')
    return sendCommand(1)
}

def close() {
    log.info('Closing Garage')
    return sendCommand(0)
}

def updated() {
    log.info('Updated')
    initialize()
}

def parse(String description) {
    def msg = parseLanMessage(description)
    def body = parseJson(msg.body)
    
    if(msg.status != 200) {
         log.error("Request failed")
         return
    }
    
    // Close/Open request was sent
    if(body.header.method == "SETACK") return
    
    if (body.payload.all) {
        def state = body.payload.all.digest.garageDoor[settings.channel.intValue() - 1].open
        sendEvent(name: 'door', value: state ? 'open' : 'closed')
        sendEvent(name: 'contact', value: state ? 'open' : 'closed')
        sendEvent(name: 'version', value: body.payload.all.system.firmware.version, isStateChange: false)
        sendEvent(name: 'model', value: body.payload.all.system.hardware.type, isStateChange: false)
    } else {
        //refresh()
        log.error ("Request failed")
    }
}

def getSign(int stringLength = 16){
    
    // Generate a random string 
    def chars = 'abcdefghijklmnopqrstuvwxyz0123456789'
    def randomString = new Random().with { (0..stringLength).collect { chars[ nextInt(chars.length() ) ] }.join()}    
    
    int currentTime = new Date().getTime() / 1000
    messageId = MessageDigest.getInstance("MD5").digest((randomString + currentTime.toString()).bytes).encodeHex().toString()
    sign = MessageDigest.getInstance("MD5").digest((messageId + settings.key + currentTime.toString()).bytes).encodeHex().toString()
    
    def requestData = [
         CurrentTime: currentTime,
         MessageId: messageId,
         Sign: sign
    ]
    
    return requestData
}

def log(msg) {
    if (DebugLogging) {
        log.debug(msg)
    }
}
  1. Next go to Apps -> add user apps. Then select Meross Garage Door Manager.
  2. Add your credentials that you use to login to your Meross Account and specify the IP address of the opener on your network. If you do not know this, you can login to your router and look at devices on your network and find the Meross Garage Door Opener.
  3. Next select the garage doors you would like to add.

Once this is completed the garage doors should appear in your devices list.
Click into the garage door device and hit refresh. You should see door and contact status along with firmware info. This means you have successfully integrated the MSG200 into Hubitat!

I chose to configure my device with Homekit integration as well since Meross is charging an extra $30 for the privilege. You just need to enable it within the device settings if you have the Homekit integration already enabled in the Apps section.

1 Like

The first two steps (login & device selection):
OK. :+1:

The third step ("Select one or more garage doors to add"):
"0 new doors detected" -> dropdown is empty :sleepy:

Any ideas?

When you go to List Garage Doors, do any show up there? You might have already selected them and so they would not show up in the Add section.

No, the Garage Doors List is empty.

In the meantime I have done the whole process manually (via JavaScript) - the driver itself is now working.
I think I don't need the App anymore, right? :thinking:

BTW: I changed the driver a little bit to have faster responses (1 minute polling, do you think I will have any problems?) and to have different settings for opening and closing time. :sunglasses:

Just installed the Meross Opener and the above App...
while the HomeKit automation works, I do want to add others, so am trying to add the garage to the list in the App...
I have the IP address (and added a static IP for good measure!) but I also need to enter my credentials... When I registered with Meross App it uses email and password... but these are apparently not correct?! Is there a different 'Username' that I should put in?? (I also tried the nickname that is in Meross app.

Thanks

[edit: :grimacing:might have got password wrong… that bit works now, but see below for next issue…]

A

I am facing the same situation... Step 2, the door is recognised, but clicking takes me to Step 3 which is the same, but as I have just recognised the door, the list is now empty and I cannot proceed :frowning: ... I did allow debugging, so will check what it says there...

A

[edit: does it matter that I have the MSG100 rather than the 200!?!]

2 Likes

Same "boat" as aidan with MSG100 and cannot go pass Step 3.

1 Like

Some suggestions on how to get this to work for the MSG100

  • Download the app listed above -- when installing, immediately hit done
  • Download the driver code, then install the driver as a device

There are probably better ways of doing this, but here is one way to obtain all of the fields required for the driver.

  • Use the app to obtain the UUID and Key....go through the steps until you get to the (failing) step three. Click on the gear icon (upper left). You will find the UUID (the key is also listed).
  • Follow the directions listed by ithinkdancan to obtain the other fields. Specifically, follow the directions listed for generating a key using Chrome: GitHub - ithinkdancan/hubitat-meross: Hubitat Drivers for Meross Smart Plugs

Again, there are probably better ways of doing this...but this was one way I cobbled together a solution.

1 Like

Hi, I am stuck on step 3 with an unexpected error after entering my credentials and IP address. Error shown below. Any ideas on what would cause this? Thanks for your help.

Error: No signature of method: user_app_ajardolino3_Meross_Garage_Door_Manager_131.paragraph() is applicable for argument types: (groovyx.net.http.HttpResponseException) values: [groovyx.net.http.HttpResponseException: status code: 404, reason phrase: Not Found]

Same issue for me. Authentication would fail. I forked the repo and made some fixes. Works fine for my MSG200 device now. I'll issue a pull request once I add some other enhancements including:

  • Only logging when configured to do so
  • Add support for Hubitat Package Manager
1 Like

Key generation using the ithinkdancan doesn't appear to be working for me. Is it still working for anyone else? Is there anyway else to obtain the required Key value? That is the only piece of the config data I am still missing.

See the post just above. I forked the code and updated the domain name references to get the app to work against the latest endpoint URLs. With the app, there's no need to run a separate script to get the key. The app will obtain it for you.

Thanks! I will give it a look/try

First two steps look successful and I can see the Meross devices detected on my network.

Screenshot 2024-02-26 at 3.42.37 PM

After selecting the Garage Door Opener, there is nothing listed in the drop down under step 3?

Screenshot 2024-02-26 at 3.42.49 PM

Thoughts?

To be clear, I am trying to install a MSG100 opener

I recall that there’s a limitation of the app with the MSG100 opener. I’m wondering if you could tweak the app to print the content of each device?

Add the line 113 as shown below, take a screenshot of the log output, and I’ll see if I can enhance the app to support the data being returned form the MSG100 device.

1 Like

My current definition for that step looks different. Here is what I have.

Here are the results:
app:12532024-02-27 01:48:59.036 PMinfodevice: [deviceType:mss120br, devIconId:device064_us, onlineStatus:1, devName:Smart Plug 2, fmwareVersion:4.2.11, uuid:2104299362209390850248e1e96eeccb, userDevIcon:, channels:[[:], [devIconId:device001, devName:Switch 1, type:Switch], [devIconId:device001, devName:Switch 2, type:Switch]], bindTime:1668781629, domain:mqtt-us-2.meross.com, iconType:1, reservedDomain:mqtt-us-2.meross.com, hardwareCapabilities:, subType:us, region:us, hdwareVersion:4.0.0]

app:12532024-02-27 01:48:59.031 PMinfodevice: [deviceType:mss120br, devIconId:device064_us, onlineStatus:1, devName:Smart Plug 1, fmwareVersion:4.2.11, uuid:2104290104915690850248e1e96edbdf, userDevIcon:, channels:[[:], [devIconId:device001, devName:Switch 1, type:Switch], [devIconId:device001, devName:Switch 2, type:Switch]], bindTime:1668781639, domain:mqtt-us-2.meross.com, iconType:1, reservedDomain:mqtt-us-2.meross.com, hardwareCapabilities:, subType:us, region:us, hdwareVersion:4.0.0]

app:12532024-02-27 01:48:58.998 PMinfodevice: [deviceType:msg100, devIconId:device039_un, onlineStatus:1, devName:Garage Door Opener, fmwareVersion:4.2.12, uuid:2311014265536961070348e1e9df3bb6, userDevIcon:, channels:[[:]], bindTime:1708714324, domain:mqtt-us-2.meross.com, iconType:1, reservedDomain:mqtt-us-2.meross.com, hardwareCapabilities:, subType:us, region:us, hdwareVersion:4.0.0]

Thanks for sharing the data structure associated with the msg100 single door controller.

I tweaked the app to add support for the msg100. Since I don't have an msg100, I didn't test the new code. I ran a quick test against my msg200 to verify that the changes didn't cause a regression.

The changes include detecting msg100 vs msg200 and simply adding one door to the array of doors in the case of the msg100 when a child device doesn't already exist.

See the changes here:

1 Like

Thanks!. I get the garage door in the step 3 drop down now, but when clicking on next, get the following error.

Add Garage Door Status

Unable to add door: java.lang.NullPointerException: Cannot get property 'devName' on null object

Additional logs:
app:12572024-03-01 01:52:01.767 PMinfodevice: [deviceType:mss120br, devIconId:device064_us, onlineStatus:1, devName:Smart Plug 1, fmwareVersion:4.2.11, uuid:2104290104915690850248e1e96edbdf, userDevIcon:, channels:[[:], [devIconId:device001, devName:Switch 1, type:Switch], [devIconId:device001, devName:Switch 2, type:Switch]], bindTime:1668781639, domain:mqtt-us-2.meross.com, iconType:1, reservedDomain:mqtt-us-2.meross.com, hardwareCapabilities:, subType:us, region:us, hdwareVersion:4.0.0]

app:12572024-03-01 01:52:01.762 PMinfodevice: [deviceType:msg100, devIconId:device039_un, onlineStatus:1, devName:Garage Door Opener, fmwareVersion:4.2.12, uuid:2311014265536961070348e1e9df3bb6, userDevIcon:, channels:[[:]], bindTime:1708714324, domain:mqtt-us-2.meross.com, iconType:1, reservedDomain:mqtt-us-2.meross.com, hardwareCapabilities:, subType:us, region:us, hdwareVersion:4.0.0]

app:12572024-03-01 01:52:01.757 PMinfodevice: [deviceType:mss120br, devIconId:device064_us, onlineStatus:1, devName:Smart Plug 2, fmwareVersion:4.2.11, uuid:2104299362209390850248e1e96eeccb, userDevIcon:, channels:[[:], [devIconId:device001, devName:Switch 1, type:Switch], [devIconId:device001, devName:Switch 2, type:Switch]], bindTime:1668781629, domain:mqtt-us-2.meross.com, iconType:1, reservedDomain:mqtt-us-2.meross.com, hardwareCapabilities:, subType:us, region:us, hdwareVersion:4.0.0]