How to / can I send data from app to a driver?

I'm using up my question allocation here :slight_smile: :slight_smile:

How can I send data from an App to a Driver?

I have an App/Driver to send string data to a Zigbee board.

The App subscribes to several sensors, collects and formats the data. Then needs to send it to the driver to be sent to the Zigbee board.

Essentially the App needs to call a method in the driver and pass it some data.

I've not been able to find anything about this or an example. I though I knew this once but it has left my brain (or never really existed).

Any suggestions?

Thanks
John

Have a look at this code, The app sends data to the device (called child device):
// Update child device attributes
def energyUnit = settings.energyUnit ?: "KilowattHours"
def usage = nestedChannel.usage ?: 0
def percentage = truncateToTwoDecimals(nestedChannel.percentage ?: 0)
def currentTimestamp = getFormattedDate()

                            childDevice.sendEvent(name: "usage", value: usage)
                            childDevice.sendEvent(name: "usagePercentage", value: percentage)
                            childDevice.sendEvent(name: "energyUnit", value: energyUnit)
                            childDevice.sendEvent(name: "retrievalFrequency", value: settings.retrievalFrequency)
                            childDevice.sendEvent(name: "lastUpdated", value: currentTimestamp)

It depends on which part you are struggling with.... Is it how to reference the device, how to call the methods on the device, or just "all of it"...?

Here is a method inside my app that creates a device (addChildDevice), capturing the resulting object in a variable, then calls the build in updateSetting method(?) for that device.

// Device Methods

String createMobileDevice() {
    def dni = UUID.randomUUID().toString();
    def newMobileDevice = addChildDevice("simnet", "Mobile Device", dni, 1234, ["name": "${newMobileName}", isComponent: false]);
    newMobileDevice.updateSetting("DeviceIPAddress",[value: "${newMobileIPAddress}", type: 'string']);
    state.deviceId = newMobileDevice.getDeviceNetworkId();
    return newMobileDevice.getDeviceNetworkId();
}

The call to updateSetting could just as easily be a custom method you write on the device driver if it is one of your own... Including the ability to pass whatever data you want.

If you want to find devices created by your app, i.e. child devices of the app... there will be better examples of this.. but here's a smal snippet of code that looks up devices and uses that list to driver the rendering of a page in the app:

List<com.hubitat.app.DeviceWrapper> existingDevices = getChildDevices();
              existingDevices?.each { dev ->
                  href(name: "deviceConfig", title: "Configure ${dev.getDisplayName()}", description: "Update device configuration", page: "pageDeviceConfiguration", params: ['deviceId' : dev.getDeviceNetworkId()], width: 4)
              }

You could use a similar a similar call to getChildDevices() with some neater code to find the device you are looking for.... then call whatever method you want...

Set up a device selector input on the app config page. That will give you a device wrapper reference you can use for calling methods.

Unless the driver is a child of the app, in which case you have getChildDevice()

2 Likes

Thank you for your guidance. Your suggestion seemed the simplest.
For clarity:

  • my App is a simple app I've written to do some string formatting.
  • my Device driver is written by me and equally simple.

In the App I've added an "input" for the driver device. I chose a capability of "Variable"

input "UARTdriver", "capability.variable",title: "Select UART Driver", submitOnChange: true, required: true, multiple: false

I assume the "UARTdriver is somehow the wrapper I need to access the UARTdriver method "WriteAtt_"

Do you have an example of something similar?

Thanks
John

App:

/*
App to receive data from several sensors and send the data to a cc2530 via UART.
*/

definition(
    name: "UART Data App-01",
    namespace: "hubitat",
    author: "JohnRob",
    description: "Hub data to UART",
    category: "Convenience",
    iconUrl: "",
    iconX2Url: "")

preferences {
    page(name: "mainPage")
}

def mainPage() {
    dynamicPage(name: "mainPage", title: " ", install: true, uninstall: true) {
        section {
            input "thisName", "text", title: "Name this UART data", submitOnChange: true
            if(thisName) app.updateLabel("$thisName")

            input "tempSensor", "capability.temperatureMeasurement", title: "Select Temperature Sensor", submitOnChange: true, required: true, multiple: false
            input "humidSensor", "capability.relativeHumidityMeasurement", title: "Select Humidity Sensor", submitOnChange: true, required: true, multiple: false
            input "UARTdriver", "capability.variable",title: "Select UART Driver", submitOnChange: true, required: true, multiple: false
            
            log.debug "UARTdriver  $UARTdriver"
        } // section
    }   // dymanicPage
}   // mainPage

def installed() {
    initialize()
}

def updated() {
    unsubscribe()
    initialize()
}

def initialize() {
    subscribe(tempSensor, "temperature", handlerTEMP)
    subscribe(humidSensor, "humidity", handlerHUMID)
}

def handlerHUMID(evt) {
    state.lastHUMID = evt.value
    log.debug "handlerHUMID() called: ${evt.name} ${evt.value}"
}

def handlerTEMP(evt) {
    int index = 1
    log.info "index $index, event   $evt.value"
    //buildMsg(index,evt.value)
}
/*

Driver:

//import hubitat.device.HubAction
//import hubitat.device.HubMultiAction
//import hubitat.device.Protocol

metadata
{
    definition(name: "Zigbee UART Data Driver", namespace: "johnrob", author: "johnr")
    {
        capability "Actuator"
        capability "Switch"
        capability "Variable"
        
        attribute "variable", "string"   // attempt at accessing driver method
       
       command "ReadAttr_"
       command "WriteAttr_"
    }

    preferences{
         input "tempSensor", "capability.temperatureMeasurement", title: "Select Temperature Sensor", submitOnChange: true, required: true, multiple: false
     
    }
}

def parse(String description){
    log.info  "Raw Description#$description"

    Map map = [:]
    def event = zigbee.getEvent(description)

    //if (event) sendEvent(event)

    if (description?.startsWith ('catchall')){            //('read attr -')){
        def descMap = zigbee.parseDescriptionAsMap(description)
        log.info " desc map $descMap"
        if (descMap.cluster == "0014" && descMap.attrId == "000e"){
            ZigbeeHexMap = descMap.value
            log.info "Zigbee Msg#$ZigbeeHexMap"
            float SensorValue = ConvertHexToFloat(ZigbeeHexMap)
            def otherAttrs = descMap?.additionalAttrs
            otherAttrs.each{
                unitaddress = it.value
            }
            log.info ("$unitaddress")
            def (units, address) = unitaddress.tokenize( ',' )   //   <--- units and I2C address
        } // --- if cluster ---
    } // --- if "read attr" ---
} // --- parse ---

 
def WriteAttr_( ) {   //<<<<<<< =========== this is the method I wish to access from the app
     def serialdata = "0761626364616125"
    "he raw ${device.deviceNetworkId} 0x01 0x01 0x0014 {104 3 02 0E00 42${serialdata}}"
}

def ReadAttr_(){
    zigbee.readAttribute(0x0014, 0x000e, [destEndpoint: 1], 0)
}

Yes. Groovy being the dynamic language that it is, you can access the method directly in your code, i.e. UARTdriver.WriteAtt_(). Might be a good idea to wrap it in a try/catch block to more gracefully handle the case when the selected device is not in fact of the correct type (driver).

That should work, however keep in mind how the platform uses capabilities. Probably no big deal, but your device may show up in other apps filtering specifically for capability.variable in a context that doesn't make sense for it (and unless you implement the capability's required setVariable() method, may break the app).

I have often seen capability 'Actuator' used, as there are no attributes or commands defined for that capability. Your input filter could also be set to capability.*.

I'm a weekend warrior at this stuff, there may be better advice out there. I found the documentation quite helpful for writing my own apps, as well as the examples on GitHub.

2 Likes

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.