ok, ha thanks.
thats a totally different approach isn’t it? how do you get the length of all entities in the group in this format? is that loop.length ?
label: >
[[[
if (!entity) return 0;
function loop(list, value) {
list.forEach(entity => { // This entity is different from the button-card entity, I should have named it differently...
if (states[entity])
if (entity.split('.')[0] === "group")
value = loop(states[entity].attributes.entity_id, value);
else if (["open", "on"].includes(states[entity].state))
value++;
});
return value;
}
return loop(entity.attributes.entity_id, 0);
]]]
can this be combined in 1 go? because, in fact I need them both, the ‘on’ number, and the total number:
if (count == entities.length) return 'All ' + entities.length + ' lights on';
if (count == 0) return 'No lights on';
return 'Lights on: ' + count + ' of ' + entities.length;
btw, I notice this updates immediately. How come the top template doesn’t work like that? (using groups in groups)
I ask, because in my FR just posted on the GitHub, I mention this, and your template seems to already do that…?
the lights of the double listed groups are counted twice. Meaning the recursion test Petro does in the Python should be done here too. First flatten the group and then count ?
All I’m trying to do is get you to go line by line and convert the function. Once it’s done, it will work identical to the python one. I don’t want you to change the functionality, just recreate it in JS. You’ve used all the commands, and the word recursion just means that the function calls itself.
And just to clarify, @RomRider’s second template also uses recursion.
sure, and that’s appreciated highly.
Where I am lost though is at the definition of the function in JS, I don’t get what happens if it is only this in JS now:
function flatten(entity_id, lights_result= [], lights_searched= [])
{
var state = states[entity_id];
}
where all the rest of the calucation is included in the function in Python.
And yes, I see the recursion in Romriders template of course, that’s why I have already tried to simply add the creation of the 2 lists in there, and return the counts only if an entity isn’t in an appended list, but I cant get the syntax correct.
Back to yours, going line by line was where I left it here. how to proceed from there.
my efforts stopped here:
if (state != None) {
var entities = entity.attributes.entity_id;
if (entities != None) {
searched.append(entities);
for (e in entities) {
(! lights_result.includes(e) && ! lights_searched.includes(e))
? flatten(e, lights_result, lights_searched) : null
}
}
}
return result.append(entities);
}
the fact I can not test the code doesn’t help either… would really have liked an online parser
You can totally test the code. Just make a fake button with just that JS.
As an aside, You should try to make that readable, meaning, line the close brackets up with the open brackets call.
So lets go line by line…
None doesn’t exist in JS. You know this. You know what is in js too. You’re using it later on. null. You also have used code that replicates this exact check in other areas. Even @RomRider’s function is using it.
hint:
Correct!
see previous comment about none
That’s not how you add to a collection/list in JS. .append is a JS call. So if you went line by line, you’d just google “python .append equivalent JS”. This is what I meant with converting. You doing the leg work and figuring out the correct calls to make instead of relying on others. If you google “python .append equivalent JS” the first item in the search is the answer. .push().
Also, you’re adding entities to the list. That’s not correct. My code added entity_id.
Again, 100% python code, nothing translated to JS. Google “looping arrays JS”. Admittedly, this would be the hardest one and I would expect any beginner to ask questions here. This can be done a number of ways, @RomRider uses the method I would use, forEach. And I wouldn’t simplify entity_id down to e, but that’s a personal preference.
entities.forEach(entity_id => {
}
You’re missing the if statement, and you can’t short hand this because there is no else. So it has to be a longhand if statement. Your syntax is correct for the if statement, but you either changed the list names and didn’t update them properly or you had a typo in a previous line.
function flatten(entity_id, result= [], searched= []) {
var state = states[entity_id];
}
if (state) {
var entities = entity.attributes.entity_id;
if (entities) {
searched.push(entity_id);
entities.forEach(entity_id) => {
if (! result.includes(entity_id) && ! searched.includes(entity_id))
return flatten(entity_id, result, searched);
return null;
}
}
return result.push(entity_id);
}
corrected the list names, append -> push
not sure about the None, can this simply be if (state) ?
on the longhand if statement: Ive moved that to an explicit ‘if’, but is that what you meant?
Think I’ve taken each Python line now and replaced it with a JS equivalent?
test button:
label: >
[[[
function flatten(entity_id, result= [], searched= []) {
var state = states[entity_id];
}
if (state) {
var entities = entity.attributes.entity_id;
if (entities) {
searched.push(entity_id);
entities.forEach(entity_id) => {
if (! result.includes(entity_id) && ! searched.includes(entity_id))
return flatten(entity_id, result, searched);
return null;
}
}
return result.push(entity_id);
}
var light_ids = [];
var entitities = flatten(entity, light_ids);
var i;
var count = 0;
for (i = 0; i < entities.length; i++) {
var state = states[entities[i]].state;
if (state == 'on') {count += 1;}
}
if (count == entities.length) return 'All ' + entities.length + ' lights on';
if (count == 0) return 'No lights on';
return 'Lights on: ' + count + ' of ' + entities.length;
]]]
does work somewhat, still doesnt take out the already added lights/groups, and has this error:
ButtonCardJSTemplateError: SyntaxError: Malformed arrow function parameter list in 'function flatten(entity_id, result= [], searched= []) {
var state = states[entity_id];
}
...'
at new Function (<anonymous>)
Also, your {} are not set properly, this is what I meant about formatting.
function flatten(entity_id, result=[], searched=[])
{
var state = states[entity_id];
if (state) {
var entities = state.attributes.entity_id;
if (entities) {
searched.push(entity_id);
entities.forEach(entity_id => {
if (!result.includes(entity_id) && !searched.includes(entity_id) {
flatten(entity_id, result, searched);
}
}
}
}
}
Then using it…
var light_ids = [];
flatten(entity.entity_id, light_ids);
light_ids.forEach(entity_id => {
var state = states[entity_id].state;
... extra crap here...
}
nope, not true, please don’t think so bad of me all the time and have some faith.
It’s not that this is my livelyhood is it.
Given the fact this hasn’t been posted before on the forum at all, I’d like to focus on getting the result, not on being a bad student. Why would I?
so, back to progress:
var light_ids = [];
flatten(entity.entity_id, light_ids);
var count = 0;
light_ids.forEach(entity_id => {
var state = states[entity_id].state;
if (state == 'on') {count += 1;}
}
if (count == light_ids.length) return 'All ' + light_ids.length + ' lights on';
if (count == 0) return 'No lights on';
return 'Lights on: ' + count + ' of ' + light_ids.length;
same result…seems the flatten function doesnt de-duplicate yet, given the fact light_ids.length is still counting all (double listed) groups. The error must be in the function above, the Malformed arrow function parameter list.
wait, this is so stupid, I had another counting button there, showing as expected. But hiding the fact this new test button wasn’t showing at all… sorry for that
so, we’re stuck at the error (which is for this button) and preventing anything else from happening…