Z-Wave Switches, whats the best implementation of them?


(Adam) #1

Hello everyone! I just purchased a new house and I’m trying to retrofit all the switches in the house to be Z-Wave compatible, but I have a few questions before I move forward.

I plan on having mostly smart bulbs in the house. I have a dozen or so Hue bulbs that I want to use. I can fill the rest with GE Link bulbs. All these bulbs can be turned on/off using software. Whats the best way to implement them with Z-Wave switches?

A) The switches stay on all the time and the bulbs are turned on/off strictly using another method (scripting, phone apps, etc…)

B) The switch controls the power to the bulbs and the bulbs are unresponsive until the switch is turned on.

Is there a way to have the switch send a Z-wave command to SmartThings to turn on the light? That would be the most ideal actually.

The switches I was looking at installing are a dozen or so of these: http://www.amazon.com/product-reviews/B0035YRCR2/

Thank you!


(Cody Truscott) #2

Go with Sylvania dim to warm leds instead.

Trying to mix switches and ‘smart’ bulbs will just leave you frustrated.


(William) #3

I have link bulbs in lamps, and use switches for hard wired fixtures. I agree with Cody, I wouldn’t put link bulbs into fixture controlled by a smart switch. Just a waste of money.


(Brice; SmartRulesApp.com) #4

If you’re going to use smart bulbs, I would say remove the old switch, hard wire the lights, so that they always have power, then install the zwave switches with no load connected. Then the switch can connect to ST and ST can “forward” the on/off along to the bulbs. Although, you could get this same result by just connecting the zwave switch to the lights and using dumb bulbs.


(Cody Truscott) #5

Many switches don’t work as virtual switches without load. You have to use purpose built no load switches such as the linear auxiliary switch.

But it seems like a great way to spend more money and have a failure prone system.

Stick to smart switches and dumb bulbs.


#6

I use smart bulbs and smart remotes, some of which can be wallmounted.

A smart remote can be a tablet control center or something that looks like a regular switch. Or the Amazon Echo for voice control. The remote doesn’t control the current to the light, instead it wirelessly tells the SmartThings hub or Philips Hue Bridge to wirelessly tell the light to turn itself on. We leave the dumb switches in place as a backup.

This works very well for us, although some households might need to add switch locks on the existing dumb switches to keep people from turning power off to the smart bulbs.

But it depends on your own aesthetic sense. My house is mid century modern so gadgets fit right in and I care way more about function than form.

Here’s my own favorite wall mount remote. It happens to be zigbee, not zwave, but with ST that doesn’t matter, you can mix and match. (This reminds me: dimming is limited to presets like 75% for one button and 50% for another if you’re trying to control a smart bulb from a smart switch. If you want full dim control, see the tablet or voice options at the end of this post.)

And here are some wall mount tablet ideas. These work great with smart bulbs. Again you leave the dumb switches in place, with switch locks if necessary.

But at our house, our main lights control is now the Amazon Echo. Works great with any bulb that can be controlled through a Hue bridge. Everybody likes it. :sunglasses: :bulb:

k


(Brice; SmartRulesApp.com) #7

I’m not sure about that. I don’t think the auxiliary switches will do anything without a master switch. I use some zwave switches with no load as remote switches for my garage doors.

But I agree, sticking with the smart switches and dumb bulbs is the way to go.


(Cody Truscott) #8

I had to rip out six standard linear dimmers because they won’t pair without a load attached. The auxiliary switches I replaced them with control curtains, hues, a fireplace dry contact etc via ‘The Big Switch’. The only issue is that they sometimes ‘fall asleep’ much like the notorious 7 button switch.


(Joel Goldwein) #9

If you do not have hot lines at the wall plates, and want to replace bulbs with GE Link bulbs or equivalent. you can pull out the existing on/off switch, wire the lights to stay on, and use this combo SmartApp and Device App. Will require wiring a PEQ door open/close sensor to a 3-way switch and placing that inside the wall box, but will only cost about $25.

/**
 *  PEQ modded multi-way on off toggle switch for multiple lights
 *  July 24, 2015
 *  
 *    This SmartApp uses a modified PEQ door open/close sensor wired to SPDT (3-way) switch to toggle on and off
 *  a switch that controls a GE Link Light or similar light or switch.   The intent is to have a simple and inexpensive battery
 *  operated replacement for a standard wall switch to operate multiple, 3-way or x-way zigbee or zwave light or
 *  switch.
 * 
 *  The PEQ sensor has been on sale intermittently at Best Buy for under $20, and the 3 way SPDT wall switch costs a few
 *  dollars at any hardware store, so you can put the whole thing together for about $25.00.
 *
 *  The PEQ door open/close sensor can be easily modded and wired to the 3-way wall switch by replacing 
 *  (deslodering the relay and resoldering three wires) the magnetic reed relay with 3 wires soldered
 *  to the 1 reed common pad and 2 reed switch pads on the PCB.  I will be posting a detailed HOW-TO for
 *  modification of the PEQ Sensor on the Smartthings community board shortly.
 *
 *  A future version will be published to toggle on/off multiple switches simultaneously, 
* and to set the dimmer level.
 *
 *  Copyright (c) 2015 Joel Goldwein (goldwein at gmail.com)
*
 *  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.
 *
 */
definition(
    name: "Multi-way On/Off Toggle Switch Using a Modded PEQ Door Open/Close Sensor for Multiple Lights",
    namespace: "JWGthings",
    author: "Joel Goldwein (goldwein at gmail.com)",
    description: "PEQ multi-way On/Off Switch - This SmartApp uses a modified PEQ door open/close sensor wired to SPDT (3-way) switch to toggle on and off MULTIPLE GE Link Light, similar light or switch.   The intent is to have a simple and inexpensive battery operated replacement for a standard wall switch to operate a single, 3-way or multi-way zigbee or zwave light or switch.",
    category: "My Apps",
    iconUrl: "http://cdn.device-icons.smartthings.com/Home/home30-icn@2x.png",
    iconX2Url: "http://cdn.device-icons.smartthings.com/Home/home30-icn@2x.png",    
    iconX3Url: "http://cdn.device-icons.smartthings.com/Home/home30-icn@2x.png"
)

//        //https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png

preferences {
    section("Select switch or switches") {
        input "contact1", "capability.contactSensor", title: "Which Switch or Switches?", required: true, multiple: true, submitOnChange: true
    }
    // What light should this app be configured for?
       section("Turn on/off which Link Lights...") {
        input "switches", "capability.switch", title: "Which Lights", required: true, multiple: true
    }
}

def installed() {
//    log.debug "Installed with settings: ${settings}"
    initialize()
}

def updated() {
//    log.debug "Updated with settings: ${settings}"
    unsubscribe()
    initialize()
}

def initialize() {
    subscribe(contact1, "contact", contactHandler)
}

def contactHandler(evt) {
    def cname = "${contact1.displayName}"
    def lname = "${switches.displayName}"
    log.debug "Triggered. Sensor was $evt.value and $lname was $switches.currentSwitch"
//     The contactSensor capability can be either "open" or "closed"
//     The actual sensor state is not relevant.  What is only important is the state change, 
//  Code is triggered any time there is a state change in the door sensor
//  Eventually, I'm going to try doing this with a SPST switch instead of a SPDT switch
//
//  If the switch is UP (closed)
    if("closed" == evt.value)  {
//    Turn it ON
        log.debug "JWG: UP"
        switches.on()
//    and if the switch is DOWN (open) 
    } else if("open" == evt.value) {
//    Turn it ON    
        log.debug "Status: Down"
        switches.off()
    } else {
           log.debug "Status: Uncertain"
        switches.off()
    }
//  Send a info message
  log.debug "PEQ switch $cname is now $evt.value and $lname is now $switches.currentSwitch"
}

Combined with this device:

/**
 *  Modified SmartSense (PEQ) Open/Closed Sensor to behave like an on/off toggle switch 
 *
 *  Copyright 2014 SmartThings and 2015 - Joel Goldwein
 *  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.
 *
 */
 
metadata {
    definition (name: "Modded SmartSense Open/Closed Sensor/Switch", namespace: "smartthings", author: "SmartThings") {
        capability "Battery"
        capability "Configuration"
        capability "Contact Sensor"
        capability "Refresh"
        capability "Temperature Measurement"

        command "enrollResponse"
 
 
        fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3300-S"
        fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3300"
    }

    simulator {
 
    }

    preferences {
        input description: "This feature allows you to correct any temperature variations by selecting an offset. Ex: If your sensor consistently reports a temp that's 5 degrees too warm, you'd enter \"-5\". If 3 degrees too cold, enter \"+3\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
        input "tempOffset", "number", title: "Temperature Offset", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
    }
 
    tiles {
        standardTile("contact", "device.contact", width: 2, height: 2) {
            state("open", label:"DOWN", icon:"st.Home.home30", backgroundColor:"#C7B4FA")
            state("closed", label:"UP", icon:"st.Home.home30", backgroundColor:"#2E5FF2")
        }
        
        valueTile("temperature", "device.temperature", inactiveLabel: false) {
            state "temperature", label:'${currentValue}°',
                backgroundColors:[
                    [value: 31, color: "#153591"],
                    [value: 44, color: "#1e9cbb"],
                    [value: 59, color: "#90d2a7"],
                    [value: 74, color: "#44b621"],
                    [value: 84, color: "#f1d801"],
                    [value: 95, color: "#d04e00"],
                    [value: 96, color: "#bc2323"]
                ]
        }
        valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false) {
            state "battery", label:'${currentValue}% battery', unit:""
             }
        
        standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat") {
            state "default", action:"refresh.refresh", icon:"st.secondary.refresh"
        }
 
        main (["contact", "temperature"])
        details(["contact","temperature","battery","refresh"])
    }
}
 
def parse(String description) {
//    log.debug "description: $description"
    Map map = [:]
    if (description?.startsWith('catchall:')) {
        map = parseCatchAllMessage(description)
    }
    else if (description?.startsWith('read attr -')) {
        map = parseReportAttributeMessage(description)
    }
    else if (description?.startsWith('temperature: ')) {
        map = parseCustomMessage(description)
    }
    else if (description?.startsWith('zone status')) {
        map = parseIasMessage(description)
    }
//    log.debug "Parse returned $map"
    def result = map ? createEvent(map) : null
    if (description?.startsWith('enroll request')) {
        List cmds = enrollResponse()
//        log.debug "enroll response: ${cmds}"
        result = cmds?.collect { new physicalgraph.device.HubAction(it) }
    }
      return result
}
 
private Map parseCatchAllMessage(String description) {
//    log.debug "description A: $description"
    Map resultMap = [:]
    def cluster = zigbee.parse(description)
    if (shouldProcessMessage(cluster)) {
        switch(cluster.clusterId) {
            case 0x0001:
                resultMap = getBatteryResult(cluster.data.last())
                break

            case 0x0402:
//                log.debug 'TEMP'
                // temp is last 2 data values. reverse to swap endian
                String temp = cluster.data[-2..-1].reverse().collect { cluster.hex1(it) }.join()
                def value = getTemperature(temp)
                resultMap = getTemperatureResult(value)
                break
        }
    }

    return resultMap
}

private boolean shouldProcessMessage(cluster) {
    // 0x0B is default response indicating message got through
    // 0x07 is bind message
    boolean ignoredMessage = cluster.profileId != 0x0104 || 
        cluster.command == 0x0B ||
        cluster.command == 0x07 ||
        (cluster.data.size() > 0 && cluster.data.first() == 0x3e)
    return !ignoredMessage
}

private int getHumidity(value) {
    return Math.round(Double.parseDouble(value))
}

private Map parseReportAttributeMessage(String description) {
    Map descMap = (description - "read attr - ").split(",").inject([:]) { map, param ->
        def nameAndValue = param.split(":")
        map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
    }
//    log.debug "Desc Map: $descMap"
    Map resultMap = [:]
    if (descMap.cluster == "0402" && descMap.attrId == "0000") {
        def value = getTemperature(descMap.value)
        resultMap = getTemperatureResult(value)
    }
    else if (descMap.cluster == "0001" && descMap.attrId == "0020") {
        resultMap = getBatteryResult(Integer.parseInt(descMap.value, 16))
    }
 
    return resultMap
}
 
private Map parseCustomMessage(String description) {
//    log.debug "description B: $description"
    Map resultMap = [:]
    if (description?.startsWith('temperature: ')) {
        def value = zigbee.parseHATemperatureValue(description, "temperature: ", getTemperatureScale())
        resultMap = getTemperatureResult(value)
    }
    return resultMap
}

private Map parseIasMessage(String description) {
    List parsedMsg = description.split(' ')
    String msgCode = parsedMsg[2]
//    log.debug "msgCode: $msgCode"     
    Map resultMap = [:]
    switch(msgCode) {
        case '0x0020': // Closed/No Motion/Dry/On/Up
            resultMap = getContactResult('closed')
            break

        case '0x0021': // Open/Motion/Wet/off/down
            resultMap = getContactResult('open')
            break

        case '0x0022': // Tamper Alarm
            break

        case '0x0023': // Battery Alarm
            break

        case '0x0024': // Supervision Report
            resultMap = getContactResult('closed')
            break

        case '0x0025': // Restore Report
            resultMap = getContactResult('open')
            break

        case '0x0026': // Trouble/Failure
            break

        case '0x0028': // Test Mode
            break
    }
    return resultMap
}
 
def getTemperature(value) {
    def celsius = Integer.parseInt(value, 16).shortValue() / 100
    if(getTemperatureScale() == "C"){
        return celsius
    } else {
        return celsiusToFahrenheit(celsius) as Integer
    }
}

private Map getBatteryResult(rawValue) {
//    log.debug 'Battery'
    def linkText = getLinkText(device)
    
    def result = [
        name: 'battery'
    ]
    
    def volts = rawValue / 10
    def descriptionText
    if (volts > 3.5) {
        result.descriptionText = "${linkText} battery has too much power (${volts} volts)."
    }
    else {
        def minVolts = 2.1
        def maxVolts = 3.0
        def pct = (volts - minVolts) / (maxVolts - minVolts)
        result.value = Math.min(100, (int) pct * 100)
        result.descriptionText = "${linkText} battery was ${result.value}%"
    }

    return result
}

private Map getTemperatureResult(value) {
//    log.debug 'TEMP'
    def linkText = getLinkText(device)
    if (tempOffset) {
        def offset = tempOffset as int
        def v = value as int
        value = v + offset
    }
    def descriptionText = "${linkText} was ${value}°${temperatureScale}"
    return [
        name: 'temperature',
        value: value,
        descriptionText: descriptionText
    ]
}

private Map getContactResult(value) {
//    log.debug 'Switch Status'
    def linkText = getLinkText(device)
    def descriptionText = "${linkText} was ${value == 'open' ? 'opened' : 'closed'}"
//    log.debug "linkText: $descriptionText"
    return [
        name: 'contact',
        value: value,
        descriptionText: descriptionText
    ]
}

def refresh()
{
    log.debug "Refreshing Temperature and Battery"
    [
    
        "st rattr 0x${device.deviceNetworkId} 1 0x402 0", "delay 200",
        "st rattr 0x${device.deviceNetworkId} 1 1 0x20"

    ]
}

def configure() {

    String zigbeeId = swapEndianHex(device.hub.zigbeeId)
    log.debug "Confuguring Reporting, IAS CIE, and Bindings."
    def configCmds = [
        "zcl global write 0x500 0x10 0xf0 {${zigbeeId}}", "delay 200",
        "send 0x${device.deviceNetworkId} 1 1", "delay 1500",
        
        "zcl global send-me-a-report 1 0x20 0x20 600 3600 {01}", "delay 200",
        "send 0x${device.deviceNetworkId} 1 1", "delay 1500",
        
        "zcl global send-me-a-report 0x402 0 0x29 300 3600 {6400}", "delay 200",
        "send 0x${device.deviceNetworkId} 1 1", "delay 1500",
        
        
        //"raw 0x500 {01 23 00 00 00}", "delay 200",
        //"send 0x${device.deviceNetworkId} 1 1", "delay 1500",
        
        
        "zdo bind 0x${device.deviceNetworkId} 1 1 0x402 {${device.zigbeeId}} {}", "delay 500",
        "zdo bind 0x${device.deviceNetworkId} 1 1 1 {${device.zigbeeId}} {}"
    ]
    return configCmds + refresh() // send refresh cmds as part of config
}

def enrollResponse() {
    log.debug "Sending enroll response"
    [    
            
    "raw 0x500 {01 23 00 00 00}", "delay 200",
    "send 0x${device.deviceNetworkId} 1 1"
        
    ]
}
private hex(value) {
    new BigInteger(Math.round(value).toString()).toString(16)
}

private String swapEndianHex(String hex) {
    reverseArray(hex.decodeHex()).encodeHex()
}

private byte[] reverseArray(byte[] array) {
    int i = 0;
    int j = array.length - 1;
    byte tmp;
    while (j > i) {
        tmp = array[j];
        array[j] = array[i];
        array[i] = tmp;
        j--;
        i++;
    }
    return array
}

(Brice; SmartRulesApp.com) #10

Interesting. Mine are either the GE or Jasco ones and they don’t mind pairing with no load.


(Christian Laney) #11

Just wanted to drop in and say that I currently use 3 regular old Linear z-wave switches (not labeled as no load) and a GE z-wave as virtual switches to control my Link bulbs, and they all work fine with no load. Looking back, and especially if you’re already planning on using z-wave switches, just buy switches for main fixtures and leave the bulbs for things that are harder to add a switch to.