Flows Builder - a visual designer for the new rules engine

workflow
ui
ruleengineexperim
Tags: #<Tag:0x00007f51e498dd60> #<Tag:0x00007f51e498db08> #<Tag:0x00007f51e498d798>

(Yannick Schaus) #1

Hi,

I’ve been playing with the “experimental” rules engine lately, to see if I could migrate most of my DSL rules to it (they are pretty simple). It’s working OK so far, and I think it has a lot of potential, especially with the JSR 223 scripting support, but the Paper UI interface to design them was IMHO not great - lots of clicks, dialog boxes, unneeded animations and so on. So I went ahead and forked HABPanel to make my own IDE :smiley: … it’s still good old AngularJS 1.x, not the fancy new frameworks, but staying with that technology stack allowed me to borrow a lot from cool libraries like ngFlowchart as well as HABPanel assets (environment, services and directives like the item picker etc.) to reduce the development effort to a minimum.

Next I wanted to challenge the rules engine to see if the rules could be “chained” with the “run rules” action to have some sort of control flow. This worked well so I decided to allow that in my app, and make it build several rules from the same flow and link them automatically. Writing the “compilation” algorithm was a fun challenge - but it’s far from perfect.

The result is Flows Builder (sorry about my lack of imagination for the name).

I’ve decided to package it properly and release it as it’s pretty useable already in case anyone wants to give it a try. Download the binary here and follow the instructions in the readme at GitHub.

It’s by no means complete or production-ready, so consider yourselves warned :slight_smile:

Flowcharts are saved in the service configuration independently of rules, and and then published (or deployed) to the rules engine. Think of them as “source code” which is then compiled into one or more rules that can be executed. For instance, here’s the build result of the example above:

[
    {
        "uid": "example_flow_1",
        "name": "example_flow - 1: When Door_Opened changed from OFF to ON",
        "description": "Created with Flows Builder - will be overwritten if the workflow is published again",
        "visibility": "VISIBLE",
        "enabled": true,
        "triggers": [
            {
                "id": 1,
                "label": "When Door_Opened changed from OFF to ON",
                "configuration": {
                    "itemName": "Door_Opened",
                    "previousState": "OFF",
                    "state": "ON"
                },
                "type": "core.ItemStateChangeTrigger"
            }
        ],
        "conditions": [],
        "actions": [
            {
                "id": 2,
                "label": "Send command ON to Patio_Lights",
                "inputs": {},
                "configuration": {
                    "itemName": "Patio_Lights",
                    "command": "ON"
                },
                "type": "core.ItemCommandAction"
            },
            {
                "id": 3,
                "label": "Proceed to condition: If Away_Mode = ON",
                "description": "Run next rules: example_flow_1_3 & example_flow_1_3neg",
                "inputs": {},
                "configuration": {
                    "considerConditions": true,
                    "ruleUIDs": [
                        "example_flow_1_3",
                        "example_flow_1_3neg"
                    ]
                },
                "type": "core.RunRuleAction"
            }
        ]
    },
    {
        "uid": "example_flow_1_3",
        "name": "If Away_Mode = ON",
        "description": "Created by Flows Builder for the \"then\" path of a condition. Do not run separately, will be overwritten if the flow is published again.",
        "visibility": "VISIBLE",
        "enabled": true,
        "triggers": [],
        "conditions": [
            {
                "id": 1,
                "label": "If Away_Mode = ON",
                "inputs": {},
                "configuration": {
                    "itemName": "Away_Mode",
                    "operator": "=",
                    "state": "ON"
                },
                "type": "core.ItemStateCondition"
            }
        ],
        "actions": [
            {
                "id": 2,
                "label": "Say \"Intrusion detected!\"",
                "inputs": {},
                "configuration": {
                    "text": "Intrusion detected!",
                    "sink": "webaudio"
                },
                "type": "media.SayAction"
            },
            {
                "id": 3,
                "label": "Play barking.mp3",
                "inputs": {},
                "configuration": {
                    "sound": "barking.mp3",
                    "sink": "webaudio"
                },
                "type": "media.PlayAction"
            },
            {
                "id": 4,
                "label": "Report intrusion",
                "inputs": {},
                "configuration": {
                    "type": "application/javascript",
                    "script": "print('Intrusion report');"
                },
                "type": "script.ScriptAction"
            }
        ]
    },
    {
        "uid": "example_flow_1_3neg",
        "name": "NOT(If Away_Mode = ON)",
        "description": "Created by Flows Builder for the \"else\" path of a condition. Do not run separately, will be overwritten if the flow is published again.",
        "visibility": "VISIBLE",
        "enabled": true,
        "triggers": [],
        "conditions": [
            {
                "id": 1,
                "label": "NOT(If Away_Mode = ON)",
                "inputs": {},
                "configuration": {
                    "itemName": "Away_Mode",
                    "operator": "!=",
                    "state": "ON"
                },
                "type": "core.ItemStateCondition"
            }
        ],
        "actions": [
            {
                "id": 2,
                "label": "Proceed to condition: If the day is SAT,SUN",
                "description": "Run next rules: example_flow_1_3neg_7 & example_flow_1_3neg_7neg",
                "inputs": {},
                "configuration": {
                    "considerConditions": true,
                    "ruleUIDs": [
                        "example_flow_1_3neg_7",
                        "example_flow_1_3neg_7neg"
                    ]
                },
                "type": "core.RunRuleAction"
            }
        ]
    },
    {
        "uid": "example_flow_1_3neg_7",
        "name": "If the day is SAT,SUN",
        "description": "Created by Flows Builder for the \"then\" path of a condition. Do not run separately, will be overwritten if the flow is published again.",
        "visibility": "VISIBLE",
        "enabled": true,
        "triggers": [],
        "conditions": [
            {
                "id": 1,
                "label": "If the day is SAT,SUN",
                "inputs": {},
                "configuration": {
                    "days": [
                        "SAT",
                        "SUN"
                    ]
                },
                "type": "timer.DayOfWeekCondition"
            }
        ],
        "actions": [
            {
                "id": 2,
                "label": "Start weekend playlist",
                "inputs": {},
                "configuration": {
                    "itemName": "Yamaha_NetRadio",
                    "command": "1"
                },
                "type": "core.ItemCommandAction"
            }
        ]
    },
    {
        "uid": "example_flow_1_3neg_7neg",
        "name": "NOT(If the day is SAT,SUN)",
        "description": "Created by Flows Builder for the \"else\" path of a condition. Do not run separately, will be overwritten if the flow is published again.",
        "visibility": "VISIBLE",
        "enabled": true,
        "triggers": [],
        "conditions": [
            {
                "id": 1,
                "label": "NOT(If the day is SAT,SUN)",
                "inputs": {},
                "configuration": {
                    "days": [
                        "MON",
                        "TUE",
                        "WED",
                        "THU",
                        "FRI"
                    ]
                },
                "type": "timer.DayOfWeekCondition"
            }
        ],
        "actions": [
            {
                "id": 2,
                "label": "Say \"Welcome home\"",
                "inputs": {},
                "configuration": {
                    "text": "Welcome home",
                    "sink": "webaudio"
                },
                "type": "media.SayAction"
            },
            {
                "id": 3,
                "label": "Run afterwork routine",
                "inputs": {},
                "configuration": {
                    "type": "application/javascript",
                    "script": "print('Afterwork routine')"
                },
                "type": "script.ScriptAction"
            }
        ]
    }
]

There are a number of caveats, which can probably be addressed in the future; most notably, multi-trigger rules, loops and path merges are not possible, for instance this is not allowed:

image

Anyways, let me know if my design decisions are wrong (some of them are for sure, but it’s only another fun experiment); otherwise, it’s EPL-licensed, so feel free to use it, fork it and do what you want with it :slight_smile:

Cheers!


Node-RED as Alternative Rule Engine
PaperUI Rules vs HABmin
Making OH Rules Easier for Everyone
OpenHAB or not? (KNX, HUE...)
Integrate a Snips voice assistant with openHAB - Walkthrough
Next generation design: A Paper UI replacement proposal
If you are a software developer, try Jython rules
Next generation design: A Paper UI replacement proposal
(Mattwire) #2

@ysc This is brilliant! Makes rules so much more accessible


(Artyom Syomushkin) #3

It looks similar to Node-RED logic.


(Mattwire) #4

I’m getting a blank screen on latest snapshot of openhab (openhab 2.2.0 #1020)


(Yannick Schaus) #5

Which browser? What’s in the dev tools console?


(Mattwire) #6

Both chrome and firefox. This is the error in firefox:

openHAB 2 service configuration loaded  storage.service.js:25:21
Error: Name not valid.
e@http://marvin:8080/flowsbuilder/vendor/vendor.js:496:5713
h/<.instantiate@http://marvin:8080/flowsbuilder/vendor/vendor.js:42:109
fb/n.$provide.service</<@http://marvin:8080/flowsbuilder/vendor/vendor.js:42:403
h/<.invoke@http://marvin:8080/flowsbuilder/vendor/vendor.js:42:6
f/<@http://marvin:8080/flowsbuilder/vendor/vendor.js:39:203
h/<.invoke@http://marvin:8080/flowsbuilder/vendor/vendor.js:42:6
fb/H<@http://marvin:8080/flowsbuilder/vendor/vendor.js:43:308
d@http://marvin:8080/flowsbuilder/vendor/vendor.js:41:1
e@http://marvin:8080/flowsbuilder/vendor/vendor.js:41:242
h/<.instantiate@http://marvin:8080/flowsbuilder/vendor/vendor.js:42:77
fb/n.$provide.service</<@http://marvin:8080/flowsbuilder/vendor/vendor.js:42:403
h/<.invoke@http://marvin:8080/flowsbuilder/vendor/vendor.js:42:6
f/<@http://marvin:8080/flowsbuilder/vendor/vendor.js:39:203
h/<.invoke@http://marvin:8080/flowsbuilder/vendor/vendor.js:42:6
fb/H<@http://marvin:8080/flowsbuilder/vendor/vendor.js:43:308
d@http://marvin:8080/flowsbuilder/vendor/vendor.js:41:1
e@http://marvin:8080/flowsbuilder/vendor/vendor.js:41:242
h/<.instantiate@http://marvin:8080/flowsbuilder/vendor/vendor.js:42:77
tf/this.$get</<@http://marvin:8080/flowsbuilder/vendor/vendor.js:91:372
w/<.link@http://marvin:8080/flowsbuilder/vendor/vendor.js:332:274
db/<@http://marvin:8080/flowsbuilder/vendor/vendor.js:16:69
sa@http://marvin:8080/flowsbuilder/vendor/vendor.js:82:244
n@http://marvin:8080/flowsbuilder/vendor/vendor.js:68:6
g@http://marvin:8080/flowsbuilder/vendor/vendor.js:59:428
ca/<@http://marvin:8080/flowsbuilder/vendor/vendor.js:59:67
dc/<@http://marvin:8080/flowsbuilder/vendor/vendor.js:64:65
d@http://marvin:8080/flowsbuilder/vendor/vendor.js:60:404
m@http://marvin:8080/flowsbuilder/vendor/vendor.js:65:88
B@http://marvin:8080/flowsbuilder/vendor/vendor.js:331:373
If/this.$get</m.prototype.$broadcast@http://marvin:8080/flowsbuilder/vendor/vendor.js:149:460
n/<@http://marvin:8080/flowsbuilder/vendor/vendor.js:336:319
e/<@http://marvin:8080/flowsbuilder/vendor/vendor.js:132:434
If/this.$get</m.prototype.$eval@http://marvin:8080/flowsbuilder/vendor/vendor.js:147:65
If/this.$get</m.prototype.$digest@http://marvin:8080/flowsbuilder/vendor/vendor.js:144:123
If/this.$get</m.prototype.$apply@http://marvin:8080/flowsbuilder/vendor/vendor.js:147:361
l@http://marvin:8080/flowsbuilder/vendor/vendor.js:99:192
K@http://marvin:8080/flowsbuilder/vendor/vendor.js:103:376
tg/</N.onload@http://marvin:8080/flowsbuilder/vendor/vendor.js:104:338
 <div ng-view="" class="ng-scope">  vendor.js:119:446
Loaded 325 openHAB items  items.service.js:32:21

(Yannick Schaus) #7

Ugh.
Do you have anything in Paper UI, Configuration > Services > UI > Flows Builder?
Is your “Flows Registry” empty?


(Antares2001) #8

Hello

that looks really great! By the way, did anybody so far do a performance comparison between textual rules and rules created with the help of the graphical rule creator?


(Mattwire) #9

I have the following in UI > Flows Builder:
Current flow id: lounge_downstairs_off_button

Flows JSON:

{
    "0700_House_to_day_mode": {
        "next_node_id": 7,
        "next_connector_id": 12,
        "nodes": [
            {
                "module_type_uid": "timer.TimeOfDayTrigger",
                "name": "",
                "id": 1,
                "x": 315,
                "y": 54,
                "type": "trigger",
                "connectors": [
                    {
                        "id": 1,
                        "type": "bottomConnector",
                        "$$hashKey": "object:1409"
                    }
                ],
                "module_config": {
                    "time": "07:00"
                },
                "$$hashKey": "object:1391"
            },
            {
                "module_type_uid": "core.ItemCommandAction",
                "name": "",
                "id": 2,
                "x": 458,
                "y": 209,
                "type": "action",
                "connectors": [
                    {
                        "id": 2,
                        "type": "topConnector",
                        "$$hashKey": "object:1412"
                    },
                    {
                        "id": 3,
                        "type": "bottomConnector",
                        "$$hashKey": "object:1414"
                    }
                ],
                "module_config": {
                    "itemName": "FF_Lights",
                    "command": "OFF"
                },
                "$$hashKey": "object:1392"
            },
            {
                "module_type_uid": "core.ItemCommandAction",
                "name": "",
                "id": 3,
                "x": 118,
                "y": 142,
                "type": "action",
                "connectors": [
                    {
                        "id": 4,
                        "type": "topConnector",
                        "$$hashKey": "object:1417"
                    },
                    {
                        "id": 5,
                        "type": "bottomConnector",
                        "$$hashKey": "object:1419"
                    }
                ],
                "module_config": {
                    "itemName": "upstairs_mode",
                    "command": "Day"
                },
                "$$hashKey": "object:1393"
            },
            {
                "module_type_uid": "core.ItemCommandAction",
                "name": "",
                "id": 4,
                "x": 462,
                "y": 374,
                "type": "action",
                "connectors": [
                    {
                        "id": 6,
                        "type": "topConnector",
                        "$$hashKey": "object:1422"
                    },
                    {
                        "id": 7,
                        "type": "bottomConnector",
                        "$$hashKey": "object:1424"
                    }
                ],
                "module_config": {
                    "itemName": "GF_Lights",
                    "command": "OFF"
                },
                "$$hashKey": "object:1394"
            },
            {
                "module_type_uid": "core.ItemCommandAction",
                "name": "",
                "id": 5,
                "x": 128,
                "y": 295,
                "type": "action",
                "connectors": [
                    {
                        "id": 8,
                        "type": "topConnector",
                        "$$hashKey": "object:1427"
                    },
                    {
                        "id": 9,
                        "type": "bottomConnector",
                        "$$hashKey": "object:1429"
                    }
                ],
                "module_config": {
                    "itemName": "MF_Lights",
                    "command": "OFF"
                },
                "$$hashKey": "object:1395"
            },
            {
                "module_type_uid": "core.ItemCommandAction",
                "name": "",
                "id": 6,
                "x": 164,
                "y": 473,
                "type": "action",
                "connectors": [
                    {
                        "id": 10,
                        "type": "topConnector",
                        "$$hashKey": "object:1432"
                    },
                    {
                        "id": 11,
                        "type": "bottomConnector",
                        "$$hashKey": "object:1434"
                    }
                ],
                "module_config": {
                    "itemName": "downstairs_occupied",
                    "command": "ON"
                },
                "$$hashKey": "object:1396"
            }
        ],
        "edges": [
            {
                "source": 3,
                "destination": 8,
                "$$hashKey": "object:1384"
            },
            {
                "source": 9,
                "destination": 6,
                "$$hashKey": "object:1385"
            },
            {
                "source": 1,
                "destination": 4,
                "$$hashKey": "object:6427"
            },
            {
                "source": 5,
                "destination": 2,
                "$$hashKey": "object:6430"
            },
            {
                "source": 7,
                "destination": 10,
                "$$hashKey": "object:7901"
            }
        ],
        "name": "When the time is 07:00",
        "description": "Turn all lights off, heating on downstairs",
        "updatedTime": "2017-08-20T18:36:24.128Z"
    },
    "0000_House_to_night_mode": {
        "next_node_id": 7,
        "next_connector_id": 12,
        "nodes": [
            {
                "module_type_uid": "timer.TimeOfDayTrigger",
                "name": "",
                "id": 1,
                "x": 315,
                "y": 54,
                "type": "trigger",
                "connectors": [
                    {
                        "id": 1,
                        "type": "bottomConnector",
                        "$$hashKey": "object:1409"
                    }
                ],
                "module_config": {
                    "time": "07:00"
                },
                "$$hashKey": "object:1391"
            },
            {
                "module_type_uid": "core.ItemCommandAction",
                "name": "",
                "id": 2,
                "x": 458,
                "y": 209,
                "type": "action",
                "connectors": [
                    {
                        "id": 2,
                        "type": "topConnector",
                        "$$hashKey": "object:1412"
                    },
                    {
                        "id": 3,
                        "type": "bottomConnector",
                        "$$hashKey": "object:1414"
                    }
                ],
                "module_config": {
                    "itemName": "FF_Lights",
                    "command": "OFF"
                },
                "$$hashKey": "object:1392"
            },
            {
                "module_type_uid": "core.ItemCommandAction",
                "name": "",
                "id": 3,
                "x": 118,
                "y": 142,
                "type": "action",
                "connectors": [
                    {
                        "id": 4,
                        "type": "topConnector",
                        "$$hashKey": "object:1417"
                    },
                    {
                        "id": 5,
                        "type": "bottomConnector",
                        "$$hashKey": "object:1419"
                    }
                ],
                "module_config": {
                    "itemName": "upstairs_mode",
                    "command": "Day"
                },
                "$$hashKey": "object:1393"
            },
            {
                "module_type_uid": "core.ItemCommandAction",
                "name": "",
                "id": 4,
                "x": 462,
                "y": 374,
                "type": "action",
                "connectors": [
                    {
                        "id": 6,
                        "type": "topConnector",
                        "$$hashKey": "object:1422"
                    },
                    {
                        "id": 7,
                        "type": "bottomConnector",
                        "$$hashKey": "object:1424"
                    }
                ],
                "module_config": {
                    "itemName": "GF_Lights",
                    "command": "OFF"
                },
                "$$hashKey": "object:1394"
            },
            {
                "module_type_uid": "core.ItemCommandAction",
                "name": "",
                "id": 5,
                "x": 128,
                "y": 295,
                "type": "action",
                "connectors": [
                    {
                        "id": 8,
                        "type": "topConnector",
                        "$$hashKey": "object:1427"
                    },
                    {
                        "id": 9,
                        "type": "bottomConnector",
                        "$$hashKey": "object:1429"
                    }
                ],
                "module_config": {
                    "itemName": "MF_Lights",
                    "command": "OFF"
                },
                "$$hashKey": "object:1395"
            },
            {
                "module_type_uid": "core.ItemCommandAction",
                "name": "",
                "id": 6,
                "x": 164,
                "y": 473,
                "type": "action",
                "connectors": [
                    {
                        "id": 10,
                        "type": "topConnector",
                        "$$hashKey": "object:1432"
                    },
                    {
                        "id": 11,
                        "type": "bottomConnector",
                        "$$hashKey": "object:1434"
                    }
                ],
                "module_config": {
                    "itemName": "downstairs_occupied",
                    "command": "ON"
                },
                "$$hashKey": "object:1396"
            }
        ],
        "edges": [
            {
                "source": 3,
                "destination": 8,
                "$$hashKey": "object:1384"
            },
            {
                "source": 9,
                "destination": 6,
                "$$hashKey": "object:1385"
            },
            {
                "source": 1,
                "destination": 4,
                "$$hashKey": "object:6427"
            },
            {
                "source": 5,
                "destination": 2,
                "$$hashKey": "object:6430"
            },
            {
                "source": 7,
                "destination": 10,
                "$$hashKey": "object:7901"
            }
        ],
        "name": "When the time is 07:00",
        "description": "Turn all lights off, heating on downstairs",
        "updatedTime": "2017-08-20T18:36:24.128Z"
    },
    "lounge_downstairs_off_button": {
        "next_node_id": 3,
        "next_connector_id": 4,
        "nodes": [
            {
                "module_type_uid": "core.ChannelEventTrigger",
                "id": 1,
                "x": 210,
                "y": 97,
                "type": "trigger",
                "connectors": [
                    {
                        "id": 1,
                        "type": "bottomConnector",
                        "$$hashKey": "object:169"
                    }
                ],
                "module_config": {
                    "channelUID": "amazondashbutton:dashbutton:15dd2cfa384:press"
                },
                "$$hashKey": "object:166"
            },
            {
                "module_type_uid": "core.ItemCommandAction",
                "name": "",
                "id": 2,
                "x": 317,
                "y": 283,
                "type": "action",
                "connectors": [
                    {
                        "id": 2,
                        "type": "topConnector",
                        "$$hashKey": "object:208"
                    },
                    {
                        "id": 3,
                        "type": "bottomConnector",
                        "$$hashKey": "object:210"
                    }
                ],
                "module_config": {
                    "itemName": "downstairs_occupied",
                    "command": "OFF"
                },
                "$$hashKey": "object:205"
            }
        ],
        "edges": [
            {
                "source": 1,
                "destination": 2,
                "$$hashKey": "object:214"
            }
        ],
        "name": "lounge_downstairs_off_button",
        "description": "Amazon Dash Button to set downstairs_occupied = OFF",
        "updatedTime": "2017-08-21T21:16:32.940Z"
    }
}

(Yannick Schaus) #10

Looks like this node is missing a name for some reason… ngFlowchart requires one when it parses the flow. Must be a bug. You can either add one (even an empty string will do) or get rid of the entire flow (the parent “lounge_downstairs_off_button”) and change the currentFlow value to another flow id, your choice.
If this doesn’t solve the problem, check for other nodes without names.


(Mattwire) #11

Looks like this node is missing a name for some reason

That fixed it, thankyou. I think it probably is a bug. I think I did something like this to trigger it:

  1. Add flow.
  2. Edit node - add name.
  3. Edit node - remove name (use default computed).
  4. Broken.

(Yannick Schaus) #12

Yes, that’s what I supposed happened. Thanks for betatesting! :wink:


Experimental Next-Gen Rules Engine Documentation 1 of : Introduction
Next generation design: A Paper UI replacement proposal
Experimental Next-Gen Rules Engine Documentation 3 of : Your First Rule
(Yannick Schaus) #13

I fixed it, and also added a sweet new feature :slight_smile:
Thanks to Tern and its CodeMirror addon, when editing JavaScript (ECMAScript actually) the script editor now has very decent intellisense, with embedded documentation, type awareness and even partial definitions for ESH/openHAB types and global objects!

It will also try to infer the script context, that is, the outputs of parent nodes (for instance a “state change” trigger will output oldState and newState) and provide autocompletion and intellisense for these too.

Demo of a potential real-work scenario:

Please try it out and report back problems. Enjoy!

Release here: https://github.com/ghys/flowsbuilder/releases/latest


(Yannick Schaus) #14

Useful resource for scripting rules:
https://www.n-k.de/riding-the-nashorn/#_loading_scripts

I learned it’s perfectly possible to do this in a script:

load('https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.11.1/moment.min.js');

// will update the item's state to 'Last Friday at 2:13 PM'
events.postUpdate('MyItem', moment().subtract(6, 'days').calendar());

Another reason for me to definitely try to move away from Xtext rules :wink:


(Yannick Schaus) #15

And even though XMLHttpRequests are not possible in Nashorn, this worked too:
(found here: https://gist.github.com/billybong/a462152889b6616deb02)

/*####################################################################################################################################
# As Nashorn does not have http capabilities through XMLHttpRequest (DOM API), we have to use regular Java classes instead.
# This sample shows how this can be acheived without depending on any third party libraries. Just a standard Java 8 JDK.
# Make sure to have JAVA_HOME/bin on your PATH for the shebang to work. Then just chmod +x away and run...
# Alternatively if you're on a non *nix OS, start with jjs -scritping httpsample.js
####################################################################################################################################*/

var url = "https://api.github.com/repos/openhab/openhab-distro";
var response;

response = httpGet(url).data;

var repo = JSON.parse(response);

events.postUpdate('Open_Issues', repo.open_issues_count);


/*************
UTILITY FUNCTIONS
*************/

function httpGet(theUrl){
    var con = new java.net.URL(theUrl).openConnection();
    con.requestMethod = "GET";

    return asResponse(con);
}

function httpPost(theUrl, data, contentType){
    contentType = contentType || "application/json";
    var con = new java.net.URL(theUrl).openConnection();

    con.requestMethod = "POST";
    con.setRequestProperty("Content-Type", contentType);

    // Send post request
    con.doOutput=true;
    write(con.outputStream, data);

    return asResponse(con);
}

function asResponse(con){
    var d = read(con.inputStream);

    return {data : d, statusCode : con.responseCode};
}

function write(outputStream, data){
    var wr = new java.io.DataOutputStream(outputStream);
    wr.writeBytes(data);
    wr.flush();
    wr.close();
}

function read(inputStream){
    var inReader = new java.io.BufferedReader(new java.io.InputStreamReader(inputStream));
    var inputLine;
    var response = new java.lang.StringBuffer();

    while ((inputLine = inReader.readLine()) != null) {
           response.append(inputLine);
    }
    inReader.close();
    return response.toString();
}

(Yannick Schaus) #16

I added an autocompletion for items in the script editor, just open a string literal ("") where an item name is expected and hit Ctrl+Space:

flowsbuilder-items-autocomplete

and a little debugger for sending commands and monitoring SSE events because why not :smile: :

Also you can copy-paste nodes now, including between flows. Use the regular keyboard shortcuts, Ctrl-X/C/V.

IMPORTANT: Unpublish all rules made with previous releases before trying out 2.2.0.201708251715! This release won’t be able to see them!


(Kuba Wolanin) #17

Dude. This is like the most awesome thing ever! :open_mouth:


(Yannick Schaus) #18

Spent a couple hours on the debugger this morning:

The log messages are more user friendly (not all event types are recognized).

Add items to a watchlist and monitor their states.
You can also send update the state from the watchlist by editing the text box and hitting Return:
this will send a command (or hit Esc to discard your changes).
Add an item to the watchlist by clicking its name in the log.

This might be useful to you even if you don’t use the other features.


(Mattwire) #19

@ysc Really enjoying the debugger!

I see you set “visibility: hidden” for rules in the latest version - but that seems to have the side effect of them not being shown when using the REST API: eg. http://openhab:8080/rest/rules. Do you have any suggestions? Otherwise I’ll raise this as an issue against eclipse smarthome


(Yannick Schaus) #20

@mattwire Thanks!

About the rules being hidden now: this is intentional, to avoid polluting the view in Paper UI with “technical” rules, and discourage editing them directly. Certainly not an issue with ESH!

They are tagged with the ID of the flow so for example if you published rules related to “flow1” they can still be listed with: http://openhab:8080/rest/rules?tags=flow1

If you’re not sure exactly about which rules are in your system you can always check out the JSON DB in userdata/jsondb/automation_rules.json


Event Scheduler - REST API?