Script to find all ungrouped items

which is exactly the way I see it. still the dev mode would be switched on, since this how you left it before reboot, and the value_template would say it is off, since that’s the way the group.developer is initialized… confusing this is.

No…it will be off on restart, because it takes the state from the template.

1 Like

had to solve that differently because of usage of Modes in my setup not being boolean.

if it’s of any use for others, here’s the way I found it to work fine. and expandable, just adding services to the dev_on and dev_off scripts is simple as can be:

  - alias: 'set Dev Mode at startup'
    id: 'set Dev Mode at startup'
    initial_state: 'on'
    trigger:
      platform: homeassistant
      event: start
    condition:
      condition: template
      value_template: >
        {{ is_state('input_select.mode', 'Developer') }}
    action:
      - service: group.set
        data:
          object_id: developer
          view: true
          visible: true

  - alias: 'set Dev groups'
    id: 'set Dev groups'
    initial_state: 'on'
    trigger:
      platform: state
      entity_id: input_select.mode
    condition: []
    action:
      service_template: >
        {% if is_state('input_select.mode', 'Developer') %}
          script.dev_on
        {% else %}
          script.dev_off
        {%endif%}

I have 2 questions on the script in its latest version:

what does this line do and why is it now python_script, while in the original script is was simply script.?
entity_ids.insert(0, "python_script.scan_for_new_entities") and why doesn’t it make any difference switching between that ??

  • secondly: I have several scripts, regular, that are not found by the python script, and I havent ignored them: ignore = data.get("domains_to_ignore", "scene, zwave")

is that an error of kinds? isnt it supposed to catch these too?

I don’t think we need that line…there is no such entity.

On my system, that’s this script :slight_smile:

And in case you somehow miss it, here’s a script to find all dead items as requested

so this line takes care it doesnt find itself, or at least report that?

is funny, because in my system the python_script has another name, but it doesn’t report itself either…

just as a tester, also related to Script to find items that no longer exist ive added this line to the python to see what happens:

caught = '{}\n'.format(entity_ids)
hass.states.set('sensor.catchall', '', {
    'custom_ui_state_card': 'state-card-value_only',
    'text': caught
    })

and created a group with this sensor.

the groups shows up, (albeit being empty…) and the sensor is created with the correct entities. now how to have it show up…

5511

seems the list [ ] is bugging this somehow? If I let is scan some more, this is showing up:

54
cool. back to the formatting table :wink: trying to get each entity on a new line, and a color per domain, see other thread for the code to do so if your interested.

since we’re in the dev-tools in the thread, please allow this side questions:

before I had this simple but cool service_templates:

#    action:
#      - service: group.set_visibility
#        entity_id:
#          - group.developer_links
#        data_template:
#          visible: "{{ is_state('input_select.mode', 'Developer') }}"

and

#        {% if is_state('input_select.mode', 'Developer') %}
#          script.dev_on
#        {% else %}
#          script.dev_off
#        {%endif%}

combining the ideas and techniques of these, ive conjured up this:

  - alias: 'set Dev mode'
    id: 'set Dev mode'
    initial_state: 'on'
    trigger:
      platform: state
      entity_id: input_select.mode
    condition: []
    action:
      service_template: >
        script.dev_{{ is_state('input_select.mode', 'Developer')|replace ('True','on')|replace('False','off') }}

@petro would the above be the best way to implement the replace ? It does work as intended. Cool thing is it allows me to have the automation remain the same, while adding or deleting services to the scripts dev_on /off.

Tried it in one statement like replace ('True'/'False', 'On'/'Off') or (['True','False'],['on','off'] ) but no luck yet. Cant find the right docs on this…

With what you’re doing, you could probably get away with a simple if statement instead of trying to replace string values.

script.dev_{{ 'on' if is_state('input_select.mode', 'Developer') else 'off' }}
1 Like

beautiful! brilliant this.
cut and pasted already…

but just for educational purposes, would the replace template be possible?

I tested out the replace template in the template editor and it worked, not sure why it wasn’t working for you. Only reason I would steer clear from using it is the fact that the simple if statement is easier to read at a glance and it has less operations.

which of the 2? replace (‘True’/‘False’, ‘On’/‘Off’) or ([‘True’,‘False’],[‘on’,‘off’] )

This worked for me

yes, here too.

maybe I wasn’t precise enough but i meant to ask if

| replace('True','on') | replace('False','off')

could be formulated in a better, possibly shorter way,

tried the other 2 but had no such luck…

No, you gotta do it that way or with regex but regex is limited in HA, also regex can be slow. I think your best option is the if statement check to be honest. It’s straight to the point, less code, and easier to read.

quite. ok thanks!

Here’s an updated version of the script. The major update this time is to create the view/custom_ui sensor the same way I do in the find_ghosts script.

If you leave the use_custom_ui set at false, you will get a list of hyperlinks (I’m trying to cancel any navigation attempt, but it’s not working so far). If you set it to True, and have the state-card-value_only card setup, you will get a text list, which can be formatted if you choose.

def scan_for_new_entities(hass, logger, data):
  ignore = data.get("domains_to_ignore","zone,automation,script,zwave")
  domains_to_ignore=ignore.split(",")
  target_group=data.get("target_group","catchall")
  show_as_view = data.get("show_as_view", False)
  show_if_empty = data.get("show_if_empty", False)
  min_items_to_show = data.get("min_items_to_show", 1)
  use_custom_ui = data.get("use_custom_ui", False)
  
  logger.info("ignoring {} domain(s)".format(len(domains_to_ignore)))
  logger.info("Targetting group {}".format(target_group))

  entity_ids=[]
  groups=[]
  
  for s in hass.states.all():
    state=hass.states.get(s.entity_id)
    domain = state.entity_id.split(".")[0]
    
    if (domain not in domains_to_ignore):
      if (domain != "group"):
        if (("hidden" not in state.attributes) or
             (state.attributes["hidden"] == False)):  
          entity_ids.append(state.entity_id)
      else:
        if (("view" not in state.attributes) or 
          (state.attributes["view"] == False)):
          entity_ids.append(state.entity_id)
        
    if (domain == "group") and (state.entity_id != target_group):
      groups.append(state.entity_id)

  logger.info("==== Entity count ====")
  logger.info("{0} entities".format(len(entity_ids)))
  logger.info("{0} groups".format(len(groups)))

  high_order=0
  for groupname in groups:
    group = hass.states.get(groupname)
    if int(group.attributes["order"]) > high_order:
      high_order=int(group.attributes["order"])
    for a in group.attributes["entity_id"]:
      if a in entity_ids:
        entity_ids.remove(a)

  visible=False
 
  if (len(entity_ids)) > min_items_to_show or show_if_empty:
    visible=True
  else:
    return
  
  result_ids = []
  
  if (use_custom_ui):
    summary = ""
    for e in entity_ids:
      summary = "{}{}\n".format(summary, e)
    
    hass.states.set('sensor.orphan_items', len(entity_ids), {
        'custom_ui_state_card': 'state-card-value_only',
        'text': summary, 'unit_of_measurement': 'orphans'
    })
    
    result_ids.append("sensor.orphan_items")

  else:
    counter=0
    for e in entity_ids:
      name = "weblink.orphan{}".format(counter)
      hass.states.set(name, "javascript:return false", {"friendly_name":e})
      result_ids.append(name)
      counter = counter +1

  service_data = {'object_id': target_group, 'name': 'Orphan Items',
                    'view': show_as_view, 'icon': 'mdi:magnify',
                    'control': 'hidden', 'entities': result_ids,
                    'visible': True}

  hass.services.call('group', 'set', service_data, False)


scan_for_new_entities(hass, logger, data)
1 Like

had a little go at it and formatted the output some further:

48

would really like the orphans sensor to list the entities separately but then I can t find the correct syntax in python, since i need the other {} to in the same line.

orphans:

 caught = '!- {}\n\n*==== Entity count ====\n!- {} entities to list \n+- {} groups processed\n*==== Settings ====\n/- targetting group: {}\n$- ignoring domains: {}'.format(entity_ids,len(entity_ids),len(groups),target_group, domains_to_ignore) #    caught="\n".join(entity_ids)
hass.states.set('sensor.orphans_sensor', len(entity_ids), {
    'custom_ui_state_card': 'state-card-value_only',
    'text': caught,
    'unit_of_measurement': 'Orphans'
    })

and Ghosts:

  busted=""
  for e in results:
    busted = "!- {}{}\n\n\n*==== Entity count ====\n!- {} entities to list\n#- {} real entities\n+- {} grouped entities\n+- {} groups processed".format(busted,e,len(results),len(real_entities),len(grouped_entities),len(processed_groups) ) #"{}\n".format(e) #

  hass.states.set('sensor.ghosts_sensor', len(results), {
      'custom_ui_state_card': 'state-card-value_only',
      'text': busted,
      'unit_of_measurement': 'ghosts'
  })

could you check and see if we can format as above, but with caught="\n".join(entity_ids)

this might need some extra error checking…oops:

I’ll take a look at this tomorrow if that’s okay. I’m pretty sure we can do what you want

cool, and of course thats ok!

might I add 2 small questions:

ive tried to have the orphans script to read its settings from data: under the script calling in the yaml like this:

  scan_orphans:
    alias: Scan ungrouped Orphans
    sequence:
      - service: python_script.scan_orphans
        data:
          domains_to_ignore:
            - scene
            - zwave
            - weblink

and change the lines:

def scan_for_orphans(hass, logger, data):
    ignore = data.get("domains_to_ignore", "scene, zwave, weblink")
    domains_to_ignore = ignore.replace(" ", "").split(",") 

to

def scan_for_orphans(hass, logger, data):
    ignore = data.get("domains_to_ignore",[])
    domains_to_ignore = ignore.replace(" ", "").split(",")

but the script errors out on an invalid action in the domains_to_ignore line with the replace.
also the script def isnt correct then (scan_for_orphans(hass, logger, data,) ) as it obviously needs the process_groups_entities like the ghosts script? Added that too, but no luck yet. so if you could please peek t that too?

second: as you know, and is reported out, the ghosts script targets target_group=data.get("target_group","ghosts")

while the orphans script targets:
target_group = data.get("target_group", "group.orphans")

as I report that out on the card, I wanted them both to show group.ghosts and group.orphans and change the setting in the ghosts script. It runs alright (i think, but doesnt show the group ghosts any longer ) but errors with an invalid slug on the ghosts group. Where do I need to change the script or service_data to accept that?

Log Details (ERROR)
Fri Jun 29 2018 07:33:45 GMT+0200 (CEST)

Invalid service data for group.set: invalid slug group.ghosts (try groupghosts) for dictionary value @ data['object_id']. Got 'group.ghosts'

30

this gives no slug error:

57