Somfy Motorized Blinds (Z-Wave)

I need some community help! I finally got my motorized blind installed yesterday and now I am stuck getting it connected to Smartthings. The blind is from Kirsch with a Somfy motor. I had several email interactions with the retailer before the purchase to confirm that the motor to be installed was a Somfy Autoview, as it is my understanding that I would not need an additional interface, the ZRTSII. They assured me it would be the autoview motor. The blind was $1300 and I did not want additional expense. Today I am attempting to connect it to my hub and I do not see any paring buttons. I am attaching a picture for your confirmation, but I suspect it is a RTS motor and I will need to buy an additional interface.

I hate to say it but it looks like you’ve been duped. The Somfy Z-wave motors are licensed by Somfy to manufacture for a company called Springs Window Fashions exclusively. The motor itself was designed by SWF and they hold the intellectual rights to it. The easiest way to identify these motors is that the motor has a light on it which is also a switch that raises and lowers the shade.

Springs Window Fashions has 2 companies, one is Bali which has the Autoview Branding on these motors and Graber which has the Virtual Cord branding on these motors. Bali is the big box store product, while Graber is available only through select retailers and has a slightly better finish and build quality.

In most cases once a custom shade has been manufactured
 they usually don’t take it back. I hope things work out for you. :disappointed:

You can get more info on the Graber ones here: Z-Wave + integrated blinds/shades: Custom Graber Virtual Cord Shades

What interface do you recommend to make the zwave connection?

Did you manage to get it configured?

You would need the ZRTSI (only device officially supported by Somfy), but give them a call with the motor number to make sure as well. The only downside with the ZRTSI is that you don’t have 2 way communication and are limited to on/off and preset positions. What I mean by 2 way communication is that if you use a remote to control the shade, SmartThings won’t know the shade has moved. The ZRTSI can only push commands to the shade, it can’t push information from the shade to SmartThings.

What is the difference with the ZRTSII?

Ok
 so here goes. ZRTSI is also the ZRTSII. Previously ZRTSI referred to version one of the unit so most companies including us referred to the version 2 as ZRTSII to avoid confusion, but now since the old one is no longer available, every one has gone back to calling the new version ZRTSI.

:grin:I know
 it’s confusing.

1 Like

Thank you for the very helpful information!
Very much appreciated.

2 Likes

I don’t see any Z Wave specs on it? Might be the old RTS, which is radio technology. I think you will need the RTS to Z wave converter.

Something like this


I do have a question for those that have integrated and use batteries.

Generally speaking the batteries are supposed to last about 1yr with normal use. I have been thinking lately that SmartThings polls the device and therefore minimizes the life of the battery. I think it does it because ST thinks it is an outlet/switch. I did not think ST would poll a battery driven device.

Is there any way to extend the polling period or turning it off 100%?

Also any idea when ST might have integration for it officially?

I missed your initial response Neal, but will give it a go tonight. I didn’t know you could pair a shade directly to the ZRTSI.

To recap – I need to set upper/lower/My positions on all shades via the remote, and don’t bother trying to do groupings with the remote. I assume that means I’d program 5 of my 7 shades into the remote first. Pair each motor with the ZRTSI. Program final 2 shades into remote (overwriting 2 of the original 5). Then pair those motors with ZRTSI. Finally handle groupings within ZRTSI.

Do I have that right?

For anyone else confused about grouping shades together on the remote or ZRTSI, here’s the response from Somfy tech support:

You can use the 5 channel remote to program the 7 shades into the ZRTSI. To create a group, you basically program the motors you want to the same channel to make it a group. Let say you have 2 motors and you want to group them the channel 1 on the ZRTSI. Follow the instructions on how to add a shade on the ZRTSI manual. Once you’ve added the first motor, disconnect it from power. Do the second motor following the same instructions on the ZRTSI and add it to the same channel which is channel 1 on the ZRTSI. Once both are programmed, power them both up and they will run as a group.

1 Like

Any easier way for us non programmers?

I have an usual question. Installed the app (as I’ve installed many others), however this one seems to work in the opp direction. Meaning “open” closes the blinds and “close” opens the blinds. Has anyone seen this before?

do you have shades, or blinds?

Did you set this setting in the dth for each shade/blind?

Go to your individual device, and go into the settings gear

I have shades
 no matter what I do. The opposite happens, open closes the shades (they are curtains) and close opens the shades.

Seems like your shades were programmed incorrectly.

You can modify the shade programming, or customize the dth to suit your needs.

Its funny I was just thinking the same thing. I would presume I would just need to go into DTH through the IDE and find the command for “on” and change to “off” and vice versa and then find the status for “open” and change to “closed” and vice versa.

Care to point me to the relevant lines of code?

So I’ve tried to do ths myself with no luck. Any assistance?

So I’ve gotten somewhere in reversing out all the commands
 but I am stuck at one final point.

       `/**
 * 
 * https://community.smartthings.com/t/my-somfy-smartthings-integration/13492
 * Modified ERS 12/29/2016
 *
 * Version 1.0.6
 *
 * Version History
 *
 * 1.0.6    29 Dec 2016		Health Check
 * 1.0.5    01 May 2016		bug fixes
 * 1.0.4    01 May 2016		Sync commands for cases where blinds respond to multiple channels (all vs. single)
 * 1.0.3    17 Apr 2016		Expanded runIn timer for movement and  completed states
 * 1.0.2    04 Apr 2016		Added runIn timer for movement vs. completed states
 * 1.0.1    07 Mar 2016		Add Blinds support by edit device to set to blinds type
 * 1.0.0    24 Feb 2016		Multi-tile, Window Shade Capability, Device Handler attempts to maintain state
 *
 * Notes:
 *
 * Somfy ZRTSII does not report accurate status for the device.
 *
 * This device handler maintains an internal view of device status based on last command
 * reissuing a command to the shade (up, down, preset (when stopped)) does not move the shade/blinds if it is already in that position
 * My/stop command does different actions depending if the shade is idle (go to MY or closed position) or moving (stop)
 *
 * Once the device is installed, it defaults to "shade" operation.  If "blinds" operation is desired, for the device go to settings (gear)
 * and change the device operation to Window Blinds
 *
 *	Shade and Blinds operate differently in ZRTSII buttons
 *	- Shades actions: up button: open (on switch),  down button: close (off switch),       my/stop button: presetPosition (50%)
 *	- Blinds actions: up button: open (on switch),  down button: tilt open (off switch),   my/stop button: close (50%)
 *
 * Window Shade Capability standardizes:  (these should not be changed, except by SmartThings capabilities updates)
 *	- windowShade: unknown, closed, open, partially open, closing, opening 
 *	- Commands:  open(), close(), presetPosition()
 *
 */
  metadata {
    definition (name: "Somfy Z-Wave Shades and Blinds Multi tile", namespace: "imnotbob", author: "Eric, Ash, Others") {
        capability "Switch Level"
        capability "Switch"
        capability "Window Shade"
        //capability "Polling"
        capability "Refresh"
        capability "Actuator"
        capability "Health Check"

        attribute "stopStr", "enum", ["preset/stop", "close/stop"]

        command "OpenSync"
        command "CloseSync"
        command "TiltSync"
        command "levelOpenClose"

        fingerprint deviceId: "0x1105", inClusters: "0x2C, 0x72, 0x26, 0x20, 0x25, 0x2B, 0x86"
    }

    simulator {
        status "on":  "command: 2003, payload: 00"
        status "off": "command: 2003, payload: FF"
        status "09%": "command: 2003, payload: 09"
        status "10%": "command: 2003, payload: 0A"
        status "33%": "command: 2003, payload: 21"
        status "66%": "command: 2003, payload: 42"
        status "99%": "command: 2003, payload: 63"
        
        // reply messages
        reply "2001FF,delay 5000,2602": "command: 2603, payload: 00"
        reply "200100,delay 5000,2602": "command: 2603, payload: FF"
        reply "200119,delay 5000,2602": "command: 2603, payload: 19"
        reply "200132,delay 5000,2602": "command: 2603, payload: 32"
        reply "20014B,delay 5000,2602": "command: 2603, payload: 4B"
        reply "200163,delay 5000,2602": "command: 2603, payload: 63"
    }

    preferences {
	input ("shadeType", "enum", options:[
		"shades": "Window Shades",
		"blinds": "Window Blinds"],
		title: "Window Shades or Blinds?", description: "set type (shades or blinds)", defaultValue: "shades",
                required: false, displayDuringSetup: true )
	}

    tiles(scale: 2) {
        multiAttributeTile(name:"shade", type: "lighting", width: 6, height: 4) {
            tileAttribute("device.windowShade", key: "PRIMARY_CONTROL") {
                attributeState("unknown", label:'${name}', action:"refresh.refresh", icon:"st.doors.garage.garage-open", backgroundColor:"#ffa81e")
                attributeState("open",  label:'${name}', action:"open", icon:"st.doors.garage.garage-open", backgroundColor:"#ffcc33", nextState: "closing")
                attributeState("closed",    label:'${name}', action:"close", icon:"st.doors.garage.garage-closed", backgroundColor:"#bbbbdd", nextState: "opening")
                attributeState("partially open", label:'preset', action:"presetPosition", icon:"st.Transportation.transportation13", backgroundColor:"#ffcc33")
                attributeState("closing", label:'${name}', action:"presetPosition", icon:"st.doors.garage.garage-closing", backgroundColor:"#bbbbdd")
                attributeState("opening", label:'${name}', action:"presetPosition", icon:"st.doors.garage.garage-opening", backgroundColor:"#ffcc33")
            }
            tileAttribute ("device.level", key: "SLIDER_CONTROL") {
                attributeState("level", action:"switch level.setLevel")
            }
            tileAttribute ("device.speedLevel", key: "VALUE_CONTROL") {
                attributeState("level", action: "levelOpenClose")
            }
        }

        standardTile("switchmain", "device.windowShade") {
            state("unknown", label:'${name}', action:"refresh.refresh", icon:"st.doors.garage.garage-open", backgroundColor:"#ffa81e")
            state("open",  label:'${name}', action:"open", icon:"st.doors.garage.garage-open", backgroundColor:"#ffcc33", nextState: "closing")
            state("closed",    label:'${name}', action:"close", icon:"st.doors.garage.garage-closed", backgroundColor:"#bbbbdd", nextState: "opening")
            state("partially open", label:'preset', action:"presetPosition", icon:"st.Transportation.transportation13", backgroundColor:"#ffcc33")
            state("closing", label:'${name}', action:"presetPosition", icon:"st.doors.garage.garage-closing", backgroundColor:"#bbbbdd")
            state("opening", label:'${name}', action:"presetPosition", icon:"st.doors.garage.garage-opening", backgroundColor:"#ffcc33")

//            state("on", label:'up', action:"switch.off", icon:"st.doors.garage.garage-open", backgroundColor:"#ffcc33")
//            state("off", label:'closed', action:"switch.on", icon:"st.doors.garage.garage-closed", backgroundColor:"#bbbbdd")
//            state("default", label:'preset', action:"presetPosition", icon:"st.Transportation.transportation13", backgroundColor:"#ffcc33")
        }

        standardTile("on", "device.switch", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
            state("on", label:'open', action:"switch.on", icon:"st.doors.garage.garage-opening")
        }
        standardTile("off", "device.stopStr", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
            state("close/stop", label:'close/stop', action:"switch.off", icon:"st.doors.garage.garage-closing")
            state("default", label:'close', action:"switch.off", icon:"st.doors.garage.garage-closing")
        }
        standardTile("preset", "device.stopStr", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
            state("close/stop", label:'slats open', action:"switch level.setLevel", icon:"st.Transportation.transportation13")
            state("default", label:'preset/stop', action:"switch level.setLevel", icon:"st.Transportation.transportation13")
        }
        controlTile("levelSliderControl", "device.level", "slider", height: 1, width: 3, inactiveLabel: false) {
            state("level", action:"switch level.setLevel")
        }

        standardTile("refresh", "command.refresh", width:2, height:2, inactiveLabel: false, decoration: "flat") {
                state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
        }

//  Poll provides data, but the ZRTSII does not provide accurate status
//
//      standardTile("poll", "command.poll", width:2, height:2, inactiveLabel: false, decoration: "flat") {
//              state "default", label:'poll', action:"poll", icon:"st.secondary.poll"
//      }

        main(["switchmain"])
        details(["shade", "on", "off", "preset"])
    }
}

def configure() {
    log.trace "configure() called"
    updated()
}

def ping() {
	refresh()
}

def updated() {
    log.trace "updated() called"

    sendEvent(name: "checkInterval", value: 60 * 60 * 8, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID], displayed: false)

    def currstat = device.latestValue("level")
    def currstat1 = device.latestValue("windowShade")

    log.debug "Shade type: ${settings?.shadeType}"
    if (settings?.shadeType) {
        if (settings.shadeType == "shades") {
            sendEvent(name: "stopStr", value: "preset/stop")
        } else {
            sendEvent(name: "stopStr", value: "close/stop")
        }
    } else {
        sendEvent(name: "stopStr", value: "preset/stop")
    }

    log.debug "switch state: ${currstat}  windowShade state: ${currstat1}"
    if ( (currstat == null) || (currstat1 == null)) {
        if (currstat > null) {
            if (currstat >= 75) {
                //sendEvent(name: "windowShade", value: "open")
                finishOpenShade()
            } else if (currstat <= 25) {
                //sendEvent(name: "windowShade", value: "closed")
                finishCloseShade()
            } else {
                //sendEvent(name: "windowShade", value: "partially open")
                finishPartialOpenShade()
            }
        }
    }
}

def parse(String description) {
    description
    def result = null
    def cmd = zwave.parse(description, [0x20: 1, 0x26: 1, 0x70: 1])
    log.debug "Parsed ${description} to ${cmd}"
    if (cmd) {
        result = zwaveEvent(cmd)
        log.debug "zwaveEvent( ${cmd} ) returned ${result.inspect()}"
    } else {
        log.debug "Non-parsed event: ${description}"
    }
    return result
}

def levelOpenClose(value) {
    log.trace "levelOpenClose called with value $value"
    if (value) {
        on()
    } else {
        off()
    }
}

// Somfy ZRTSII does not report accurate status for the device.
// This device handler maintains an internal view of device status based on last command
// reissuing a command to the shade (up, down, preset (my) (when stopped)) does not move the shade if it is already in that position
// My/stop command does different actions depending if the shade is idle (go to MY position) or moving (stop)

def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd)
{
    def result = []
    def tempstr = ""
    def statstr = "SAME"

    log.trace "Basic report cmd.value:  ${cmd.value}"

    if (cmd.value == 0xFF) {
        //result << createEvent(name: "switch", value: "off")

        tempstr = "closed"
        if (settings?.shadeType) {
            if (settings.shadeType == "blinds") {
                tempstr = "tilted open"
            }
        }
    } else if (cmd.value == 0) {
        //result << createEvent(name: "switch", value: "on")
        tempstr = "open"

    } else {  // This has never happend
        //result << createEvent(name: "switch", value: "default")
        tempstr="neither open or closed"
    }
    def swstatstr = "${device.latestValue('switch')}"
    if (cmd.value == 0xFF && swstatstr == "on") { statstr = "DIFFERENT" }
    if (cmd.value == 0 && swstatstr == "off") { statstr = "DIFFERENT" }
        
    log.debug "${statstr} Zwave state is ${tempstr}; device stored state is ${device.latestValue('switch')} dimmer level: ${device.latestValue('level')} "
    return result
}

def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd) {
    def result = []
    def tempstr = ""

    log.debug "SwitchBinaryReport cmd.value:  ${cmd.value}"
    
    if (cmd.value == 0xFF) {
        tempstr = "closed"
        if (settings?.shadeType) {
            if (settings.shadeType == "blinds") {
                tempstr = "tilted open"
            }
        }

    } else if (cmd.value == 0) {
        tempstr = "open"

    } else {  // this has never happened
        tempstr="neither open or closed"
    }
    log.debug "Reported state is ${tempstr}; device is ${device.latestValue('switch')}  ${device.latestValue('level')} "
    
    //result << createEvent(name:"switch", value: cmd.value ? "on" : "off")
    //result << createEvent(name: "level",value: cmd.value, unit:"%",
        //descriptionText:"${device.displayName} dimmed ${cmd.value==255 ? 100 : cmd.value}%")
    return result
}

def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv1.SwitchMultilevelReport cmd)
{
    def result = []
    def tempstr = ""

    log.trace "SwitchMultilevelReport cmd.value:  ${cmd.value}"
    
    if (cmd.value == 0xFF) {
        //result << createEvent(name: "switch", value: "off")
        tempstr = "closed"
        if (settings?.shadeType) {
            if (settings.shadeType == "blinds") {
                tempstr = "tilted open"
            }
        }

    } else if (cmd.value == 0) {
        //result << createEvent(name: "switch", value: "on")
        tempstr = "open"
    } else {
        //result << createEvent(name: "switch", value: "default")
        tempstr="neither open or closed"
    }
    //result << createEvent(name: "level",value: cmd.value, unit:"%",
      //descriptionText:"${device.displayName} dimmed ${cmd.value==255 ? 100 : cmd.value}%")
    log.debug "Reported state is ${tempstr}; device is ${device.latestValue('switch')}  ${device.latestValue('level')} "
    return result
}

def on() {
    int level = 100
    log.trace "on() treated as open()"
    setLevel(level) 
}

def off() {
    int level = 0
    log.trace "off() treated as close()"
    setLevel(level) 
}

def setLevel() {
    log.trace "setLevel() treated as preset position"
    setLevel(50) 
}

def open() {
    log.trace "open()"
    off()
}

def close() {
    log.trace "close()"
    close()
}

def presetPosition() {
    log.trace "presetPosition()"
    setLevel(50)
}

def OpenSync() {
    log.trace "OpenSync()"
    finishOpenShade()
}

def CloseSync() {
    log.trace "CloseSync()"
    finishCloseShade()
}

def TiltSync() {
    log.trace "TiltSync()"
    finishPartialOpenShade()
}

def refresh() {
    log.trace "refresh()"
    delayBetween([
        //zwave.switchBinaryV1.switchBinaryGet().format(),
        //zwave.switchMultilevelV1.switchMultilevelGet().format(),
        //zwave.meterV2.meterGet(scale: 0).format(),      // get kWh
        //zwave.meterV2.meterGet(scale: 2).format(),      // get Watts
        //zwave.sensorMultilevelV1.sensorMultilevelGet().format(),
        //zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType:1, scale:1).format(),  // get temp in Fahrenheit
        //zwave.batteryV1.batteryGet().format(),
        zwave.basicV1.basicGet().format()
    ], 3000)
}

// If you add the Polling capability to your device type, this command
// will be called approximately every 5 minutes to check the device's state
// zrtsII does not provide accurate status of shade position

//def poll() {
//        log.trace "Poll"
//        zwave.basicV1.basicGet().format()
//}

def setLevel(level) {
    log.trace "setLevel(level)  {$level}"
    log.debug "level.inspect " + level.inspect()

    int newlevel = level

    if (level > null) {

        if (level >= 75) {
            sendEvent(name: "windowShade", value: "opening")
            sendEvent(name: "level", value: level)
            sendEvent(name: "switch", value: "on")
            runIn(25, "finishOpenShade", [overwrite: true])
            delayBetween([
                zwave.switchMultilevelV1.switchMultilevelSet(value: 0).format(),
                zwave.basicV1.basicGet().format()
//                sendEvent(name: "windowShade", value: "closed"),
//                sendEvent(name: "switch", value: "off")
            ], 4000)
        } else if (level <= 25) {
            sendEvent(name: "windowShade", value: "closing")
            sendEvent(name: "switch", value: "off")
            runIn(25, "finishCloseShade", [overwrite: true])
            if (settings.shadeType == "shades") {
                delayBetween([
                    zwave.switchMultilevelV1.switchMultilevelSet(value: 0xFF).format(),
                    zwave.basicV1.basicGet().format()
//                    sendEvent(name: "windowShade", value: "up"),
//                    sendEvent(name: "switch", value: "on")
                ], 4000)
            } else {
                delayBetween([
                    zwave.switchMultilevelV1.switchMultilevelStopLevelChange().format(),
                    zwave.basicV1.basicGet().format()
//                    sendEvent(name: "windowShade", value: "up"),
//                    sendEvent(name: "switch", value: "on")
                ], 4000)
            }
        } else {
            def currstat = device.latestValue("windowShade")
            if (currstat == "open") { sendEvent(name: "windowShade", value: "closing") }
            else { sendEvent(name: "windowShade", value: "opening") }
            sendEvent(name: "level", value: level)
            sendEvent(name: "switch", value: "on")
            runIn(15, "finishPartialOpenShade", [overwrite: true])
            if (settings.shadeType == "shades") {
                delayBetween([
                    zwave.switchMultilevelV1.switchMultilevelStopLevelChange().format(),
                    zwave.basicV1.basicGet().format()
//                    sendEvent(name: "windowShade", value: "partially open"),
//                    sendEvent(name: "switch", value: "default")
                ], 4000)
            } else {
                delayBetween([
                    zwave.switchMultilevelV1.switchMultilevelSet(value: 0x00).format(),
                    zwave.basicV1.basicGet().format()
//                    sendEvent(name: "windowShade", value: "partially open"),
//                    sendEvent(name: "switch", value: "default")
                ], 4000)
            }
        }

        // this code below causes commands not be sent/received by the Somfy ZRTSII - I assume delayBetween is asynchronous...

        //log.trace("finished level adjust")
        //if (newlevel != level) { 
            //log.trace("finished level adjust1")
            //delayBetween([
                //sendEvent(name: "level", value: newlevel)
            //], 1000)
        //}
    }
}

def finishOpenShade() {
    sendEvent(name: "windowShade", value: "open")
    def newlevel = 100
    sendEvent(name: "level", value: newlevel)
    sendEvent(name: "switch", value: "on")
}

def finishCloseShade() {
    sendEvent(name: "windowShade", value: "closed")
    def newlevel = 0
    sendEvent(name: "level", value: newlevel)
    sendEvent(name: "switch", value: "off")
}

def finishPartialOpenShade() {
    sendEvent(name: "windowShade", value: "partially open")
    def newlevel = 50
    sendEvent(name: "level", value: newlevel)
    sendEvent(name: "switch", value: "on")
}

// this appears to never be called

//def setLevel(level, duration) {
//    log.trace "setLevel(level, duration)  {$level} ${duration}"
//    setLevel(level)
//    return
//}`

On the main screen the curtains report as closed and they are closed which is accurate.  

However, when I hit ‘Closed’ to open the curtains, it says ‘Opening’ for a split second but, the curtains do not happen, the status of the device in the device screen remains ‘Closed’ and the following happens in the logs.

I’ve obviously done something in the reversing of the on/off commands to interfere with what happens when I try to Open the curtains from a closed position from the main screen. The main screen for the device functions perfectly as all those commands for on/off, open/close have been reversed.

This lone issue consequently, interferes with certain automations that rely on closing the blinds (ie. CORE) because the command ‘Close’ is executed but it throws the trace message above and fails to close the curtains.

I’m so close, so any help that I can get would be awesome!