Blockly: AND- and OR-Blocks with multiple inputs (mutuator)

What bothers me about many of my blockly rules is the inevitable nesting of AND or OR blocks when I have more than two conditions.
Are there others who are bothered by this? My attempts to create my own blocks with a variable number of input parameters failed. If I understand correctly, mutuators cannot be used in custom libraries. Is that correct?

My suggestion would be to include one block for AND and one for OR with mutuator in the openHAB standard. No mixed blocks, because that would make it complex and confusing again.

There is already a block with a mutuator under “dates and times”, so it should be possible. @stefan.hoehn was that perhaps yours? What do you (or other blockly-users) think?

Sometime back I thought how this could be done but I never had a good idea. I even thought about implementing a switch case form but then I thought it wouldn’t help a lot.

First of all could you visualize your idea as I am not sure I grasp it fully.

Second, implementing it might not be that easy but first come up with a good idea.

Third, sorry that I haven’t been active that much in last weeks. I have been distracted by too much with other private obligations


Don’t be sorry, you contributed so much. (I’m nine months behind updating the install routine for zigbee2mqtt in openhabian :flushed_face:). Anyway it’s good to have you on board, no matter how much time you can spend.

I’ll prepare a sketch later. It’s dead simple really.

@stefan.hoehn : So this is a little sketch I prepared for CodePen https://codepen.io/pen/ (with the help of AI):

Code in CodePen
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Blockly Logic Extended</title>
<script src="https://unpkg.com/blockly/blockly.min.js"></script>
<style>
html, body { height: 100%; margin: 0; overflow: hidden; }
#blocklyDiv { height: 100%; width: 100%; }
</style>
</head>
<body>
<div id="blocklyDiv"></div>

<xml id="toolbox" style="display:none">
  <category name="Logic Extended" colour="210">
    <block type="logic_and_or_left_plus"></block>
  </category>
  <category name="Standard Logic" colour="210">
    <block type="logic_compare"></block>
    <block type="logic_operation"></block>
    <block type="logic_boolean"></block>
  </category>
</xml>

<script>
var workspace = Blockly.inject('blocklyDiv', {
  toolbox: document.getElementById('toolbox')
});

// "+"-Button-Feld
class FieldAddInput extends Blockly.Field {
  constructor() { super('+'); }
  showEditor_() {
    const block = this.sourceBlock_;
    block.addDynamicInput();
  }
}

// "-"-Button-Feld
class FieldRemoveInput extends Blockly.Field {
  constructor() { super('-'); }
  showEditor_() {
    const block = this.sourceBlock_;
    block.removeDynamicInput();
  }
}

// Erweiterter Logic-Block
Blockly.Blocks['logic_and_or_left_plus'] = {
  init: function() {
    this.setInputsInline(false);
    this.inputCount = 2;

    // Dropdown fĂŒr Operatoren
    this.appendValueInput('ARG0').setCheck('Boolean')
      .appendField(new Blockly.FieldDropdown([
        ['AND','&&'],
        ['OR','||'],
        ['XOR','XOR'],
        ['NAND','NAND'],
        ['NOR','NOR']
      ], (newVal)=>{this.op=newVal;}), 'OP');

    // Zweiter Eingang mit + und - Buttons
    const input1 = this.appendValueInput('ARG1').setCheck('Boolean');
    input1.appendField(new FieldAddInput(), 'ADD_BUTTON');
    input1.appendField(new FieldRemoveInput(), 'REMOVE_BUTTON');

    this.setOutput(true, 'Boolean');
    this.setColour(210);
    this.setTooltip('Logic Block mit variabler Anzahl EingÀnge und + / - Buttons');

    this.op = '&&';
  },

  addDynamicInput: function() {
    const index = this.inputCount;
    this.inputCount++;
    const input = this.appendValueInput('ARG' + index).setCheck('Boolean');
    const lastInputIndex = this.inputList.findIndex(i=>i.name.startsWith('ARG'+(index-1)));
    this.moveInputBefore(input.name, this.inputList[lastInputIndex+1]?.name || null);
    this.render();
  },

  removeDynamicInput: function() {
    if (this.inputCount <= 2) return; // mindestens 2 EingÀnge
    const lastIndex = this.inputCount - 1;
    const inputName = 'ARG' + lastIndex;
    if (this.getInput(inputName)) this.removeInput(inputName);
    this.inputCount--;
    this.render();
  },

  // KontextmenĂŒ anpassen → "Inline inputs" entfernen
  customContextMenu: function(options) {
    for (let i = options.length - 1; i >= 0; i--) {
      if (options[i].text === Blockly.Msg.INLINE_INPUTS) {
        options.splice(i, 1);
      }
    }
  }
};

// JavaScript-Generator
Blockly.JavaScript['logic_and_or_left_plus'] = function(block) {
  const op = block.getFieldValue('OP');
  let args = [];
  for (let i = 0; i < block.inputCount; i++) {
    args.push(Blockly.JavaScript.valueToCode(block, 'ARG' + i, Blockly.JavaScript.ORDER_NONE) || 'false');
  }

  let code;
  switch (op) {
    case '&&':
      code = '(' + args.join(' && ') + ')';
      break;
    case '||':
      code = '(' + args.join(' || ') + ')';
      break;
    case 'XOR':
      code = '(' + args.map(a => '('+a+')').join(' ^ ') + ')'; // XOR ĂŒber Bitwise
      break;
    case 'NAND':
      code = '(!(' + args.join(' && ') + '))';
      break;
    case 'NOR':
      code = '(!(' + args.join(' || ') + '))';
      break;
    default:
      code = '(' + args.join(' && ') + ')';
  }
  return [code, Blockly.JavaScript.ORDER_LOGICAL_AND];
};
</script>
</body>
</html>

The first shows the block after inserting it. The second after adding two inputs. The last is one with OR.
How do you like it?

1 Like

two additional remarks to my proposal

  • Why didn’t I choose the standard mutator (gear)? If there’s only one type of element to expand, buttons are much quicker to use on both PC and mobile. On mobile, it’s quite hard to hit the gear.
  • External inputs or inline inputs? The design is made for external inputs. “Inline inputs” don’t make sense for a block that’s intended for more than two inputs anyway. That’s why I removed the option to switch to “inline inputs” from the context menu.
  • As AND and OR is a drop-down anyway, shouldn’t we include XOR, NAND and NOR too? Edit: I changed the above code to include them.

Thanks for your idea and let me see when I can find time to approach this (or any other maintainer).

  • I would still go for the gear icon as it is more consistent for adding inputs
  • The default can be external inputs. You can always switch between inline and external. I don’t think why this would hurts

There is already someone who had a similar idea. The discussion is here: https://groups.google.com/g/blockly/c/aIlJVdVyRDk?pli=1 and the solution is here Blockly multiple and block - Pastebin.com which is probably a pretty quick start to implement the idea.

Hi,
If you can live with the fact that the generated code is no longer so easy to read, every() and some() could also help. Yesterday, I created a block (which has only been roughly tested so far) that uses these. A proper block with input validation (Boolean) is no substitute for that.

    - component: BlockType
      config:
        args0:
          - name: options
            options:
              - - and
                - every
              - - or
                - some
            type: field_dropdown
          - align: left
            check: Array
            name: booleanList
            type: input_value
        colour: 260
        message0: "%1  %2 "
        output: Boolean
        tooltip: OR oder AND in einer Liste
        type: OR_AND_Liste
      slots:
        code:
          - component: BlockCodeTemplate
            config:
              template: "{{input:booleanList}}.{{field:options}}(x => x === true)"

Cool Idea. Much better than stuff like this

I am in the middle of providing the idea that I linked above for openHAB and added the choice of AND/OR.

If you like that idea I would continue
 (and as the original author for the allowance to use his code in openHAB.

3 Likes

that looks absolutely great👏

Here you go:

4 Likes

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