Developing flows: tips and best practices

Note:

  • It is based on personal preferences, but this may help you to assess what works for you and develop your own style.
  • Various links to specific solutions/flows in this community,
  • You will see in past and present examples an evolution from simple flows to advanced flows and various styles.
  • A number of apps mentioned (eg. Advanced Triggers, Advanced Virtual Devices) are best used when you have some experience in flows.
  1. Avoid starting flows at a specific time; other triggers are preferred.
  2. Avoid dis/enabling flows in a flow
  3. Avoid ALL cards for simplicity
  4. Group triggers and central logic
  5. Split flows into functional parts, connect them with for example “Flow is started with” cards, virtual triggers, advanced virtual device triggers or Advanced Triggers.
  6. Use a logging app to trace flows (and use notifications for select events)
  7. Use Note cards to document flows. Use a note color system.
  8. Avoid constructing flows with app specific devices: Instead, try using zones and (advanced) virtual devices or Groups
  9. Use zone timers where you can to avoid building your own timer
  10. Use advanced flows. Limit the use of simple flows to cases where you may want to change the flow on your mobile app. Align cards in advanced flows consistently so you can read them back efficiently in the future.
  11. As timer (that can be interrupted) use Chronograph, limit the use of built-in Delay timers
  12. Variables: Use logical names for variables so you can trace back what they are used for.
    An advanced virtual device can be used to store text/number/boolean variables that are related to each other. These device variables will not show in the logical variable list.
  13. Did I mention to use zone cards where possible? (WHEN, AND, THEN). Great for motion, alarms, devices, etc.

Examples:

(use Chrome automatic translate to English for Dutch examples) (edited)

2 Likes

Guess you mean the ALL operator in Advanced Flows?
That one isn’t an AND card hence it has the name “ALL” 


1 Like

I agree on most of the points except for the first 3. For the first 2, I actually advise the opposite. I also apply point 5 to split larger flows into their triggers and their functional part. However, sometimes I just want to disable the flow based on conditions. I can also do that by creating a boolean variable and check it with the logic card inside the flow, but there are 2 issues with that. The first being, clean coding guidelines state that you’ll want to create variables as locally as possible to avoid them being misused by other components and prevent unexpected side-effects. However, variables and tags in Homey are always global. Meaning, you cannot guarantee that the value is what you expect it to be. The disabled state of a flow on the other hand is very local to the flow and its meaning is absolutely explicit. It will never have an unexpected effect on any other flow.
The second problems with using variables and logic state is that it is effectively slower. Homey already has a system that will always check whether or not a flow is enabled before it runs it. Why would you let that always resolve to true only to then do another check inside to flow itself? Homey flows and cards are all nicely build on promises. If a homey flow can’t be started because it’s disabled, the promise will be rejected early.

When it comes to point 3. This is ridiculous actually. If you do any type of advanced logic, you will come across situations where you want multiple conditions to be true before you continue. Sure you can check them all one by one. But like I said before, Homey’s cards are all just based Promises. The “All” card is basically a wrapper around Promise.all. It will you to run all cards simultaneously and reject as early as possible. This will make your flows run way faster than if you were to check all conditions after each other.

Not true. Here a few examples:

  • You can start flows with a variable text, number or boolean.
  • There’s a very useful app Advanced Triggers that lets you pass multiple local variables.

Related: You can also store variables in an Advanced Device

Correct. And if you construct the flow to check in the right order it’s efficient. A single flow and logic makes for easy readability.

Using ALL (when it fails) can:

  1. Complicate the remainder of the flow because you don’t know which condition(s) failed.
  2. Require additional flows to deal with the conditions that fail
  3. Make it harder to read/follow the meaning of multiple flows

I’ve given examples that show this.
But no problem if you find it’s working for you as experienced developer.

If you found a solid way of working with enabled/disabled flows, I’m interested to see it.

I found it hard when debugging to know if a flow was enabled or disabled at the time of running as the system doesn’t trace that.

Having enabled/disabled flows is another element where you do not have most logic in a single view.

Readability and debugging are important factors for me.

Again, it’s personal preferences and one will evolve or change programming styles over time.

I wholeheartedly disagree with any of this. I work as a software engineer. And in my company we even have a linting rule that prevent you from awaiting multiple promises in sequence if they are not dependent on each other. This is because it is just unnecessarily slow. The “All” cards themselves don’t have any error handling indeed, but that is because you would already handle that in the cards you want to await. Here’s an example on how The all cards dramatically improve performance. In my example I took some fictional api calls that return in pretty decent response times. I’m not saying that all flow cards will be this slow, but with the principle that each flow card is basically an abstraction behind a promise, you simply don’t know how long it takes for a card to resolve. You can also see that I add error handling on each step where something could go wrong. The “All” card really didn’t prevent me from anything i could do in sequence. It also didn’t massively increased the complexity of the flow. I just added the “All” card and reconnected some wires. All it did was massively improved the speed of the entire flow. So if you really want your homey to perform fast, I really suggest you dive in to it a bit further.


This is not the use case I was describing. I know that you can pass variables from one flow/chain to another. But I sometimes also want to remember state for a longer time even after a flow/chain is finished. This can only be done in setting a global variable or tag. It would be very handy if Homey would let you declare variables and tags scoped to one advanced flow, but sadly that’s not the case right now. I know that there are flow cards that create and return so called local tags, but in reality these are just global tags that are deleted again after a few seconds.

I have found a nice way to work with disabled flows. Of course, the system doesn’t traces disabled advanced flow that have listener cards in them. I only disable/enable those subroutine flows that you identified in point 5. These flows are only triggered by other flows with the “Start [flow] with [tag}” card. This card can be traced, as it will throw an error when the flow it wants to run is disabled. I also do not recommend disabling flows that has listener cards, but for subroutines it can be quite useful.

Here I have an example of how I combine the use of disabling/enabling of flows, and the All/Any cards effectively in my flows. In these flows I programmed my living room lights to autonomously dim depending on different times of the day and the measured luminance of my motion detector. During the day I want the luminance in my room to be around 200lx, while at night I want to dim to a more cozy atmosphere. I also configured two moods to override the autonomous dimming for watching movies or practicing yoga. When I activate one of these moods, the autonomous dimming flow is disabled. When I activate the Autonomous Dimming Mood, the flow is enabled again and then run at least on time. In my flows I already removed all error handling cards because these flows have been running very stable for over a year now. But the principle remains the same as the above, you can just add error handling on each and every step if you want.



Also, if your intent was to share some experience and things that worked for you, then I would also suggest to change the tone of the post a bit.
If the title of this article would be something like: “principles that I found worked for me”, then my response would have been different. But a title including words like “best practice’s” and sentences like “avoid this” really suggests that people should adhere to these rules. It’s statements like that that are bound to cause a discussion.
New people on this forum that are also new to Homey, Home automation or to programming in general that read these posts will think that they should follow your principles that worked for you, thinking that they are best practices while in fact, there’s no real evidence to back up that claim.

1 Like

ALL cards: you’ve described a good use case where using ALL works well and fast and is justifiable to use.

However, there’s fast and there’s being CPU efficient. Your fastest result has simultaneous branches. If they are very CPU intensive you have to take into account that the CPU can take less care of other important events outside of this flow. Again, the CPU of the Homey pro is pretty good, but if you have many simultaneous flows, you need to take it into account.

My comments where for different use cases as shown in examples. And I focused on readability and ease of debugging in those cases.

Disabling/enabling flows: You showed a nice example where this works for you. It’s a tool that Homey offers, but I’ve already provided my view. And it’s not that black or white either.

Thanks for your contribution.

If there is such thing as a tone in posts, it’s very hard to ‘read’, and it also depends on your own state of mind at the time of reading.
I also don’t agree with all recommendations by Rrrr and you, but we always can discuss about it as fellow users?
Your ‘tone’, to me, is also not the kindest, sorry.

Hmmmm I think you are getting a bit off track here, don’t you think?
Imho most people can think for themselves, and do realize it is a user’s post with some info which might come in handy.
Forum posts are not “The Only Truth”.

Thanks Daan!

1 Like

I’m missing a hint here:
According to Flow Checker I have : 8 flows and allready 85 (!) variables. I do a lot of math for energy usage and PV’s


Is the an advice on how to keep track of all varables and where the ‘belong to’ or are used in ?
And sorry, I don’t think I can do with less variables


@JohanP Good point: I have added variables to the list in the OP.

  • There may be a way to use less variables if you can show us what they are used for.
  • Use a naming structure for the global variables according to their use (which most likely you’ve already done).
  • One alternative is to set up an advanced virtual device with text/number/boolean variables.
    You will then have in one view all the variables related to the logic you are implementing.

Like I said, I do a lot of math to calculate daily usage of power, how much the PV’s produce, how much I deliver back to then Net, how the % of direct-use is etc. So I need to store the numbers of a dozen of counters, and the sum of them, the difference between these at the start of the day, and at the current moment, and the difference between them


The flow is ‘in Dutch’ not sure if you can understand, but this is my ‘Stroom & Saldering’ flow to fil in an Advanced Virtual device :

I might cut down on some of the varables, but this way the whole flow and whet I’m dooing is stiil ‘readable’ and understandable. I know I could do something with BLL, but it would take some of the calculations ‘out of sight’


pls keep in mind I’m only working with Homey less then a month :wink:

I’m considering adding a ‘code’ in front of the flow and variable to ‘conenct’ them, like ‘aa_flowname’ and ‘aa_variable’. next flow would be ‘bb_flow’ and ‘bb_variable’. If i use a variable in both flows i could name it ‘ab_variable’


1 Like

Please use the Google Translate icon in Chrome and your cards will show in English. Otherwise we’ll need to take your post separate in a Dutch topic.

  1. Well done, a very clear layout!

  2. I see you are already using Advanced Virtual Devices, that’s great. However, you can also use the fields in AVDs to calculate, this will save you a lot of logic variables and logic calculation cards.

  3. PBTH has a summarizer that automatically tracks power, gas, water. This will save you tracking these values over time. This will also allow you to cancel the “Every 15 Minutes” card and the “When it is 00:00” card who are both generating CPU demand on the dot, when potentially other flows do the same.

Translate makes garbage of some of the TAGS and variables :

The reason not to do calculations in AVD I allready explained, I want to keep what I’m doing and how I manipulate the values readable


I had a look at PBTH, but it doesn’t quite do what I want, so I’d rather ‘program’ it myself


I see what you mean, we will live with the Dutch version then :slight_smile:

I do not understand your comment fully, but of course, it is your choice.
Note that you can also store your intermediary results in AVD variables. It means replacing the logic calculation cards with AVD calculation cards.
This will not reduce the number of cards, but it will provide you with an much easier way to view the current values in device variables AND it reduces the number of logic variables for intermediate results and for final results.

Ok, only you as user can decide the balance between reducing cards and variables versus getting exactly what you want.

Ok, you mean i could reduce
afbeelding
this one to one card.

But this one

I cloud not create in 2, let alone one. Ok, maybe I could, but it would become a very hard to read and very long string


As far as CPU concerns, I’m not sure how ‘automaticly tracking values’ are less demanding on the CPU then once every 15 mins. The Homey must have poor performance if the calculations are that CPU intensive. Maybe if you have hundreds of flows running


No, this is what I meant:

PBTH Summarizer just tracks one energy number over time. It is well designed.

I think the only way to make advanced calculations with true temporary constants while only making a few variables or tags public is by using homey script. You can use the “run code and return number value” card, or just create a separate script that sets a bunch of tags and just the “run script” card.