Skip to content

Using console commands

Anna edited this page Nov 30, 2022 · 1 revision

1/ Using commands with MQTT and NodeRed to operate a multi-relay unit from a single button

Click to expand section

This example uses a combination of event handlers in OpenBeken to send button presses to NodeRed, and a NodeRed node to track the button presses and flash a single LED on the device to provide a simple 'menu' of operations on the device.

The intent is to flash an LED for the number of flashes representing how many times the button has been pressed as feedback to the user to know which relay they will toggle by a double press.

As the LED flashing is done over MQTT by NodeRed, the result is not perfect, but perfectly useable.

The device is a Calex power strip with 5 'relays' (4 power sockets and 1 controlled USB outlet). It has two LEDs and a single button.

The configuration is:

P6 Rel 5
P7 Rel 2
P8 Rel 3
P9 Rel 1
P10 LED 6
P14 Btn 8
P24 LED 7
P26 Rel 4

Note how I have separated the button on channel 8, so that it has no default action.

Commands to issue to OpenBeken:

Buttons provide the events OnClick, OnDblClick, OnToggle, OnHold - but these are published against PIN numbers.

In my case, my single button is on pin 14, and I want to publish the button presses to MQTT. Publishing to MQTT can be done with the command publish <topic> <value> which will result in <value> being sent to the topic <devicename>/<topic>/get.

I issued these commands to the OpenBeken command line:

addEventHandler OnClick 14 publish button click
addEventHandler OnDblClick 14 publish button dblclick

Then, in NodeRed, subscribe to <device>/# to receive the MQTT which will be

<device>/button/get click
<device>/button/get dblclick

We also need to capture the current relay values, which look like

<device>/<channel>/get <value 0|1>

Once tested, the commands can be run at boot by adding this startup command line in Change Startup Command Text:

backlog addEventHandler OnClick 14 publish button click; addEventHandler OnDblClick 14 publish button dblclick

Controlling the LED

I chose to use the LED on channel 6 as the feedback to the 'menu' state. To control this over MQTT, we send

<device>/6/set <value 0|1>

NodeRed flow

The simple flow in NodeRed looks like this:

image

The 200msTick inject sends an empty payload on topic 'tick' every 200ms to operate our LED flash logic.

Button presses are received and acted upon. Each time the OnClick event is received, a variable context.selected is incremented, mod 6 (so it takes values of 0-5). Each time a tick is received, a variable context.counter is incremented mod (7*2) (so range 0-13). If context.selected is non-zero and (context.selected < context.counter/2) the led is turned on if !(context.counter & 1), else the led is turned off. So, the led flashes for the count of context.selected, with a flash period of 2 x 200ms, and overall period of 14 x 200ms.

Relay set/get MQTT packets are used to know the current state, and if dblclick is received and context.selected != 0, then the relevant relay is toggled.

Click to expand content of CalexButton function node
// expected messages:
// 1/ <dev>/<pin>/get|set <val> - we record the value
// 2/ <dev>/button/get click - indicates button clicked
// 3/ <dev>/button/get dblclick - indicates button double clicked
// 4/ tick - every 200ms


// send:
// 1/ <dev>/6/set <val> set flashy led
// 2/ <dev>/<channel>/set <val> control relay.


let parts = msg.topic.split('/');
if (parts[0] !== 'tick'){
    // isfirst COULD be used to read deviice values.
    // currenltly I turned on the 'send once per minute' feature instead
    if (!context.device) isfirst = true;
    // remember device prefix for led set from tick
    context.device = parts[0];
}


//node.warn(parts);
//node.warn(context.counter);

context.pins = context.pins || {
    0:0,
    1:0,
    2:0,
    3:0,
    4:0,
    5:0,
    6:0,
    7:0
};

// if parts[1] is numeric, it's a relay or other value,
// so store it.
if ((parts[2] === 'get' || parts[2] === 'set') && +parts[1]){
    context.pins[parts[1]] = msg.payload;
    //node.warn(context.pins);
    return;
}


if (parts[1] === 'button'){
    switch(msg.payload){
        case 'click':
            context.selected = ((context.selected || 0)+1)%6;
            // restart count for faster response;
            context.counter = 0;
            node.warn('selected '+context.selected);
            break;
        case 'dblclick':
            node.warn('action '+context.selected);
            if (context.selected){
                msg.topic = context.device+'/'+context.selected+'/set';
                context.pins[context.selected] = (+context.pins[context.selected])^1;
                msg.payload = context.pins[context.selected];
                node.send(msg);
                //node.warn(msg);
            }
            break;
    }
    context.lastButton = Date.now();
    return;  
}

let now = Date.now();
function setLed(val){
    if (!context.device) return;
    // only send on change
    if (context.ledstate !== val){
        context.ledstate = val;
        msg.topic = context.device+'/6/set';
        msg.payload = val;
        node.send(msg);
    }
}

// stop triggering led if 20s since last button press;
context.lastButton = context.lastButton || 0;
if (now - context.lastButton > 20000){
    if (context.ledstate) setLed(0);
    context.selected = 0;
    return;
}

// no toggling required
if (!context.selected){
    if (context.ledstate) setLed(0);
    return;
}

// we get a tick msg every 200ms
if (msg.topic === 'tick'){
    context.counter = ((context.counter || 0)+1) %(7*2);
    context.selected = context.selected || 0;
    if (context.counter & 1){
        ///node.warn(context.selected +' '+(context.counter/2));
        if (context.selected >= (context.counter/2)){
            setLed(1);
        } else {
            setLed(0);
        }
    } else {
        setLed(0);
    }
}