Suggestions for improved user experience with timers

You are absolutely right: the current interface is limited - but it doesn’t have to stay that way…

And: There are always ways to “abuse” even such limited interfaces: since no transformation configuration (ie: regex) is required (data should remain unchanged, only be delayed) the delay information can be coded in the transformation parameter (where a regex would be for the regex transform for example). This would even “feel” right: was is the parameter to a delay transformation? the delay value.

The issue of blocking is technically more involved as the delay should be implemented in another execution context, presumable managed by some (new) central service instance (which would issue the command to the thing after the timer expired). And since the item, the link and the thing-channel aren’t available, those (or at least the item name) would also need to be coded into transform parameter.

That’s all a little bit hackish, but it can be implemented without (java) interface changes. Maybe interface changes for TransformationService should be considered, tough.

Yes!

The thing is that transformation does not know in which context it is working. It might be state description, it might be ie. mqtt handler reading or writing value. You can’t really know when it is called.
Personally I consider TransformationService kind of legacy of OH 1.x (and probably 2.x) which is there to serve cases where basic operation on retrieved must be made. Technically its Function<String, String>. I’d rather see its use decreasing in favor of profiles. Profile on other hand in some ways can be seen as both Consumer<State> and Supplier<State>. More importantly it knows in which direction it is begin executed. In other words if it was triggered by an handler or an item. :wink:

Profile is statefull and has 1:1 relationship with link. Because there is a factory for it it can also reference other services. Technically speaking it can hold a reference to “timer manager” who manages timers and an future which is a state to be published at certain time. In practice you can see:

Thing Handler ---> DelayProfile ---> Item
                     |      ^
                     |      | (Future)
                     v      | 
                Delay/TimeoutManager --> ScheduledThreadPool

I think it looks similar to expiration handling in some ways.

Good point - didn’t think about that.

Sounds like a valid implementation approach.

But profiles are tied to channels. Are we inventing virtual channels now? Or is this a “timer binding” idea? (See Expire binding 1.x, now defunct)

That seems to be a pretty subjective feeling. Rules are there to implement behaviors. What constitutes “interesting” is completely subjective. But since the beginning, rule have been and always were intended to implement behaviors, no matter how complex or simple they may be. When event X occurs and all you want to do is send a command to Item Y, a rule is what you’d use even though it’s just one command.

In my mind it doesn’t make OH simpler or easier to use to create alternative ways to do things like this. All it really does is just move the problem. Users now have to figure out whether or not their problem becomes interesting enough that this new way to do timers will not work or not. And if they need to grow their system later they may have to abandon their new timers for rules anyway.

I completely agree that a transformation is absolutely not the place for any sort of timers.

It sounds like you probably have lots of room for consolidation. There are tons of approaches that could be used but without seeing the rules I couldn’t recommend one. If using JS or Python for the rules language I’d probably set some custom Item metadata on the Item with the delay and the ID of the rule to call and have just 1 rule to implement the delay.

In fact, I’ve already written such a rule that might work. See https://github.com/rkoshak/openhab-rules-tools/tree/main/debounce. Though that will require proxy Items. NOTE: when OH 3.2 is released rules like this will be findable and installable just like add-ons.

But you still have to implement the “thing to do after the delay” which will still require “interesting” code which will require a rule. So all you’ve really done is eliminate one line of code from each of your rules and replaced it with a brand new concept for implementing timers. I’m not yet convinced that’s a fair trade. (I could be convinced. I’m not completely against the idea, just skeptical based on my years of experiencing helping users on this forum.)

Which is one way to define a rule. You can also have rules defined in text files. Any new concept for timers (with one exception) would have to work with both.

In UI rules there are several different types of Actions one can choose from, one of which is a Script Action. The same goes for Conditions where one can choose a Script Condition among several different types of conditions.

But what you call a “script” is part of a rule. It doesn’t exist independently. You can have a rule without a “script” but you cannot have a “script” without a rule. If you are parsing “rules” then you are by definition also parsing scripts unless you say “rules except scripts”. The stuff you see under “Scripts” in MainUI are a special type of rule that consists of only a single Script Action with the tag “Script” applied. It’s still a rule though.

And if you are ignoring Script Actions and Script Conditions then you are missing a huge area where Items are referenced and used. I don’t think you can just ignore them and have a tool that is complete. And as I said there will still be cases where you can’t identify when I Item is used by a given rule, which is probably OK. This is a case where good enough is good enough.

Finally, that syntax/structure you show only exists in MainUI. The actual syntax and structure used by rules varies based on the language and how it’s defined. But for this conversation we can probably focus on the JSON returned by the REST API as all the other text based ways to define a rule can be acquired in this form.

For example:

{
  "status": {
    "status": "IDLE",
    "statusDetail": "NONE"
  },
  "editable": true,
  "triggers": [
    {
      "id": "1",
      "configuration": {
        "groupName": "Debounce"
      },
      "type": "core.GroupStateChangeTrigger"
    }
  ],
  "conditions": [
    {
      "inputs": {},
      "id": "3",
      "configuration": {
        "type": "application/javascript",
        "script": "event.itemState.class != UnDefType.class"
      },
      "type": "script.ScriptCondition"
    }
  ],
  "actions": [
    {
      "inputs": {},
      "id": "2",
      "configuration": {
        "type": "application/javascript",
        "script": "var logger = Java.type(\"org.slf4j.LoggerFactory\").getLogger(\"org.openhab.model.script.Rules.Debounce\");\n\n// Get Metadata query stuff\nthis.FrameworkUtil = (this.FrameworkUtil === undefined) ? Java.type(\"org.osgi.framework.FrameworkUtil\") : this.FrameworkUtil;\nthis._bundle = (this._bundle === undefined) ? FrameworkUtil.getBundle(scriptExtension.class) : this._bundle;\nthis.bundle_context = (this.bundle_context === undefined) ? this._bundle.getBundleContext() : this.bundle_context;\nthis.MetadataRegistry_Ref = (this.MetadataRegistry_Ref === undefined) ? bundle_context.getServiceReference(\"org.openhab.core.items.MetadataRegistry\") : this.MetadataRegistry_Ref;\nthis.MetadataRegistry = (this.MetadataRegistry === undefined) ? bundle_context.getService(MetadataRegistry_Ref) : this.MetadataRegistry;\nthis.Metadata = (this.Metadata === undefined) ? Java.type(\"org.openhab.core.items.Metadata\") : this.Metadata;\nthis.MetadataKey = (this.MetadataKey === undefined) ? Java.type(\"org.openhab.core.items.MetadataKey\") : this.MetadataKey;\n\n// Load TimerMgr\nthis.OPENHAB_CONF = (this.OPENHAB_CONF === undefined) ? java.lang.System.getenv(\"OPENHAB_CONF\") : this.OPENHAB_CONF;\nload(this.OPENHAB_CONF+'/automation/lib/javascript/community/timerMgr.js');\n\n/**\n * Get and check the item metadata.\n * @return {dict} The metadata parsed and validated\n */\nvar checkMetadata = function(itemName) {\n  var USAGE = \"Debounce metadata should follow debounce=ProxyItem[command=true, timeout='2s', state='ON,OFF'].\"\n  var cfg = MetadataRegistry.get(new MetadataKey(\"debounce\", itemName));\n  if(cfg === null) {\n    throw itemName + \" does not have debounce metadata! \" + USAGE;\n  }\n  \n  if(cfg.value === undefined || cfg.value === null) {\n    throw itemName + \" does not have a proxy Item defined! \" + USAGE;\n  }\n  if(items[cfg.value === undefined]) {\n    throw \"Proxy Item \" + cfg.value + \" does not exist! \" + USAGE;\n  }\n  if(cfg.configuration[\"timeout\"] == undefined || cfg.configuration[\"timeout\"] === null) {\n    throw itemName + \" does not have a timeout parameter defined! \" + USAGE;\n  }\n  var dict = {\"proxy\": cfg.value,\n              \"timeout\": cfg.configuration[\"timeout\"],\n              \"command\": \"command\" in cfg.configuration && cfg.configuration[\"command\"].toLowerCase() == \"true\",\n              };\n              \n  var stateStr = cfg.configuration[\"state\"];\n  if(stateStr === undefined || stateStr === null) {\n    throw itemName + \" does not have proper debounce metadata \" + cfg.toString();\n  }\n  var split = stateStr.split(\",\");\n  dict[\"states\"] = [];\n  for(st in split) {\n    dict[\"states\"].push(split[st]);\n  }\n  return dict;\n}\n\n/**\n * Called when the debounce timer expires, transfers the current state to the \n * proxy Item.\n * @param {string} state the state to transfer to the proxy Item\n * @param {string} name of the proxy Item\n * @param {Boolean} when true, the state is sent as a command\n */\nvar end_debounce_generator = function(state, proxy, isCommand) {\n    return function() {\n        logger.debug(\"End debounce for \" + proxy + \", new state = \" + state + \", curr state = \" + items[proxy] + \", command = \" + isCommand);\n        if(isCommand && items[proxy] != state) {\n          logger.debug(\"Sending command \" + state + \" to \" + proxy);\n          events.sendCommand(proxy, state);\n        }\n        else if (items[proxy] != state) {\n          logger.debug(\"Posting update \" + state + \" to \" + proxy);\n          events.postUpdate(proxy, state);\n        }\n      }\n}\n\nthis.timers = (this.timers === undefined) ? new TimerMgr() : this.timers;\nvar cfg = checkMetadata(event.itemName);\n\nif(cfg[\"states\"].length == 0 || \n  (cfg[\"states\"].length > 0 && cfg[\"states\"].indexOf(event.itemState.toString()) >= 0)) {\n  logger.debug(\"Debouncing \" + event.itemName + \" with proxy = \" + cfg[\"proxy\"] \n               + \" timeout = \" + cfg[\"timeout\"] + \" and states = \" + cfg[\"states\"]);\n  this.timers.check(event.itemName, cfg[\"timeout\"], \n                    end_debounce_generator(event.itemState, cfg[\"proxy\"], cfg[\"command\"]));    \n}\nelse {\n  logger.debug(event.itemName + \" changed to \" + event.itemState + \" which is not debouncing\");\n  end_debounce_generator(event.itemState, cfg[\"proxy\"], cfg[\"command\"])();\n}\n"
      },
      "type": "script.ScriptAction"
    }
  ],
  "configuration": {},
  "configDescriptions": [],
  "uid": "debounce",
  "name": "Debounce",
  "tags": [
    "rules_tools"
  ],
  "visibility": "VISIBLE",
  "description": "Waits a configured time before passing an Item's state to a proxy"
}

That’s the JSON representation of that Debounce rule I linked to above.

Here is what a “Script” looks like:

{
  "status": {
    "status": "IDLE",
    "statusDetail": "NONE"
  },
  "editable": true,
  "triggers": [],
  "conditions": [],
  "actions": [
    {
      "inputs": {},
      "id": "script",
      "configuration": {
        "type": "application/javascript",
        "script": "var logger = Java.type(\"org.slf4j.LoggerFactory\").getLogger(\"org.openhab.model.script.Rules.Expamples\");\nlogger.info(\"About to test createTimer\");\nvar ScriptExecution = Java.type(\"org.openhab.core.model.script.actions.ScriptExecution\");\nvar runme = function(){ logger.info(\"Timer expired!\"); }\nvar ZonedDateTime = Java.type(\"java.time.ZonedDateTime\");\nvar now = ZonedDateTime.now();\nvar timer = ScriptExecution.createTimer(now.plusSeconds(1), runme);\n"
      },
      "type": "script.ScriptAction"
    }
  ],
  "configuration": {},
  "configDescriptions": [],
  "uid": "timerexample",
  "name": "createTimer example",
  "tags": [
    "Script"
  ],
  "visibility": "VISIBLE",
  "description": "Shows how to create a timer"
}

Notice it’s the identical structure. It just doesn’t have triggers or conditions, the single script action and the tag “Script”.

While it’s not yet a tool built into OH, there is a script that does a lot of the work of parsing through the OH entities to figure out where they are used. See [script] find items or other objects in OH environment

It would be indeed awesome if that were turned into a Developer Tool in MainUI.

When/if you ever file an issue, separate the two. It’s confusing and not really appropriate to try to deal with both at the same time in the same issue.

And I still stand by the idea that adding basically the equivalent to Thread.sleep as a separate UI Action is a good idea. So focus on that. But note that this must be very simple. All that such a UI action should do is block execution of the rule for the defined amount of time. Then you can do some simple time sequencing in a UI rule without needing to resort to “code”. This action doesn’t need to know or care about any context. All it needs to do is block execution for the configured amount of time. With that you can select it as one of the actions from the UI.

Implementing something like this will required changes to OH core as well as MainUI.

There is already an issue open for a Debounce profile which is basically what we are talking about implementing. [profiles] Proposing debouncing profile · Issue #2172 · openhab/openhab-core · GitHub

I think both a Delay Action one can choose from in the UI Rules and a Debounce profile (or what ever you want to call it, but there is already an issue for Debounce) are good ideas.

The finding which Items are used where and such are also good ideas but need to be treated in a separate issue.

I’ll try to be objective for the start. Rules are worst way to implement basic things cause:

  1. They bring higher complexity than eventual result is. Putting “consistency” without comparison of “complexity” makes it look appealing. Given there are multiple ways to write rules, each with own time management quirks, you can’t make it consistent.
  2. More over, rules are working beside item-channel relation which means that eventual event must arrive in one item in order to get processed. You can’t implement effective filtering mechanism in rules, especially if frequency of updates is pretty high.
  3. Time management within various rules is supported, yet has multiple border conditions which user has to learn prior writing his own scenario.
  4. Since introduction of profiles somewhere at the end of ESH there is a CommunicationManager component. It calls profiles which can and should do basic behavior handling.
  5. Unit testing of rules, does it exist?

Now, switching to subjective mode. I am not a progressive type kind of guy, yet point where you state that “it always has been this way” proves in some terms that the efforts or pressure to improve situation are insufficient. Some of “design patterns” are simply workarounds for openHAB shortages which were retained over years. Fact that they are called a “design pattern” in current system design doesn’t make them a final or the only one solution.

Fair enough. I will let you discuss it further.

But we trade one complexity for another.

A user starts and wants something simple. So they choose to use a profile. Then they out grow that. Sorry, you have to rewrite it now using UI rules. When they out grow that, sorry, you have to throw that away and use Blockly. Out grow that. Sorry, you have to throw that away and rewrite it in Rules DSL. Out grow that. Sorry, you have to rewrite it in JavaScript (or choose some other language of choice).

We’ve replaced the complexity of using rules to implement behaviors with a progression of different progressively more complex ways to implement it which then has to be completely thrown away when the needs become even a little more complex. Simple is fine if that’s all you’ll ever need but if you do need more complex you basically have to start over.

If I were king I’d rather effort were spent making one or two ways to implement behaviors that can handle both the simple cases in a simple way and has a path to handle the complex case. What we have now is a system that seems almost purposefully designed to create rework for everyone. And adding more “simple” features just means there are more steps that will be outgrown, thrown away and reworked.

Profiles have some significant limitations. You can only apply one Profile at a time to an ItemChannelLink. You can’t use them on Items without links. You can’t use them on Event Channels. You can’t chain them together. That doesn’t mean I don’t think they should exist nor that I think there shouldn’t be a timer type profile. In fact, Expire should be a profile except for the fact that one can’t use it unless there’s a link present.

In JavaScript, GraalVM JavaScript, Jython, jRuby (I think) and Java it’s possible. I’ve not looked into it myself but others have implemented some unit tests of rules. There have been some PRs in recent months also to address this a little as well and there was some work in the Helper Libraries. Obviously we’ve a long way to go but it’s not an area that is being ignored.

But unit tests is kind of a non-sequitur in this discussion. If we are talking about implementing simple things what do we need unit tests for? Do we need unit tests for Profiles?

I’ve never claimed otherwise. And they have nothing to do with this conversation.

I’m coming from the long term experience I’ve had trying to help users, especially new users, figure out which of the many many ways to accomplish the exact same thing they should be using in which circumstances. And year-by-year I’m not seeing the number of choices get smaller. It’s not becoming easier for users to know when they should use a Profile and when to use Expire or a Transformation or a Rule. And if they do need to use a Rule which language? UI or text based? You have two choices in JavaScript but installing one completely breaks the other. jRuby, GraalVM JavaScript, and Java rules bear almost no resemblance to each other nor to Rules DSL, JavaScript, and Jython.

It’s a f$%^ing mess and only getting worse. So I’m going to be very hard to convince that adding yet another system, approach, or concept to OH, no matter how simple, is going to make things better. It will only add yet another choice the user needs to make, a choice a new user in particular is ill equipped to make.

I’m all for adding more Profiles. I think the delay function described above should be a profile. I’m also all for adding some more simple actions to UI rules so users can go a bit further before they have to scrap it all and reimplement it in a “real language”.

The number one frustration I deal with from users is not that there isn’t a simpler way to do something. It’s that the simple things don’t do enough. By the time you make the simple things complex enough to satisfy them you’ll basically have rules.

Supportive of Rich’s view here. For a newcomer, there is a bewildering array of elements - Items, Things, Rules, channels, sitemaps, apps, profiles, cards, metadata.
“I just want to turn on the light”
Okay here’s a way.
“Cool. Can I do that only when it’s dark?”
Here’s another method.
“… but just for five minutes?”
Ah, here’s this new thing to learn
“Except on weekends”
Alright …
“And longer time if Grandma is at home”

We shouldn’t plan answers tailored to “I just want” problems.

I don’t get why you and others put ourselves into a guard man in an open source project of such a scale. Making more guarding points such this definitely reduces amount of contributions and innovation coming to the project. That’s what eventually lead to ESH definitive end as well as openHAB stagnation.
A lot of problems you talk about such as “which language” comes from basic fact that you actually force a use of external tool (programming language) to do a basic operation such as $x*10 and $x*100. So mess you talking about is just result of decade long guarding that “things been done this way”.
If you wish to continue that way, I’m fine with it, yet I will rather try making own approach than wait for consensus or permission to do things in OH.

@rossko57 Lets have a look on how simple things looks a like right now.

(U - user, O - openhab)
U: I have couple of things which have a heating period, how can I get it into system?
O: Sure, you can use binding which will map it to DateTimeType
U: Even if its not a date, but period?
O: Yes, it is capable of doing that.
U: Sure, lets go this way.
O: Actually, one of your bindings uses a quantity type which is number of seconds.
U: I don’t mind that.
O: Me too, but you can’t sum quantity and date time.
U: I understand, then lets convert DateTimeType to quantity and have seconds.
O: Great, lets write our first rule.
U: ???
O: It will allow you to convert date time into seconds.
U: Got it, lets do a rule then, where it is?
O: First, pick your destiny - DSL, Python, Ruby or JavaScript
U: I know neither
O: Pick JS, its simplest.
U: If you say so…
O: Create Number:Time temporary a.k.a ‘proxy’ item for desired value
U: Done
O: Create DateTimeType item for received value.
U: Done
O: Write the code
U: …
O: Remember that JS and Java time handling can differ.
U: Java?
O: Nevermind, just use milliseconds from date time, divide it by 1000 and create quantity and update proxy item.
U: … give me a minute
… two hours later
U: I think I have it!
O: Great, you see its simple
U: I don’t think so, I just spent 4 hours on that.
O: You’re new so you have to learn. With this mechanism you can handle any situation, isn’t it great?
U: I just needed to swap your DateTimeType to Number:Time.

… we can expand this conversations and multiply it with many “but” conditions needed to write a rule. Point is, this above conversation does not have any if, its a basic misalignment of data coming from bindings. Making things conditional will always require place to enter a logic. Unconditional and sequential state transformation require no advanced mechanism.

Please don’t get me started on a fact that for very long time, if not still, a recommended way to scale/divide a number is “state description pattern” combined with an transformation (possibly a javascript). Separate one for each “scale”.

The decisions made by the developers to proliferate and add new ways to do things makes my job harder to help support new users on this forum. So if choices are made that make my job harder I’ve pushed back.

Notice though how not once has my pushing back ever led to a change in direction. Never have I been listened to. Instead more and more different ways to do the same thing keeps getting added with no thought to what it means to the users. Each and every little new feature makes wonderful sense in isolation but when you look at things holistically all those simple little features add up to overwhelming complexity to the end users.

I can’t say I’ve ever had a developer ask “what’s the impact of this change? What does adding this feature mean to the overall usability of OH? Maybe it makes more sense to implement in a different way.” No, new features just drop on us and we scramble here on the forum to learn it and start helping user use it.

I’ve tried to raise these issues. I’ve tried to answer these questions even when they are not asked. I’ve tried to advocate for the end users. But I guess that makes be a “gatekeeper”. A pretty poor one at that since I’m mostly ignored anyway.

I don’t force anyone to use anything. I do my best to try to help people use what’s there. I try to help them navigate the huge range of possibilities. But given the choice between having one tool to learn to do such calculations and three or four to choose from, each of which has it’s own limitations, quirks, and optimal use cases ,I’m going to choose the one tool every time. It’s a lot easier to learn one moderately complex tool than all the quirks of four different ones, especially when that knowledge is transferrable to other parts of OH.

I’ve tried my best to make the developers understand the implications of the changes they want to make here and on GitHub. That was my intent in this thread: to first understand what is being proposed and then maybe guide the developer towards a path that would be easier for users to adopt. I would think the amount of time and effort I’ve spent on this forum would give me at least a little bit of credibility on that topic. I’m pretty much entirely ignored at best or met with hostility at worse, so I guess not.

I guess I was too thick to get the hint. The message is now understood. “Stay in your lane. Unless you are writing the code your opinion doesn’t matter. Your experience doesn’t matter. You’re just standing in the way.”

Though I think if you actually read and understood what I’ve said in this thread you would see we are in almost perfect agreement. Can you describe your little dialog in response to @rossko57 as anything but a mess? That’s exactly what I’ve been fighting to fix. You seem to think I like it this way. That I’m fighting to keep it this way.

Adding new ways to do things without removing the old ways isn’t going to do anything but add even more lines to that dialog. It doesn’t make anything simpler. So I have fought against it in the past. But no more. I now understand my lane and my role. Developers gonna do what developers gonna do. Don’t dare push against ideas that will make things worse. I’ll just watch from the sidelines until it all collapses under the weight of all the “simple” options.

I can only hope that with the upcoming marketplace of rules can help make it so many users simply don’t need to implement behaviors at all. They just need to install them.

For the record:

  • I like Profiles, I just wish they were more generally applicable beyond just on Links, and there should be more of them: debounce, scale, etc
  • Expire should be a Profile except for the fact that Profiles only work on links, not Items
  • I think transformations should be removed from all bindings; the Profile should be used and chaining of profiles allowed in the profile
  • Units of Measurement are more trouble than they are worth; they either need to be reworked so they work more like/can be used with regular Numbers or removed
  • There needs to be more UI rule actions so user’s don’t have to resort to code so soon
  • Blockly should be removed until it’s more complete
  • Sharable libraries should be supported in all OH rules languages
  • There should be no more than two or three choices in rules languages and they should be relatively similar in structure and use so users can convert from one language to another one
  • Ease of use within OH should trump the “purity” of the language for rules
  • Most importantly of all, knowledge should be transferable. OH should be consistent. Users should only have to learn how to add two numbers once and apply that everywhere it’s needed. Not have one way to do it in a transform and another way to do it in a Profile and N ways to do it in Rules where N is the number of different Rules languages; and then multiply that all by 2 because you have the UI and text based configs.

I’ve fought really hard for the last two. That’s what I’ve done in this thread, trying to avoid yet another way to do timers.

Anyway, I’m tired. I’m tired of being ignored. Developers gonna do what developers gonna do. There’s lots of good work going on. Maybe it’ll get better. But I’m not going to push it any more. I’ll stay in my lane.

1 Like

OK, so I am the new guy here…but I’ve been in “professional” software development (at least with regard to being paid) for decades and thus have my own set of oppinions regarding everything being discussed here.

I want to adress the recent turn in discussions from an entirely different approach, please bear with me:

  1. good usability usually means that there is a low entry barrier with a continous but low learning curve; that usually implies that only a handful of concepts must be understood by the users (some of which might assumably be general knowledge the average joe/jane/… already “has”)

  2. trying to achieve good usability is usually a long and winding journey that inevitably includes wrong turns, roadblocks and lots of things that weren’t on the roadmap or even imagined before

  3. achieving good usuability is a team effort and monocultural teams share the same fate as monocultures in real life in the long run → they fail to adapt and cease to exist

  4. maintaining good usability is even harder once feature creep starts happening that impacts the meanwhile well established fundamental concepts (back to #1)

What am I trying to say (referring back to the points above):

  1. All of your different arguments seem plausible and relevant; but the big picture is probably bigger than any one can grasp by themself - it requires teamwork.

  2. OH1 → OH2 → OH3: the changes with each major version are huge and it’s completely expectable for the current state to be in not-so-perfect-conditions. Keep going.

  3. To everyone active in the “social” ecosystem (forums/…): THANK YOU FOR YOUR WORK! And please stay involved and thus add your views and experiences (and even oppinions, thats perfectly fine) to the mix. That’s essential for the success in the long run.

  4. Strive for a good balanced approach and have your technical (and conceptual) debt always on your mind.

What I am really trying to say:

This should be fun - keep in mind that we share a common goal (or at least interest) and “working” together is the only option.

Jürgen

First off, Jurgen, welcome to the openHAB community!!!

so you know your way around

Yes!, just the talent that we need!!!
:+1:
Great discussion and great first post
yes, openHAB isn’t perfect but it is super powerful and working together, we can lower the barrier for noobs. Yannick has blessed us with a new awesome UI interface that significantly lowers that learning curve.

Right on Lukasz :+1:
everyone’s contribution is important and new perspective can bring forward progress. bravo gentlemen

I’m done with my first debounce attempt.

Sources: https://github.com/ConnectorIO/connectorio-addons/tree/master/bundles/org.connectorio.addons.profile.timer

Binary: org.connectorio.addons.feature.profile-3.0.0-20210915.kar, drop it into addons folder and look for features starting with co7io- prefix. One you look for is named co7io-profile-timer.
Above KAR contains few more profiles coming from same source repository:

  • Quantity (converts a number to a quantity and back to number reflecting specified base unit)
  • Scale (multiply values from handler/divide values from item)
  • isg (see Modbus combined registers)
  • profiles (chained profiles)

Last one is a “multi profile” profile which is a very experimental thing. It allows to chain multiple profiles one by one. They are executed in specified order from item to handler and in reverse order from handler to an item (as far I remember).
I have tested it with just most basic situations (scale + quantity). Anyhow, source code and very basic tests are there to learn. UI is obviously lost with multi profile profile so any advanced configurations will definitely not fly.

Enjoy or not. Your choice.

3 Likes

As an anti-proliferation measure, how about combining these (and likely offset) into one profile, where each of factor / offset / units parameters is optional.

1 Like

I think its quite opposite. With smaller profiles which do one thing you can assemble behavior you need. For example you can have two profiles average and debounce. Depending on order you place them you will have different results. For example:

  1. handler > debounce (60s) > avarage (10) - gives you avarage from 10 values received every 60 seconds
  2. handler > avarage (10) > debounce (60s) - gives you avarage value of last 10 values every 60 seconds

I know official profiles tend to do ie. range + inverse operation but I don’t have to. If you go with chained profile approach, which I proved is doable, then you can simply do range profile separately from inverse. Later one can be used standalone with miss wired contacts or to do trivial switches (!occupied → free).

1 Like

I hope people considering new profiles also take into account their two-way nature e.g. massaging state from channel to Item and massaging command from Item to channel.
It gets a bit complicated for those bindings that can issue commands or listen to states, maybe a profile needs a command path and a state path.

This becomes more important if chaining.

1 Like

This is right. Currently most profile assumption is that operations are same (or reversed in some ways). I could imagine myself situation we wish to filter incoming updates (from handler) but not outgoing (item) values or vice versa.
Technically speaking it is doable as configuration mechanism is not contained by anthing. It’s a matter of making such profiles. In fact I think that with a bit of luck it is possible to make an “adaptive” ConfigDescriptionProvider which will make whole process within UI a iterative process. We can keep adding new config parameters to description as profiles are appended letting UI draw a nice form with it. :wink:

So… a bit later I actually come across a need to run code I drafted. Now I have more field experience to share. Turns out that trick with dynamic configuration will not fly. This is due to fact that OH core does not support nested configurations (key-value under key-value). Then there is also an issue of tricking UI which didn’t fly since profile configuration is semi static and UI does not inquiry for config of particular link. It always asks about profile-type:xyz configuration. Yet, this might be improved in OH 3.2 which I didn’t check yet.

Non nonetheless I was able to workaround issue by flattening maps. I wanted to avoid use of JSON which would complicate processing of link config. In the end using the XML config format I refered in other topic I ended up with something like that:

<links>
  <link item="TInstallations_PV_DC_Power">
    <channel>modbus:data:sunnyboy_power:dcms_watt:number</channel>
    <config>
      <profile>connectorio:profiles</profile>
      <quantity.profile>connectorio:quantity</quantity.profile>
      <quantity.unit>W</quantity.unit>
      <sma-filter.profile>connectorio:sma-filter</sma-filter.profile>
    </config>
  </link>
</links>

Using text files it would look probably like below. In real life it will not fly due to dots in the keys, but that’s least of my concerns right now. :wink:

Number:Power TInstallations_PV_DC_Power {
  channel="modbus:data:sunnyboy_power:dcms_watt:number" [
    profile="connectorio:profiles",
    quantity.profile="connectorio:quantity",
    quantity.unit="W",
    sma-filter.profile="connectorio:sma-filter"
  ]
}

I worked on standalone linking mechanism (externalized from item definitions). This allows to make a semi static model across multiple installations and do all heavy lifting in things and links. Above example has two profiles associated - one turns decimals into quantity and other filters out 0x8000 values reported by SMA inverters when its dark.

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