Textual vs. Ui driven configuration

You can back up using this command:
sudo openhab-cli backup ~/openhab3-WIP-good-so-far.zip

I use only the UI. I used to use files but why bother trying to work out the syntax etc.
The UI is pretty good.
Yes I love my vim editor but the UI is how I am doing it now.
I also dropped DSL and all my rules are JavaScript.

A hybrid solution

Since OH 3.0, I am using a hybrid of file-based and UI configuration.

The file based part

Items, things and sitemaps are completely configured via files, because I came from OH 2.5 and there was no good UI-based way of configuration. Personally, I like the files for easy search and renaming items and they are really easy to backup. Also, I can work offline on my openHAB configuration and upload it to the openHAB server later. In my opinion, the only drawback of the files is that loading them takes quite a while.
I have about 550 items and 53 things.

The hybrid part

Most of my rules are still file-based in .rules, but I am mirgrating them now to UI-based rules and scripts for better code-reuse.
The biggest disadvantage for me of the file-based .rules is, that I have many rules which do exactly the same with different items. I had to copy and paste them many times and I had to adjust the item names.
Now with the new UI-based scripts and rules I can easily reuse the code.
And UI-based rules and scripts have another big advantage: my rules and JavaScript scripts are extremely fast compared to the file-based rules.

Conclusion

When you come from openHAB 2.x, stay at your file-based configuration for items, things and sitemaps and maybe switch them over to UI-based later.
But you should really try it the UI-based rules and scripts for their speed.
When you are new to openHAB 3.x, personally I would completely go with the UI-based configuration as you don‘t have to learn much and it is very intuitive.

2 Likes

Is there a good way to extract the parameters from the UI, or are you just doing that for each item individually?

It isn’t automatic but looking at the ‘’’code’’’ tab in the thing definition in the UI shows the parameter names and values that you can replicate.

Similarly with items, you can use the ‘’’Channels’’’ tab to get the ‘’’channel=‘’’ string to link it to an item.

3 Likes

Guys, thanks a lot for all your inputs, that gives me a good starting point! Helped me a lot.

As written in my inital post i have massive amount of things and items currently configured file based in openHAB2.5. So i will proceed using the file based approach and will test the Ui step by step (and maybe use the semantic model).

Thanks
Patrick

For info, you don’t need the UI to generate the semantic model, though it is a bit friendlier.

1 Like

Nice. I didn’t upgrade to OH3 until February and missed seeing when you originally posted this. Thanks for mentioning it here.

I feel this problem. How to get clear overview of item metadata (e.g. expire) and how to compare many similar items / things easily to spot configuration errors.

Most of my items are bound to modbus and traditionally many use text for these. In the end I bit the bullet and built everything with UI. Semantic model was one the main reasons for me, but also syntax error free configuration.

I ended up doing a small python command line app (calling OH HTTP API) to split out details of things, items, links in a tabular format. I highlight unbound items that are probably result of configuration error. I know this is definitely not for everyone but for me it really worked and allowed me to identify issues in the config.

You can use the semantic model with text config. [OH3] Semantic Model setup via tags in configuration Items files

I am aware. But prefer the visuals to see the hiearchy…

I would say that the textual vs UI driver discussion will never end, cause both have their own advantages and disadvantages. What I would like to point, is that current way of handling textual configuration is based on xtext which is very specific to openHAB.
Over time we have seen a failed attempt to make an IDE around that syntax (anyone recall Eclipse SmartHome designer?). We also have community members who work pretty hard to add support for this syntax in modern tools to simplify people’s life. Yet, I believe, these do not solve a core issue of the xtext based approach. The issue is that you cant read, modify and write it back. Or there is no one in this community who knows, or want to learn, how to do it.
I recently wrote a new XML based configuration for persistence services (sample persistence.xml file) especially for that reason. Over time I hope to get a way to manage persistence over UI and write it back to configuration file when needed.
I am not sure if I will invest time to do same for things and items files, but I would welcome an alternative to jsondb (anyone who tried to edit json files knows how hard it is). A “read only” handling of user supplied files should be a switchable feature and not limitation imposed by the framework. For some reason openhab core is able to parse above definitions, create model elements out of it but it can’t synchronize it back to the disk. To me this is pretty artificial and should be marked as first to shoot in OH 4. :wink:

Anyhow, where is a will there will be a solution!
Cheers,
Łukasz

1 Like

Hello, can you post me some Javascript rules you
Implemented in your openhab setup. I’am a beginner and like to learn about rules and Javascript.
Greetings Peter

Here is a rule that I have that is disabled and it is called ztemplate because it is a collection of commands I have used and I can cut and paste from this. You cannot run this rule it is for reference only. Just get the bits you want.
I don’t use DSL as I read somewhere it caused memory issues. Javascript works better apparently. Whether that is still the case I don’t know.

Click here for sample code
//This contains things that I have found that work in javascript
//More information here: https://openhab-scripters.github.io/openhab-helper-libraries/Guides/But%20How%20Do%20I.html

//need below to log to openhab.log file
var logger = Java.type('org.slf4j.LoggerFactory').getLogger('org.openhab.rule.' + ctx.ruleUID);
//below is if you are going to use the ececute command
var Exec = Java.type("org.openhab.core.model.script.actions.Exec");
//below is also neede for the execute command
var Duration = Java.type("java.time.Duration");

//below is example of executeComand
Exec.executeCommandLine(Duration.ofSeconds(5), "/usr/bin/mosquitto_pub","\-h","192.168.0.164","\-t","cmnd/coffee/displaytext","\-m","[zOa2f0s1l1c1]" + device + "[s3l2c1]" +stateof + "[s1l8c1]Updated[c9]at:[c13tS]");

//This is the tricky part to get the triggering item. It uses event and event does NOT exist until the trigger
//has run and that is what creates the event. You CANNOT just press run now from the editor
//and expect it to work. It MUST be tested using an item trigger.

//below is get the triggering item Label
var device = itemRegistry.getItem(event.itemName).getLabel();
//or you can use the shorthand version
var device = ir.getItem(event.itemName).getLabel();

//below is to get the triggering item command from the item
var stateof = event.itemCommand ;

//below is an example to test the triggering event and see the old state and the new state
logger.info([itemRegistry.getItem(event.itemName).getLabel(), 'Changed From:', event.oldItemState ? event.oldItemState : 'Null', 'To:', newState].join(' '));

//below is how to get the state of an item
var rfinput = itemRegistry.getItem('RFBridgekitchen_Receiveddata').getState();

//below is example of logging
  logger.info('Back door opened' + variablename);

// below is a sleep funtion to slow down execution. I want to flash a lamp 3 times
function sleep(milliseconds) {
  date = Date.now();
  currentDate = null;
  do {
    currentDate = Date.now();
  } while (currentDate - date < milliseconds);
}
if (itemRegistry.getItem('Doorgarage_Doorgarage').getState() == 'OPEN' || itemRegistry.getItem('DoorshedLH_DoorshedLH').getState() == 'OPEN') {
  for (var count = 0; count < 3; count++) {
    events.sendCommand('Zigbeesengledbulb_Zigbeesengledbulb', 'ON');
    sleep(2000);
    events.sendCommand('Zigbeesengledbulb_Zigbeesengledbulb', 'OFF');
    logger.info('Door open somewhere');
  }
    events.sendCommand('Zigbeesengledbulb_Zigbeesengledbulb', 'ON');
}
// end of sleep example


//below is an example of how to send email assuming you have the mail binding correctly set up
//the mail:smtp:a514b96247 is found by looking at the mail thing

//below is how to sent html email with multiple attachemments (NOTE the plural of attachments)
var ArrayList = Java.type('java.util.ArrayList');
message = "<H2>Someone is at the front door at " + Date() + "</H2>";
attachmentUrlList = new ArrayList();
attachmentUrlList.add("http://192.168.0.164/zm/cgi-bin/zms?mode=single&monitor=6&scale=50");
attachmentUrlList.add("http://192.168.0.164/zm/cgi-bin/zms?mode=single&monitor=7&scale=50");
actions.get("mail", "mail:smtp:a514b96247").sendHtmlMailWithAttachments("testuser@gmail.com", "openHAB 3 front door bell", message , attachmentUrlList );


//below is how to post an update to an item
events.postUpdate('Doorcat_Doorcat', 'CLOSED');

//below is how to send a command to an item
events.sendCommand('CoffeeMachine_CoffeeMachine', 'OFF');

//below is how to test a value of an item and then do something
  if (itemRegistry.getItem('Tricklecharger_Tricklechargerpower').getState() < '5') {
  events.sendCommand('Tricklecharger_Tricklecharger', 'OFF');
//   logger.info('Script ran inner loop');
}


//I have left the code in to calculate the day etc as it was a pain to find any docs about it.
//calculate the day number
var datetoday = new Date();
var numberofweek = datetoday.getDay();

//if it is not NIGHT then change colour
if (itemRegistry.getItem("LocalSun_SunPhaseName").getState() != 'NIGHT') {

 if (numberofweek == 0){
 //It is Sunday and that week day is 0 and that doesn't work with the colour
  //set it to 7
  numberofweek = 7 ; 
  numberofweek = 5  //green
}else{
//it is NIGHT so only do red light  
  numberofweek = 1  //red
}
}

//end of calculate the day of the week


//this is how to find and replace - with _
events.postUpdate(itemRegistry.getItem(event.itemName).getName(), itemRegistry.getItem(event.itemName).getState().toString().replace(/-/g, '_'));

//Get the weeday name
var datetoday = new Date();
//var numberofweek = datetoday.getDate();
var weekday = datetoday.toLocaleString("default", { weekday: "short" })


//get list of group members
itemRegistry.getItem("gAllLights").members
itemRegistry.getItem("gAllLights").members.stream().forEach(function(i) { events.sendCommand(i.name, i.state.toString()); } );

var whatitis = "";
ir.getItem("Batteries").members
                   .stream()
                   .filter(function(batt) {return batt.state.intValue() <= 10; } )
                   .forEach(function(batt) { whatitis = whatitis + batt.label +": "+ batt.state +"%" + "\r\n"; } );
if(whatitis != ""){
    events.sendCommand('TelegramText', "Batteriewarnung für: \r\n" + whatitis);
}

//below will list all items and states in a group
var whatitis = "";
ir.getItem("Bedroom").members
                   .stream()
                   .forEach(function(batt) { whatitis = whatitis + batt.label +": "+ batt.state + "\r\n"; } );
if(whatitis != ""){
//    events.sendCommand('TelegramText', "Batteriewarnung für: \r\n" + whatitis);
  logger.info('\r\nGroup list: \r\n' + whatitis );
}

//below is json parse
var obj = itemRegistry.getItem("BOMdata").getState();
var test = JSON.parse(obj) ;


for (var count = 0; count < 7; count++) {
logger.info('Icon: ' + test.data[count].icon_descriptor);
logger.info('Short text: ' + test.data[count].short_text);
logger.info('Date: ' + test.data[count].date);
logger.info('Max: ' + test.data[count].temp_max);
logger.info('Min: ' + test.data[count].temp_min);
logger.info('Forecast ' + test.data[count].extended_text);
}

//>>>>>>>>>>>>>>>>>>>>>>>>>>
//More information here: https://openhab-scripters.github.io/openhab-helper-libraries/Guides/But%20How%20Do%20I.html
var OPENHAB_CONF = Java.type('java.lang.System').getenv('OPENHAB_CONF');

//need below to log to openhab.log file
var logger = Java.type('org.slf4j.LoggerFactory').getLogger('org.openhab.rule.' + ctx.ruleUID);
//below is if you are going to use the ececute command
var Exec = Java.type("org.openhab.core.model.script.actions.Exec");
//below is also neede for the execute command
var Duration = Java.type("java.time.Duration");

//var HttpUtil = Java.type("org.openhab.core.io.net.http.HttpUtil");
var HttpGet = Java.type("org.openhab.core.model.script.actions.HTTP");


//because icon names with dashes in them are reserved I replace the dash with underscore
function input(i) {
    return i.replace(/-/g,'_');
}

//var DATA = HttpUtil.executeUrl("GET","https://api.weather.bom.gov.au/v1/locations/r3fgmyd/forecasts/daily", 2000).replace(/icon_descriptor...\w+/g,input);
var DATA = HttpGet.sendHttpGetRequest("https://api.weather.bom.gov.au/v1/locations/r3fgmyd/forecasts/daily", 2000).replace(/icon_descriptor...\w+/g,input);

events.postUpdate("BOMdata", DATA);



                   
//>>>>>>>>>>>>>>>>>>>>>>>>>>>
//this is how to run a script file
var FrameworkUtil = Java.type("org.osgi.framework.FrameworkUtil");
var _bundle = FrameworkUtil.getBundle(scriptExtension.class);
var bundle_context = _bundle.getBundleContext()
var classname = "org.openhab.core.automation.RuleManager"
var RuleManager_Ref = bundle_context.getServiceReference(classname);
var RuleManager = bundle_context.getService(RuleManager_Ref);
RuleManager.runNow("8e1e6893d6"); //the 8e1e6893d6 is the id of the script found when you look in the script list

//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//Stuff below keeps the results so you can run scripts and use the this.Storage result                   
//below is to create a global array
//this.Storage = (this.Storage === undefined) ? [] : this.Storage;
this.Storage = (this.Storage === undefined) ? 1 : this.Storage;
//below is to create a global number
this.Storage = this.Storage + 1;

//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>.
These 2 statements are the do the same thing:
logger.info(items["CoffeeMachine_CoffeeMachine"])
logger.info(itemRegistry.getItem("CoffeeMachine_CoffeeMachine").getState())

//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//below splits the results comma separated
var test = itemRegistry.getItem("LightHall_Lighthallcolour").getState().toString().split(",");
logger.info("Test " +  test);
logger.info("Test " +  test[0]);
logger.info("Test " +  test[1]);
logger.info("Test " +  test[2]);

logger.info("Hue " + items["LightHall_Lighthallcolour"].getHue());
logger.info("Saturation " + items["LightHall_Lighthallcolour"].getSaturation());
logger.info("Brightness " + items["LightHall_Lighthallcolour"].getBrightness());

//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  //below will list all items and states in a group
var whatitis = "";
ir.getItem("gBatterycheck").members
                   .stream()
                   .filter(function(batt) {return batt.state.intValue() <= 45; } )
                   .forEach(function(batt) { whatitis = whatitis + batt.label +": "+ batt.state + "\r\n"; } );
if(whatitis != ""){
//    events.sendCommand('TelegramText', "Batteriewarnung für: \r\n" + whatitis);
  logger.info('\r\nGroup list: \r\n' + whatitis );
}
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//below gets the historical sate if an item
var PersistenceExtensions = Java.type("org.openhab.core.persistence.extensions.PersistenceExtensions");
var ZonedDateTime = Java.type("java.time.ZonedDateTime");

var myPersistentItem = ir.getItem("Zigbeesengledbulb_Zigbeesengledbulb");
logger.info("The pyload is {}", myPersistentItem);

var sinceDate = ZonedDateTime.now().minusDays(7);
//var sinceDate = ZonedDateTime.now().minusHours(5);

var maximumValueSince = PersistenceExtensions.maximumSince(myPersistentItem, sinceDate);

logger.info("Historic item is {}", maximumValueSince);
logger.info("Item name is {}", maximumValueSince.name);
logger.info("Item state is {}", maximumValueSince.state);

//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//below is logic for a javascript timer. I have used similar in the coffee machine on off logic
var logger          = Java.type('org.slf4j.LoggerFactory').getLogger('org.openhab.rule.' + ctx.ruleUID); 
    var ScriptExecution = Java.type("org.openhab.core.model.script.actions.ScriptExecution");  
    var ZonedDateTime   = Java.type("java.time.ZonedDateTime");  
var Exec = Java.type("org.openhab.core.model.script.actions.Exec");
var Duration = Java.type("java.time.Duration");

//set up the timercoffee
  var TimerMinutes = 55;
    logger.info("Coffee timer minutes: "+TimerMinutes);
  
if (this.timercoffee != undefined) {
      this.timercoffee.cancel();
      logger.info("Coffee timer undefined: ");
    }

    
    function displaywaring() {
      Exec.executeCommandLine(Duration.ofSeconds(5), "/usr/bin/mosquitto_pub","\-h","192.168.0.164","\-t","cmnd/coffee/displaytext","\-m","[zOf0s2]Coffee[c1l2]machine[c1l3]OFF in 5[c1l4]minutes.~3");
//      logger.info("CoffeeMachine_CoffeeMachine turned "+items["CoffeeMachine_CoffeeMachine"]);
      this.timercoffee = undefined;
            logger.info("Sending message to display: ");
    }

    this.timercoffee = ScriptExecution.createTimer(ZonedDateTime.now().plusMinutes(TimerMinutes), displaywaring);

}else{
results = Exec.executeCommandLine(Duration.ofSeconds(5), "/usr/bin/mosquitto_pub","\-h","192.168.0.164","\-t","cmnd/coffee/displaytext","\-m","[zOf0s2]Coffee[c1l2]machine[c1l3]is now off[c1l4]Bye.~3");
//If off then cancel the timercoffee
  if (this.timercoffee != undefined) {
      this.timercoffee.cancel();
      logger.info("Cancelled timer for coffee machine: ");
    }else{
      logger.info("Expired timer coffee machine: ");}

}  
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  //Get a time in between two times:
  if ((LocalDateTime.now().isAfter(LocalDate.now().atTime(19, 0))) && (LocalDateTime.now().isBefore(LocalDate.now().atTime(22, 0)))) {
	sendCommand(blablabla)
}

//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//parse json data:
  var test = JSON.parse('{"localNumber":"012345","remoteNumber":"012345","date":"2021-06-26T17:55:00+02","type":2,"duration":0}') 
logger.info("test: " + test.remoteNumber)
logger.info("date: " + test.date)
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

You will use this the most:

var logger = Java.type('org.slf4j.LoggerFactory').getLogger('org.openhab.rule.' + ctx.ruleUID);

So you can see what is happening in the code in the log files.
For example:

 logger.info('Back door opened' + variablename);

image

1 Like

I posted my examples for you to look at.

I use DSL and I’ve never had memory issues. The bigger issue is that it’s specific to openHAB, so newer users with coding experience can find it confusing/frustrating to learn.

If someone already has DSL rules in OH2, there’s no significant benefit to changing them. For anyone who’s just starting, I’d recommend against learning DSL.

The tricky thing is that many of the older tutorials and examples in the community are written in DSL. The transition away from them will take some time, but posting your Javascript definitely helps. If you’re up for it, you might even want to make a new post to share the sample code you posted above, with headings/descriptions so that it’s easy for people to scroll through and find what they’re looking for. I think a lot of people would benefit.

Hello,
I am migrating to OH3.1 and I’d like to keep textual definitions. However, I am struggling to get my MQTT Setup in a text file and the Main UI definitions.

Whenever I modify something on the UI this huge json file is changed:
./userdata/jsondb/uicomponents_ui_page.json
All pages go in here. Is it possible somehow to have the main ui config in the conf folder and hierarchic (meaning a json file per page for example)?

in OH 2.x the mqtt broker could be defined here:
conf/services/mqtt.cfg

Seems not be possible either.
Now the mqtt definitions using the automatic approach go here:
./userdata/jsondb/org.openhab.core.thing.Thing.json

AFAIK no.

Err, no. That was in OH 1.x (or in compatibility mode).
MQTT for OH 2.3 and newer is different since this was implemented.

Well in theory you should be able to define any Thing via .thing file.
You “just” have to figure out the format. Haven’t tried with MQTT but there’s a section in the link I gave above.

1 Like

Hi Markus,
this didn’t work for me:

Bridge mqtt:broker:myUnsecureBroker [ host="192.168.0.42", secure=false ]
{
    Thing mqtt:topic:mything {
    Channels:
        Type switch : lamp "Kitchen Lamp" [ stateTopic="lamp/enabled", commandTopic="lamp/enabled/set" ]
        Type switch : fancylamp "Fancy Lamp" [ stateTopic="fancy/lamp/state", commandTopic="fancy/lamp/command", on="i-am-on", off="i-am-off" ]
        Type string : alarmpanel "Alarm system" [ stateTopic="alarm/panel/state", commandTopic="alarm/panel/set", allowedStates="ARMED_HOME,ARMED_AWAY,UNARMED" ]
        Type color : lampcolor "Kitchen Lamp color" [ stateTopic="lamp/color", commandTopic="lamp/color/set", rgb=true ]
        Type dimmer : blind "Blind" [ stateTopic="blind/state", commandTopic="blind/set", min=0, max=5, step=1 ]
    }
}

Should this be possible in OH3.1?

I have mqtt working but only using the “Magic”.

Dunno, as I wrote I didn’t try that myself. Possibly the syntax has changed, might be worth to cross-check with these binding docs.

You might look at Roomba980-Python/roomba.things at master · NickWaterton/Roomba980-Python · GitHub as an example for configuring MQTT. The structure looks a bit different from yours. Did work for me.