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

Simple (Sys) LOG

Live now.

large

The new and improved Simple (Sys) LOG.

This app makes it possible to add logging and/or notifications to your Flows.
Optionally a log can have, next to it’s message, an App/group, severity and facility/category.

Connect to a Syslog

You can also connect to multiple Syslog-clients to a) send specific message and b) have all default messages (with certain severities or higher) send there.

New App Settings: Syslog interface

This new version saves the logs into files and not in memory, granting a much larger log then before.

There is a completly new Settings page, with a Syslog-kind-of-interface, in which you can:

  • Rotate the screen for a better view on a Mobile or other landscape-screen device.
  • Search and filter specific messages, dates, etc.
  • Hide or move columns.
  • Save the filters or columns as default.

Export/Download the logs

  • Download the complete Log as nicely formatted Excel, Csv or JSON, in the Mobile App, Developertools or within flows (base64), on your network or through the cloud, save and secure, without opening ports or anything (see the Better Logic Library App Settings for more info on the File Server).

API

The folowing API endpoints can be used:

  • Grab log data:
    GET /api/app/nl.nielsdeklerk.log/
  • Add log data:
    PUT /api/app/nl.nielsdeklerk.log/addlog/
    Content-Type: application/json
    {“log”: “Test 1,2,3”, “group”: “TEST”}
  • Clear log
    GET /api/app/nl.nielsdeklerk.log/clearlog



** This app still functions as Stand Alone app like it used to, however, to enable all functionalities and new interface, it requires the Better Logic Library App to be installed.


Simple LOG app - Former main topic [Deprecated]


Please report issues or feature requests at QlusterIT / nl.qluster-it.SimpleLog/ issues — Bitbucket.


Apps:

Donations or Sponsorship

4 Likes

Syslog interface

On the Homey Mobile App in landscape mode:

Menu options

[Settings]

[Syslog client]

[Export/download]

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

Script to remove all flowcards in Flows and Advanced Flows from a certain app

Use this script to remove all flowcards from a certain app from flows and advanced flows. Lines are removed too, tokens are not.

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

const version = '1.0.5';
//You need to run this script in the API Playground to really remove all flowcards.

//const flowNames = ['AddLogCards (Test)', 'DeleteCardsFlow']; // Use this line to run a few select flows by name.
const flowNames = null; // Use this line to run all flows

//const appId = 'homey:app:nl.nielsdeklerk.log'; // Will delete all cards and removed the lines for all Simple Log cards!
const appId = 'homey:app:nu.dijker.papertrails'; // Will delete all cards and removed the lines for all Papertrails cards!

const reallyExecute = false; //set this to false for a test run (in HS), true to really run (in the Playground)
//const reallyExecute = true; //set this to false for a test run (in HS), true to really run (in the Playground)




async function run() {
  const lineHolders = ['outputError', 'outputSuccess', 'outputTrue', 'outputFalse'];
  const flowGroups = ['triggers', 'conditions', 'actions'];

  let APIv3 = Number.parseInt((await Homey.system.getInfo()).homeyVersion.split('.')) >= 10;

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

  //return afs;
  if (flowNames) afs = _.filter(afs, x => flowNames.indexOf(x.name) > -1);
  afs = _.filter(afs, af=> _.find(af.cards, c=>c.ownerUri==appId));

  for(let i = 0;i<afs.length;i++) {
    const af = afs[i];
    const cardsToDelete = _.pickBy(af.cards, c=>c.ownerUri==appId);
    af.cards = _.pickBy(af.cards, c=>c.ownerUri!==appId);

    for(const key in af.cards) {
      //console.log(key);
      const card = af.cards[key];
      let lines;
      for(let j = 0;j<lineHolders.length;j++) {
        if(card[lineHolders[j]] && card[lineHolders[j]].length>0 && Object.keys(lines = _.pickBy(cardsToDelete, (c,k)=> card[lineHolders[j]].indexOf(k)>-1)).length ) {
          _.each(lines,(c,k)=> card[lineHolders[j]].splice(card[lineHolders[j]].indexOf(k),1));
          if(card[lineHolders[j]].length==0) delete card[lineHolders[j]];
        }
      }
    }
  }


  let flows = await Homey.flow.getFlows();
  flows = _.toArray(flows);

  //return afs;
  if (flowNames) flows = _.filter(flows, x => flowNames.indexOf(x.name) > -1);
  flows = _.filter(flows, af=> _.find(af.triggers, c=>c.uri==appId) || _.find(af.conditions, c=>c.uri==appId) || _.find(af.actions, c=>c.uri==appId));
    
  for(let i = 0;i<flows.length;i++) {
    const flow = flows[i];
    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]];
        }
      }
  }

  if(reallyExecute) _.each(afs, async af=>{
   try  {
     await Homey.flow.updateAdvancedFlow({id:af.id, advancedflow:{cards:af.cards}});
   } catch(err) {
     console.log(err);
   }
  });

  if(reallyExecute) _.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(err);
   }
  }); else console.log(flows);
  return {afs, flows};
}


run().then(x=>{ if(this!=null) this.log(x)});
2 Likes

[reserved2]

[reserved4-me] :slight_smile:

[reserved]

[last reserved]

@Peter_Kawa

I have updated the script Peter:

You can position the loggingcards yourself.

I’ll be working on perfecting the script and adding many options in the start of the script.
Also, i am thinking about adding a “invisible”/deprecated special flowcard for automatic logging, this way, i can specificly improve it’s title/description to better show what is needed: The location of the (connected) card.

All other fields, like it’s message (i have an idea for the unique number), the group (flowname), it’s level (automatic/setting/option) and category (Flow) doesn’t need to be visible.

So perhaps a card only(!) showing a number is way easier to see, recognise and smaller to place, is more handy.
But i’ll first try to implement my new card-ID system, if it’ll work :slight_smile:

1 Like

Pure magic my man, thanks!
Great to be able to define the position now!

The other ideas sound great also, Arie :sunglasses:

1 Like

Okay, check out the new Script version (you will need the latest SL test version for this):

1 Like

Okay, just updated the script again.

Nice one, Arie.
I see the identifier tXX cXX aXX for the 3 kinds of flowcards, a triggercard added, and the Automater card.

The trigger logcards works great, the flowname is included, but Severity is “Error”, should be “Info” or the like:

The condition and action cards have an error at the moment:


)

There is some ‘undefined’ mentions in the returned log:

"ce3e96ff-12b4-421c-9050-ff46898ca5c6":{6 items
"ownerUri":string"homey:app:nl.nielsdeklerk.log"
"id":string"Automater"
"args":{12 items
"group":string"BLL timestamp XMPL"
"triggerIndex":int0
"conditionIndex":int0
"actionIndex":int2
"update":booltrue
"updateCards":booltrue
"auto_created":booltrue
"id":string"98fc9c06-f37a-4a37-83f7-64779d56fb53"
"errorSeverity":undefined
"conditionSeverity":undefined
"actionSeverity":undefined
"facility":undefined
}

Thx, just a minute

Yeah, lol, it’s still work in progress, i just added the different severities :wink:
Testing that now…

One moment…

I know I know :crazy_face:

@Arie_J_Godschalk OK, the Trigger logcard severity is “informational” now
Cool, the action logcards are now connected to the output nipples as well, and work

1 Like

Okay, update to the newest test, and you can now configure the Automater card for each flow it’s in…
(And use the newest script, i have now included a version number in it.

1 Like