[Alpha] Community-maintained Google Home integration

And which driver are you using for the Go Control please?

When posting code, leave one extra line, then highlight it and then click on the code format icon above the editor.
image

That will get it to look pretty.

/**
 *  Z-Wave Garage Door Opener
 *  Z-Wave Garage Door Opener Modified for GD00Z by @Garyd, Copied and modifed by @Ron
 *
 *  Copyright 2014 SmartThings
 *
 *  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.
 *
 *
 *
 *
 * Sample Battery Cmd Message ?
 * cmd=NotificationReport(event: 74, eventParameter: [1], eventParametersLength: 1, notificationStatus: 255, 
 *                        notificationType: 6, reserved61: 0, sequence: false, v1AlarmLevel: 0,
 *                        v1AlarmType: 0, zensorNetSourceNodeId: 0)
 */
 

preferences {
    input name: "enableSwitch", type: "enum", title: "Enable Switch Capability?", options: ["Disabled","On=Open","On=Close"], description: "Enable Switch Capability?", required: false
}


metadata {
    definition(name: "RG Linear GD00Z Garage Door Opener - V2", namespace: "gouldner", author: "Ronald Gouldner") {
        capability "Actuator"
        capability "Door Control"
        capability "Contact Sensor"
        capability "Refresh"
        capability "Sensor"
        capability "Polling"
        capability "Switch"
        capability "Momentary"
        capability "Relay Switch"
        capability "Garage Door Control"
        capability "Battery"
        
        attribute "lastBatteryStatus", "STRING"
        //attribute "batteryStatus", "STRING"
        command "batteryReset"
        
        fingerprint deviceId:"0x4007", inClusters:"0x72 0x98 0x5A"
    }

   
}
import hubitat.zwave.commands.barrieroperatorv1.*

def parse(String description) {
    def result = null
    if (description.startsWith("Err")) {
        if (state.sec) {
            result = createEvent(descriptionText: description, displayed: false)
        } else {
            result = createEvent(
                    descriptionText: "This device failed to complete the network security key exchange. If you are unable to control it via SmartThings, you must remove it from your network and add it again.",
                    eventType: "ALERT",
                    name: "secureInclusion",
                    value: "failed",
                    displayed: true,
            )
        }
    } else {
        def cmd = zwave.parse(description, [0x98: 1, 0x72: 2])
        if (cmd) {
            result = zwaveEvent(cmd)
        }
    }
    log.debug "\"$description\" parsed to ${result.inspect()}"
    result
}

def zwaveEvent(hubitat.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
    def encapsulatedCommand = cmd.encapsulatedCommand([0x71: 3, 0x80: 1, 0x85: 2, 0x63: 1, 0x98: 1])
    log.debug "encapsulated: $encapsulatedCommand"
    if (encapsulatedCommand) {
        zwaveEvent(encapsulatedCommand)
    }
}

def zwaveEvent(hubitat.zwave.commands.securityv1.NetworkKeyVerify cmd) {
    createEvent(name: "secureInclusion", value: "success", descriptionText: "Secure inclusion was successful")
}

def zwaveEvent(hubitat.zwave.commands.securityv1.SecurityCommandsSupportedReport cmd) {
    state.sec = cmd.commandClassSupport.collect { String.format("%02X ", it) }.join()
    if (cmd.commandClassControl) {
        state.secCon = cmd.commandClassControl.collect { String.format("%02X ", it) }.join()
    }
    log.debug "Security command classes: $state.sec"
    createEvent(name: "secureInclusion", value: "success", descriptionText: "$device.displayText is securely included")
}

def zwaveEvent(BarrierOperatorReport cmd) {
    def result = []
    def map = [name: "door"]
    def switchMap = [name: "switch"]

    switch (cmd.barrierState) {
        case BarrierOperatorReport.BARRIER_STATE_CLOSED:
            map.value = "closed"
            result << createEvent(name: "contact", value: "closed", displayed: false)
            result << createEvent(name: "switch", value: "off", displayed: false)
            break
        case BarrierOperatorReport.BARRIER_STATE_UNKNOWN_POSITION_MOVING_TO_CLOSE:
            map.value = "closing"
            break
        case BarrierOperatorReport.BARRIER_STATE_UNKNOWN_POSITION_STOPPED:
            map.descriptionText = "$device.displayName door state is unknown"
            map.value = "unknown"
            break
        case BarrierOperatorReport.BARRIER_STATE_UNKNOWN_POSITION_MOVING_TO_OPEN:
            map.value = "opening"
            break
        case BarrierOperatorReport.BARRIER_STATE_OPEN:
            map.value = "open"
            result << createEvent(name: "contact", value: "open", displayed: false)
            result << createEvent(name: "switch", value: "on", displayed: false)
            break
    }
    result + createEvent(map)
}

def zwaveEvent(hubitat.zwave.commands.notificationv3.NotificationReport cmd) {
    log.debug "Aquiring Notification Report cmd=$cmd"
    def result = []
    def map = [:]
    if (cmd.notificationType == 6) {
        map.displayed = true
        switch (cmd.event) {
            case 0x40:
                if (cmd.eventParameter[0]) {
                    map.descriptionText = "$device.displayName performing initialization process"
                } else {
                    map.descriptionText = "$device.displayName initialization process complete"
                }
                break
            case 0x41:
                map.descriptionText = "$device.displayName door operation force has been exceeded"
                break
            case 0x42:
                map.descriptionText = "$device.displayName motor has exceeded operational time limit"
                break
            case 0x43:
                map.descriptionText = "$device.displayName has exceeded physical mechanical limits"
                break
            case 0x44:
                map.descriptionText = "$device.displayName unable to perform requested operation (UL requirement)"
                break
            case 0x45:
                map.descriptionText = "$device.displayName remote operation disabled (UL requirement)"
                break
            case 0x46:
                map.descriptionText = "$device.displayName failed to perform operation due to device malfunction"
                break
            case 0x47:
                if (cmd.eventParameter[0]) {
                    map.descriptionText = "$device.displayName vacation mode enabled"
                } else {
                    map.descriptionText = "$device.displayName vacation mode disabled"
                }
                break
            case 0x48:
                if (cmd.eventParameter[0]) {
                    map.descriptionText = "$device.displayName safety beam obstructed"
                } else {
                    map.descriptionText = "$device.displayName safety beam obstruction cleared"
                }
                break
            case 0x49:
                if (cmd.eventParameter[0]) {
                    map.descriptionText = "$device.displayName door sensor ${cmd.eventParameter[0]} not detected"
                } else {
                    map.descriptionText = "$device.displayName door sensor not detected"
                }
                break
            case 0x4A:
                if (cmd.eventParameter[0]) {
                    map.descriptionText = "$device.displayName door sensor ${cmd.eventParameter[0]} has a low battery"
                } else {
                    map.descriptionText = "$device.displayName door sensor has a low battery"
                }
                result << createEvent(name: "battery", value: 1, descriptionText: map.descriptionText)
                def now=new Date()
                def tz = location.timeZone
                def nowString = "Low:" + now.format("MMM/dd HH:mm",tz)
                result << createEvent(name:"lastBatteryStatus", value:nowString, descriptionText: map.descriptionText)
                break
            case 0x4B:
                map.descriptionText = "$device.displayName detected a short in wall station wires"
                break
            case 0x4C:
                map.descriptionText = "$device.displayName is associated with non-Z-Wave remote control"
                break
            default:
                map.descriptionText = "$device.displayName: access control alarm $cmd.event"
                map.displayed = false
                break
        }
    } else if (cmd.notificationType == 7) {
        switch (cmd.event) {
            case 1:
            case 2:
                map.descriptionText = "$device.displayName detected intrusion"
                break
            case 3:
                map.descriptionText = "$device.displayName tampering detected: product cover removed"
                break
            case 4:
                map.descriptionText = "$device.displayName tampering detected: incorrect code"
                break
            case 7:
            case 8:
                map.descriptionText = "$device.displayName detected motion"
                break
            default:
                map.descriptionText = "$device.displayName: security alarm $cmd.event"
                map.displayed = false
        }
    } else if (cmd.notificationType) {
        map.descriptionText = "$device.displayName: alarm type $cmd.notificationType event $cmd.event"
    } else {
        map.descriptionText = "$device.displayName: alarm $cmd.v1AlarmType is ${cmd.v1AlarmLevel == 255 ? 'active' : cmd.v1AlarmLevel ?: 'inactive'}"
    }
    result ? [createEvent(map), *result] : createEvent(map)
}

// RRG 1/8/2016
// This never gets called, I am pretty sure Linear doesn't support betteryGet
def zwaveEvent(hubitat.zwave.commands.batteryv1.BatteryReport cmd) {
    log.debug "Battery Reporting cmd=$cmd"
    def map = [name: "battery", unit: "%"]
    if (cmd.batteryLevel == 0xFF) {
        map.value = 1
        log.debug "Battery Level=low=1"
        map.descriptionText = "$device.displayName has a low battery"
    } else {
        log.debug "Battery Level=cmd.batteryLevel"
        map.value = cmd.batteryLevel
    }
    state.lastbatt = new Date().time
    createEvent(map)
}

def zwaveEvent(hubitat.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) {
    def result = []

    def msr = String.format("%04X-%04X-%04X", cmd.manufacturerId, cmd.productTypeId, cmd.productId)
    log.debug "msr: $msr"
    updateDataValue("MSR", msr)

    result << createEvent(descriptionText: "$device.displayName MSR: $msr", isStateChange: false)
    result
}

def zwaveEvent(hubitat.zwave.commands.versionv1.VersionReport cmd) {
    log.debug "cmd:$cmd"
    def fw = "${cmd.applicationVersion}.${cmd.applicationSubVersion}"
    updateDataValue("fw", fw)
    def text = "$device.displayName: firmware version: $fw, Z-Wave version: ${cmd.zWaveProtocolVersion}.${cmd.zWaveProtocolSubVersion}"
    createEvent(descriptionText: text, isStateChange: false)
}

def zwaveEvent(hubitat.zwave.commands.applicationstatusv1.ApplicationBusy cmd) {
    def msg = cmd.status == 0 ? "try again later" :
            cmd.status == 1 ? "try again in $cmd.waitTime seconds" :
                    cmd.status == 2 ? "request queued" : "sorry"
    createEvent(displayed: true, descriptionText: "$device.displayName is busy, $msg")
}

def zwaveEvent(hubitat.zwave.commands.powerlevelv1.PowerlevelReport cmd) {
    log.debug "Power Level Report cmd=$cmd"
    createEvent(displayed: true, descriptionText: "$device.displayName rejected the last request")
}

def zwaveEvent(hubitat.zwave.commands.applicationstatusv1.ApplicationRejectedRequest cmd) {
    createEvent(displayed: true, descriptionText: "$device.displayName rejected the last request")
}

def zwaveEvent(hubitat.zwave.commands.applicationcapabilityv1.CommandCommandClassNotSupported cmd) {
    log.debug "Command Class Not Supported cmd:$cmd"
}

def zwaveEvent(hubitat.zwave.Command cmd) {
    createEvent(displayed: false, descriptionText: "$device.displayName: $cmd")
}

def open() {
    secure(zwave.barrierOperatorV1.barrierOperatorSet(requestedBarrierState: BarrierOperatorSet.REQUESTED_BARRIER_STATE_OPEN))
}

def close() {
    secure(zwave.barrierOperatorV1.barrierOperatorSet(requestedBarrierState: BarrierOperatorSet.REQUESTED_BARRIER_STATE_CLOSE))
}

def on() {
    log.debug "on() was called treat this like Open"
    open()
}

def off() {
    log.debug "off() was called treat like Close"
    close()
}

def refresh() {
    //secure(zwave.barrierOperatorV1.barrierOperatorGet())
    /* BatteryGet and NotificationGet not working */
    log.debug "Issuing Refresh (barrier state, and version report to log)"
    log.debug "Preferences are set as follows"
    log.debug "enableSwitch:$enableSwitch"
    secureSequence([
                zwave.barrierOperatorV1.barrierOperatorGet()
                ,zwave.versionV1.versionGet()
                //,zwave.batteryV1.batteryGet()
                //,zwave.powerlevelV1.powerlevelGet()
                ,zwave.notificationV3.notificationGet()
                //,zwave.notificationV3.notificationSupportedGet()
        ], 4200)
    /* */
}

def poll() {
    secure(zwave.barrierOperatorV1.barrierOperatorGet())
}

def batteryReset() {
    log.debug "Battery Reset"
    def now=new Date()
    def tz = location.timeZone
    def nowString = "RESET:" + now.format("MMM/dd HH:mm",tz)
    sendEvent("name": "battery", "value":100, "descriptionText":"Battery Reset to OK")
    sendEvent("name":"lastBatteryStatus", "value":nowString)
}

def push() {

    // get the current "door" attribute value
    //
    // For some reason, I can't use "device.doorState" or just "doorState".  Not sure why not.

    def lastValue = device.latestValue("door");

    // if its open, then close the door
    if (lastValue == "open") {
        return close()

        // if its closed, then open the door
    } else if (lastValue == "closed") {
        return open()

    } else {
        log.debug "push() called when door state is $lastValue - there's nothing push() can do"
    }
}

private secure(hubitat.zwave.Command cmd) {
    zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format()
}

private secureSequence(commands, delay = 200) {
    delayBetween(commands.collect { secure(it) }, delay)
}

> Blockquote

Apparently it is all about device name in Google. Renamed "Front door" to "Door" and garage door to "Gate" both work now.

1 Like

And you are asking google home "Open the garage door" and it wasn't working before? It will never show up correctly in the Google Home app...that's a Google limitation currently for locks/doors. But it should allow you to use voice commands.

I cannot second this strongly enough. Now that temps are starting to rise, we might have our windows open. I read articles last year about people walking through neighborhoods yelling into windows "Alexa, open the garage door" or "Hey Google, unlock the front door" and seeing if they can get a response. Sometimes, just to screw with people. Now, these stories might just be urban legend, but that doesn't mean you shouldn't protect yourself. So, PLEASE, add a pin to barrier devices like locks and garage doors.

In fact, I would even support a pin being REQUIRED in the app for those two specific device types.

2 Likes

Voice command "Open the garage door is not working"... I would of set a PIN...

What is the response from Google Home when you do that? You have synced your device to Google Home and added it to a room when you tried that? Can you show the logs?

Google was saying, opening ... or closing... But actually was not doing anything. I renamed Garage Door, to "Gate" and it works as desired now. I guess there is nothing to be done.

Interesting. Do you have another device named "Front Door"? Alternatively, was your "Front Door" device previously using the official Google Home integration? I've seen "phantom" devices persist when switching between the two before.

1 Like

"Official" Google integration does not allow to add device with Lock and Garage Control capabilities if I am not mistaken.

You can add them as a switch if the driver supports it. That's been the workaround for a lot of folks.

Yes. That is what I do too. Just was hoping that with new integration no work around will be needed :slight_smile:

No...you don't. LMAO. You just showed us your setup....and you don't use the switch capability anymore. You use the commands Open/Close, remember?
image

Small Update: Run scene activation/deactivation asynchronously

Since scene activation often requires controlling lots of devices, it sometimes takes long enough that the command from Google Home will time out. That was causing the assistant to report an error even though the command was ultimately successful. I changed scene activation and deactivation so that it immediately reports success while running the appropriate device command asynchronously.

2 Likes

New Trait: Rotation

I just added support for the Rotation trait. This trait is useful for things that can be rotated to a specific position like slat blinds or standing fans that can rotate to point different directions.

I've only implemented percentage-based rotation and not degree-based rotation. There are no Hubitat device capabilities that accept a rotation in degrees. If anyone has a specific device that could use degree-based rotation let me know.

Hi,

so glad I found this project - really great work, tank you so much @mbudnek

One question. Should this work?

Both traits are set to query only. Google Assistant correctly reports the temperature, but not the humidity.

From the debug log:

app:12182020-04-09 12:43:57.409 debug[requestId:17577960441915319851, payload:[devices:[1290:[humidityAmbientPercent:41, thermostatTemperatureAmbient:22.48, thermostatMode:on, thermostatTemperatureSetpoint:22.48]]]]

Thanks,
Jan

Yeah, that should work. I have a few sensors set up exactly like that.

The Google Assistant is a bit picky about how you word requests for humidity though. "Hey Google, what's the humidity of {device}?" won't work, but "Hey Google, what's the humidity reading of {device}?" or "Hey Google, what's the humidity on {device}?" will. You can also say "Hey Google, what's the humidity in {room}?" and it will report the humidity reading of all humidity sensors in that room.

1 Like

Thanks, got the reading by asking for the room humidity like you suggested. GA is really picky about a lot of things :wink:

Hi,

Do you plan to add the Television device type? I guess it would be relatively simple to support at least the On/Off trait.

I tried to add it myself for my LG WebOS TV, but for some reason, the device wouldn't show up in the device selector.

Best,
Jan

Update: Added new device types

Google has added several new device types since I created my initial list:

  • Carbon Monoxide Sensor
  • Charger
  • Remote Control
  • Set-Top Box
  • Smoke Detector
  • Television
  • Water Purifier
  • Water Softener

Those are now all selectable as the Google Device Type when defining a device type.

4 Likes