MDI Glyph Substitutions

I made a substitution file that makes including mdi icons in fonts easier and friendlier. The glyphs can be added using substitutions, which also means you do not have to remember which icon a certain unicode represents.

The yaml file can be downloaded from the gist here:

To use it, include the substitutions into your yaml file in the following way:

substitutions:
  name: bluetooth-proxy
  <<: !include mdi_glyph_substitutions.yaml

Which extendends the substitutions with the entire file (you will notice some slowdown when esphome reads out your config).

You still need to include the mdi font and the glyphs as is described. When esphome prints your config, you will also notice the unicode is used instead of the icon name.

font:
  - file: "fonts/materialdesignicons-webfont.ttf"
    id: mdi_icon
    size: 20
    glyphs: 
      - ${mdi_wifi}
      - ${mdi_wifi_remove}
      - ${mdi_thermometer}

Using it in, for example, an lvgl label:

    - label:
        text: ${mdi_wifi_remove}
        id: lbl_hastatus
        text_font: mdi_icon
        align: top_mid

If you use the mdi vscode intellisens extension (Material Design Icons Intellisense - Visual Studio Marketplace), you can add this matcher so it correctly identifies them as icons:

        {
            "match": "\\bmdi_{snake}\\b",
            "insert": "mdi_{snake}",
            "displayName": "ESPHome Substitutions",
            "name": "esphome"
        },

And lastly, the python code used to generate it:

import mdi_pil
import yaml

yaml_dict = {}
IDENTIFIER = "mdi_"
HEX_PREFIX = r"\U000"
file = "mdi_glyph_substitutions.yaml"

for icon, hex in mdi_pil.mdi_icons.items():
    icon: str
    icon_id = IDENTIFIER + icon.replace("-","_")
    icon_unicode : str = HEX_PREFIX + hex
    yaml_dict[icon_id] = icon_unicode.encode().decode('unicode_escape')

def quoted_presenter(dumper, data : str):
    if data.startswith(HEX_PREFIX):
        data = data.encode().decode('unicode_escape')
        return dumper.represent_scalar('tag:yaml.org,2002:str', data, style='"')
    return dumper.represent_scalar('tag:yaml.org,2002:str', data)

with open(file,"w") as f:
    yaml.dump(yaml_dict, f, indent=4, default_flow_style=False, explicit_start=True)
4 Likes

I’ve implemented your solution, thanks for sharing!

As far as I can see, no additional memory is allocated in ESPHome for the included file and I assume the linker only includes the used references.

1 Like

That is really helpful. I was thinking about this the other day and a it would be cool to have a utility that can generate font files.

So for example if I set a font to “mdi_icon_20” a preprocessing script would generate this for me.

font:
  - file: "fonts/materialdesignicons-webfont.ttf"
    id: mdi_icon_20
    size: 20

And if I created the label with ${mdi_wifi_remove} in it the preprocessing script would generate.

font:
  - file: "fonts/materialdesignicons-webfont.ttf"
    id: mdi_icon
    size: 20
    glyphs: 
      - ${mdi_wifi_remove}

This way you can add a font or icon whenever you want to your code and not worry about building the font file.

I was thinking of something similar at first. I think the substitution engine can be used for that, in fact. At the moment, it does not allow variables with characters other than underscores and alfanumerical (for good reason), so I think it could be utilised to implement built in mdi substitutions using the Home Assistant identifier (so mdi:my-icon).

However, it would come quite some bookkeeping, and perhaps architectural changes (at the very least changes to the substitution engine). Mainly because using the mdi substitutions may not be usable everywhere.

It would also likely use the mdi_pil library, which I wrote because there was not really anything to simply put mdi icons on python images. That does mean I do not know if it is up to par with the coding standards of ESPHome :sweat_smile: It does come with the font file installed, so it does prevent the hassle of having to get that from somewhere.

If anyone wants to take on that task (or another way of implementing it), I am happy to assist where possible :slight_smile: