[APP][Pro] Flow Checker

@FKey exactly that. same as the flow card trigger

2 Likes

Hi @martijnpoppen, I would like to make a feature request.
Is it possible to add the type to the unused logic variables like :white_check_mark: for Yes/No, :hash: for Number and :a_button_blood_type: for Text, or separate them into sub-categories?

The reason why I ask is that your App shows me an unused variable with the name Vacation, but I have two variables with the name Vacation, one as a Yes/No and one as a Number variable. Now I have to search a few flows to check which variable Vacation is no longer in use.

@DirkG I’ll check what I can do :slight_smile:

1 Like

@martijnpoppen I use your app, but I have a Problem. I deleted a Sensor. So two flows get broken because the sensor card was missing. But I get no massage from your app, even when i restartet it. What make I wrong?

@Stefan_Doring broken flows are not working on HP23 due to a change from Athom
Still waiting for a fix

1 Like

Thanks for the info. Do you have a deadline to fix?

@Stefan_Doring no I have no control over that. That completely depends on Athom. Given it doesn’t work since the launch of the HP23 I won’t expect it anytime soon :wink:

1 Like

Hi @martijnpoppen, is there any change that you implement a work-around for the broken IsBroken() code?

The code below is a minor adjustment from the original AtHom implementation (found at homey-api - npm, /homey-api/lib/HomeyAPI/HomeyAPIV3/ManagerFlow/Flow.js, starting at line 18.

This is the code interfacing with the HomeyAPIV3Local, running on a my laptop in the same local network. Being a relative novice in JavaScript/TypeScript, I couldn’t get it to work in the API playground, but I think (hope?) this should be fairly easy to implement in your code.

Note that when I run this code on my HomeyPro 2023 (recently migrated from a HomeyPro 2019), it reports 7 broken flows out of 203 flows. However, when I fail to initialise flowTokens (or fail to await the getFlowTokens() call), it reports 70 broken flows. Quite a few false positives. That made me wonder if that is the source of the false positive problem that you reported. (Of course, it could still be something unrelated).

#!/usr/bin/env node

// Log in to Homey and find broken flows.

// Connect to Homey on a local network
import { HomeyAPI } from 'homey-api';
const CONFIG = {
    "homey": {
        "address": "http://198.51.100.113",
        "token": "[YOUR API KEY]" // https://my.homey.app/settings/system/api-keys
    }
}
const Homey = await HomeyAPI.createLocalAPI(CONFIG.homey);

// Actual code

const flows = await Homey.flow.getFlows();

// Fill the cache
const flowTokens = await Homey.flowtoken.getFlowTokens();

// To get false positives, replace the above with:
// const flowTokens = {};

// Since filter must be synchronous, run in two steps.
// Store boolean results in an array, and await those result
const asyncFilter = async (arr, predicate) => {
  const results = await Promise.all(arr.map(predicate));
  return arr.filter((_v, index) => results[index]);
}

const brokenFlows = await asyncFilter(Object.values(flows), flow => IsBroken(flow));

console.log(brokenFlows.length + " of " + Object.values(flows).length + " flows are broken")
console.log(brokenFlows);

async function IsBroken(flow) {
  // Array of local & global Token IDs.
  // For example [ 'foo', 'homey:x:y|abc' ]
  const tokenIds = [];

  const checkToken = async tokenId => {
    // If this is a global Token, fetch all FlowTokens
    if (tokenId.includes('|')) {
      for (const flowTokenId of Object.keys(flowTokens)) {
        tokenIds.push(flowTokenId);
      }
      tokenId = tokenId.replace('|', ':');
    }

    if (!tokenIds.includes(tokenId)) {
      throw new Error(`Missing Token: ${tokenId}`);
    }
  };

  const checkTokens = async card => {
    // Check droptoken
    if (card.droptoken) {
      await checkToken(card.droptoken);
    }

    if (typeof card.args === 'object') {
      for (const arg of Object.values(card.args)) {
        if (typeof arg !== 'string') continue;
        for (const [tokenMatch, tokenId] of arg.matchAll(/\[\[(.*?)\]\]/g)) {
          await checkToken(tokenId);
        }
      }
    }
  };

  // Check Trigger
  if (flow.trigger) {
    try {
      // getFlowCardTriggers
      // warning: getFlowCardTrigger() is very slow
      const triggerCard = await flow.manager.getFlowCardTrigger({ id: flow.trigger.id });
      await checkTokens(flow.trigger);
      // Add FlowCardTrigger.tokens to internal tokens cache
      if (Array.isArray(triggerCard.tokens)) {
        for (const tokenId of Object.keys(triggerCard.tokens)) {
          tokenIds.push(tokenId);
        }
      }
    } catch (err) {
      flow.error = err.message;
      // flow.broken = true;
      return true;
    }
  }

  // Check Conditions
  if (Array.isArray(flow.conditions)) {
    for (const condition of Object.values(flow.conditions)) {
      try {
        // getFlowCardConditions
        // eslint-disable-next-line no-unused-vars
        const conditionCard = await flow.manager.getFlowCardCondition({ id: condition.id });
        await checkTokens(condition);
      } catch (err) {
        flow.error = err.message;
        // flow.broken = true;
        return true;
      }
    }
  }

  // Check Actions
  if (Array.isArray(flow.actions)) {
    for (const action of Object.values(flow.actions)) {
      try {
        // getFlowCardActions
        // eslint-disable-next-line no-unused-vars
        const actionCard = await flow.manager.getFlowCardAction({ id: action.id });
        await checkTokens(action);
      } catch (err) {
        flow.error = err.message;
        // flow.broken = true;
        return true;
      }
    }
  }

  return false;
}

Hi @macfreek
The isBroken check has been updated for the v3 API, and it no longer works the same way as the old isBroken check. Unfortunately, promises sometimes fail, leading to false positives. I’m still waiting for Athom to fix the issue. I made a small change to the homey-api for FlowChecker some time ago, but I don’t plan to do that again.

If you found something useful, I would suggest to let Athom know here: WebApi V3 Flow IsBroken() promise false positives Ā· Issue #53 Ā· athombv/homey-web-api-issues Ā· GitHub

To be clear: the above work-around I completely bypasses the isBroken()] call altogether, so I actually don’t know what’s wrong with it, or how to fix it. And as such, have little to report to AtHom about it. I can dive into the call, but given that I’m relative new to TypeScript, I’m not sure if I’m of much help.

Just to be clear: would you accept a pull request for the above work-around, or prefer not to have workaround in the first place? (I can fully understand if you don’t. I’m personally happy ro continue to use my stand-alone script, but if you are, I might spend some time helping others out with the workaround in your code).

@macfreek I really prefer to have an official fix from Athom, rather than adding a workaround to the code. I’m not very keen on including temporary fixes, as they can cause maintenance issues later on. I fully understand and appreciate your offer, though!

For now, I think it’s better if we wait for Athom to address the root cause properly.

Next to that it looks like your code is actually doing what flow checker already does and that is to search for broken variables

I really prefer to have an official fix from Athom

I can only agree to that.

Next to that it looks like your code is actually doing what flow checker already does and that is to search for broken variables

No. It does more than that. For example, it also reports a Not Found: FlowCardTrigger with ID homey:zone:9919ee1e-ffbc-480b-bc4b-77fb047e9e68:dead. That’s not a broken variable :slight_smile:

Also, this code right now reports 7 broken flows (out of the 203 flows in my Homey), while your app currently only reports 2 flows with broken variables.

As I wrote, the code is actually almost a verbatim copy of the isBroken() code by AtHom. Link in my post above. Since I have not examined your broken variable logical, I’m not sure why this code detects 5 more issues, but at least it should be noted that the (slow) calls to getFlowCardTrigger(), getFlowCardCondition() and getFlowCardAction() may raise exceptions that I don’t think are in your broken variable logic.

1 Like

@macfreek At the moment i’m really limited in time to dive into this. but probably in June i’ll have a look at it :slight_smile:

2 Likes

For those who want a workaround: I’ve published the above script at GitHub - macfreek/homey-scripts: Miscellaneous scripts for interacting with a Homey smart home controller

To get started, it now has a README. That said, I have not spend time duplicating the effort of Flow Checker. With HomeyScripts (another app), you can run the script, and for example return the number of broken flows. That said, I don’t see an easy way to report which flows are broken with HomeyScripts. So personally, I’ll just run this script locally whenever I think something is wrong, or after I’ve replaced devices. Let’s hope AtHom will fix the issue reported by Martijn. Much easier for everyone.

1 Like

@Doekse can you maybe take this along internally? WebApi V3 Flow IsBroken() promise false positives Ā· Issue #53 Ā· athombv/homey-web-api-issues Ā· GitHub issue is already open for a year :see_no_evil_monkey:

1 Like

Will do!

3 Likes

@martijnpoppen would it be possible for you to add check for app updates?
Like a flow card to call that will check whether any app has an update or not.

There is a ā€œupdateAvailableā€ field to check for. I do this in my Python script after downloading JSON but would be nice to have it automated.

@Anders_Gregow that’s not really a feature for flow checker
Besides that the app version checker app can already do that

Didn’t know, thanks for enlightening me.

Additionally, you can also run scripts on regular intervals:
https://community.homey.app/t/alert-on-app-updates/134794/4

2 Likes