Scripting Simple Attribute Clients

The Attribute daemon also provides an event-based system to call scripts to handle attribute changes. This interface does not provide the depth of functionality that the Attribute Daemon API does, but in cases where you just want to set attributes or be notified on attribute changes, this event system can provide a quick, simple interface. Using the Afero Profile Editor, you can create attributes that your script can manipulate. These attributes, from IDs 1-1023, are sometimes called “MCU Attributes” or “Edge Attributes” but these terms all describe attributes in the range of 1-1023 and the terms can be used interchangeably.

The Attribute Handling Script

You will need to provide an executable script for Attribute daemon to call when there are any changes to attributes that you want to access. This script can be in any language; the daemon simply calls it with specific parameters to indicate certain events:

Your script will be expected to act on these commands in the following manner:

Attribute Handling Script Notes

There are a few things to be aware of when scripting with the Attribute daemon:

Attribute Handling Script Example

Below you’ll find a simple Bash script to demonstrate handling these events:

#!/bin/bash

handle_init() {
    logger "init() called"
}

handle_get() {
    # we just return the string “return value” here for any attribute ID
    echo "return value"
}

handle_notify() {
    eval value="$2"
    logger "attribute_notify:id=$1,value=$value"
}

handle_set() {
    # if the value of the attribute is “fail”, return an error to test error handling
    eval value="$2"
    logger "attribute_set:id=$1,value=$value"
    [ "x${value}x" != "xfailx" ]
    return $?
}

[ "x${1}x" == "xx" ] && exit 1

case $1 in
    init) handle_init ;;
    set) handle_set $2 "$3" ;;
    get) handle_get $2 ;;
    notify) handle_notify $2 "$3" ;;
    *) ;;
esac

You can test this script simply by calling it manually with the four commands and attribute IDs and verifying the script responds appropriately. In the sample script above, the logger command will write a line of text to the Linux syslog; you can view the system log when your script is called via the event handler below and see it logging as it executes.

Configuring the Event Handler

You must tell the Attribute daemon what attribute(s) your script wishes to be called for. This is done by adding lines in the /etc/af_events.conf file. The general format of the lines in this file is:

<command> <AttributeID> <Path to Script>

In order to register the /usr/bin/attr_test.sh script for all operations on attribute 1100, add the lines below to /etc/af_events.conf:

set    1100 /usr/bin/attr_test.sh
notify 1100 /usr/bin/attr_test.sh
get    1100 /usr/bin/attr_test.sh

By default, the Attribute daemon doesn’t put anything in this file, so completely replacing its contents is not an issue. You can, however, have multiple scripts called via this event handler to process different attributes.

Whenever you change the contents of this file, you must restart Attribute daemon. You can reboot the Linux device, or you can issue the command killall attrd to kill the currently running Attribute daemon. The operating system will automatically restart Attribute daemon if it is stopped, and this will pick up the changes to af_events.conf.

Note that the Attribute daemon will look for event configuration files in the /etc/af_attr.d/ directory. This allows you to use multiple af_events.conf files at the same time.

A Simple Example

On page Defining Your Own Attributes, we created a sample profile for your device that included two attributes, “Output” and “Input”. Below is an example script to handle the attributes in that profile, and a sample af_events.conf file. You can put these on your device, along with the profile created earlier, and watch Attribute daemon work for you.

af_events.conf

  1. Type the command:
    cat > /etc/af_events.conf
  2. Then paste the lines below to create the file:
    notify 65013 /usr/bin/mcu_test.sh
    set 1 /usr/bin/mcu_test.sh
  3. Press Enter to start a new line, then type Control-D to save the file.
  4. Restart the Attribute daemon with the command:
    killall attrd

/usr/bin/mcu_test.sh

  1. Type the command:
    cat > /usr/bin/mcu_test.sh
  2. Then paste the lines below to create the file:
    #!/bin/sh
    
    handle_notify() {
    	 # check if ASR_STATE == AF_MODULE_STATE_INITIALIZED
    	 if [ "$1" -eq 65013 -a "$2" -eq 4 ] ; then
        	attrc owner_set 1 0
        	attrc owner_set 2 0
        fi
    }
    
    handle_set() {
        eval value="$2"
        logger "MCU_attribute_set:attrId=$1,value=$value"
        return 0
    }
    
    [ "x$1x" == "xx" ] && exit 1
    
    case $1 in
        notify) handle_notify $2 $3 ;;
        set) handle_set $2 "$3" ;;
        *) ;;
    esac
  3. Press Enter to start a new line, then type Control-D to save the file.
  4. Make the script executable with the command:
    chmod 755 /usr/bin/mcu_test.sh

Script Functions

The script performs two functions:

Testing the Script

If you have published this Profile to a device, your Afero mobile app will show the two attribute settings under the hub device.

  1. Watch the Linux syslog on the hub device with the command:
    journalctl -f

    or

    tail -f /var/log/messages
  2. Turn on and off the Output attribute from the mobile app. On the Linux device, the system log should show the attribute values changing:
    user.notice root: MCU_attribute_set:attrId=1,value=1
    user.notice root: MCU_attribute_set:attrId=1,value=0
  3. On the hub, use the attrc command to set the value of the Input attribute:
    attrc owner_set 2 1
    attrc owner_set 2 0
  4. The mobile app should reflect the changes in the Input attribute as you change it.

Attribute Daemon Client (attrc)

To simplify debugging your Attribute scripts, Afero provides an Attribute daemon client application called attrc. This command-line utility can:

Set Attributes

You can set attributes using the Attribute daemon client. The syntax is:

attrc set <attributeId> <value>

Here’s an example where we set the Attribute daemon debug level:

attrc set 51613 1

You can also specify the attribute ID using its name. The format of the value depends on the type of the attribute:

attrc set ATTRD_DEBUG_LEVEL 1

Get Attributes

The syntax for getting the value of an attribute is:

attrc get <attributeId>

Here’s an example where we get the Attribute daemon debug level:

attrc get ATTRD_DEBUG_LEVEL

The client application prints:

1

to stdout.

Strings are output delimited by double quotes. The following command:

attrc get WIFISTAD_CONFIGURED_SSID

would print:

"dantest"

to stdout.

Wait for a Notification

The Attribute daemon client can wait for an attribute change notification. The syntax is:

attrc wait <attributeId>

The app hangs until the specified attribute is set. Then it prints the attribute ID and its new value in the specified format.

Here’s an example where we wait for an ATTRD_REPORT_RSSI_CHANGES notification in a background process:

attrc wait ATTRD_REPORT_RSSI_CHANGES &

If you set the value also using attrc:

attrc set ATTRD_REPORT_RSSI_CHANGES 1

The first command prints:

51600 1

The first value is the attribute ID and the second value is the new value.

Set an Attribute as Owner

If you set an attribute, the Attribute daemon goes to the attribute owner and asks it whether the set should succeed or not. If the attribute owner is the one who set the attribute in the first place, this check should not be done. When a script owns an attribute, it needs to be able to set the attribute without having the Attribute daemon confirm the set request again. The script can do this using the attribute client with the owner_set operation. The syntax is:

attrc owner_set <attribute> <value>

Here’s an example where a script sets the attribute value for attribute 2:

attrc owner_set 2 0

List Attributes

If you want to know what the valid attributes are, you can list them from the Attribute daemon client. The syntax is:

attrc list

This command lists all of the attributes supported by the Attribute daemon. If you only want to see the attributes owned by a specific client, you can filter the results using grep. For example:

attrc list | grep ATTRD

You’ll get something like this:

51600 ATTRD_REPORT_RSSI_CHANGES BOOLEAN
51613 ATTRD_DEBUG_LEVEL SINT8
51623 ATTRD_REVISION UTF8S
65001 ATTRD_UTC_OFFSET_DATA BYTES
65019 ATTRD_REBOOT_REASON UTF8S
65065 ATTRD_SYSTEM_TIME SINT32

The first column is the attribute number. The second column is the attribute name. The third column is the type.

Scripting Gotchas and Tips

Scripting with the Attribute daemon and Edge daemon has some pitfalls, explained below.

✔  Multiple Versions of the Same Script Can Run Concurrently

Both the Attribute daemon and the Edge daemon can invoke scripts, even if they are already running. If you use a single script that has global persistent state, your script may run into state consistency issues. If you run into a problem with global state you can lock the state using a lock.

One common way to do this is to set noclobber in your script and redirect the output of some command to a lock file. Then:

✔  Strings with Spaces Can Appear to Be Multiple Parameters

When the Attribute daemon invokes a script with a string value, it encloses the string within single quotes. The result takes advantage of Bash concatenation of strings; for example, 'He said, '\"Hi!\"'. However, if you pass the parameter into a Bash function, be sure to put it in double quotes. Within the function you should use the eval keyword to eliminate the quotes. See Attribute Handling Script Example.

✔  Modifying Attributes in Response to Attribute Events

If an attribute should change in response to an attribute event such as a set operation, a get operation, or a notify operation, the attribute change is performed as an attribute set operation. There are two cases:

✔  Replacing an Attribute Daemon Client with a Script

You can replace an Attribute daemon client with a script; however, it is important to note two ownership rules:

✔  Timeouts

The following timeouts are enforced by the Attribute daemon:

Script Operation Timeout (Seconds)
Set Attribute (set) 3
Get Attribute (get) 3
Notify Attribute (notify) 20
Initialize (init) 20