Changing SVG image values based on multiple sensors states

I originally posted this here, but I figured this may be bigger than a custom component, so let’s start something new shall we?

I’ve got a challenge on my hands.

It may not be challenging for you beautiful people, but hey ho, that’s why I’m here seeking brains that aren’t as frazzled as mine. :slight_smile:

Background

I’ve tried doing what I’m about to describe using a picture-entity card - got most of the way there with separate images, but for some I get a spinner of death after a certain number of entity state images (I think it was after 8…).

So two options to overcome this - a: card-mod… or b: custom button card c: I’m open to suggestions!

Here I am trying out the custom button card solution and it’s getting too late for me to get it working on own.

My challenges (to me and) to you!

  • I’m trying to create a card that displays a picture of my car - as an SVG image. (The easy part)

  • I’m trying to update the fill colour of various paths (identified by class tags) based on the state of a sensor entity [and to make sure we give sensor’s attribute’s some love - a sensor’s attribute ‘state’ too!]. (my JavaScript is weak… and googling says I’m the only one mad enough to try it in lovelace and the custom components I’ve got… or I’m using the wrong search terms).

  • Each item is on a different sensor: eg, front right door lock = sensor.car_front_door_right, front left window = sensor.car_front_left_window

  • I’m not sure where to put the JavaScript so it’ll update (I’ve got a hunch, but am I right?) the one image from multiple sensors… :slightly_frowning_face:

Below is where I got to so far, I’ll carry on hacking away

YAML card:

    - type:  'custom:button-card'
      # template: 
      # - card_generic_swap      
      entity: sensor.ml71nym_car
      
      show_entity_picture: true
      entity_picture: /local/EQA/EQA-Merc-Top-RHD.svg
      # extra_styles: >
      #   [[[
      #     let 
      #   ]]]   
      show_icon: false
      show_label: false
      show_name: false
      show_state: false
      styles:
        card:
          - height: 190px
          - border-radius: 14px
          - box-shadow: none
          # - font-size: 30px
          - text-align: left
          - cursor: default
        entity_picture: 
          - width: 95%
          - transform: >
              [[[
                if (states['sensor.front_right_door'].state == "Open")
                return "
                    .theClassOfThePathInTheSVG {
                      filter: hue-rotate(180deg);
                      }";
                if (states['sensor.front_right_door'].state == "Open")
                return "
                    .theClassOfThePathInTheSVG {
                      filter: hue-rotate(180deg);
                      }";
                if (states['sensor.front_right_door'].state == "Open")
                return "
                    .theClassOfThePathInTheSVG {
                      filter: hue-rotate(180deg);
                      }";
              ]]]
          # - color: [] # >
              # [[[
              #   var svgHolder = document.querySelector('svg#carOutline');
              #   svgHolder.onload = function () {
              #       var svgDocument = svgHolder.contentDocument;
              #       var style = svgDocument.createElementNS("http://www.w3.org/2000/svg", "style");

              #       // Now (ab)use the @import directive to load make the browser load our css // '@import url("/css/your-dynamic-css.css");'; or..
              #       style.textContent = "<style>
              #                             .bvl-icon-lamp-outline{
              #                               fill:rgba(var(--rgb-primary-text-color), 0.7);
              #                               } 
              #                             </style>";
              #       var svgElem = svgDocument.querySelector('svg');
              #       svgElem.insertBefore(style, svgElem.firstChild);
              # ]]]

        img_cell:
          - border-radius: 14px
          - height: 100%
      # custom_fields:

Sample of SVG Image with sample classes: carOutline, doorHandle_rearLeft, doorHandle_rearRight,
doorHandle_frontLeft,doorHandle_frontRight … can be found here on dropbox.

Use css variables in your svg fill (fill: var(--my-first-color)). Then set the value of your variable within button-card in the card styles.

styles:
  card:
    - --my-first-color: |
        [[[
          if (xxx == yyy) return '#FF0000'
          else return '#00FF00' 
        ]]] 

etc… :slight_smile:

Awesome, thanks, will give it a try!

So, still no dice. Not sure what is going wrong here… I’ve tried the style both under the card: and the entity-picture: … both get triggered with the JavaScript. I’ve tried swapping out underscores for hyphens in the naming, tried putting the fill color in a style tag and stand-alone fill tag.

Here’s the yaml/JS I’ve put in the style entities:

          - --sunroofWindow-front: |
              [[[                
                if (states['light.lounge_lamp'].state != "on")
                  return "rgba(160,82,45,1)", console.log(states['sensor.car_lock_hood'].state), console.log("Sunroof color should have changed to:" + "rgba(160,82,45,1)")
                  else return "rgba(var(--rgb-primary-text-color), 1)"
              ]]]
          - --bonnet-color: |
              [[[
                
                if (states['light.lounge_lamp'].state == "on")
                  return "rgba(160,82,45,1)", console.log(states['sensor.car_lock_hood'].state), console.log("Bonnet color should have changed to:" + "rgba(160,82,45,1)")
                  else return "rgba(var(--rgb-primary-text-color), 1)"
              ]]]

Here’s the result - you can see in the console it’s triggering something. It’s like the image just doesn’t have the tags - cause I can’t find it in the inspector.

Could this be because it’s an external SVG rather than inline??
Is there a way to include an object tag that I don’t know about in home assistant:
<object data="/local/car-picture.svg" type="image/svg+xml"></object>

I’ve re-uploaded the svg with the modified fill tags to the same dropbox link.

Right! It is finally done and solved. I used the custom card: ha-floorplan. Much easier than hacking away at javascript, they did all the hard work for me. So was able to do it in a matter of hours last night and a bit this evening.

Final results - colours change based on status. Working in both light & dark mode.


2 Likes

Hi! Could you please share what trick have you used to make stroke color change in your SVG basing on mode? I tried to set it through the CSS file, but it does not work…

@media (prefers-color-scheme: dark) {
  element {stroke: white;}
}

@media (prefers-color-scheme: light) {
  element {stroke:black;}
}

1 Like

I ended up using ha-floorplan to control the stroke colours :wink: Check it out here.

Sorry for the delayed response! I may be able to help further next weekend if you haven’t cracked it! I’d be keen to see what you’ve done with it the svg!

Thank you! Seems I have understood the issue I have been experiencing - the ‘@media’ reacts on the settings of my browser, but I have changed the setting in HA frontend. I need to align setting between system-browser-hafrontend. then it works properly. Thank you!