"Smarter" way to conditionally make 100 elements clear/opaque (based on percentage of sensor state)

It looks like I’m flooding this section of the forum today, sorry about that.

I have made some battery indicators a while back that I thought would look nice as circle with colors fading from red to green based on charge status:

Screenshot 2022-10-11 at 21.36.11

Each of the little “bars” is its own element. Is there a “smart” way to make only the number of indicator lines corresponding to the charge percentage show as opaque? I.e. only have the “bars” in the upper half visible if the phone had 50% charge left?

I have practically no js or css experience, so I can only imagine the brute force method, which is not something I want to do.

No doubt there is but I am not a CSS guru so can only point you to a couple of posts. We have an example of a bar graph that shows different colours based on percentage here Home Example - Floorplan for Home Assistant

So I’m thinking you might be able to make a circular version of that.

Because yours looks so good with the little elements perhaps you could have a opaque and transparent circle which shows or hides the relevant part. Start with this post and continue with Google.

If you get it working please share!

Thanks for the pointers. I’m getting somewhere. But I can’t really figure out how to pass the state from the yaml file/javascript to the css style.

This is just filling in over time, so with the “state” being created artificially in css.

Screen Recording 2022-10-12 at 18.54.19

and I have just created a circle overlaying, that is filled in to hide the colored bars. Just a black circle, everything else is handles in css:

.circle_percent_hide {
  fill: transparent;
  stroke: #323232;
  stroke-width: 6px;
  stroke-dasharray: 471;
  stroke-dashoffset: 471;
  animation: circle-length 10s linear infinite;
}

@keyframes circle-length {
  0% {
    stroke-dashoffset: 471;
  }
  100% {
    stroke-dashoffset: 0;
  }
}

The animation: circle-length 10s linear infinite; would be deleted adn I imagine I’d have to change stroke-dashoffset: 471; to stroke-dashoffset: circle-length;.

But first I need to figure out how to send the entity.state to the css, and have the @keyframesuse that to calculate the circle-length. And here, I’m kind of stuck.

Any ideas?

You will likely want to use floorplan.style_set Here is a snippet from one of our examples that I linked before that has a variable mosture sensor bar graph. So clarifying you don’t use the CSS file in this example, you are constructing the CSS code here and applying it.

      - entity: sensor.moisture_level
        state_action:
          action: call-service
          service: floorplan.style_set
          service_data:
            element: moisture-level-clip-path
            style: |
              >
              var height = Math.ceil(elements['sensor.moisture_level'].getBBox().height);
              return `transform: translate(0, ${height - Math.floor(entity.attributes.level / (100 / height))}px)`;

This will be great when you get it working!

Ahh, so you don’t actually pass anything to the css. Good to know. Adding the battery sensor state works fine.

But there seems to me to be some inconsistency between the editor live view (which shows what I’d expect) and the view once you save the yaml changes.

Using this testing circle (svg snippet) that I want to fill in to hide the colored status bar (not shown in this test example):

    <circle
       style="display:inline;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#323232;stroke-width:6;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
       id="real_circle_2"
       cx="243.02406"
       cy="232.72011"
       r="28.786091" />

The radius needed would be r_real = r - (stroke-width/2) = 28.786091 - (6/2) = 25.786091. Meaning the circumference is c = 2 * pi * r_real ~ 162.

With this floorplan.yaml

            - name: Test Circle
              entities:
                - entity: sensor.iphone_battery_level
                  element: real_circle_2
              state_action:
                action: call-service
                service: floorplan.style_set
                service_data:
                  element: real_circle_2
                  style: |
                    >
                    return `
                      stroke-dasharray: 81;
                      transform-box: fill-box;
                      transform-origin: center;
                      transform: rotate(180deg);
                    `;

specifically the stroke-dasharray: 81; (81 being the half of the circumference of 162) would result in a half filled in circle. In the editor view that looks about right:

Screenshot 2022-10-14 at 16.31.48

But once I click “save”, the same circle in the actual dashboard, with the exact same code, looks like this:

Screenshot 2022-10-14 at 16.35.58

Any idea why the math stops working once I click “save”? Does it e.g. resize the circle so I need to multiply by some factor? And if so, (1) how do I find that factor, (2) is that factor dependent on screen size so I have to check for that and scale differently for different devices?

Unfortunately I don’t have the CSS knowledge to know why that is happening. You might have to wait and see if anyone else responds here or try a different forum which specialises in this….?

OK, I finally have it all figured out, so here’s everything in case anyone wants to use this to make a battery indicator for floorplan in this style:

battery

Screenshot 2022-10-31 at 16.00.36

Create your svg file. You can use this if you want the specific style of indicator (100 colored segments in a red-to-green gradient) to insert into your svg file, there’s 100 rectangles that make up the colored circular bar (you might want to group them), and the circle obscuring those (id: battery_circle.p30_pro_battery_level):

Minimal svg file with only the status bar and obscuring circle - you can copy-paste it into your svg file - can be found here: HomeAssistantConfig/battery.svg at 17dcdbcaa87f8d3811708f9b9326238bc39be7f0 · Aephir/HomeAssistantConfig · GitHub

Take note of a few things, in case you want to change/scale anything.

The radius of battery_circle.p30_pro_battery_level is 28.786091, and that informs the stroke-dasharray and stroke-dashoffset in the floorplan.yaml. These correspond to the circumference of the circle, given by c = 2 * pi * r so in my case c = 2 * 3.14... * 28.786091 = 180.868.

floorplan.yaml file
            - name: Battery Level
              entities:
                - entity: sensor.p30_pro_battery_level
                  element: battery_circle.p30_pro_battery_level
              tap_action: disable
              hover_action: disable
              state_action:
                service: floorplan.style_set
                service_data:
                  style: |
                    >
                    return `
                      stroke-dasharray: 180.868 180.868;
                      stroke-dashoffset: ${-(180.868*(entity.state/100))};
                      transform-box: fill-box;
                      transform-origin: center;
                      transform: rotate(181.3deg);
                      vector-effect:"none" !important;
                    `;

You can of course add as many as you’d like by just adding svg objects and entity / element lines under entities. Also note that if you, like me, copied your floorplan.css from any of the examples, you’ll likely have in your css file:

svg, svg * {
  vector-effect: non-scaling-stroke !important;
  pointer-events: all !important;
}

This won’t work, you need to delete this (at least the vector-effect: non-scaling-stroke !important;, although I’ve been recommended to not use * or !important at all in css!)

Now it’s just a few tweaks to make it look nicer, but the functionality is there. Thanks a lot to a few people who have helped with troubleshooting, especially @OzGav both here and in the github issue.

2 Likes