[APP][Pro] Simple (Sys) LOG - Use this app for Simple (Sys) Logging

Script to automaticly include Simple Logging cards into your flows

Use this script to automaticly add logging cards to your (advanced and normal) flows:

  • It adds logging to each flowcard’s error catch in advanced flows.
  • Also it adds cards to the triggers (WHEN), conditions (AND) and actions (THEN).
  • It will generate one Automater flowcard in each flow.
    This card can be used to configure each flow on it’s own:
    • Update: When you set this to No, the Autmoater card itself will not be changed when running the script. You can use this to configure a different actionSeverity f.i. for the flowcards within this flow.
    • UpdateCards: When you change the Automatercard and run the script, the logcards will be updated with the configuration from the Automatercard.
      Set to No if you do not want logcards to be updated with the Automater configuration.

Each time you execute this script, it will update all flows (that are given in the flowNames).

Execute this in the WebApi Playground.
Note: Work in progress.

const version = '1.0.6';


let flowNames = ['_Normal Test', '']// Use this line for selected flows
//let flowNames; // Use this line for all flows

const excludeBrokenFlows = true;
const automaterLocation = 'BottomLeft'; //TopLeft, LeftTop, BottomLeft
const automaterOffsetY = 0;
const automaterOffsetX = 0;
const automaterAutoMoveCards = true;
const errorSeverity = '3', triggerSeverity = '6', conditionSeverity = '6', actionSeverity = '6', facility = '16';
const triggerFacility = '20', conditionFacility = '21', actionFacility = '22';


const positionErrorCards = { y: -3, x: 1 }; //Number of blocks relative from the card to which it is appended.
const createErrorCards = true; // = true, automaticly create logging card for each error in the selected (or all) flows.
const deleteErrorCards = false; !createErrorCards; // = true, delete all automaticly created logging card from the selected (or all) flows.

const positionTriggerCards = { y: -3, x: 1 }; //Number of blocks relative from the card to which it is appended.
const createTriggerCards = true; // = true, automaticly create logging card for each error in the selected (or all) flows.
const deleteTriggerCards = false; !createTriggerCards; // = true, delete all automaticly created logging card from the selected (or all) flows.

const positionConditionCards = { y: -3, x: 1 }; //Number of blocks relative from the card to which it is appended.
const createConditionCards = true; // = true, automaticly create logging card for each error in the selected (or all) flows.
const deleteConditionCards = false; !createConditionCards; // = true, delete all automaticly created logging card from the selected (or all) flows.

const positionActionCards = { y: -3, x: 1 }; //Number of blocks relative from the card to which it is appended.
const createActionCards = true; // = true, automaticly create logging card for each error in the selected (or all) flows.
const deleteActionCards = false; !createActionCards; // = true, delete all automaticly created logging card from the selected (or all) flows.


/* Automatic const, leave it be */
const createAutomaterCard = 'homey:app:nl.nielsdeklerk.log:Automater'; // Zet to null for no card (required as of now)
const automaterUri = createAutomaterCard.substring(0, createAutomaterCard.lastIndexOf(':'));
const automaterId = createAutomaterCard.substring(createAutomaterCard.lastIndexOf(':') + 1);
const automaterMoveCardsY = _.max([positionTriggerCards.y, positionErrorCards.y]) * 20 * -1;
const automaterTopMargin = 160;
//const flowGroups = ['triggers', 'conditions', 'actions'];




async function run() {
  let APIv3 = Number.parseInt((await Homey.system.getInfo()).homeyVersion.split('.')) >= 10;
  
  
  let flows = await Homey.flow.getFlows();
  flows = _.toArray(flows);

  //return afs;
  
  if (excludeBrokenFlows) flows = _.filter(flows , x => !x.broken);
  if (flowNames) flows = _.filter(flows, x => flowNames.indexOf(x.name) > -1);
  
   for(let i = 0;i<flows.length;i++) {
    const flow = flows[i];
    let triggerLog = flow.name + ' triggered';
    let conditionLog1= flow.name + ' condition group 1 true';
    let conditionLog2= flow.name + ' condition group 2 true';
    let conditionLog3= flow.name + ' condition group 3 true';
    let actionLogThen= flow.name + ' executed: Then';
    let actionLogElse= flow.name + ' executed: Else';
    
    //flow.conditions = [];
    if(flow.conditions.length===0 || !checkId(flow.conditions[0], 'homey:app:nl.nielsdeklerk.log', 'Automater_condition_log')) {
      flow.conditions.unshift(getNewCardNormalFlow({type:'trigger', flow, severity:triggerSeverity, facility:triggerFacility, log:triggerLog, group:'group1' } ));
    }
    if(!flow.conditions.find(x=>x.group=='group1' && x.args.type==='condition' && checkId(x, 'homey:app:nl.nielsdeklerk.log', 'Automater_condition_log'))) {
      let group1 = _.findLastIndex(flow.conditions, x=>x.group=='group1' && !checkId(x, 'homey:app:nl.nielsdeklerk.log', 'Automater_condition_log'));
      if(group1>-1) {
        flow.conditions.splice(group1+1, 0, getNewCardNormalFlow({type:'condition', flow, severity:conditionSeverity, facility:conditionFacility, log:conditionLog1, group:'group1' } ));
      }
    }
    if(!flow.conditions.find(x=>x.group=='group2' && x.args.type==='condition' && checkId(x, 'homey:app:nl.nielsdeklerk.log', 'Automater_condition_log'))) {
      let group2 = _.findLastIndex(flow.conditions, x=>x.group=='group2' && !checkId(x, 'homey:app:nl.nielsdeklerk.log', 'Automater_condition_log'));
      if(group2>-1) {
        flow.conditions.splice(group2+1, 0, getNewCardNormalFlow({type:'condition', flow, severity:conditionSeverity, facility:conditionFacility, log:conditionLog2, group:'group2' } ));
      }
    }
    if(!flow.conditions.find(x=>x.group=='group3' && x.args.type==='condition' && checkId(x, 'homey:app:nl.nielsdeklerk.log', 'Automater_condition_log'))) {
      let group3 = _.findLastIndex(flow.conditions, x=>x.group=='group3' && !checkId(x, 'homey:app:nl.nielsdeklerk.log', 'Automater_condition_log'));
      if(group3>-1) {
        flow.conditions.splice(group3+1, 0, getNewCardNormalFlow({type:'condition', flow, severity:conditionSeverity, facility:conditionFacility, log:conditionLog3, group:'group3' } ));
      }
    }
    if(!flow.actions.find(x=>x.group=='then' && x.args.type==='action' && checkId(x, 'homey:app:nl.nielsdeklerk.log', 'Automater_log'))) {
      let then = _.findLastIndex(flow.actions, x=>x.group=='then' && !checkId(x, 'homey:app:nl.nielsdeklerk.log', 'Automater_log'));
      if(then >-1) {
        flow.actions.splice(then +1, 0, getNewCardNormalFlow({type:'action', flow, severity:actionSeverity, facility:actionFacility, log:actionLogThen, group:'then' } ));
      }
    }
    if(!flow.actions.find(x=>x.group=='else' && x.args.type==='action' && checkId(x, 'homey:app:nl.nielsdeklerk.log', 'Automater_log'))) {
      let then = _.findLastIndex(flow.actions, x=>x.group=='else' && !checkId(x, 'homey:app:nl.nielsdeklerk.log', 'Automater_log'));
      if(then >-1) {
        flow.actions.splice(then +1, 0, getNewCardNormalFlow({type:'action', flow, severity:actionSeverity, facility:actionFacility, log:actionLogElse, group:'else' } ));
      }
    }
      //for(let j = 0;j<flowGroups.length;j++) {
        //if(flow[flowGroups[j]] && flow[flowGroups[j]].length>0) {
          //flow[flowGroups[j]] = _.filter(flow[flowGroups[j]], c=>c.uri!=appId);
          
          // _.each(lines,(c,k)=> card[flowGroups[j]].splice(card[flowGroups[j]].indexOf(k),1));
          // if(card[flowGroups[j]].length==0) delete card[lineHolders[j]];
        //}
      //}
  }
  
     console.log(flows);
  _.each(flows, async flow=>{
   try  {
     await Homey.flow.updateFlow({id:flow.id, flow:{actions:flow.actions, conditions:flow.conditions, trigger:flow.trigger}});
   } catch(err) {
     console.log('111' + err);
   }
  });
  
  

  let afs = await Homey.flow.getAdvancedFlows();
  afs = _.toArray(afs);

  if (excludeBrokenFlows) afs = _.filter(afs, x => !x.broken);

  if (flowNames) afs = _.filter(afs, x => flowNames.indexOf(x.name) > -1);

  //console.log(afs[1]);

  for (let i = 0; i < afs.length; i++) {
    let af = afs[i];
    let logCard;
    let automaterCard = _.find(af.cards, x => checkId(x, automaterUri, automaterId));
    let automaterArgs = {
      af, position: { location: automaterLocation, offsetY: automaterOffsetY, offsetX: automaterOffsetX, autoMoveCards: automaterAutoMoveCards },
      errorSeverity, triggerSeverity, conditionSeverity, actionSeverity, facility, triggerFacility, conditionFacility, actionFacility, createErrorCards, createTriggerCards, createConditionCards, createActionCards
    };
    if (automaterCard)
      automaterCard.args.id = af.id;
    if (automaterCard && automaterCard.args.update) {
      automaterArgs.card = automaterCard;
      updateAutomaterCard(automaterArgs);
    }
    if (!automaterCard) {
      automaterCard = getNewAutomaterCard(automaterArgs);
      automaterArgs.card = automaterCard;
      af.cards[uuid()] = automaterCard;
    }
    console.log(automaterCard);
    for (const cardKey in af.cards) {
      if (Object.hasOwnProperty.call(af.cards, cardKey)) {
        //const af.cards[cardKey] = af.cards[cardKey];
        if (checkId(af.cards[cardKey], automaterUri, automaterId)) continue;
        if (!af.cards[cardKey].id) continue;

        if (automaterCard.args.errorCards===false) {

          if (checkId(af.cards[cardKey], 'homey:app:nl.nielsdeklerk.log', 'Automater_log') && af.cards[cardKey].args.auto_created == true) {
            delete af.cards[cardKey];
            //continue;
          }
          if (af.cards[cardKey].outputError) af.cards[cardKey].outputError = _.filter(af.cards[cardKey].outputError, x => af.cards[x]);
          //continue;
        }
        if (automaterCard.args.errorCards===true && !checkId(af.cards[cardKey], 'homey:app:nl.nielsdeklerk.log') && af.cards[cardKey].type != 'trigger' && (!af.cards[cardKey].outputError || !af.cards[cardKey].outputError.length || !(logCard = _.find(af.cards[cardKey].outputError, x => af.cards[x] && af.cards[x].id && checkId(af.cards[x], 'homey:app:nl.nielsdeklerk.log'))))) {
          let uid = uuid();
          let newCard = getNewCard({ card: af.cards[cardKey], cardKey, af, position: positionErrorCards, automaterCard, type: 'error', severity: errorSeverity, facility });
          if (!af.cards[cardKey].outputError) af.cards[cardKey].outputError = [];
          af.cards[cardKey].outputError.push(uid);
          let l = {};
          l[uid] = newCard;
          af.cards = _.merge(l, af.cards);
        }
        if (!checkId(af.cards[cardKey], 'homey:app:nl.nielsdeklerk.log') && af.cards[cardKey].type === 'trigger') {

          if ((!af.cards[cardKey].outputSuccess || !af.cards[cardKey].outputSuccess.length || !(logCard = _.find(af.cards[cardKey].outputSuccess, x => af.cards[x] && af.cards[x].id && checkId(af.cards[x], 'homey:app:nl.nielsdeklerk.log')))) && automaterCard.args.triggerCards===true) {
            let uid = uuid();
            let newCard = getNewCard({ card: af.cards[cardKey], cardKey, af, position: positionTriggerCards, automaterCard, type: 'trigger', severity: automaterCard.args.triggerSeverity, facility: automaterCard.args.triggerFacility });
            if (!af.cards[cardKey].outputSuccess) af.cards[cardKey].outputSuccess = [];
            af.cards[cardKey].outputSuccess.push(uid);
            let l = {};
            l[uid] = newCard;
            af.cards = _.merge(l, af.cards);
          } else if (logCard && automaterCard.args.updateCards===true) updateLogCard({ card: logCard, af, automaterCard, severity: automaterCard.args.triggerSeverity, facility: automaterCard.args.triggerFacility });
        }
        if (!checkId(af.cards[cardKey], 'homey:app:nl.nielsdeklerk.log') && af.cards[cardKey].type === 'condition') {
          let errorCard = _.find(af.cards[cardKey].outputError, x => af.cards[x] && af.cards[x].id && checkId(af.cards[x], 'homey:app:nl.nielsdeklerk.log'));
          if (errorCard) errorCard = af.cards[errorCard];

          if ((!af.cards[cardKey].outputTrue || !af.cards[cardKey].outputTrue.length || !(logCard = _.find(af.cards[cardKey].outputTrue, x => af.cards[x] && af.cards[x].id && checkId(af.cards[x], 'homey:app:nl.nielsdeklerk.log')))) && automaterCard.args.conditionCards===true) {
            let uid = uuid();
            let newCard = getNewCard({ card: af.cards[cardKey], cardKey, af, position: positionConditionCards, automaterCard, type: 'condition', value: true, id: errorCard.args.id, severity: automaterCard.args.conditionSeverity, facility: automaterCard.args.conditionFacility });

            if (!af.cards[cardKey].outputTrue) af.cards[cardKey].outputTrue = [];
            af.cards[cardKey].outputTrue.push(uid);
            let l = {};
            l[uid] = newCard;
            af.cards = _.merge(l, af.cards);
          } else if (logCard && automaterCard.args.updateCards===true) updateLogCard({ card: logCard, af, automaterCard, severity: automaterCard.args.conditionSeverity, facility: automaterCard.args.conditionFacility });

          logCard = undefined;
          if ((!af.cards[cardKey].outputFalse || !af.cards[cardKey].outputFalse.length || !(logCard = _.find(af.cards[cardKey].outputFalse, x => af.cards[x] && af.cards[x].id && checkId(af.cards[x], 'homey:app:nl.nielsdeklerk.log')))) && automaterCard.args.conditionCards===true) {
            let uid = uuid();
            let newCard = getNewCard({ card: af.cards[cardKey], cardKey, af, position: positionConditionCards, automaterCard, type: 'condition', value: false, id: errorCard.args.id, severity: automaterCard.args.conditionSeverity, facility: automaterCard.args.conditionFacility  });
            if (!af.cards[cardKey].outputFalse) af.cards[cardKey].outputFalse = [];
            af.cards[cardKey].outputFalse.push(uid);
            let l = {};
            l[uid] = newCard;
            af.cards = _.merge(l, af.cards);
          } else if (logCard && automaterCard.args.updateCards===true) updateLogCard({ card: logCard, af, automaterCard, severity: automaterCard.args.conditionSeverity, facility: automaterCard.args.conditionFacility });

        }
        if (!checkId(af.cards[cardKey], 'homey:app:nl.nielsdeklerk.log') && af.cards[cardKey].type === 'action') {
          if ((!af.cards[cardKey].outputSuccess || !af.cards[cardKey].outputSuccess.length || !(logCard = _.find(af.cards[cardKey].outputSuccess, x => af.cards[x] && af.cards[x].id && checkId(af.cards[x], 'homey:app:nl.nielsdeklerk.log')))) && automaterCard.args.actionCards===true) {
            let errorCard = _.find(af.cards[cardKey].outputError, x => af.cards[x] && af.cards[x].id && checkId(af.cards[x], 'homey:app:nl.nielsdeklerk.log'));
            if (errorCard) errorCard = af.cards[errorCard];
            let uid = uuid();
            let newCard = getNewCard({ card: af.cards[cardKey], cardKey, af, position: positionActionCards, automaterCard, type: 'action', id: errorCard.args.id, severity: automaterCard.args.actionSeverity, facility: automaterCard.args.actionFacility  });
            if (!af.cards[cardKey].outputSuccess) af.cards[cardKey].outputSuccess = [];
            af.cards[cardKey].outputSuccess.push(uid);
            let l = {};
            l[uid] = newCard;
            af.cards = _.merge(l, af.cards);
          } else if (logCard && automaterCard.args.updateCards===true) updateLogCard({ card: logCard, af, automaterCard, severity: automaterCard.args.actionSeverity, facility: automaterCard.args.actionFacility }); 
        }
      }
    }

    try {
      let id = af.id;
      af = {
        //id: af.id,
        name: af.name,
        cards: af.cards
      };
      //console.log(JSON.stringify(af));
      afs[i] = af;
      await Homey.flow.updateAdvancedFlow({ id, advancedflow: af });
    } catch (ex) {
      console.log(ex);
    }

  }
  return afs;
  //log(JSON.stringify(afs));



  function uuid() {
    var d = new Date().getTime();
    var d2 = ((typeof performance !== 'undefined') && performance.now && (performance.now() * 1000)) || 0;
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
      var r = Math.random() * 16;
      if (d > 0) {
        r = (d + r) % 16 | 0;
        d = Math.floor(d / 16);
      } else {
        r = (d2 + r) % 16 | 0;
        d2 = Math.floor(d2 / 16);
      }
      return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
    });
  }

  function checkId(card, uri, id) {
    return id ?
      (APIv3 ? card.id == uri + ':' + id : (card.ownerUri == uri  || card.uri == uri ) && card.id == id)
      :
      (APIv3 ? card.id.startsWith(uri + ':') : (card.ownerUri == uri || card.uri == uri ) )
      ;
  }
  
  function getNewCardNormalFlow({type, flow, severity, facility, log, group}) {
    let r = {};
    
    if (APIv3) {
      r.uri = "homey:app:nl.nielsdeklerk.log";
      r.id = 'homey:app:nl.nielsdeklerk.log:Automater_'+(type=='condition'|| type=='trigger'?'condition_':'')+'log';
    } else {
      r.uri = "homey:app:nl.nielsdeklerk.log";
      r.id = 'Automater_'+(type=='condition' || type=='trigger'?'condition_':'')+'log';
    }
    r.group = group;
    r.args = {
      log,
      group: flow.name,
      severity: severity || "6",
      facility: facility  || '16',
      auto_created: true,
      type
    };
    return r;
  }

  function getNewCard({  card, cardKey, af, position: { x, y }, automaterCard, type, value, id, severity, facility }) {
    let r = {};

    if (APIv3) {
      r.ownerUri = "homey:app:nl.nielsdeklerk.log";
      r.id = 'homey:app:nl.nielsdeklerk.log:Automater_log';
    } else {
      r.ownerUri = "homey:app:nl.nielsdeklerk.log";
      r.id = "Automater_log";
    }

    if (!id) automaterCard.args[card.type + 'Index']++;
    id = id ? id : (card.type.substring(0, 1).toUpperCase() + automaterCard.args[card.type + 'Index']);
    let log = id;
    let _facility = facility;
    switch (type) {
      case "error": log += ": [[" + (APIv3 ? 'card' : card.type) + "::" + cardKey + "::error]]"; _facility  = automaterCard.args.facility; break;
      case "trigger": log += ': Triggered'; _facility  = automaterCard.args.triggerFacility; break;
      case "condition": log += ': ' + value; _facility  = automaterCard.args.conditionFacility; break;
      case "action": log += ': Executed'; _facility  = automaterCard.args.actionFacility; break;
    }
    r.args = {
      id,
      log,
      group: af.name,
      severity: severity || "6",
      facility: _facility  || '16',
      auto_created: true
    };


    r.type = "action";
    x = card.x + (20 * x);
    r.x = x > 0 ? x : 0;
    y = card.y + (20 * y);
    r.y = y > 0 ? y : 0;

    return r;
  }

  function getNewAutomaterCard({ af, errorSeverity, triggerSeverity, conditionSeverity, actionSeverity, facility, position: { location, offsetX, offsetY, autoMoveCards }, createErrorCards, createTriggerCards, createConditionCards, createActionCards, triggerFacility, conditionFacility, actionFacility }) {
    let r = {};
    const uri = createAutomaterCard.substring(0, createAutomaterCard.lastIndexOf(':'));
    const id = createAutomaterCard.substring(createAutomaterCard.lastIndexOf(':') + 1);

    if (APIv3) {
      r.ownerUri = uri;
      r.id = uri + ':' + id;
    } else {
      r.ownerUri = uri;
      r.id = id;
    }
    r.args = {
      //id:af.id,
      group: af.name,
      triggerIndex: 0,
      conditionIndex: 0,
      actionIndex: 0,
      update: true,
      updateCards: true,
      errorCards: createErrorCards,
      triggerCards: createTriggerCards,
      conditionCards: createConditionCards,
      actionCards: createActionCards,
      //autoMoveCards,
      // errorSeverity:3,
      // conditionSeverity:6,
      // actionSeverity:6,
      // facility: "16",
      auto_created: true
    };

    r.type = "action";
    let x = 0, y = 0;
    switch (location) {
      case "LeftTop":
        x = 0 + (offsetX > 0 ? (offsetX) : 0);
        y = 0 + (offsetY > 0 ? (offsetY) : 0);
        _.each(af.cards, x => { x.x += 400; x.y += automaterMoveCardsY });
        break;
      case "TopLeft":
        x = 0 + (offsetX > 0 ? (offsetX) : 0);
        y = 0 + (offsetY > 0 ? (offsetY) : 0);
        _.each(af.cards, x => x.y += automaterTopMargin + automaterMoveCardsY);
        break;
      case "BottomLeft":
        x = 0 + offsetX > 0 ? (offsetX || 0) : 0;
        let max = _.maxBy(_.toArray(af.cards), x => x.y);
        max = max ? max.y + automaterTopMargin + automaterMoveCardsY : 0;
        y = max + (offsetY > 0 ? (offsetY) : 0);
        _.each(af.cards, x => { x.y += automaterMoveCardsY });
        break;
    }
    r.x = x > 0 ? x : 0;
    r.y = y > 0 ? y : 0;

    r.args.errorSeverity = errorSeverity ? errorSeverity.toString() : '';
    r.args.conditionSeverity = conditionSeverity ? conditionSeverity.toString() : '';
    r.args.actionSeverity = actionSeverity ? actionSeverity.toString() : '';
    r.args.triggerSeverity = triggerSeverity ? triggerSeverity.toString() : '';
    r.args.facility = facility ? facility.toString() : '';

    updateAutomaterCard({ card: r, af, errorSeverity, triggerSeverity, conditionSeverity, actionSeverity, facility, createErrorCards, createTriggerCards, createConditionCards, createActionCards, triggerFacility, conditionFacility, actionFacility })
    // x= card.x + (20 * x);
    // r.x= x>0?x:0;
    // y = card.y + (20 * y);
    // r.y= y>0?y:0;
    return r;
  }

  function updateAutomaterCard({ card, af, errorSeverity, triggerSeverity, conditionSeverity, actionSeverity, facility, createErrorCards, createTriggerCards, createConditionCards, createActionCards, triggerFacility, conditionFacility, actionFacility }) {
    card.args.id = af.id;
    card.args.group = af.name;

    card.args.errorCards = createErrorCards;
    card.args.triggerCards = createTriggerCards;
    card.args.conditionCards = createConditionCards;
    card.args.actionCards = createActionCards;
    
    card.args.errorSeverity = errorSeverity ? errorSeverity.toString() : '';
    card.args.conditionSeverity = conditionSeverity ? conditionSeverity.toString() : '';
    card.args.actionSeverity = actionSeverity ? actionSeverity.toString() : '';
    card.args.triggerSeverity = triggerSeverity ? triggerSeverity.toString() : '';
    card.args.facility = facility ? facility.toString() : '';
    card.args.triggerFacility= triggerFacility? triggerFacility.toString() : '';
    card.args.conditionFacility= conditionFacility? conditionFacility.toString() : '';
    card.args.actionFacility = actionFacility ? actionFacility .toString() : '';

  }

  function updateLogCard({ card, af, severity, facility, automaterCard }) {
    af.cards[card].args.group = automaterCard.args.group;
    af.cards[card].args.severity = severity ? severity.toString() : '';
    af.cards[card].args.facility = facility ? facility.toString() : '';
  }

}
run();


3 Likes