companion icon indicating copy to clipboard operation
companion copied to clipboard

[Feat] Modernize way of action advancement

Open dnmeid opened this issue 1 year ago • 4 comments

Is this a feature relevant to companion itself, and not a module?

  • [X] I believe this to be a feature for companion, not a module

Is there an existing issue for this?

  • [X] I have searched the existing issues

Describe the feature

Today although the actions of a set are shown in a list, they are all executed at the same time or with fixed delays. I think it would be a good feature if we would have the possibility of a better control of the actions execution. Im thinking of something like the powerpoint way of advancing to the next animation.
The user would have a choice if:

  • the action starts together with the previous action
  • the action starts after the previous action has finished
  • the action starts after a manual trigger (i.e. the action set pauses until this happens). This trigger could be something like a button press or release or even the same logic used for triggering a trigger.

Actions can still have an optional delay, but this delay would always be relative.

Biggest advantage would be the part "after previous action has finished" making it possible to program sequential workflows without unnecessary delay times. E.g. time until a variable is set or time until http get has a result. Biggest concern here is that modules now should actively handle return values for the action callbacks, at least for actions which may run for a long time or which may fail. And we would have to deal with what happens if the action fails. Shoud it throw an error or return a special value or reject a promise. And do we just log it and proceed in the action set or give users a way to decide themselfes how to handle failed actions.

Usecases

No response

dnmeid avatar May 26 '24 11:05 dnmeid

I like this idea, especially as I've seen a growth in more complex setups, and automation, that requires a lot of actions on a button or trigger.

My suggestion would be to make it its own thing, for example just like we have a page just for triggers, why not a page for 'Action Sequences' or whatever you may call them. The user could create and name a sequence, and the page could allow them to easily create parallel groups, or series groups, add actions to those groups, chain one in to another, maybe even have each group split at the end into a 'Success' or 'Failure' section so if a set of actions succeeds it continues on to the next thing, but it also lets the user branch off to do something if it fails, like log to console, or retry x times, whatever the user may want. When a sequence is run it could even act as a monitoring page to allow users to see what action it's up to, what actions may be holding it up, etc... Then it'd just be a case of the user adding a 'Run Sequence' action to a button/trigger in the usual button/trigger pages.

On the module dev side of things this would likely require actions to return a promise to be properly supported, which shouldn't be particularly difficult but I would expect that some modules may not be able to fully support it if their actions do things that don't have a way to determine if the action succeeded or not.

thedist avatar May 26 '24 12:05 thedist

the action starts after the previous action has finished

I was confident this was how it behaved today, but I am wrong on that.. :facepalm: Internal actions will generally behave this way because they aren't async so will execute synchronously, but yeah module actions are fire and forget.

Biggest concern here is that modules now should actively handle return values for the action callbacks, at least for actions which may run for a long time or which may fail. And we would have to deal with what happens if the action fails

With the way the module callbacks are in the new module api, when the callback returns a promise, the call companion makes into the module only completes when that promise resolves (or the 5s timeout that companion defines is reached). we do this today mostly just so that we can log any errors the call throws.
So the api is ready for this, it just depends on modules doing things correctly.


I am on board with this. I think this would be best achieved by creating new actions which have these behaviours. This would mean it is very related to #1052. (they would both need the framework of 'composite actions') My reasoning for this is that it means that it would be possible to compose these much better. such as:

  • internal: try-catch
    • actions:
      • internal: action sequential
        • stop on error: true
        • actions: ...
    • on fail actions: ...

This does feel very much like our own building block programming language. I'm not sure if that is a good or bad thing..

I would be tempted to say that with this model, maybe the delays we currently have should be moved to be composite actions like these too, rather than a builtin concept.

I'm not immediately convinced by the action starts after a manual trigger, I think this needs some thought on how it should work and be implemented that the other two don't pose due to the nature of delaying for an unknown duration, possibly infinite.

Julusian avatar May 26 '24 13:05 Julusian

I think the time has come to take the next step in this marvelous program.I propose a way of connecting triggers, actions and conditions in a (easy and limited) structured programming setup.Let us define programmed snippets which can be fired by button presses, triggers and contain conditionals and actions.

fbosman avatar May 26 '24 13:05 fbosman

I'm glad to see that some of you have the same or similar idea like I proposed in #2387:

My suggestion would be to make it its own thing 'Action Sequences'

framework of 'composite actions'

Let us define programmed snippets which can be fired by button presses, triggers and contain conditionals and actions

In #2387 I called this a "stepped script" (working title). Go find the details of the idea there, the bottom line is that I want to separate the functionality from the button. We would have something like a script or snippet or composit actions, which is a new entity. The script can be run by a button or by trigger or by API. So buttons wouldn't have actions any more and triggers wouldn't have actions any more and they'd have a script instead. In #2387 I also propose inline editing for the script, so you can edit the actions of the script right where it is used in the button or in the trigger.

But all of this is independent of just changing the way how/when the decision is made to run one particular action.

maybe the delays we currently have should be moved to be composite actions

Im also not super happy with the current implementation of delays. If we give actions the choice wether to run at start or at end of the previous action, I'm wondering wether it would be better to have the delays as a separate wait action. This wait can then just be inserted where you need it and it could also easily take variables or an expression as a parameter and you can temporarily enable/disable the delay (#2172).

This does feel very much like our own building block programming language. I'm not sure if that is a good or bad thing..

Does anybode here know Medialon? I'm a veteran Medialon programmer, it is quite old and nowadays not maintained a lot any more but it still is by far the best and most complete show control solution. For me Medialon is a constant source of inspiration for the development of Companion. Their "language" looks like this: image If you look closely, you can see how they implemented a if/else. The lines within each block are indented, this also works multi level. You can simply drag lines in and out of a block.

In the screenshot you can also see a wait like I described above.

The Medialon way of doing "start action at a trigger" is called "wait for" where you can give a condition and it waits until the condition evaluates true.

I'm not sure if the Medialon way is 100% what we should aim for. It is super powerful and for me quite simple to use, but maybe it is too advanced for a lot of our users.

dnmeid avatar May 26 '24 14:05 dnmeid

I am thinking about looking into some of this soon. Following https://github.com/bitfocus/companion/pull/2947, there is some structure that this should match.

So it sounds to me like we need an internal action:

  1. internal: sequence - runs each action in order, waiting for the previous one to complete execution (with a warning, that not all modules/actions let us know for how long they are executing)
  2. internal: parallel - run all the actions in parallel
  3. internal: delay - runs the contained actions after the specified delay. If used inside a 'sequence', it will also delay anything following it.

The root level should behave like today, running everything in parallel.

For a MVP, I don't think we should help the user with any error handling. Maybe having a checkbox on the 'sequence' action to say 'stop on action error' is essential, but should be the extent of it at this stage Anything more than that will be pushing into the territory of #1052 (which will likely be looked at soon after the MVP for this is done), so would do better as a follow up.

I am trying to avoid having to add properties to each action for 'after previous action has finished', as I think that will be harder to get an overview of. For people using this sequencing, I suspect they will like to collapse the actions to be able to get a better overview, which composite actions will present more clearly.
Even if it is a property, we will still need one of these internal actions, so that you can create two parallel branches (A -> B -> C, but D starts with B and does D -> E)

With these 3 new 'composite actions', I think that addresses the root case of what you are asking for with a hopefully intuitive structure of composition. And it lays the groundwork for #1052, as this is same structure as will be needed there, but just a bit less work needed.

One thing I am not certain on is whether internal: sequence and internal: parallel should be one action with a mode, or be two separate actions. I think that is a preference/UX question, so I don't have an answer for it.

Julusian avatar Nov 24 '24 13:11 Julusian

While I initially was thinking of an advancement property to every action, I also like the idea of the actions container for parallel or sequential sub-actions. We would need this for conditionals anyway and having unified workflows is always good.

I'd vote for a action group container and make sequence and parallel an option. That makes it much easier to change your mind after creation and reuse bits and pieces.

dnmeid avatar Nov 24 '24 23:11 dnmeid