Help Needed with Command Line Sensor and large JSON parsing

Hi everyone,

I’ve been struggling with this issue for several days and, despite reading numerous threads, I haven’t been able to resolve it. I’m working on tracking ski routes at a nearby mountain.

Here’s my setup:

  1. I have a Python script that scrapes data from the mountain’s website and outputs a JSON file like this (shortened for brevity):
[
  {
    "id": "inconnu_le_campagnol_0_7_km",
    "domain": "Inconnu",
    "name": "Le Campagnol - 0.7 km",
    "status": "Fermé",
    "trail_type": "ascension"
  },
  {
    "id": "mont_orford_le_campagnol_0_7_km",
    "domain": "Mont-Orford",
    "name": "Le Campagnol - 0.7 km",
    "status": "Fermé",
    "trail_type": "ascension"
  },
  {
    "id": "mont_orford_sentier_inconnu",
    "domain": "Mont-Orford",
    "name": "Sentier inconnu",
    "status": "Inconnu",
    "trail_type": "ascension"
  },
  {
    "id": "mont_orford_la_mille_pattes_1_2_km",
    "domain": "Mont-Orford",
    "name": "La Mille-Pattes - 1.2 km",
    "status": "Fermé",
    "trail_type": "ascension"
  }
]
  1. I tried integrating this with Home Assistant by using a command_line sensor and a template sensor in my configuration.yaml:
command_line:
  - sensor:
      name: Ski Conditions Mont-Orford
      command: "python /config/blueprints/script/homeassistant/ski_condition/ski_condition.py"
      value_template: "{{ value_json[0].status }}"
      json_attributes:
        - id
        - name
        - domain
        - trail_type
      unique_id: "mont_orford_device"
      device_class: "sensor"
      unit_of_measurement: ""
      icon: mdi:image-filter-hdr
      scan_interval: 3600

template:
  - sensor:
      - name: "Ski Mont Orford - Le Campagnol"
        state: "{{ value_json[0].status }}"
        attributes:
          domain: "{{ value_json[0].domain }}"
          name: "{{ value_json[0].name }}"
          trail_type: "{{ value_json[0].trail_type }}"
        unique_id: "mont_orford_campagnol"
        unit_of_measurement: ""
        icon: mdi:image-filter-hdr

However, this doesn’t work as expected. In Home Assistant’s history, I only see one status without any reference to the trail name.

I found this topic, which I believe is very similar to my use case, but it hasn’t been answered.

Could someone guide me on how to properly display all the trail status along with the name? Any help is appreciated!

Logically (well…it is) you cannot define attribute ‘id’ as ?which? id would it need to take, 0,1,2?
Multiple options, depends on what you want to achieve in the end (!), report, list, tracking state changes…etc.
If you want a sensor per item and your output is stable then you could use the CL to create a file and REST to create sensors per item, or create a CL per item making sure the json_attributes_path is correct

If you want all the data in one sensor-attributes then you can look here
REST sensor - how to reference top level array in response? - Configuration - Home Assistant Community

1 Like

The issue lies in your template sensor only processing the first trail in the JSON data. To display all trails, use a loop in the template to iterate through Tandem Skydiving in Pondicherry the value_json list. Create separate sensors for each trail with unique IDs, and use the trail name and status for each sensor’s name and state respectively. This will provide a comprehensive view of all ski trail statuses in Home Assistant.

1 Like

Remove the unit_of_measurement: "" line, as that implies a numeric state. You should find an error in your log related to that.

1 Like

Thank you so much, everyone! This topic might have been a bit too complex for my current knowledge and research, so I decided to simplify my Python script by using a brute-force approach. It might not be the most efficient solution, but at least it works.

I ended up creating a series of command sensors—19 in total.

  - sensor:
      name: Orford_Toussiski
      scan_interval: 3600
      command: >
        python /config/blueprints/script/homeassistant/ski_condition/checkTrailStatus.py "Toussiski"
      value_template: "{{ value }}"
      icon: >
        {% if value == 'Ouvert' %} mdi:image-filter-hdr
        {% else %} mdi:image-filter-hdr-outline
        {% endif %}

  - sensor:
      name: Orford_Grande_Allee
      scan_interval: 3600
      command: >
        python /config/blueprints/script/homeassistant/ski_condition/checkTrailStatus.py "Grande Allée"
      value_template: "{{ value }}"
      icon: >
        {% if value == 'Ouvert' %} mdi:image-filter-hdr
        {% else %} mdi:image-filter-hdr-outline
        {% endif %}

  - sensor:
      name: Orford_Descente
      scan_interval: 3600
      command: >
        python /config/blueprints/script/homeassistant/ski_condition/checkTrailStatus.py "Descente"
      value_template: "{{ value }}"
      icon: >
        {% if value == 'Ouvert' %} mdi:image-filter-hdr
        {% else %} mdi:image-filter-hdr-outline
        {% endif %}

If you’re curious about it here is my python script:

Résumé
import requests
from bs4 import BeautifulSoup
import argparse

def check_trail_status(trail_name):
    url = "https://montorford.com/fr-ca/hiver/condition-randonnee/"
    try:
        # Retrieve the HTML page
        response = requests.get(url)
        response.raise_for_status()
    except requests.RequestException as e:
        print(f"Error while retrieving the page: {e}")
        return

    # Parse the page with BeautifulSoup
    soup = BeautifulSoup(response.text, 'html.parser')

    # Search for all trails
    trails = soup.find_all("li", class_="item track")
    for trail in trails:
        name_tag = trail.find("span", class_="name")
        status_icon = trail.find("i", class_="icon status")
        
        if not name_tag or not status_icon:
            continue

        # Trail name and status
        name = name_tag.text.strip()
        status = status_icon.get("data-tippy", "").strip()

        # Check if the name matches
        if trail_name.lower() in name.lower():
            print(f"{status}")
            return

    print(f"Error")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Checks the status of a trail on the Mont Orford website.")
    parser.add_argument("trail_name", type=str, help="Name of the trail to check")
    args = parser.parse_args()

    check_trail_status(args.trail_name)

If you have any suggestions for optimization, I’m all ears!

This line does nothing and can be removed.

1 Like

Thank you @Troon! :herb: