Home

Awesome

hacs_badge Validate GitHub all releases GitHub GitHub issues by-label GitHub contributors semantic-release: angular

Spoolman Home Assistant Integration

This integration integrates Spoolman (https://github.com/Donkie/Spoolman/) into Home Assistant. This enables you to observe your filament spools and in example get notified when a spool runs out of filament.

✨ Features

[!NOTE] If one of the threshold is exceeded the integration fires an event. The event is named spoolman_spool_threshold_exceeded. Currently there are three thresholds defined: info, warning and critical.

[!IMPORTANT] If one of the threshold is exceeded, there is no other event for that particular threshold and spool until restart of HomeAssistant.

🏗️ Installation

Just add this repo to the custom repos in HACS (https://hacs.xyz/docs/faq/custom_repositories/) and you're good to go. You should now see the integration in HACS where you can download the latest version.

🛠️ Usage

Just add a new integration for Spoolman and fill in the URL to your Spoolman instance, like this:

image

You should now see a new integration entry with one device per location (your spoolman instance) and all archived spools grouped in an own device:

image

image

For every spool there is a sensor created with the exact color you've provided. Additionally the integration tracks your current weight and lengths.

All other information provides by Spoolman are stored in the attributes of the sensor:

image

[!IMPORTANT] Your spool needs at least a name and a material to get added to Home Assistant.

Usage in cards

You can use the default entities card for this:

image

Or auto-entities-card for getting all entities by this integration dynamically: https://github.com/thomasloven/lovelace-auto-entities

And a mushroom-template-card card for example. https://github.com/piitaya/lovelace-mushroom/blob/main/docs/cards/template.md

A simple card utilizing mushroom-template-card and auto-entities to dynamically show all spools could look like this:

type: custom:auto-entities
filter:
  include:
    - integration: '*spoolman*'
      sort:
        method: attribute
        attribute: location
        reverse: false
      attributes:
        archived: false
      options:
        type: custom:mushroom-template-card
        vertical: false
        icon_color: '#{{ state_attr(entity, ''filament_color_hex'') }}'
        icon: mdi:printer-3d-nozzle
        badge_icon: |
          {% if state_attr(entity, 'archived') == true %}
            mdi:archive
          {% elif state_attr(entity, 'klipper_active_spool') == true %}
            mdi:check-circle
          {% endif %}
        badge_color: |
          {% if state_attr(entity, 'archived') == true %}
            orange
          {% elif state_attr(entity, 'klipper_active_spool') == true %}
            green
          {% else %}
            default_color
          {% endif %}
        primary: |
          {% set location = state_attr(entity, 'location') %} {% if location %}
            {{ state_attr(entity, 'filament_name') }} ({{ location }})
          {% else %}
            {{ state_attr(entity, 'filament_name') }}
          {% endif %}
        secondary: '{{ (state_attr(entity, ''remaining_weight'') | float)  | round(2) }} g'
        tap_action:
          action: more-info
sort:
  method: attribute
  attribute: klipper_active_spool
  reverse: true
card:
  type: grid
  columns: 2
  square: false
card_param: cards

This card does:

image

Automation example

An automation in Homeassistant could be something like this:

alias: Filament almost empty
description: ""
trigger:
  - platform: event
    event_type: spoolman_spool_threshold_exceeded
condition: []
action:
  - service: notify.notify
    data_template:
      title: >-
        {{ trigger.event.data['threshold_name'] | capitalize }}: Spool almost
        empty
      message: >-
        The spool {{ trigger.event.data['spool']['filament']['vendor']['name']
        }} {{ trigger.event.data['spool']['filament']['name'] }} {{
        trigger.event.data['spool']['filament']['material'] }} has reached {{
        trigger.event.data['spool']['used_percentage'] }}% usage
mode: restart

You can use the following data within your templates:

{
    "entity_id": self.entity_id,
    "spool": spool,
    "threshold_name": threshold_name,
    "threshold_value": config_threshold,
    "used_percentage": used_percentage,
}

A spool has this structure (according to the OpenAPI description of Spoolman):

{
    "id": 1,
    "registered": "2023-09-22T19:52:36Z",
    "first_used": "2023-09-23T04:22:26.975000Z",
    "last_used": "2023-09-30T04:09:34.242017Z",
    "filament": {
        "id": 1,
        "registered": "2023-09-22T19:52:07Z",
        "name": "Black",
        "vendor": {
            "id": 1,
            "registered": "2023-09-22T19:43:26Z",
            "name": "Jayo"
        },
        "material": "PLA+",
        "price": 15.99,
        "density": 1.24,
        "diameter": 1.75,
        "weight": 1100,
        "article_number": "B0BJ1FR86Y",
        "comment": "",
        "settings_extruder_temp": 210,
        "settings_bed_temp": 60,
        "color_hex": "000000"
    },
    "remaining_weight": 4.468290721106769,
    "used_weight": 1095.5317092788932,
    "remaining_length": 1498.1446855790216,
    "used_length": 367313.8366728618,
    "location": "Lager",
    "lot_nr": "1704306141A",
    "archived": false
}

Home Assistant services

This integration creates services to be used in automations:

spoolman.patch_spool

This service is used to change values and properties if a spool. The data must match the data for the Spoolman API

[!IMPORTANT] You can't update remaining_weight and used_weight in one update. You can only set one of them. Spoolmann calculates the missing field by itself.

service: spoolman.patch_spool
data:
  id: 45
  first_used: "2019-08-24T14:15:22.000Z"
  last_used: "2019-08-24T14:15:22.000Z"
  price: 20
  initial_weight: 200
  spool_weight: 200
  location: Shelf B
  remaining_weight: 200
  lot_nr: 52342

Contributing

If you're developer and want to contribute to the project, please feel free to do a PR! But there are some contraints I want to enforce by convention (currently I evaluate the possibility to enforce this by rules. If you have a good hint, please let me know 🎉):

Cheers 🔥