Hi all,
first of all thanks for the great pieces of shared code to all contributors. Iām currently implementing this myself and since I had some trouble figuring stuff out I wanna share my experience here if people may face the same issues. This is based on @shbatm 's and @dicera 's code so props to them.
I am not sure whether all the names of the Menus Iāll give are correct since my HA-Installation is set to German and so are the menus but Iāll try and give it a good translation so you will hopefully find it.
-
Requirements:
You need to have button-card installed
-
Entities:
For each chore you want to track you need to create a helper entity of type āinput_datetimeā. You need at least one to implement a card so I advise to create a dummy one upfront while you experiment. You can change them later. To do so go to settings > Devices & Services > On the top select āHelpersā > On the bottom right click ā+ Create Helperā > Select āDate and/or Timeā > Give it a name, symbol and make sure the radio button for just āDateā is selected > Click create. Repeat this for every chore you want to track.
-
Where does which code belong?
Had to try a bit to figure it out so I wanna share it. Add them step by step in the order I describe because some parts rely on code added in earlier parts. Otherwise things might not work
This part:
sensor:
- platform: time_date
display_options:
- 'time'
- 'date'
goes into you configuration.yaml. If you donāt have any sensors configuerd yet you can just add it at the end. If you already have sensors (look if yo find the word āsensor:ā somewhere) you can leave out the first line and just add it to your list of sensors.
This part:
button_chores_direct:
aspect_ratio: 2/1
show_state: true
show_label: false
show_name: true
show_icon: true
size: 20%
triggers_update: sensor.time
tap_action:
action: call-service
service: input_datetime.set_datetime
service_data:
entity_id: entity
date: |
[[[
return states['sensor.date'].state;
]]]
hold_action:
action: more-info
styles:
card:
- border-radius: var(--border-radius)
- filter: opacity(100%)
- background-color: |
[[[
if (Date.now()/1000 - entity.attributes.timestamp < entity.attributes.alert_green * 24 * 3600)
return "mediumaquamarine";
if (Date.now()/1000 - entity.attributes.timestamp < entity.attributes.alert_orange * 24 * 3600)
return "orange";
return "red";
]]]
grid:
- grid-template-areas: '"i" "n" "s"'
- grid-template-columns: 1fr
- grid-template-rows: 1fr min-content min-content
label:
- font-size: 11px
- font-family: Helvetica
- padding: 0px 10px
- justify-self: start
- color: var(--label-color-off)
state:
- font-size: 11px
- font-family: Helvetica
- padding: 0px 10px
- justify-self: start
- font-weight: bold
- color: var(--upcoming-color)
img_cell:
- justify-content: start
- padding-left: 13%
name:
- color: var(--upcoming-color)
- justify-self: start
- font-weight: bold
- font-family: Helvetica
- font-size: 13px
- margin-top: 0px
- margin-left: 10px
state_display: |
[[[
const time = c => {
const s = (c / 1000);
const m = (c / (1000 * 60));
const h = (c / (1000 * 60 * 60));
const d = (c / (1000 * 60 * 60 * 24));
if (s < 60) {
return parseInt(s) + 's ago';
} else if (m < 60) {
return parseInt(m) + 'm ago';
} else if (h < 24) {
return parseInt(h) + 'h ago';
} else {
return parseInt(d) + 'd ago';
}
};
let last_changed = entity === undefined || time(Date.now() - Date.parse(entity.state));
return last_changed;
]]]
goes into the Raw code of the dashboard you want to implement your button cards. On your dashboard click the three dots on the top right > click edit dashboard > click the three dots again > select Raw configuration editor. Then paste this code just at the end of the code window.
Here is the code for simpler date display:
state_display: |
[[[
const time = c => {
const d = Math.floor(c / (1000 * 60 * 60 * 24));
if (d < 1) {
return 'Today';
}
if (d === 1) {
return 'Yesterday';
}
return `${parseInt(d)} days ago`;
};
return entity === undefined || time(Date.now() - Date.parse(entity.state));
]]]
If someone is interested, I also made a version which additionaly shows you when the task was done last, when the task is due next time if itās not due and how long it is (over)due if it is (over)due. It also breaks the due days down to weeks if it is more than seven and switches between singular and plural for weeks and days (I hope correctly).
Here is a screenshot from my dashboard:
So feel free to use and modify:
state_display: |
[[[
var ago = ''
var until = ''
const time = c =>
{
const d = Math.floor(c / (1000 * 60 * 60 * 24));
if (d < 1)
{
ago = 'last today';
}
else if (d === 1)
{
ago = 'last yesterday';
}
else if (d < 7)
{
ago = 'last ' + (d+1) + ' days ago';
}
else if (Math.floor(d/7) === 1)
{
if ((d-(Math.floor(d/7)*7)) < 1)
{
ago = 'last ' + Math.floor(d/7) + ' week ago';
}
else if ((d-(Math.floor(d/7)*7)) === 1)
{
ago = 'last ' + Math.floor(d/7) + ' week and ' + (d-(Math.floor(d/7)*7)) + ' day ago';
}
else
{
ago = 'last ' + Math.floor(d/7) + ' week and ' + (d-(Math.floor(d/7)*7)) + ' days ago';
}
}
else
{
if ((d-(Math.floor(d/7)*7)) < 1)
{
ago = 'last ' + Math.floor(d/7) + ' weeks ago';
}
else if ((d-(Math.floor(d/7)*7)) === 1)
{
ago = 'last ' + Math.floor(d/7) + ' weeks and ' + (d-(Math.floor(d/7)*7)) + ' day';
}
else
{
ago = 'last ' + Math.floor(d/7) + ' weeks and ' + (d-(Math.floor(d/7)*7)) + ' days';
}
}
if (Date.now()/1000 - entity.attributes.timestamp < entity.attributes.alert_green * 24 * 3600)
{
var calc = Math.floor(((entity.attributes.timestamp + (entity.attributes.alert_green * 24 * 3600)) - Math.floor(Date.now()/1000))/(60 * 60 * 24))
if (calc < 1)
{
until = 'due tomorrow';
}
else if (calc < 7)
{
until = 'due in ' + (calc+1) + ' days';
}
else if (Math.floor(calc/7) === 1)
{
if ((calc-(Math.floor(calc/7)*7)) < 1)
{
until = 'due in ' + Math.floor(calc/7) + ' week';
}
else if ((calc-(Math.floor(calc/7)*7)) === 1)
{
until = 'due in ' + Math.floor(calc/7) + ' week and ' + (calc-(Math.floor(calc/7)*7)) + ' day';
}
else
{
until = 'due in ' + Math.floor(calc/7) + ' week and ' + (calc-(Math.floor(calc/7)*7)) + ' days';
}
}
else
{
if ((calc-(Math.floor(calc/7)*7)) < 1)
{
until = 'due in ' + Math.floor(calc/7) + ' weeks';
}
else if ((calc-(Math.floor(calc/7)*7)) === 1)
{
until = 'due in ' + Math.floor(calc/7) + ' weeks and ' + (calc-(Math.floor(calc/7)*7)) + ' day';
}
else
{
until = 'due in ' + Math.floor(calc/7) + ' weeks and ' + (calc-(Math.floor(calc/7)*7)) + ' days';
}
}
}
else if (Date.now()/1000 - entity.attributes.timestamp < entity.attributes.alert_orange * 24 * 3600)
{
var calc = Math.floor((Math.floor(Date.now()/1000) - entity.attributes.timestamp - entity.attributes.alert_green * 24 * 3600)/(60 * 60 * 24))
if (calc < 1)
{
until = 'due since today';
}
else if (calc === 1)
{
until = 'due since yesterday';
}
else if (calc < 7)
{
until = 'due for ' + calc + ' days';
}
else if (Math.floor(calc/7) === 1)
{
if ((calc-(Math.floor(calc/7)*7)) < 1)
{
until = 'due for ' + Math.floor(calc/7) + ' week';
}
else if ((calc-(Math.floor(calc/7)*7)) === 1)
{
until = 'due for ' + Math.floor(calc/7) + ' week and ' + (calc-(Math.floor(calc/7)*7)) + ' day';
}
else
{
until = 'due for ' + Math.floor(calc/7) + ' week and ' + (calc-(Math.floor(calc/7)*7)) + ' days';
}
}
else
{
if ((calc-(Math.floor(calc/7)*7)) < 1)
{
until = 'due for ' + Math.floor(calc/7) + ' weeks';
}
else if ((calc-(Math.floor(calc/7)*7)) === 1)
{
until = 'due for ' + Math.floor(calc/7) + ' weeks and ' + (calc-(Math.floor(calc/7)*7)) + ' day';
}
else
{
until = 'due for ' + Math.floor(calc/7) + ' weeks and ' + (calc-(Math.floor(calc/7)*7)) + ' days';
}
}
}
else
{
var calc = Math.floor((Math.floor(Date.now()/1000) - entity.attributes.timestamp - entity.attributes.alert_orange * 24 * 3600)/(60 * 60 * 24))
if (calc < 1)
{
until = 'overdue since today';
}
else if (calc === 1)
{
until = 'overdue since yesterday';
}
else if (calc < 7)
{
until = 'overdue for ' + calc + ' days';
}
else if (Math.floor(calc/7) === 1)
{
if ((calc-(Math.floor(calc/7)*7)) < 1)
{
until = 'overdue for ' + Math.floor(calc/7) + ' week';
}
else if ((calc-(Math.floor(calc/7)*7)) === 1)
{
until = 'overdue for ' + Math.floor(calc/7) + ' week and ' + (calc-(Math.floor(calc/7)*7)) + ' day';
}
else
{
until = 'overdue for ' + Math.floor(calc/7) + ' week and ' + (calc-(Math.floor(calc/7)*7)) + ' days';
}
}
else
{
if ((calc-(Math.floor(calc/7)*7)) < 1)
{
until = 'overdue for ' + Math.floor(calc/7) + ' weeks';
}
else if ((calc-(Math.floor(calc/7)*7)) === 1)
{
until = 'overdue for ' + Math.floor(calc/7) + ' weeks and ' + (calc-(Math.floor(calc/7)*7)) + ' day';
}
else
{
until = 'overdue for ' + Math.floor(calc/7) + ' weeks and ' + (calc-(Math.floor(calc/7)*7)) + ' days';
}
}
}
return '<DIV align="left">' + ago + '<br/>' + until + '</DIV>'
};
return entity === undefined || time(Date.now() - Date.parse(entity.state));
]]]
Sure there is some potential to shorten this down but I didnāt have the time yet.
Here comes the part I am not sure of. Maybe @shbatm or someone else can explan this:
# For each input_datetime use the following customizations:
homeassistant:
customize:
input_datetime.entity_name:
alert_green: 6
alert_orange: 13
I guess this goes into configuration.yaml. At least this wors for me. This adds the variables alert_green and alert_orange to your helper. These values how many days the card will stay green and orange respectively and can be set ther. You need to do this for every helper you create. However these variables are also passed in the configuration of the card in the next step. So do we need them twice? It works for me if I just leave it out of the card config card but it seems kind of annoying that you have to restart HA every time to save the changes in configuration.yaml. So any help is welcome.
Finally this part:
type: custom:button-card
template: button_chores_direct
entity: input_datetime.entity_name
name: Chore name
variables:
alert_green: 13
alert_orange: 14
goes into the code of the button card. On your normal dashboard editor click ā+ Add cardā on the bottom right and search for ābutton-cardā and select it. This will open a new window with a small code editor to the left. Paste the code there. The first two lines can stay as they are. In line three you need to put in the name of the entity created earlier. They are always called input_datetime.something. You can look up the correct entity ID in your list of helpers from step 2.Line four has the chore name. I think this explains itself.
For the variables I am not sure as I wrot in the previous part. They seem to be optional since they are set in configuration.yaml. However can I override them here? Does not seem so for me. Is there a way to somehow define the variables globally and overwrite it via the card setting? Maybe someone can help with this.
So maybe someone can help me with my open questions and I hope I can help people who are maybe stuck while trying to get this work.