Card para Tarefas rotineiras

tasks

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

Button card template

Colocar o seguinte código no código fonte do lovelace, fora das views
Se não souberes vê abaixo :point_down:

  • 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

5 Likes

Excelente trabalho.

Alguns exemplos de lembretes onde o card pode ser usado:

:point_right: Desparatização
:point_right: Pagamentos de mensalidades ex: Netflix, Nabucasa etc…
:point_right: Manutenção de equipamentos, ferramentas
:point_right: Aspiração de veículos
:point_right: Limpeza de areas
:point_right: Dias de recolha de lixo
:point_right: Backups de máquinas

Etc…

2 Likes

Copyright © 2017-2020. Todos os direitos reservados
CPHA.pt - info@cpha.pt


FAQ | Termos de Serviço/Regras | Política de Privacidade