Introdução
Esta solução permite controlar tarefas rotineiras, com o minimo de esforço possível. Não é necessário criar entidades manualmente, apenas é necessário a introdução de código no lovelace, tudo o resto é feito pelo Mqtt discovery automaticamente.
Requerimentos
- MQTT
- MQTT Discovery
- Lovelace custom button-card
Button card template
Colocar o seguinte código no código fonte do lovelace, fora das views
Se não souberes vê abaixo
- Vai à página que queres adicionar o card
- No canto superior direito, carregas nos 3 pontos, e carregas em Configurar Painel
- Novamente, carregas nos 3 pontos, e carregas em Editor de configuração do código-fonte
- Colar o código no topo do código e clicar em Guardar
button_card_templates:
task:
label: |
[[[
let last_trigger = variables._last_trigger();
if (last_trigger == "NE") return "Clique para criar";
if (last_trigger == "NS") return "Clique para publicar";
if (last_trigger == 0) return "Feito hoje";
let last_trigger_suffix = (last_trigger == 1 ? " dia atrás" :" dias atrás");
return last_trigger + last_trigger_suffix;
]]]
show_label: true
icon: |
[[[
let days_left = variables._days_left(variables);
if (days_left == "NE" || days_left == "NS") return "mdi:alert-plus";
if (days_left < 0) return "mdi:alert-circle";
if (days_left <= variables.warning_before) return "mdi:clock-alert";
return "mdi:checkbox-marked-circle-outline";
]]]
custom_fields:
days_left: |
[[[
let days_left = variables._days_left(variables);
if (days_left == "NE") return;
if (days_left == "NS") return;
// publish if end_date is wrong
if (typeof(entity.attributes.timestamp_end) != 'undefined') {
if (entity.attributes.timestamp_end != variables._days_left(variables, true).getTime()/1000) {
hass.callService("mqtt", "publish", {topic: variables._state_topic(variables), payload: variables._state_payload(variables, true), retain: true});
}
}
let color = variables._colors["success"];
if (days_left <= variables.warning_before) color = variables._colors["warning"];
if (days_left < 0) color = variables._colors["error"];
let message = ( days_left >= 0 ? (days_left == 1 ? days_left+" dia restante" : days_left+" dias restantes") : (Math.abs(days_left) == 1 ? Math.abs(days_left)+" dia em atraso" : Math.abs(days_left)+" dias em atraso" ) );
return '<span style="display: inline-block; color: white; background: '+color+'; padding: 0 5px; border-radius: 5px;">' + message + '</span>'
]]]
styles:
grid:
- grid-template-areas: '"i n days_left" "i l days_left"'
- grid-template-columns: 15% 1fr 1fr
- grid-template-rows: 1fr 1fr
icon:
- color: |
[[[
let days_left = variables._days_left(variables);
if (days_left == "NE" || days_left == "NS") return variables._colors["disabled"];
if (days_left < 0) return variables._colors["error"];
if (days_left <= variables.warning_before) return variables._colors["warning"];
return variables._colors["success"];
]]]
label:
- color: var(--disabled-text-color)
- justify-self: start
name:
- justify-self: start
variables:
cycle_days: 30
warning_before: 5
mqtt_prefix: homeassistant
mqtt_state_prefix: tasks
_entity_id: |
[[[ return this._config.entity; ]]]
_sensor_name: |
[[[
if (this._config.entity.startsWith("sensor.")) return this._config.entity.substring(7);
throw new Error('Entity must be a sensor');
]]]
_colors: |
[[[
return {
"success": "#8BC24A",
"warning": "#FFC107",
"error": "#FF5252",
"disabled": "var(--disabled-text-color)"
};
]]]
_last_trigger: |
[[[
return function(){
if (typeof(entity) == 'undefined') return "NE";
if (typeof(entity.attributes.timestamp) == 'undefined') return "NS";
let one_day = 24 * 60 * 60 * 1000;
let now_date = new Date();
now_date.setHours(0, 0, 0);
let start_date = new Date(entity.attributes.timestamp*1000);
start_date.setHours(0, 0, 0);
return Math.round(Math.abs((start_date - now_date) / one_day));
}
]]]
_discovery_topic: |
[[[
return function(variables){
return (variables.mqtt_prefix.endsWith('/') ? variables.mqtt_prefix : variables.mqtt_prefix+'/')+'sensor/'+variables._sensor_name+'/config';
};
]]]
_config_payload: |
[[[
return function(variables){
let version = "v1.1";
return '{ "name": "'+variables._sensor_name+'", "stat_t": "'+variables._state_topic(variables)+'", "json_attr_t": "'+variables._state_topic(variables)+'", "val_tpl": "{{value_json[\'timestamp\']|int|timestamp_local}}", "dev_cla": "timestamp", "uniq_id": "'+variables._sensor_name+'", "dev":{"ids": ["tasks-'+variables._sensor_name+'"], "name": "Task '+variables._sensor_name+'", "mf": "@ricreis394", "sw": "'+version+'"} }';
};
]]]
_state_payload: |
[[[
return function(variables, only_update_timestamp_end=false){
let end_date_timestamp = '"unknown"';
if (variables._days_left(variables, true) != "NE" && variables._days_left(variables, true) != "NS")
end_date_timestamp = variables._days_left(variables, true).getTime()/1000;
if (only_update_timestamp_end) {
return '{ "timestamp": '+entity.attributes.timestamp+', "timestamp_end": '+end_date_timestamp+', "user_id": "'+entity.attributes.user_id+'", "user": "'+entity.attributes.user+'" }';
}
return '{ "timestamp": '+Math.round(Date.now() / 1000)+', "timestamp_end": '+end_date_timestamp+', "user_id": "'+user.id+'", "user": "'+user.name+'" }';
};
]]]
_state_topic: |
[[[
return function(variables){
return (variables.mqtt_state_prefix.endsWith('/') ? variables.mqtt_state_prefix : variables.mqtt_state_prefix+'/')+variables._sensor_name;
};
]]]
_days_left: |
[[[
return function(variables, report_end_date=false){
if (typeof(entity) == 'undefined') return "NE";
if (typeof(entity.attributes.timestamp) == 'undefined') return "NS";
let one_day = 24 * 60 * 60 * 1000;
let now_date = new Date();
now_date.setHours(0, 0, 0);
let start_date = new Date(entity.attributes.timestamp*1000);
start_date.setHours(0, 0, 0);
let end_date = new Date(
new Date(start_date).setDate(start_date.getDate() + variables.cycle_days)
);
end_date.setHours(0, 0, 0, 0);
if (report_end_date) {
return end_date;
}
return Math.round((end_date - now_date) / one_day);
};
]]]
tap_action:
confirmation: |
[[[
if (typeof(entity) != 'undefined') {
return { text: 'Tem a certeza que quer marcar a tarefa como concluída?' }
}
]]]
action: call-service
service: mqtt.publish
service_data:
topic: |
[[[
if (typeof(entity) == 'undefined')
return variables._discovery_topic(variables);
else
return variables._state_topic(variables);
]]]
payload: |
[[[
let state_payload = variables._state_payload(variables);
let state_data = {
topic: variables._state_topic(variables),
payload: state_payload,
retain: true
};
let config_payload = variables._config_payload(variables);
if (typeof(entity) == 'undefined') {
hass.callService("mqtt", "publish", state_data);
return config_payload;
} else {
return state_payload;
}
]]]
retain: true
hold_action:
action: |
[[[
if (typeof(entity) == 'undefined') return;
return 'more-info';
]]]
Card
É preciso definir algumas variáveis para que tudo funcione correto
variável | função |
---|---|
entity | o nome da entidade que vai guardar o valor |
name | o nome que vai aparecer no frontend |
variables.warning_before | Quando faltar X dias fica laranja |
variables.cycle_days | O nr de dias do ciclo, sendo uma tarefa rotineira tem de se estipular um tempo para voltar a repetir a tarefa |
variables.mqtt_prefix | Por defeito, assume o valor “homeassistant”, se tiverem configurado para um nome diferente, devem colocar aqui |
variables.mqtt_state_prefix | Por defeito, assume o valor de “tasks”, é onde vai armazenar as mensagens de estado no Mqtt |
Exemplo
type: 'custom:button-card'
template: task
entity: sensor.task_advantix
name: Desparazitação
variables:
warning_before: 5
cycle_days: 30
Como usar
A primeira vez que o card aparece, vai pedir para clicar, ao clicar vai criar a entidade e publicar o data atual do clique, logo de seguida o card vai vos mostrar os dias restantes. Podem fazer Clique longo para ver a entidade.
A entidade criada tem os seguintes atributos:
- timestamp → o timestamp do último clique
-
timestamp_end → o timestamp da data limite para efetuar a tarefa, depende da variável
cycle_days
definida no vosso card - user_id → o id do utilizador que fez o último clique
- user → o nome do utilizador que fez o último clique
As entidades criadas por este código, vão aparecer nas entidades do Mqtt com o prefixo Tasks-
Para Eliminar
Basta irem às integrações -> Mqtt -> Seleccionar o device e apagar
Créditos
-
check-button-card (github.com)
tem o mesmo conceito -
Chores - Keep track using HA (community.home-assistant.io)
conceito semelhante, mas necessita de criação dos sensores previamente, design igual