This is going to be a long post. I am going to explain what started it, how it progressed, and what it finally evolved into.
We are empty nesters and find it tough to cook for two these days. Not sure how in the hell we did it before
We recently purchased a vacuum sealer, started cooking ‘casserole’ (Lasagne, baked Mac and Cheese, etc) type dishes, cut them into eight portions, seal, and freeze. Then, each of us could select what we wanted each evening and pull it out in the morning. This was cool for both of us because we didn’t both need to eat the same thing every night.
I wanted to be able to track these foods so I knew when to make more and what to make.
So, here is what I was doing in HA:
- Manually create an input_number for each item that allowed me to keep track of them.
- Use custom:numberbox-card to display the amount available with a + and - icon to make changes to our inventory.
- Manually add each item to a dashboard card so the inventory was available and could be adjusted as we ‘pulled’ items from the freezer.
As this evolved, we didn’t want to hit the basement each morning to pull from inventory and put in the fridge each morning for our dinners. So, we needed two inventories. Basement freezer and kitchen freezer (in the fridge). Once moved to the actual fridge for the evening, it should not be in any inventory count.
At this point, I manually created another input_number for the fridge. So, I had input_number.freezer_item1 and input_number.fridge_item1 for each item. I was using:
- type: horizontal-stack
title: Item 1
cards:
- type: custom:numberbox-card
icon_plus: mdi:void
picture: mdi:null
entity: input_number.fridge_item1
name: false
- type: custom:numberbox-card
picture: mdi:null
entity: input_number.freezer_item1
name: false
This hides the Plus sign on the fridge card. Yes, it is still there and can be used if something does get out of sync.
I built two automations to trigger when either the fridge or freezer type of input_number was changed.
When the fridge type changed, I only cared if it was zero and went to -1. If so, put it back to zero and decrement the freezer type. (Straight from the freezer to the fridge for that night)
When the freezer type changed and went down from what it was, increment the fridge type. (From the freezer to the fridge freezer for future)
Eventually, we wanted a larger variety. We both searched the nets for other ‘casserole’ types of dishes to expand our selection.
Okay, how can I automate this so each item magically appears?
So, I started Googling about HA and the different things I wanted to do.
- Dynamically build input_numbers
- Automatically build the numberbox-cards for those input_numbers
- Automatically add the input_numbers to the automation triggers
- Automatically build dashboard using the input_numbers
My original plan was to use areas for the input_numbers for the different types of frozen food we had (breakfast, dinner, frozen foods). But, you can only use areas from the UI. So, I used the naming convention to define the types.
For example:
input_number.fridge_breakfast_item1
input_number.freezer_breakfast_item1
input_number.fridge_dinner_item2
input_number.freezer_dinner_item2
input_number.fridge_frozen_item3
input_number.freezer_frozen_item3
Frozen is for store bought frozen foods (chopped onions, hash browns, fries, etc).
Custom items used:
decluttering_card-GitHub - custom-cards/decluttering-card: 🧹 Declutter your lovelace configuration with the help of this card
card-templater-GitHub - gadgetchnnel/lovelace-card-templater: Custom Lovelace card which allows Jinja2 templates to be applied to other cards
numberbox-card-GitHub - htmltiger/numberbox-card: Replace input_number sliders with plus and minus buttons
auto-entities-GitHub - thomasloven/lovelace-auto-entities: 🔹Automatically populate the entities-list of lovelace cards
What is needed:
- Additions to configuration.yaml
- An HA script to build the input_number text file (along with two other text files to paste into the two automations)
- An HA automation for each input_number location (freezer and fridge)
- Two shell scripts to deal with the three text files
- Two views in a dashboard
configuration.yaml - Adds three notify.files, two shell_commands, and !include my inputnumber YAML file.
notify:
- platform: file
name: scriptfridge
filename: scriptfridge.txt
timestamp: false
- platform: file
name: scriptfreezer
filename: scriptfreezer.txt
timestamp: false
- platform: file
name: inputnumber_frozenfoods
filename: inputnumber_frozenfoods.yaml
timestamp: false
shell_command:
clear_frozen: bash /config/clearfrozen.sh
fix_frozen: bash /config/fixfrozen.sh
input_number: !include inputnumber_frozenfoods.yaml
clearfrozen.sh - empties the text files I build in my script below
cd "${0%/*}"
cp inputnumber_frozenfoods.yaml inputnumber_frozenfoods.old
rm scriptfridge.txt scriptfreezer.txt inputnumber_frozenfoods.yaml
touch scriptfridge.txt scriptfreezer.txt inputnumber_frozenfoods.yaml
Build frozen foods input numbers Script - Sets the items[] variable to the list of input_numbers I need to create, loop through that list to build the inputnumber_frozenfoods.yaml, scriptfridge.txt, and scriptfreezer.txt files.
alias: Build frozen foods input numbers
variables:
items:
- breakfast|Item 1
- dinner|Item 2
- dinner|Item 3
- frozen|Item 4
- frozen|Item 5
sequence:
- service: shell_command.clear_frozen
data: {}
- service: notify.inputnumber_frozenfoods
data_template:
message: |
{% for item in items|sort %}{% set type, name = item.split('|') %}
fridge_{{ type }}_{{ name|lower|replace(' ', '_') }}:
name: {{ name }}
icon: mdi:food-steak
min: -1
max: 20
step: 1
freezer_{{ type }}_{{ name|lower|replace(' ', '_') }}:
name: {{ name }}
icon: mdi:food-steak
min: 0
max: 20
step: 1{% endfor %}
- service: notify.scriptfreezer
data_template:
message: |
{% for item in items|sort %}{% set type, name = item.split('|') %}
- input_number.freezer_{{ type }}_{{ name|lower|replace(' ', '_') }}{% endfor %}
- service: notify.scriptfridge
data_template:
message: |
{% for item in items|sort %}{% set type, name = item.split('|') %}
- input_number.fridge_{{ type }}_{{ name|lower|replace(' ', '_') }}{% endfor %}
- service: shell_command.fix_frozen
data: {}
enabled: true
- service: homeassistant.restart
data: {}
enabled: true
mode: single
The three output files are below. The notify.file service adds the first two lines to each file. Also, I got tired of trying to get the first line in the two script files to stop starting at the left. So, the following shell script fixes those files using sed.
inputnumber_frozenfoods.yaml
Home Assistant notifications (Log started: 2023-07-08T13:38:30.676722+00:00)
--------------------------------------------------------------------------------
fridge_breakfast_item_1:
name: Item 1
icon: mdi:food-steak
min: -1
max: 20
step: 1
freezer_breakfast_item_1:
name: Item 1
icon: mdi:food-steak
min: 0
max: 20
step: 1
fridge_dinner_item_2:
name: Item 2
icon: mdi:food-steak
min: -1
max: 20
step: 1
freezer_dinner_item_2:
name: Item 2
icon: mdi:food-steak
min: 0
max: 20
step: 1
fridge_dinner_item_3:
name: Item 3
icon: mdi:food-steak
min: -1
max: 20
step: 1
freezer_dinner_item_3:
name: Item 3
icon: mdi:food-steak
min: 0
max: 20
step: 1
fridge_frozen_item_4:
name: Item 4
icon: mdi:food-steak
min: -1
max: 20
step: 1
freezer_frozen_item_4:
name: Item 4
icon: mdi:food-steak
min: 0
max: 20
step: 1
fridge_frozen_item_5:
name: Item 5
icon: mdi:food-steak
min: -1
max: 20
step: 1
freezer_frozen_item_5:
name: Item 5
icon: mdi:food-steak
min: 0
max: 20
step: 1
scriptfridge.txt
Home Assistant notifications (Log started: 2023-07-08T13:38:30.683458+00:00)
--------------------------------------------------------------------------------
- input_number.fridge_breakfast_item_1
- input_number.fridge_dinner_item_2
- input_number.fridge_dinner_item_3
- input_number.fridge_frozen_item_4
- input_number.fridge_frozen_item_5
scriptfreezer.txt
Home Assistant notifications (Log started: 2023-07-08T13:38:30.679060+00:00)
--------------------------------------------------------------------------------
- input_number.freezer_breakfast_item_1
- input_number.freezer_dinner_item_2
- input_number.freezer_dinner_item_3
- input_number.freezer_frozen_item_4
- input_number.freezer_frozen_item_5
fixfrozen.sh - Removes the top two lines from each of the files created above. Also, shifts the first two lines of the script files characters to line up as they should.
cd "${0%/*}"
sed -i -e '1,2d' inputnumber_frozenfoods.yaml
sed -i -e '1,2d' -e 's/^-/ -/' scriptfridge.txt
sed -i -e '1,2d' -e 's/^-/ -/' scriptfreezer.txt
After the script is complete, the yaml and two text files are ready. The yaml file is included in the configuration.yaml file for the input_numbers.
The script then restarts HA.
The two following automations are triggered off of each input_number changing. Use the above script*.txt files to copy/paste the list into the trigger entity_ids in each script below. If we are ever able to template the entities, this will go away.
Adjust Fridge Automation
alias: Adjust Fridge
description: ""
trigger:
- platform: state
entity_id:
- input_number.fridge_breakfast_item_1
- input_number.fridge_dinner_item_2
- input_number.fridge_dinner_item_3
- input_number.fridge_frozen_item_4
- input_number.fridge_frozen_item_5
condition: []
action:
- if:
- condition: and
conditions:
- "{{ trigger.from_state.state|int == 0 }}"
- "{{ trigger.to_state.state|int == -1 }}"
then:
- service: input_number.decrement
data: {}
target:
entity_id: input_number.freezer_{{ trigger.entity_id[20:] }}
- delay:
hours: 0
minutes: 0
seconds: 1
milliseconds: 0
- service: input_number.set_value
data:
value: 0
target:
entity_id: input_number.fridge_{{ trigger.entity_id[20:] }}
mode: queued
Adjust Freezer Automation
alias: Adjust Freezer
description: ""
trigger:
- platform: state
entity_id:
- input_number.freezer_breakfast_item_1
- input_number.freezer_dinner_item_2
- input_number.freezer_dinner_item_3
- input_number.freezer_frozen_item_4
- input_number.freezer_frozen_item_5
condition: []
action:
- if:
- condition: and
conditions:
- "{{ trigger.to_state.state|int < trigger.from_state.state|int }}"
- "{{ fridge_state|int != -1 }}"
then:
- service: input_number.increment
data: {}
target:
entity_id: input_number.fridge_{{ trigger.entity_id[21:] }}
mode: queued
variables:
fridge_state: "{{ states('input_number.fridge_' ~ trigger.entity_id[21:]) }}"
And, finally, the dashboard has two views. Screenshots below.
- The first view uses auto-entities, decluttering-card, card-templater, and numberbox-card to select all of the fridge input_numbers. This allows us to select all of the foods in one view, separate them by type, and fill in both the fridge and the freezer numberbox-cards for each item.
- The second view uses auto-entities and numberbox-card. Here we only select the fridge input_numbers that are greater than zero. We then show only what is in the fridge freezer and is already upstairs.
Dashboard
decluttering_templates:
frozen_foods:
card:
type: custom:card-templater
card:
type: horizontal-stack
title_template: '{{ state_attr(''[[entity]]'', ''friendly_name'') }}'
cards:
- type: custom:numberbox-card
icon_plus: mdi:void
picture: mdi:null
entity: '[[entity]]'
name: false
- type: custom:numberbox-card
picture: mdi:null
entity_template: '{{ ''[[entity]]'' | replace(''fridge'', ''freezer'') }}'
name: false
entities:
- '[[entity]]'
- '{{ ''[[entity]]'' | replace(''fridge'', ''freezer'') }}'
views:
- title: Frozen Food
path: frozen-food
icon: mdi:food-steak
badges: []
cards:
- type: vertical-stack
cards:
- type: markdown
content: |
## Breakfast
---
- type: custom:auto-entities
card:
type: entities
card_parm: cards
filter:
include:
- entity_id: input_number.fridge_breakfast*
options:
type: custom:decluttering-card
template: frozen_foods
variables:
- entity: this.entity_id
sort:
method: name
- type: markdown
content: |
## Dinner
---
- type: custom:auto-entities
card:
type: entities
card_parm: cards
filter:
include:
- entity_id: input_number.fridge_dinner*
options:
type: custom:decluttering-card
template: frozen_foods
variables:
- entity: this.entity_id
sort:
method: name
- type: markdown
content: |
## Frozen Foods
---
- type: custom:auto-entities
card:
type: entities
card_parm: cards
filter:
include:
- entity_id: input_number.fridge_frozen*
options:
type: custom:decluttering-card
template: frozen_foods
variables:
- entity: this.entity_id
show_empty: true
sort:
method: name
- title: Fridge
path: fridge
icon: mdi:fridge-outline
badges: []
cards:
- type: vertical-stack
cards:
- type: markdown
content: |
## Breakfast
---
- type: custom:auto-entities
card:
type: entities
card_parm: cards
filter:
include:
- entity_id: input_number.fridge_breakfast*
state: '> 0'
options:
type: custom:numberbox-card
sort:
method: name
- type: markdown
content: |
## Dinner
---
- type: custom:auto-entities
card:
type: entities
card_parm: cards
filter:
include:
- entity_id: input_number.fridge_dinner*
state: '> 0'
options:
type: custom:numberbox-card
sort:
method: name
- type: markdown
content: |
## Frozen Foods
---
- type: custom:auto-entities
card:
type: entities
card_parm: cards
filter:
include:
- entity_id: input_number.fridge_frozen*
state: '> 0'
options:
type: custom:numberbox-card
sort:
method: name
Wow. That was a lot. I hope I remembered everything and it all makes sense.
Hopefully, this will give some ideas on how to do some things that you have always wanted to do, but didn’t know how.
Of course, feel free to ask any questions and I’ll try to expand on anything that doesn’t make sense.