Skip to content

Custom-card "Printer"

Image title

This is the custom_card_sisimomo_printer, used to show the state of a printer including, if wanted, ink sensors. The card has support any number of ink sensors under the state of the printer. These can be configured using custom colors and labels.

Credits

Author: Sisimomo (based on hiddevanbrussel pictures) Version: 0.1.0

Contributors:

Changelog

0.1.0 - Initial Release - Initial release
0.2.0 - Added additional cases - Added support for other cartridge types (tricolor) - Added further error checking for previously uncaught states - Added support for the IPP 'unavailable' state - Added CSS for better theming of error screens - Added card string translation files - Changed `text-transform` of label to capitalize - Changed `ulm_unavailable` to `ulm_translation_unavailable` - Removed some unnecessary inline stylings - Fixed the handling of unavailable and idle state styling

Card options

Options Required Description
entity The entity_id for the large card

Variables

Variable Required Description
printer_name The chosen display name of the printer.
If not provided, will use the friendly name of the provided entity.
cartridges A list of Cartridge entity objects. (See below)

Cartridge entity

Variable Required Description Requirement
label The label for the ink sensor. For better aesthetic, keep this string short eg: "BK", "Y", "M", "C", "PB"
entity_id The entity_id of the ink sensor Must be a value between 0-100 (percentage).
type The type of cartridge associated with the ink sensor Must be either 'unicolor' or 'tricolor'.
If not provided, 'unicolor' is assumed for backwards compatibility.
color The color of the ink bar For unicolor cartridges, must be a single CSS Legal Color Value. For tricolor cartridges, 3 colours are required. (See usage for more info)

Usage

Unicolor Printers

- type: "custom:button-card"
  template: "custom_card_sisimomo_printer"
  entity: sensor.hp_printer_status
  variables:
    printer_name: HP LaserJet MFP M28w
    cartridges:
      - label: "BK"
        entity_id: sensor.printer_black_ink
        type: unicolor
        color: "black"
      - label: "B"
        entity_id: sensor.printer_photo_black_ink
        type: unicolor
        color: "black"
      - label: "Y"
        entity_id: sensor.printer_yellow_ink
        type: unicolor
        color: "rgba(var(--color-yellow), 1)"
      - label: "M"
        entity_id: sensor.printer_magenta_ink
        type: unicolor
        color: "#F84B7A"
      - label: "C"
        entity_id: sensor.printer_cyan_ink
        type: unicolor
        color: "#427EDE"
      - label: "PB"
        entity_id: sensor.printer_photo_blue_ink
        type: unicolor
        color: "#9272BE"

Tricolor Printers

- type: "custom:button-card"
  template: custom_card_sisimomo_printer
  entity: sensor.canon_mg3600_series
  variables:
    ulm_card_printer_name: Canon MG3650
    cartridges:
      - label: "Col"
        entity_id: sensor.canon_mg3600_series_black
        type: tricolor
        color:
          - cyan
          - magenta
          - yellow
      - label: "BK"
        entity_id: sensor.canon_mg3600_series_black
        type: unicolor
        color: black

Template code

Template Code
custom_card_sisimomo_printer.yaml
---
custom_card_sisimomo_printer:
  template:
    - "ulm_translation_engine"
    - "ulm_language_variables"
  variables:
    printer_name: "[[[ entity.attributes.friendly_name; ]]]"
    ulm_idle: "idle"
  show_icon: false
  show_label: false
  show_name: false
  triggers_update: "all"
  tap_action:
    action: ""
  styles:
    grid:
      - grid-template-areas: "'printer_state' 'cartridges'"
      - grid-template-columns: "1fr"
      - grid-template-rows: "min-content"
    card:
      - border-radius: "var(--border-radius)"
      - box-shadow: "var(--box-shadow)"
      - padding: "12px"
      - "--mdc-ripple-press-opacity": 0
      - cursor: "default"
  custom_fields:
    printer_state:
      card:
        type: "custom:button-card"
        template: |
          [[[
            return (
              entity.state.toLowerCase() != variables.ulm_idle.toLowerCase()
              && entity.state.toLowerCase() != variables.ulm_translation_unavailable.toLowerCase()
              ? [ "icon_info", "blue_no_state" ] : [ "icon_info" ]
            );
          ]]]
        tap_action:
          action: "more-info"
        label: "[[[ return entity.state; ]]]"
        name: "[[[ return variables.printer_name; ]]]"
        entity: "[[[ return entity.entity_id; ]]]"
        styles:
          card:
            - padding: "0"
            - "--mdc-ripple-press-opacity": 0.12
            - cursor: "pointer"
          label:
            - text-transform: "capitalize"
    cartridges: |
      [[[
        // Source: https://stackoverflow.com/a/56266358
        const isColor = (strColor) => {
          const s = new Option().style;
          s.color = strColor;
          return s.color !== '';
        }

        let toner_info_available = true;
        if (variables.cartridges !== undefined ? Array.isArray(variables.cartridges) && variables.cartridges.length > 0 : false) {
          let errorArray = [];
          variables.cartridges.forEach(cartridge => {
            let index = variables.cartridges.indexOf(cartridge);
            let valid_cartridge_types = ['unicolor', 'tricolor']

            // Confirm that the label is provided.
            if (cartridge.label === undefined) {
              errorArray.push(`cartridges.[${index}].label: You must provide a value.`);
            }

            // Confirm that a valid cartridge type is provided, if not default to 'unicolor'
            // for backwards compatibility with older configuration files
            if (cartridge.type === undefined) {
                cartridge.type = 'unicolor'
            } else if (!valid_cartridge_types.includes(cartridge.type)) {
                errorArray.push(`cartridges.[${index}].type: You must provide a valid cartridge type`);
            }

            // Confirm that the color is provided and is valid color css.
            if (cartridge.color !== undefined) {
              if (cartridge.type === 'unicolor') {
                if (typeof cartridge.color === 'string' || cartridge.color instanceof String ? !isColor(cartridge.color) : false) {
                  errorArray.push(`cartridges.[${index}].color: You must provide a single valid CSS color value.`);
                }
              } else if (Array.isArray(cartridge.color) && cartridge.color.length === 3 ? cartridge.type === 'tricolor' : false) {
                cartridge.color.forEach(color => {
                  let col_index = cartridge.color.indexOf(color);
                  if (!isColor(String(color))) {
                    errorArray.push(`cartridges.[${index}].color.[${col_index}]: You must provide a single valid CSS color value.`);
                  }
                });
              } else {
                errorArray.push(`cartridges.[${index}].color: Invalid combination of colour and type.`);
              }
            } else {
              errorArray.push(`cartridges.[${index}].color: You must provide a value.`);
            }

            // Confirm that the entity_id is provided, is a valid entity_id, a integer and a value between 0 and 100 inclusively.
            if (cartridge.entity_id === undefined) {
              errorArray.push(`cartridges.[${index}].entity_id: You must provide a value.`);
            } else if (states[cartridge.entity_id] === undefined) {
              errorArray.push(`cartridges.[${index}].entity_id: You must provide a existing entity_id.`);
            } else if (String(states[cartridge.entity_id].state).toLowerCase() === String(variables.ulm_translation_unavailable).toLowerCase()) {
              toner_info_available = false;
            } else if (isNaN(states[cartridge.entity_id].state) || typeof states[cartridge.entity_id].state === "boolean") {
              errorArray.push(`cartridges.[${index}].entity_id: You must provide a entity representing an integer.`);
            } else if (states[cartridge.entity_id].state < 0 || states[cartridge.entity_id].state > 100) {
              errorArray.push(`cartridges.[${index}].entity_id: You must provide a entity representing an integer between 0 and 100 inclusively.`);
            }
          });
          if (errorArray.length > 0) {
            return `<div class="error-container">
              <b>Configuration Error:</b>
              <ul>
                ${errorArray.map(error => `<li>${error}</li>`).join("")}
              </ul>
            </div>`;
          }

          if (toner_info_available) {
            return '<div class="wrapper">' +
              variables.cartridges.map(cartridge => {
                if (cartridge.type === "unicolor") {
                  cartridge.bar_style = `
                    background-color: ${cartridge.color};
                    width: ${states[cartridge.entity_id].state}%;
                  `;
                } else if (cartridge.type === "tricolor") {
                  cartridge.bar_style = `
                    background: linear-gradient(
                      180deg,
                      ${cartridge.color[0]},
                      ${cartridge.color[0]} 33%,
                      ${cartridge.color[1]} 33%,
                      ${cartridge.color[1]} 66%,
                      ${cartridge.color[2]} 66%,
                      ${cartridge.color[2]}
                    );
                    width: ${states[cartridge.entity_id].state}%;
                  `;
                }

                // Removes unnecessary whitespace from inline CSS
                cartridge.bar_style = cartridge.bar_style.replace(/\s{2,}/g, '')
                return `
                  <div class="label">${cartridge.label}</div>
                  <div class="container-bar">
                    <div class="bar" style="${cartridge.bar_style}"></div>
                  </div>
                  <div class="state">${states[cartridge.entity_id].state}%</div>
                `;
              }).join("") +
            '</div>';
          } else {
            return `
              <div class="info-unavailable">
                Toner Information Unavailable
              </div>
            `;
          }
        } else {

        }
      ]]]
  style: |
    /* Cartridge CSS */
    div#cartridges .wrapper {
      display: grid;
      grid-template-columns: auto 1fr auto;
      grid-gap: 1rem;
      padding: 12px 8px 8px 8px;
    }
    div#cartridges .wrapper > *:nth-child(3n-2), .wrapper > *:nth-child(3n) {
      place-self: center start;
    }
    div#cartridges .wrapper > .label {
      filter: opacity(70%);
      font-size: medium;
    }
    div#cartridges .wrapper > .container-bar {
      position: relative;
      border-radius: 4px;
      border: 0.01rem solid rgba(var(--color-theme),.35);
    }
    div#cartridges .wrapper > .container-bar .bar {
      height: 20px;
      border-radius: 4px;
    }
    div#cartridges .wrapper > .state {
      filter: opacity(40%);
      font-size: medium;
    }

    /* Error CSS */
    div#cartridges .error-container {
      text-align: left;
      font-size: 75%;
      font-family: var(--code-font-family, monospace);
      padding: 10px;
      background-color: rgba(219, 68, 55, 0.75);
      margin-top:10px;
      border-radius:8px;
    }
    div#cartridges .error-container ul {
      list-style: none;
      padding: 0;
      margin: 0;
      overflow-wrap: break-word;
      word-wrap: break-word;
      white-space: normal !important;
    }
    div#cartridges .error-container ul li {
      margin-top: 0.5em;
    }
    div#cartridges .info-unavailable {
      padding: 1em;
      white-space: normal;
      margin-top:10px;
      border-radius:8px;
      opacity: 60%;
    }