NonaSuomy/Moonraker-Home-Assistant

Displaying Thumbnails which are in a subfolder of the main gcode folder

Closed this issue · 14 comments

I've used your code for a bit but had issues with the layer count and also with the Thumbnails. I found out that if the gcode file is in the main folder the metadata for the thumbnail is given as the relative path against the gcode file itself. So if a file in the gcodes/subfolder/myfile.gcode, the relative path of the thumbnail will be .thumbs/myfile.png but and won't display as it should be subfolder/.thumbs/myfile.png.

I did a test and changed the state of the sensor.3d_printer_object_thumbnails in the developper tool to add the subfolder in front the state and then the thumbnail displayed in the card.

Is there anyway to make sure the subfolder are included in the path of the thumbnails?

Maybe I should ask the Moonraker team

Discussed with the moonraker team and we need to be able in the templates to add the subdirectory if it exists in front of the thumbnail's relative path. I'm not sure how to do this. Not proficient enough in jinja2 templating

Is the layer count correct in moonraker? As it’s just pulling data from there via json. What is determining where the gcode goes for you? The thumbnails graphic url is reported from the entity sensor.3d_printer_object_thumbnails when I look at the json nfo it already adds .thumbs/ to the front of the thumbnail for me? You can make a manual card and populate it with all the 3d printer sensor values you will see that .thumbs is there? (I only upload gcode with SuperSlicer)
E5A13B14-F36B-4922-8F07-33304891517D

making sure the code get parsed right

    - name: 3d_printer_object_thumbnails_path
      unique_id: "192.168.168.293988e9be-c910-4634-8205-38d53170d5a6"
      state: '{{ states.sensor.printer_3d_file_metadata.attributes["thumbnails"][1]["relative_path"] }}' 
      attributes:
        friendly_name: "Object Thumbnails Path"
        
    - name: 3d_printer_thumbnail
      unique_id: "192.168.168.293988e9be-c910-4634-8205-38d53170d5a7"
      state: >
          {% set dir = states('sensor.3d_printer_current_print') %}
          {% set img = states('sensor.3d_printer_object_thumbnails_path') %}
          {{ [dir.split('/')[0], img] | join('/') }}
      attributes:
        friendly_name: "Object Thumbnail"
        icon: mdi:image

And this one where I modified the rest call to be able to handle special characters in the filename

  - platform: rest
    scan_interval: 15
    name: klipper_preview_path
    unique_id: "192.168.168.29fd0ae36c-0d51-4392-a18d-89861d536ba4"
    resource_template: "http://192.168.168.29:7125/server/files/metadata?filename={{ states(('sensor.3d_printer_current_print')) |urlencode}}"
    json_attributes_path: "$.result.thumbnails.[2]"
    json_attributes:
      - relative_path
      - width
      - height
      - size
    value_template: "OK"
    
rest:
  scan_interval: 5
  resource_template: "http://192.168.168.29:7125/server/files/metadata?filename={{ states(('sensor.3d_printer_current_print')) |urlencode }}"
  sensor:
    - name: printer_3d_file_metadata
      unique_id: "192.168.168.295ec44d90-419c-419c-802d-d34071639c08"
      json_attributes_path: "$.result"
      json_attributes:
        - layer_height
        - object_height
        - thumbnails
      value_template: "OK"

Took a bit but to access three layers deep array you have to do attributes.print_stats["info"]["current_layer"]

Under Home Assistant I played with the Developer Tools -> Template:

{% set value_json=
  {
    "result": 
      {
        "eventtime": 1495044.184335206, 
        "status": 
        {
          "heater_bed": 
          {
            "temperature": 40.3, 
            "target": 0.0, 
            "power": 0.0
          }, 
          "extruder": 
          {
            "temperature": 24.87, 
            "target": 0.0, 
            "power": 0.0, 
            "can_extrude": false, 
            "pressure_advance": 0.0, 
            "smooth_time": 0.04
          }, 
          "print_stats": 
          {
            "filename": "SpringNutxyz002.gcode", 
            "total_duration": 83.25961575005203, 
            "print_duration": 0.0, 
            "filament_used": 0.0, 
            "state": "cancelled", 
            "message": "", 
            "info": 
            {
              "total_layer": 26, 
              "current_layer": 0
            }
          }, 
          "toolhead": 
          {
            "homed_axes": "", 
            "axis_minimum": [0.0, 0.0, -1.5, 0.0], 
            "axis_maximum": [117.5, 118.0, 120.0, 0.0], 
            "print_time": 1776.681568, 
            "stalls": 0, 
            "estimated_print_time": 2041.05257603125, 
            "extruder": "extruder", 
            "position": [0.0, 0.0, 0.0, 0.0], 
            "max_velocity": 200.0, 
            "max_accel": 2000.0, 
            "max_accel_to_decel": 1000.0, 
            "square_corner_velocity": 6.0
          }, 
          "display_status": 
          {
            "progress": 0.0, 
            "message": null
          }, 
          "virtual_sdcard": 
          {
            "file_path": null, 
            "progress": 0.0, 
            "is_active": false, 
            "file_position": 0.0, 
            "file_size": 0.0
          }, 
          "gcode_move": 
          {
            "speed_factor": 1.0, 
            "speed": 1500.0, 
            "extrude_factor": 1.0, 
            "absolute_coordinates": true, 
            "absolute_extrude": true, 
            "homing_origin": [0.0, 0.0, 0.0, 0.0], 
            "position": [0.0, 0.0, 0.0, 0.0], 
            "gcode_position": [0.0, 0.0, 0.0, 0.0]
          }, 
          "filament_motion_sensor btt_smart_filament_sensor": 
          {
            "filament_detected": true, 
            "enabled": true
          }, 
          "temperature_sensor Chamber_Temp": 
          {
            "temperature": 25.67, 
            "measured_min_temp": 25.46, 
            "measured_max_temp": 27.06
          }
        }
      }
    }
%}
{{value_json.result.status.print_stats["info"]["total_layer"]}}

Result type: number
26

I combined both values into one button for fun as I was going to do that with the other two layer buttons.

Which should then look like this:

image

    - name: 3d_printer_actual_layer
      unique_id: "IP167e5dec-719c-42d3-9560-4f177573f742"
      state: '{{ states.sensor.printer_3d_sensor.attributes.print_stats["info"]["current_layer"] }} / {{ states.sensor.printer_3d_sensor.attributes.print_stats["info"]["total_layer"] }}'
      availability: >
        {% set items = ['sensor.printer_3d_sensor'] %}
        {{ expand(items)|rejectattr('state','in',['unknown','unavailable'])
          |list|count == items|count }}
      icon: mdi:counter
      attributes:
        friendly_name: "Actual Layer"
    - name: 3d_printer_actual_layer
      unique_id: "IP167e5dec-719c-42d3-9560-4f177573f742"
      state: '{{ states.sensor.printer_3d_sensor.attributes.print_stats["info"]["current_layer"] }} / {{ states.sensor.printer_3d_sensor.attributes.print_stats["info"]["total_layer"] }}'
      availability: >
        {% set items = ['sensor.printer_3d_sensor'] %}
        {{ expand(items)|rejectattr('state','in',['unknown','unavailable'])
          |list|count == items|count }}
      icon: mdi:counter
      attributes:
        friendly_name: "Actual Layer"

That's great! combines both at the same time!

making sure the code get parsed right

    - name: 3d_printer_object_thumbnails_path
      unique_id: "192.168.168.293988e9be-c910-4634-8205-38d53170d5a6"
      state: '{{ states.sensor.printer_3d_file_metadata.attributes["thumbnails"][1]["relative_path"] }}' 
      attributes:
        friendly_name: "Object Thumbnails Path"
        
    - name: 3d_printer_thumbnail
      unique_id: "192.168.168.293988e9be-c910-4634-8205-38d53170d5a7"
      state: >
          {% set dir = states('sensor.3d_printer_current_print') %}
          {% set img = states('sensor.3d_printer_object_thumbnails_path') %}
          {{ [dir.split('/')[0], img] | join('/') }}
      attributes:
        friendly_name: "Object Thumbnail"
        icon: mdi:image

Did you test if this works recursively to multiple directories deep? /directory1/directory2/directory3/.thumbs/test.png etc?

Discussion on HA Discord petro says to use
[:-1] if you want everything before the last
everything after the first dir.split('/')[1:]
everything before the last 2.. dir.split('/')[:-2]

so...

{{ [dir.split('/')[:-1], img] | join('/') }}

to remove the remaining directory from the end.

Multi and no directory fix for thumbnails.

     - name: 3d_printer_object_thumbnails
       unique_id: "IP59b37837-b751-4d31-98c2-516a52edf833"
       state: >
         {% set dir = states('sensor.3d_printer_current_print') %}
         {% set img = states.sensor.printer_3d_file_metadata.attributes["thumbnails"][2]["relative_path"] %}
         {{ (dir.split('/')[:-1] + [img]) | join('/') }}
       availability: >
         {% set items = ['sensor.printer_3d_file_metadata'] %}
         {{ expand(items)|rejectattr('state','in',['unknown','unavailable'])
           |list|count == items|count }}
       icon: mdi:image
       attributes:
         friendly_name: "Object Thumbnails"

@NonaSuomy
So for full directory parsing whether there is one or 10 subdirectories, this code will work, but you need to change the camera sensor to the 3d_printer_object_thumbnails instead of 3d_printer_thumbnails

    - name: 3d_printer_object_thumbnails
      unique_id: "IP59b37837-b751-4d31-98c2-516a52edf833"
      state: >
        {% set dir = states('sensor.3d_printer_current_print') %}
        {% set img = state_attr('sensor.printer_3d_file_metadata', 'thumbnails')[1].relative_path %}
        {{ (dir.split('/')[:-1] + [img]) | join('/') }}
      availability: >
        {% set items = ['sensor.printer_3d_file_metadata'] %}
        {{ expand(items)|rejectattr('state','in',['unknown','unavailable'])
          |list|count == items|count }}
      icon: mdi:image
      attributes:
        friendly_name: "Object Thumbnails"    
       

@NonaSuomy
While configuring KlipperScreen it mentioned the same thing about getting Print_Stats_info but in order to have accurate layer position they suggest to add SET_PRINT_STATS_INFO CURRENT_LAYER={layer_num + 1} in the "After Layer Change G-Code.

The reference is here: https://klipperscreen.readthedocs.io/en/latest/Quicktips/#layer-progress

Try the current code it should always select the last thumbnail.