Simple LOG app - Former main topic [Deprecated]

Yeah you’re right Peter, i see now that i wrote it specificly for the HP2023, this one will not work on the old white one.

Now, you got a lott of trash in your flows you don’t see.

To fix it, run the above script once with the deleteCards = true;

Ill create a script for the old once as well…

1 Like

Simple log looks very good!

The scripts you mentioned, can these added to BLL functions? Or should we run them as homeyscripts in an flow (started manually)?

Thanks Marco!
Please note that the Test version is still in development and there might be (small) changes before i push for live.

Im sorry, but i am not sure which scripts you mean?
The Simple Log “Auto Add Cards” i just wrote?

If so: This script, as far as i know, cannot be executed in HomeyScript. It must be executed in the API Playground.
How or where i am gonna place this script, i am not sure yet.

I though about the Device Capabilities app, because it has allready the homey-api included.
However, it might be that this script wouldn’t need the homey-api, because i can generate the full code for the playground.

And i do have an alternative in mind, but i must test that first…

Okaye, here is the script for HPwhite and HPblacks for adding logcards to all Error-handlers:


let deleteCards = false; // = true, delete all automaticly created logging card from the selected (or all) flows; = false, create logging cards
let flowNames = ['Performance Test (Kopie)']; // Use this line for selected flows
//let flowNames; // Use this line for all flows

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





  // function genID(length) {
  //   let result = '';
  //   //const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  //   const characters = 'abcdef0123456789';
  //   const charactersLength = characters.length;
  //   let counter = 0;
  //   while (counter < length) {
  //     result += characters.charAt(Math.floor(Math.random() * charactersLength));
  //     counter += 1;
  //   }
  //   return result;
  // }
  // function uuid() {
  //   return genID(8) + '-' + genID(4) + '-' + genID(4) + '-' + genID(4) + '-' + genID(12);
  // }

function uuid() { // Public Domain/MIT
    var d = new Date().getTime();//Timestamp
    var d2 = ((typeof performance !== 'undefined') && performance.now && (performance.now()*1000)) || 0;//Time in microseconds since page-load or 0 if unsupported
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        var r = Math.random() * 16;//random number between 0 and 16
        if(d > 0){//Use timestamp until depleted
            r = (d + r)%16 | 0;
            d = Math.floor(d/16);
        } else {//Use microseconds since page-load if supported
            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.id == id)
      :
      (APIv3 ? card.id.startsWith(uri + ':') : card.ownerUri == uri)
      ;
  }

  function getNewCard(uid, card, cardKey, af) {
    let r = {};
    
    if (APIv3) {
      //r.ownerUri = "homey:app:nl.nielsdeklerk.log";
      r.id = 'homey:app:nl.nielsdeklerk.log:Input_group_log';
    } else {
      r.ownerUri = "homey:app:nl.nielsdeklerk.log";
      r.id = "Input_group_log";
    }
      r.args= {
        //log: uid.substring(0, 4) + ": [[" + (APIv3 ? 'card' : card.type ) + "::" + cardKey + "::error]]",
        log: uid.substring(0, 4) + ": [[card::" + cardKey + "::error]]",
        group: af.name,
        severity: "3",
        facility: "16",
        auto_created: true
      };

      r.type= "action";
      r.x= card.x;
      r.y= card.y - 60 >= 20 ? card.y - 60 : 20;
      r.y= card.y - 60;
    
    return r;
  }

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

  //afs = _.filter(afs, x=>x.name=='AddLogCards (Test)');
  afs = _.filter(afs, x => !x.broken);
  if (flowNames) afs = _.filter(afs, x => flowNames.indexOf(x.name) > -1);
  //afs = _.toArray(afs);

  for (let i = 0; i < afs.length; i++) {
    let af = afs[i];

    for (const cardKey in af.cards) {
      if (Object.hasOwnProperty.call(af.cards, cardKey)) {
        const card = af.cards[cardKey];
        let logCard;
        if (!card.id) continue;
        if (deleteCards) {
        
          if (checkId(card, 'homey:app:nl.nielsdeklerk.log', 'Input_group_log') && card.args.auto_created == true) {
            delete af.cards[cardKey];
            continue;
          }
          if (card.outputError) card.outputError = _.filter(card.outputError, x => af.cards[x]);
          continue;
        }
        if (!checkId(card, 'homey:app:nl.nielsdeklerk.log') && card.type!='trigger' && (!card.outputError || !card.outputError.length || !(logCard = _.find(card.outputError, x => af.cards[x] && af.cards[x].id && checkId(af.cards[x], 'homey:app:nl.nielsdeklerk.log'))))) {
          let uid = uuid();
          let newCard = getNewCard(uid, card, cardKey, af);
          if (!card.outputError) card.outputError = [];
          card.outputError.push(uid);
          let l = {};
          l[uid] = newCard;
          af.cards = _.merge(l, af.cards);
          //af.cards = _.merge(af.cards);
        }
      }
    }

    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 });
      return;
    } catch (ex) {
      console.log(ex);
    }

  }
  return afs;
  //log(JSON.stringify(afs));
}
run();
1 Like

Thanks!
It works for the 1st flow in the flownames array.
(And the flowname is in the log card, great! :tada: :partying_face: )

let flowNames = ['Transition Example Copy1','Transition Example Copy2'];


When I run it with only

let flowNames = ['Transition Example Copy2'];

that flow is getting log cards as well.


(Oh and I’m not trying the script with the ‘all flows’ selector

let flowNames;

yet :grimacing: ).


Additional question:
Is it possible to add logcards to ‘normal’ action cards as well, but just with the flowname in it, and ‘severity’ “informational”?

No, better not, thats only if you run a Test Homey like me :wink:

1 Like

Yeah, first i wanna know the script functions correctly with Errors (which are a little bit easier).

Ill check the multiple names array.

1 Like

Great, no hurries!
This was the nice result of the 1st flow, just FYI:

Lol, there was a return within the loop at the end, thus only running the first flow :slight_smile:
This is now fixed.


let deleteCards = false; // = true, delete all automaticly created logging card from the selected (or all) flows; = false, create logging cards
let flowNames = ['AddLogCards (Test)', 'AddLogCards (Test2)']; // Use this line for selected flows
//let flowNames; // Use this line for all flows

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





  // function genID(length) {
  //   let result = '';
  //   //const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  //   const characters = 'abcdef0123456789';
  //   const charactersLength = characters.length;
  //   let counter = 0;
  //   while (counter < length) {
  //     result += characters.charAt(Math.floor(Math.random() * charactersLength));
  //     counter += 1;
  //   }
  //   return result;
  // }
  // function uuid() {
  //   return genID(8) + '-' + genID(4) + '-' + genID(4) + '-' + genID(4) + '-' + genID(12);
  // }

function uuid() { // Public Domain/MIT
    var d = new Date().getTime();//Timestamp
    var d2 = ((typeof performance !== 'undefined') && performance.now && (performance.now()*1000)) || 0;//Time in microseconds since page-load or 0 if unsupported
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        var r = Math.random() * 16;//random number between 0 and 16
        if(d > 0){//Use timestamp until depleted
            r = (d + r)%16 | 0;
            d = Math.floor(d/16);
        } else {//Use microseconds since page-load if supported
            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.id == id)
      :
      (APIv3 ? card.id.startsWith(uri + ':') : card.ownerUri == uri)
      ;
  }

  function getNewCard(uid, card, cardKey, af) {
    let r = {};
    
    if (APIv3) {
      //r.ownerUri = "homey:app:nl.nielsdeklerk.log";
      r.id = 'homey:app:nl.nielsdeklerk.log:Input_group_log';
    } else {
      r.ownerUri = "homey:app:nl.nielsdeklerk.log";
      r.id = "Input_group_log";
    }
      r.args= {
        //log: uid.substring(0, 4) + ": [[" + (APIv3 ? 'card' : card.type ) + "::" + cardKey + "::error]]",
        log: uid.substring(0, 4) + ": [[card::" + cardKey + "::error]]",
        group: af.name,
        severity: "3",
        facility: "16",
        auto_created: true
      };

      r.type= "action";
      r.x= card.x;
      r.y= card.y - 60 >= 20 ? card.y - 60 : 20;
      r.y= card.y - 60;
    
    return r;
  }

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

  //afs = _.filter(afs, x=>x.name=='AddLogCards (Test)');
  afs = _.filter(afs, x => !x.broken);
  if (flowNames) afs = _.filter(afs, x => flowNames.indexOf(x.name) > -1);
  
  //afs = _.toArray(afs);

  for (let i = 0; i < afs.length; i++) {
    let af = afs[i];

    for (const cardKey in af.cards) {
      if (Object.hasOwnProperty.call(af.cards, cardKey)) {
        const card = af.cards[cardKey];
        let logCard;
        if (!card.id) continue;
        if (deleteCards) {
        
          if (checkId(card, 'homey:app:nl.nielsdeklerk.log', 'Input_group_log') && card.args.auto_created == true) {
            delete af.cards[cardKey];
            continue;
          }
          if (card.outputError) card.outputError = _.filter(card.outputError, x => af.cards[x]);
          continue;
        }
        if (!checkId(card, 'homey:app:nl.nielsdeklerk.log') && card.type!='trigger' && (!card.outputError || !card.outputError.length || !(logCard = _.find(card.outputError, x => af.cards[x] && af.cards[x].id && checkId(af.cards[x], 'homey:app:nl.nielsdeklerk.log'))))) {
          let uid = uuid();
          let newCard = getNewCard(uid, card, cardKey, af);
          if (!card.outputError) card.outputError = [];
          card.outputError.push(uid);
          let l = {};
          l[uid] = newCard;
          af.cards = _.merge(l, af.cards);
          //af.cards = _.merge(af.cards);
        }
      }
    }

    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));
}
run();
1 Like

Check, it works now with flownames array.
The delete function works as well when:

let deleteCards = true;
1 Like

Any feedback on the placing of the cards and the Message ID @Peter_Kawa ?

The cards are placed behind the action cards, but I assumed you can’t control that?
On the last screenshot, I moved them myself :grimacing:

48ee or 4175 or 7b39


are not really recognisable, but, when going from reading the logs and going to view the flow, you’ll know where to look, right?

Yes, i do that on purpose: just a littlebit higher so you can see the Add **** code.

Well indeed, and i am open for sugestions, because i also don’t really like it like this.

But using indexes or anything means when you change a flow, old logs would not match anymore.
So i thought lets use a random id.

But if you have any great idea’s i would love to hear it!

@Arie_J_Godschalk A quick question: Is this new version of Simple LOG depending on any other app to be installed (Better Logic?) to be functional, or is it (still) a standalone app?

I am using the current version of Simple LOG, and it looks like you have made some real improvements now. I haven’t yet tested the new version, but I plan to. Thanks for all your good work, both on this and other apps!

RIght now the test version is yeah, you need BLL for it’s datetime function and config and the File Server.
Also, the Settingsscreen now uses BLL components.

But, i have already made a Memory backlog.

This backlog is used as long as BLL is not running.
It’s format is based on the flowcard definitions, meaning, the exact same format/datamodel as the Old Simple (Sys) Log datamodel.
When BLL starts, the new parts of SL is activated, the BackLog is writen into the new/FileBased Log and if Syslog clients are connected, the updates get executed.

Now, in my current develop version, you can already request the BackLog as JSON through it’s default (new) GetLog flowcard.

So, “all” thats left todo is find a (nice) way of still showing the (old?) settingscreen when BLL is not installed (doesn’t even need to be running for the settings tho, just needs to be installed.

If that works correctly, i need to make sure the BackLog is saved in Homey’s Settings and reloaded on app start (right now it’s still more a temporary memory before BLL can start up).

But currently (my dev version and most of test also) will work without BLL, SL will startup, all logging flowcards will work (clear log doesn’t do anything in that situation yet i guess :slight_smile: ) and i can retrieve it’s json.

But for all the cool stuff, like Excel files on your phone, and logs with nicely formatted dates etc, and Syslog clients (altho i am looking into how to not need the BL.datetime.toString for Syslog clients), with the current test version, you will need/want BLL :wink:

Great, thanks! I have had BL installed for a long time, but I haven’t actually used it. I just updated to the latest (and renamed version), and I might update to the test version of Simple LOG in the near future, since it sounds like it is fairly close to being done and stable.

This all sounds very promising, thanks @Arie_J_Godschalk !

When I am switching from Papertrails to Simple LOG, I need a way to remove the flowcards that were automatically added by Papertrails (or do it by hand which would mean an awfull lot of work…:slight_smile: )
Is there a way to do that as well by a (modified?) script you’re working on for Simple LOG??

Alternatively; If I uninstall the Papertrails app these flowcards (THEN add to papertrailslog” become broken. So a script that would remove all broken flowcards would be fine as well, to keep it more general perhaps…

1 Like

Hey Henk,

Well, when i am done, i actually want users the option to use the script for Papertrails or SimpleLog.
It’s a big script to make and perfect for AFs and the difference in which card to use is small (i guess once small method would be enough).
That being said, i suppose you could then also use that script to remove the Papertrails cards, altho i don’t know yet which are added automaticly.

That would be great, @Arie_J_Godschalk. Thx!
Only thing is that the Papertrails app is not yet ready for Homey Pro / SDK 3.

I know this might not be a question for you and this topic perhaps, but wouldn’t it be possible to have a script that deletes broken flow cards from all flows?
To keep it more general and useful in more situations…

1 Like

Sure i can look into it (when i continue the script).
Do you mind creating a feature ticket for it?
Thanks!