Why can't I find a wall switch with motion sensor?

Inovelli released their latest roadmap for 2021 and 2022 and they have a ZigBee motion sensor switch on there...

1 Like

Cool!
I don't see it on their website thought. Do you remember when it's coming out?

Their Roadmap says Q2 2022...

1 Like

Looking forward to the zigbee switch would make a great bathroom switch.

I think I'm screwed on Hubitat support and will have to somehow go back and change my software. The trouble is is that Hubitat supports zigbee (and Z-wave) as if it is WiFi, that is on WiFi. WiFi has no standardized app layer and no standardized discovery of capabilities. For example all zigbee dimmable lights work the same way. A hub like Alexa discovers a dimmable light and knows that it will support a certain set of commands and behave a specific way to those commands. Hubitat requires a fingerprint to recognize a specific device. I then declare a set of Hubitat capabilities - a contract stating that my driver will support a set of commands for my light.

Here is where I'm screwed, my single radio module comes has different capabilities depending on what it is plug into and what sensors it has. Currently there are 48 different sets of capabilities but this number will increase. Alexa, while not supporting everything my device can do, will discover most of the capabilities and just work. Hubitat will require 48 different drivers but I can't use the fingerprint because the way Hubitat calculates the fingerprint all of my devices will have identical finger prints.

I'm going to need to write a completely generic zigbee driver that dynamically can add or remove capabilities based on the endpoint discovery results. Of coarse if I'm able to do that then it will work for all zigbee devices, not just swidget ones.

You can't dynamically add and remove capabilities. However you can dynamically add and remove child devices. So you could make a parent driver that creates the needed children devices based on which module is used/specified.

In fact that is how I would strongly recommend doing the driver for a device like this.

2 Likes

Do you have an example of that somewhere? Ideally I would remove or add child devices based on the endpoints present.

So here is an example of the code to add different children I threw together. In this example you would need drivers "MotionChild.groovy" and "DimmerChild.groovy" for it to work properly. With the new bundles that 2.2.8 allows (or HPM) this is a bit easier to deal with than loading them individually. It is something I am thinking about doing for some of my areas due to the lack of dynamic capabilities (once 2.2.8 is more readily in use so I do not make life a total hassle for people that have not upgraded yet):

def addChild( String DNI, String ChildType ){
    try{
        Logging( "Adding Child ${ DNI } of Type ${ ChildType }", 3 )
        switch( ChildType ){
            case "MotionChild":
                addChildDevice( "MotionChild", DNI, [ name: "${ DNI }" ] )
                break
            case "DimmerChild":
                addChildDevice( "DimmerChild", DNI, [ name: "${ DNI }" ] )
                break
            default:
                Logging( "${ ChildType } is unknown, correct the code.", 5 )
                break
         }
    }
    catch( Exception e ){
        def Temp = e as String
        if( Temp.contains( "not found" ) ){
            Logging( "Child driver of ${ ChildType } is not loaded, this is required.", 5 )
        } else {
            Logging( "addChild Error, likely child already exists: ${ Temp }", 5 )
        }
    }
}

OK this helps. What calls addChild()? Where does the DNI come from and String ChildType?

In my drivers I call AddChild when I want to send an Event or State to the child device, but it determines that child does not exist yet. That is when I would send the ChildType string also. Mind you, I do not actually HAVE this implemented in any of my real drivers at this time. The ChildType needs to be passed through from wherever in your code you actually know what it should be. In the below example, my PostStateToChild does not have any way of "knowing" directly, it is just meant for posting data through to the child device.

But typically when I am calling PostStateToChild (or the similar PostEventToChild for events) I would know when they are being called what it would be for. As an example, if I implement this with my UnifiProtectAPI driver, I could eliminate the more generic UnifiChild driver and make ones more specific to each type of child device. Such as the cameras, lighting, NVR, etc... Then I would not have to worry about the fact that everything thinks it can on/off or everything has dimming functions when they only apply to lights or such.

Combined with the bundle feature AND the library feature (so a lot of the duplicate code between the various child drivers could be just put in the library) this could really make my life easier while making my drivers more specifically targeted and functional. Just need to do it.

I am sure that before, if you told a user "here is the parent driver... and you also need to install these (up to) eight child drivers individually... and update them individually..." a lot of people would give up right there. HPM made that easier, but now I think Hubitat itself is making it a lot more doable.

EDIT: Forgot to mention about the DNI. In this case it is passed as "Child" from the PostState... Usually, in drivers where I know there are going to multiple of the same general thing, I send the child name as something identifying the type but with something unique also.

Example call:
PostStateToChild( "Camera ${ UniqueCameraID }", "Camera", "Is Recording", "yes" )

Example:

// Post data to child device
def PostStateToChild( Child, ChildType, Variable, Value ){
    if( "${ Child }" != null ){
        if( getChildDevice( "${ Child }" ) == null ){
            addChild( "${ Child }", "${ ChildType }" )
        }
        if( getChildDevice( "${ Child }" ) != null ){
            Logging( "${ Child } State: ${ Variable } = ${ Value }", 4 )
            getChildDevice( "${ Child }" ).ProcessState( "${ Variable }", Value )
        } else {
            Logging( "Failure to add ${ ChildParent } and post ${ Variable }=${ Value }", 5 )
        }
    } else {
        Logging( "Failure to add child because child name was null", 5 )
    }
}

So in my case depending on what my insert (radio module) is plugged into I will have different endpoints. I see in the example Hubitat/useelink-sm-so301-uz-zigbee-power-strip.groovy at master · RMoRobert/Hubitat · GitHub that the children are dynamically created but from a static list.
@Field static Map <String, String> childEndpoints = ['01': 'Outlet 1', '02': 'Outlet 2', '03': 'Outlet 3',
'04': 'Outlet 4', '07': 'USB Ports']

Can the capabilities be dynamically added and removed?
If my insert is put into a dimmable device then I want the capabilities to include changing level . If the insert is in an outlet I only want the ability to implement commands from a switch (the switch capability)

You can add the children however, that example used a map, mine did not.

Unfortunately, capabilities cannot be dynamic. That is the whole cause of having multiple child drivers. Each driver is "locked in" for what capabilities and attributes it has.

You would need one for the outlet (that excluded the level) and one for the dimmer that included it. Of course if all the other code was common, you could call the outlet driver as a library in the dimmer one to minimize the code you would duplicate.

Take a look at my GE Motion Dimmer component driver. Maybe it will help giving you a real world example of a multifunction device using component children.

1 Like

So I see the addChildDevice() function call that creates the child device but I don't see any description of this function call in any documentation. How do I give the child device capabilities?

The capabilities of the child device are defined in the child device driver.

Can you show an example of a child device driver? What calls/instantiates the child driver? I assume the addChildDevice() function but I don't see how the addChildDevice specifies a driver.

The typeName parameter

addChildDevice

Creates a new child device and returns that device from the method call.

Signature

ChildDeviceWrapper addChildDevice(String namespace, String typeName, String deviceNetworkId, Map properties = [:])

ChildDeviceWrapper addChildDevice(String typeName, String deviceNetworkId, Map properties = [:])

Parameters

namespace - The namespace of the child driver to add as a child device (optional, if not specified it will default the the namespace of the parent)

typeName - The name of the child driver to add as a child device

deviceNetworkId - unique identifier for this device

properties - optional parameters for this child device. Possible values listed below

Properties

boolean isComponent - true or false, if true, device will still show up in device list but will not be able to be deleted or edited in the UI. If false, device can be modified/deleted on the UI.

String name - name of child device, if not specified, driver name is used.

String label - label of child device, if not specified it is left blank.

Returns
ChildDeviceWrapper

2 Likes

There are a few on Hubitat's GitHub.

https://raw.githubusercontent.com/hubitat/HubitatPublic/master/examples/drivers/genericComponentDimmer.groovy