[RELEASE] Home Assistant Device Bridge (HADB)

If there's an entity for that status, then you should be able to do it. Another way might be to use the Hubitat Integration for Home Assistant available in HACS. You then create a Virtual Switch or Virtual Lock on HE and share that back to HA. Then create an HA automation that closes the switch when the lock is locked, or locks the Virtual Lock when the actual lock is locked.

If you're an Alexa user, you could also create an Alexa routine to toggle a virtual switch or a virtual lock on Hubitat when the August Lock state changes, but that's all cloud so it can be unreliable sometimes.

1 Like

Thank you so much, those make sense, I'll give it a shot.

1 Like

I was able to modify the code, I'll post it in a couple hours. I'm not a coder, more of a follower of examples, so it should be reviewed by someone else. The DoorSense status comes through too.

2 Likes

I have an Ikea Tradfri On/Off Button and a Shortcut Button attached to Home Assistant via zigbee2mqtt.

Should I be seeing them in Hubitat?
I've selected them in the app, but they aren't coming through.
I've been tinkering with things so much I've likely messed something up.

Not via this integration. Devices must have entities for this to work, and buttons in Home Assistant do not create entities.

To add locks to the integration:

In the "Home Assistant Device Bridge" app, line 84 should be:
if(["fan", "switch", "light", "binary_sensor", "sensor", "device_tracker", "cover","lock"].contains(domain))

For the "HomeAssistant Hub Parent" driver

/*
* Home Assistant to Hubitat Integration
*
* Description:
* Allow control of HA devices.
*
* Required Information:
* Home Asisstant IP and Port number
* Home Assistant long term Access Token
*
* Features List:
*
* Licensing:
* Copyright 2021 Yves Mercier.
* 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.
*
* Version Control:
* 0.1.0  2021-02-05 Yves Mercier       Orinal version
* 0.1.1  2021-02-06 Dan Ogorchock      Added basic support for simple "Light" devices from Home Assistant using Hubitat Generic Component Dimmer driver
* 0.1.2  2021-02-06 tomw               Added handling for some binary_sensor subtypes based on device_class
* 0.1.3  2021-02-06 Dan Ogorchock      Bug Fixes 
* 0.1.4  2021-02-06 Yves Mercier       Added version number and import URL
* 0.1.5  2021-02-06 Dan Ogorchock      Added support for Temperature and Humidity Sensors
* 0.1.6  2021-02-06 Dan Ogorchock      Corrected open/closed for HA door events
* 0.1.7  2021-02-07 Dan Ogorchock      Corrected open/closed for HA window, garage_door, and opening per @smarthomeprimer
* 0.1.8  2021-02-07 Dan Ogorchock      Removed temperature and humidity workaround for missing device_class on some HA sensors.  
*                                      This can be corrected on the HA side via the Customize entity feature to add the missing device_class.
* 0.1.9  2021-02-07 tomw               More generic handling for "sensor" device_classes.  Added voltage device_class to "sensor".
* 0.1.10 2021-02-07 Dan Ogorchock      Refactored the translation from HA to HE to simplify the overall design
* 0.1.11 2021-02-07 Dan Ogorchock      Completed refactoring of Dimmer Switch support
* 0.1.12 2021-02-08 Dan Ogorchock      Fixed typo in log.info statement
* 0.1.13 2021-02-08 tomw               Added "community" namespace support for component drivers.  Added Pressure and Illuminance.
* 0.1.14 2021-02-10 Dan Ogorchock      Added support for Fan devices (used Lutron Fan Controller as test device.)
* 0.1.15 2021-02-13 tomw               Adjust websocket status handling to reconnect on both close and error conditions.
* 0.1.16 2021-02-14 tomw               Revert 0.1.15
* 0.1.17 2021-02-14 Dan Ogorchock      Improved webSocket reconnect logic
* 0.1.18 2021-02-14 tomw               Avoid reconnect loop on initialize
* 0.1.19 2021-02-16 Yves Mercier       Added Refresh handler
* 0.1.20 2021-02-16 Yves mercier       Refactored webSocketStatus
* 0.1.21 2021-02-22 Yves mercier       Reinstated CloseConnection command. Added connection status on device page.
* 0.1.22 2021-02-24 tomw               Changes to support optional device filtering.  For use with haDeviceBridgeConfiguration.groovy.
* 0.1.23 2021-02-25 Dan Ogorchock      Switched from Exclude List to Include List
* 0.1.24 2021-03-07 Yves Mercier       Added device label in event description
* 0.1.25 2021-03-18 Dan Ogorchock      Updated for recent Hass Fan handling changes (use percentages to set speeds instead of deprecated speed names)
* 0.1.26 2021-04-02 DongHwan Suh       Added partial support for Color temperature, RGB, RGBW lights
*                                      (Manually updating the device type to the corresponding one is required in Hubitat. Only statuses of level and switch are shown in Hubitat.)
* 0.1.27 2021-04-11 Yves Mercier       Added option for secure connection
* 0.1.28 2021-04-14 Dan Ogorchock      Improved Fan Device handling
* 0.1.29 2021-04-17 Dan Ogorchock      Added support for Smoke Detector Binary Sensor
* 0.1.30 2021-08-10 tomw               Added support for device_tracker as Presence Sensor
* 0.1.31 2021-09-23 tomw               Added support for Power sensor
* 0.1.33 2021-09-28 tomw               Added support for cover as Garage Door Opener
*
* Thank you(s):
*/

import groovy.json.JsonSlurper
import groovy.json.JsonOutput

metadata {
definition (name: "HomeAssistant Hub Parent", namespace: "ymerj", author: "Yves Mercier", importUrl: "https://raw.githubusercontent.com/ymerj/HE-HA-control/main/HA%20parent.groovy") {
    capability "Initialize"

//        command "createChild", [[ name: "entity", type: "STRING", description: "HomeAssistant Entity ID" ]]
//        command "removeChild", [[ name: "entity", type: "STRING", description: "HomeAssistant Entity ID" ]]
    command "closeConnection"        
//        command "deleteAllChildDevices"
    
    attribute "Connection", "string"
}

preferences {
    input ("ip", "text", title: "IP", description: "HomeAssistant IP Address", required: true)
    input ("port", "text", title: "Port", description: "HomeAssistant Port Number", required: true, defaultValue: "8123")
    input ("token", "text", title: "Token", description: "HomeAssistant Long-Lived Access Token", required: true)
    input ("secure", "bool", title: "Require secure connection (https)", defaultValue: false)
    input ("logEnable", "bool", title: "Enable debug logging", defaultValue: true)
    input ("txtEnable", "bool", title: "Enable description text logging", defaultValue: true)        
}
}

def logsOff(){
log.warn("debug logging disabled...")
device.updateSetting("logEnable",[value:"false",type:"bool"])
}

def updated(){
log.info("updated...")
log.warn("debug logging is: ${logEnable == true}")
log.warn("description logging is: ${txtEnable == true}")
unschedule()
if (logEnable) runIn(1800,logsOff)
initialize()
}

def initialize() {
log.info("initializing...")

closeConnection()

state.id = 2
def connectionType = "ws"
if (secure) connectionType = "wss"
auth = '{"type":"auth","access_token":"' + "${token}" + '"}'
evenements = '{"id":1,"type":"subscribe_events","event_type":"state_changed"}'
try {
    interfaces.webSocket.connect("${connectionType}://${ip}:${port}/api/websocket", ignoreSSLIssues: true)
    interfaces.webSocket.sendMessage("${auth}")
    interfaces.webSocket.sendMessage("${evenements}")
} 
catch(e) {
    log.error("initialize error: ${e.message}")
}
}

def uninstalled() {
log.info("uninstalled...")
closeConnection()
unschedule()
deleteAllChildDevices()
}

def webSocketStatus(String status){
if (logEnable) log.debug "webSocket ${status}"

if ((status == "status: closing") && (state.wasExpectedClose)) {
    state.wasExpectedClose = false
    sendEvent(name: "Connection", value: "Closed")
    return
} 
else if(status == 'status: open') {
    log.info "websocket is open"
    // success! reset reconnect delay
    pauseExecution(1000)
    state.reconnectDelay = 1
    state.wasExpectedClose = false
    sendEvent(name: "Connection", value: "Open")
} 
else {
    log.warn "WebSocket error, reconnecting."
    sendEvent(name: "Connection", value: "Reconnecting")
    reconnectWebSocket()
}
}

def reconnectWebSocket() {
// first delay is 2 seconds, doubles every time
state.reconnectDelay = (state.reconnectDelay ?: 1) * 2
// don't let delay get too crazy, max it out at 10 minutes
if(state.reconnectDelay > 600) state.reconnectDelay = 600

//If the Home Assistant Hub is offline, give it some time before trying to reconnect
runIn(state.reconnectDelay, initialize)
}


def parse(String description) {
if (logEnable) log.debug("parse(): description = ${description}")
def response = null;
try{
    response = new groovy.json.JsonSlurper().parseText(description)
    if (response.type != "event") return
    
    def newVals = []
    def entity = response?.event?.data?.entity_id
    
    // check whether we have a parent, and search its includeList for devices to process
    def includeList = getParent()?.includeList
    if(includeList && !includeList?.contains(entity)) return
    
    def domain = entity?.tokenize(".")?.getAt(0)
    def device_class = response?.event?.data?.new_state?.attributes?.device_class
    def friendly = response?.event?.data?.new_state?.attributes?.friendly_name
    newVals << response?.event?.data?.new_state?.state
    def mapping = null
    
    if (logEnable) log.debug "parse: domain: ${domain}, device_class: ${device_class}, entity: ${entity}, newVals: ${newVals}, friendly: ${friendly}"
    
    switch (domain) {
        case "fan":
            def speed = response?.event?.data?.new_state?.attributes?.speed
            def percentage = response?.event?.data?.new_state?.attributes?.percentage
            switch (percentage.toInteger()) {
                case 0: 
                    speed = "off"
                    break
                case 25: 
                    speed = "low"
                    break
                case 50: 
                    speed = "medium"
                    break
                case 75: 
                    speed = "medium-high"
                    break
                case 100: 
                    speed = "high"
                default:
                    if (logEnable) log.info "Invalid fan percentage received - ${percentage}"
            }
            newVals += speed
            newVals += percentage
            mapping = translateDevices(domain, newVals, friendly)
            if (mapping) updateChildDevice(mapping, entity, friendly)
            break
        case "cover":
            if(!(["garage"].contains(device_class)))
            {
                // only support "garage" device_class for "cover" domain
                return
            }
        case "lock":
        case "device_tracker":
        case "switch":            
            mapping = translateDevices(domain, newVals, friendly)
            if (mapping) updateChildDevice(mapping, entity, friendly)
            break
        case "light":
            def level = response?.event?.data?.new_state?.attributes?.brightness
            if (level) level = Math.round((level.toInteger() * 100 / 255))
            newVals += level
            mapping = translateDevices(domain, newVals, friendly)
            if (!level) mapping.event.remove(1) //remove the level update since it is not provided with the HA 'off' event json data
            if (mapping) updateChildDevice(mapping, entity, friendly)
            break
        case "binary_sensor":
        case "sensor":
            mapping = translateDevices(device_class, newVals, friendly)
            if (mapping) updateChildDevice(mapping, entity, friendly)                
            break
        default:
            if (logEnable) log.info "No mapping exists for domain: ${domain}, device_class: ${device_class}.  Please contact devs to have this added."
    }
    return
}  
catch(e) {
    log.error("Parsing error: ${e}")
    return
}
}

def translateDevices(device_class, newVals, friendly)
{
def mapping =
    [
        door: [type: "Generic Component Contact Sensor",            event: [[name: "contact", value: newVals[0] == "on" ? "open":"closed", descriptionText:"${friendly} is updated"]]],
        fan: [type: "Generic Component Fan Control",                event: [[name: "switch", value: newVals[0], descriptionText:"${friendly} was turn ${newVals[0]}"],[name: "speed", value: newVals[1], descriptionText:"${friendly} speed was set to ${newVals[1]}"],[name: "level", value: newVals[2], descriptionText:"${friendly} level was set to ${newVals[2]}"]]],
        garage_door: [type: "Generic Component Contact Sensor",     event: [[name: "contact", value: newVals[0] == "on" ? "open":"closed", descriptionText:"${friendly} is updated"]]],
        humidity: [type: "Generic Component Humidity Sensor",       event: [[name: "humidity", value: newVals[0], descriptionText:"${friendly} humidity is ${newVals[0]}"]]],
        illuminance: [type: "Generic Component Illuminance Sensor", event: [[name: "illuminance", value: newVals[0], descriptionText:"${friendly} illuminance is ${newVals[0]}"]], namespace: "community"],
        light: [type: "Generic Component Dimmer",                   event: [[name: "switch", value: newVals[0], descriptionText:"${friendly} was turn ${newVals[0]}"],[name: "level", value: newVals[1], descriptionText:"${friendly} level was set to ${newVals[1]}"]]],
        moisture: [type: "Generic Component Water Sensor",          event: [[name: "water", value: newVals[0] == "on" ? "wet":"dry", descriptionText:"${friendly} is updated"]]],
        motion: [type: "Generic Component Motion Sensor",           event: [[name: "motion", value: newVals[0] == "on" ? """active""":"""inactive""", descriptionText:"${friendly} is updated"]]],
        opening: [type: "Generic Component Contact Sensor",         event: [[name: "contact", value: newVals[0] == "on" ? "open":"closed", descriptionText:"${friendly} is updated"]]],
        power: [type: "Generic Component Power Meter",              event: [[name: "power", value: newVals[0], descriptionText:"${friendly} power is ${newVals[0]}"]]],
        presence: [type: "Generic Component Presence Sensor",       event: [[name: "presence", value: newVals[0] == "on" ? "present":"not present", descriptionText:"${friendly} is updated"]], namespace: "community"],
        pressure: [type: "Generic Component Pressure Sensor",       event: [[name: "pressure", value: newVals[0], descriptionText:"${friendly} pressure is ${newVals[0]}"]], namespace: "community"],
        smoke: [type: "Generic Component Smoke Detector",           event: [[name: "smoke", value: newVals[0] == "on" ? "detected":"clear", descriptionText:"${friendly} is updated"]]],
        switch: [type: "Generic Component Switch",                  event: [[name: "switch", value: newVals[0], descriptionText:"${friendly} was turn ${newVals[0]}"]]],
        temperature: [type: "Generic Component Temperature Sensor", event: [[name: "temperature", value: newVals[0], descriptionText:"${friendly} temperature is ${newVals[0]}"]]],
        voltage: [type: "Generic Component Voltage Sensor",         event: [[name: "voltage", value: newVals[0], descriptionText:"${friendly} voltage is ${newVals[0]}"]]],
        window: [type: "Generic Component Contact Sensor",          event: [[name: "contact", value: newVals[0] == "on" ? "open":"closed", descriptionText:"${friendly} is updated"]]],
        device_tracker: [type: "Generic Component Presence Sensor", event: [[name: "presence", value: newVals[0] == "home" ? "present":"not present", descriptionText:"${friendly} is updated"]], namespace: "community"],
        cover: [type: "Generic Component Garage Door Control",      event: [[name: "door", value: newVals[0] ?: "unknown", descriptionText:"${friendly} was turn ${newVals[0]}"]], namespace: "community"],
        lock: [type: "Generic Component Lock",                      event: [[name: "lock", value: newVals[0] ?: "unknown", descriptionText:"${friendly} was turn ${newVals[0]}"]]]

    ]

return mapping[device_class]
}

def updateChildDevice(mapping, entity, friendly) {
def ch = createChild(mapping.type, entity, friendly, mapping.namespace)
if (!ch) {
    log.warn "Child type: ${mapping.type} not created for entity: ${entity}"
    return
}
else {
    ch.parse(mapping.event)
}
}

def createChild(deviceType, entity, friendly, namespace = null)
{
def ch = getChildDevice("${device.id}-${entity}")
if (!ch) ch = addChildDevice(namespace ?: "hubitat", deviceType, "${device.id}-${entity}", [name: "${entity}", label: "${friendly}", isComponent: false])
return ch
}

def removeChild(entity){
String thisId = device.id
def ch = getChildDevice("${thisId}-${entity}")
if (ch) {deleteChildDevice("${thisId}-${entity}")}
}

def componentOn(ch){
if (logEnable) log.info("received on request from ${ch.label}")
state.id = state.id + 1
entity = ch.name
domain = entity.tokenize(".")[0]
if (!ch.currentValue("level")) {
    messOn = JsonOutput.toJson([id: state.id, type: "call_service", domain: "${domain}", service: "turn_on", service_data: [entity_id: "${entity}"]])
}
else {
    messOn = JsonOutput.toJson([id: state.id, type: "call_service", domain: "${domain}", service: "turn_on", service_data: [entity_id: "${entity}", brightness_pct: "${ch.currentValue("level")}"]])        
}
if (logEnable) log.debug("messOn = ${messOn}")
interfaces.webSocket.sendMessage("${messOn}")
}

def componentOff(ch){
if (logEnable) log.info("received off request from ${ch.label}")
state.id = state.id + 1
entity = ch.name
domain = entity.tokenize(".")[0]
messOff = JsonOutput.toJson([id: state.id, type: "call_service", domain: "${domain}", service: "turn_off", service_data: [entity_id: "${entity}"]])
if (logEnable) log.debug("messOff = ${messOff}")
interfaces.webSocket.sendMessage("${messOff}")
}

def componentSetLevel(ch, level, transition=1){
if (logEnable) log.info("received setLevel request from ${ch.label}")
if (level > 100) level = 100
if (level < 0) level = 0

//if a Fan device, special handling
if (ch.currentValue("speed")) {
    switch (level.toInteger()) {
        case 0:
            componentSetSpeed(ch, "off")
        break
        case 1..25:
            componentSetSpeed(ch, "low")
        break
        case 26..50:
            componentSetSpeed(ch, "medium")
        break
        case 51..75:
            componentSetSpeed(ch, "medium-high")
        break
        case 76..100:
            componentSetSpeed(ch, "high")
        break
        default:
            if (logEnable) log.info "No case defined for Fan setLevel(${level})"
    }
} 
else {        
    state.id = state.id + 1
    entity = ch.name
    domain = entity.tokenize(".")[0]
    messLevel = JsonOutput.toJson([id: state.id, type: "call_service", domain: "${domain}", service: "turn_on", service_data: [entity_id: "${entity}", brightness_pct: "${level}", transition: "${transition}"]])
    if (logEnable) log.debug("messLevel = ${messLevel}")
    interfaces.webSocket.sendMessage("${messLevel}")
}
}

def componentSetColor(ch, color, transition=1){
if (logEnable) log.info("received setColor request from ${ch.label}")

state.id = state.id + 1
entity = ch.name
domain = entity.tokenize(".")[0]
convertedHue = Math.round(color.hue * 360/100)
messHSL = JsonOutput.toJson([id: state.id, type: "call_service", domain: "${domain}", service: "turn_on", service_data: [entity_id: "${entity}", brightness_pct: "${color.level}", hs_color: ["${convertedHue}", "${color.saturation}"], transition: "${transition}"]])
if (logEnable) log.debug("messHSL = ${messHSL}")
interfaces.webSocket.sendMessage("${messHSL}")
}

def componentSetColorTemperature(ch, colortemperature, transition=1){
if (logEnable) log.info("received setColorTemperature request from ${ch.label}")

state.id = state.id + 1
entity = ch.name
domain = entity.tokenize(".")[0]
messCT = JsonOutput.toJson([id: state.id, type: "call_service", domain: "${domain}", service: "turn_on", service_data: [entity_id: "${entity}", kelvin: "${colortemperature}", transition: "${transition}"]])
if (logEnable) log.debug("messCT = ${messCT}")
interfaces.webSocket.sendMessage("${messCT}")
}

def componentSetSpeed(ch, speed) {
if (logEnable) log.info("received setSpeed request from ${ch.label}, with speed = ${speed}")
int percentage = 0
entity = ch.name
domain = entity.tokenize(".")[0]
switch (speed) {
    case "on":
        state.id = state.id + 1
        messOn = JsonOutput.toJson([id: state.id, type: "call_service", domain: "${domain}", service: "turn_on", service_data: [entity_id: "${entity}"]])
        interfaces.webSocket.sendMessage("${messOn}")
        break
    case "off":
        state.id = state.id + 1    
        messOff = JsonOutput.toJson([id: state.id, type: "call_service", domain: "${domain}", service: "turn_off", service_data: [entity_id: "${entity}"]])
        interfaces.webSocket.sendMessage("${messOff}")
        break
    case "low":
    case "medium-low":
        percentage = 25
        break
    case "auto":
    case "medium":
        percentage = 50
        break
    case "medium-high":
        percentage = 75
        break
    case "high":
        percentage = 100
        break
    default:
        if (logEnable) log.info "No case defined for Fan setSpeed(${speed})"
}

if (percentage != 0) {
    state.id = state.id + 1
    messOn = JsonOutput.toJson([id: state.id, type: "call_service", domain: "${domain}", service: "turn_on", service_data: [entity_id: "${entity}", percentage: "${percentage}"]])
    interfaces.webSocket.sendMessage("${messOn}")
}
}

def componentCycleSpeed(ch) {
def newSpeed = ""
switch (ch.currentValue("speed")) {
    case "off":
        speed = "low"
        break
    case "low":
    case "medium-low":
        speed = "medium"
        break
    case "medium":
        speed = "medium-high"
        break
    case "medium-high":
        speed = "high"
        break
    case "high":
        speed = "off"
        break
}
componentSetSpeed(ch, speed)
}

void componentClose(ch) {
operateCover(ch, "close")
}

void componentOpen(ch) {
operateCover(ch, "open")
}

void operateCover(ch, op)
{
if (logEnable) log.info("received ${op} request from ${ch.label}")
state.id = state.id + 1
entity = ch.name
domain = entity.tokenize(".")[0]
messOff = JsonOutput.toJson([id: state.id, type: "call_service", domain: "${domain}", service: (op == "open") ? "open_cover" : "close_cover", service_data: [entity_id: "${entity}"]])
if (logEnable) log.debug("messOff = ${messOff}")
interfaces.webSocket.sendMessage("${messOff}")
}

void componentLock(ch) {
operateLock(ch, "lock")
}

void componentUnlock(ch) {
operateLock(ch, "unlock")
}

void operateLock(ch, op)
{
if (logEnable) log.info("received ${op} request from ${ch.label}")
state.id = state.id + 1
entity = ch.name
domain = entity.tokenize(".")[0]
messOff = JsonOutput.toJson([id: state.id, type: "call_service", domain: "${domain}", service: (op == "unlock") ? "unlock" : "lock", service_data: [entity_id: "${entity}"]])
if (logEnable) log.debug("messOff = ${messOff}")
interfaces.webSocket.sendMessage("${messOff}")
}

def componentRefresh(ch){
if (logEnable) log.info("received refresh request from ${ch.label}")
state.id = state.id + 1
entity = ch.name
domain = entity.tokenize(".")[0]
messUpd = JsonOutput.toJson([id: state.id, type: "call_service", domain: "homeassistant", service: "update_entity", service_data: [entity_id: "${entity}"]])
if (logEnable) log.debug("messUpd = ${messUpd}")
interfaces.webSocket.sendMessage("${messUpd}")
}

def closeConnection() {
if (logEnable) log.debug("Closing connection...")   
state.wasExpectedClose = true
interfaces.webSocket.close()
}

def deleteAllChildDevices() {
log.info "Uninstalling all Child Devices"
getChildDevices().each {
      deleteChildDevice(it.deviceNetworkId)
   }
}

As I said, this code works, but I'm not a coder. Perhaps @tomw or @ogiewon could review and comment.

Maybe you could submit a pull request so your changes could be properly reviewed.

I suppose, but I've made exactly 0 pull requests in my life. So I'll have to figure out how to do that. :slight_smile:

I guess the change for the app is pretty straightforward but the file you provided here contain too many formatting changes to be compared as is.

1 Like

I created my first pull request. I think I did it correctly. The code works, not sure if it's "good"

4 Likes

The december update to Home Assistant will introduce an new entity for buttons. Suppose to be out this weekend.

3 Likes

That's going to instantly solve a lot of issues with button devices that are incompatible with HE. I'm so grateful to you, and the others for creating this driver. It's been immensely helpful.

5 Likes

All of my Ikea "buttons" already have sensor entity entries, but they don't come across to HE.
I'm guessing the distinction is binary_sensor vs sensor.

Guess we'll find out in an hour or so.

1 Like

Not sure it's what you thought it was.

Seems like a virtual button, such as we already have here in Hubitat. I might be wrong, but it doesn't sound like this is going to create entities when you add a button, but perhaps you could create an automation in HA for real button devices that would press the new button entity and that would give you a button press in Hubitat. Would avoid the need to install the Hubitat Integration for Home Assistant in order to get the virtual switch from Hubitat to do the same thing.

I'm not home right now and my remote access to HA is down, so I'll have to test it out later.

I run HA in docker. but the new image won't pull down when I tell Portainer to rebuild it.

Tried several times this evening.

Haven't played with this too much, yet, but I don't immediately see anything different happening with buttons. In Home Assistant, I still only see the _battery entities and not the buttons themselves.

It looks like the button entity can only be obtain through integration that support it. For the moment only a handfull do. Supposedly more are comming.

I got Home Assistant for Christmas (an ODROID) and I'm trying to connect integrations that HA can do that HE can't, like August, Xbox, etc., but bring them into HE using this release.

So far I've got August working. I was wondering if there would be any support for "remote.xbox_one_remote" entity or is this better suited for a virtual switch?

FIrst off, I've been aware of (and intrigued by) this effort since @ymerj first posted his prototype driver almost a year ago. Thanks to @SmartHomePrimer for reminding me of it the other day and motivating me to give it a go over the holiday. All I can say is WOW! So much easier than the MQTT broker/client method that I am using for my Ecobee thermostat.

I know that this app/driver doesn't support climate devices (the actual thermostat), but the Ecobee Smart Sensors populate into HA as normal sensors. I'm trying to use this app to bring my Smart Sensors (motion & temp) into HE. The child devices are successfully created, and the motion sensor device (sensor_binary type) is showing active or inactive in the HE device page. However, the log entries for motion simply say 2021-12-25 07:25:25.303 pm [info] EcobeeSensor3 is updated. For a normal motion sensor, it says the actual state of active/inactive rather than just "updated." In use, this means that I'm unable to use the state change to active as a trigger. A test rule using this trigger never runs. Any ideas on what I can do to fix this? I suppose I could create a virtual device and write a rule to mirror the state of the actual device, but that seems like too much work.

It does.

Here's a screenshot of a PM that I had sent someone recently after testing whether I could import an ecobee paired with HomeKit Controller to Hubitat via Home Assistant Device Bridge:

3 Likes