Help with Function Node to Notify Node

Hi All,

I’m trying to build a weekly summary email for my son’s chore/allowance tracker. I’ve been using ChatGPT to help me and it has been great with sensors and other yaml, but it seems to struggle with Node Red in HA. At any rate, my current set up is below and it is working, but when the email sends it looks like this.

There is supposed to be ===== for separation there, and I’m not sure why they are coming back like that. Any help would be much appreciated!!

Thanks

[
    {
        "id": "f99d3fbf1b65378f",
        "type": "inject",
        "z": "db04ecfcc56afe09",
        "name": "Every Saturday at 23:55",
        "props": [],
        "repeat": "",
        "crontab": "55 23 * * 6",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "x": 250,
        "y": 3520,
        "wires": [
            [
                "b4ab3f58a0dcab8b"
            ]
        ]
    },
    {
        "id": "b4ab3f58a0dcab8b",
        "type": "function",
        "z": "db04ecfcc56afe09",
        "name": "Generate Weekly Summary",
        "func": "const states = global.get('homeassistant')?.homeAssistant?.states || {};\n\nconst days = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'];\n\nconst startDate = new Date();\nstartDate.setDate(startDate.getDate() - startDate.getDay());\nconst endDate = new Date(startDate);\nendDate.setDate(endDate.getDate() + 6);\n\nconst formatDate = d => `${d.getMonth() + 1}/${d.getDate()}/${d.getFullYear()}`;\n\nconst getState = (id) => states[id]?.state || '0';\nconst getInt = (id) => parseInt(getState(id)) || 0;\n\nconst requiredCount = days.filter(day =>\n  getState(`binary_sensor.${day}_required_chores_done`) === 'on'\n).length;\n\nconst quotaCounts = {\n  shower: getInt('sensor.weekly_shower_count'),\n  reading: getInt('sensor.weekly_reading_count'),\n  writing: getInt('sensor.weekly_writing_count')\n};\n\nconst extraTotal = days.reduce((sum, day) => sum + getInt(`sensor.${day}_extra_chores`), 0);\nconst allowance = getState('sensor.weekly_allowance');\n\nmsg.payload = {\n  title: \"Owen's Weekly Chore Summary\",\n  message: [\n    \"======================================================================\",\n    `Weekly Summary: ${formatDate(startDate)} to ${formatDate(endDate)}`,\n    \"======================================================================\",\n    \"\",\n    `Total Allowance Earned:      $${allowance}`,\n    \"\",\n    `Required Daily Chores:       ${requiredCount} out of 7 days completed`,\n    \"\",\n    `Weekly Quota Chores:`,\n    `    Showers:                 ${quotaCounts.shower} out of 3 taken`,\n    `    Reading Sessions:        ${quotaCounts.reading} out of 3 completed`,\n    `    Writing Sessions:        ${quotaCounts.writing} out of 3 completed`,\n    \"\",\n    `Extra Chores Completed:      ${extraTotal}`\n  ].join(\"\\\\n\")  // << escaped newline\n};\n\nreturn msg;\n",
        "outputs": 1,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 540,
        "y": 3520,
        "wires": [
            [
                "634b2dc0648fce97",
                "e7b7156045b72e60"
            ]
        ]
    },
    {
        "id": "e7b7156045b72e60",
        "type": "api-call-service",
        "z": "db04ecfcc56afe09",
        "name": "Send Email via SMTP",
        "server": "aa5c8e36.8844d",
        "version": 7,
        "debugenabled": false,
        "action": "notify.smtp",
        "floorId": [],
        "areaId": [],
        "deviceId": [],
        "entityId": [],
        "labelId": [],
        "data": "{\"title\":\"{{payload.title}}\",\"message\":\"{{payload.message}}\"}",
        "dataType": "json",
        "mergeContext": "",
        "mustacheAltTags": false,
        "outputProperties": [],
        "queue": "none",
        "blockInputOverrides": false,
        "domain": "notify",
        "service": "smtp",
        "x": 820,
        "y": 3520,
        "wires": [
            []
        ]
    },
    {
        "id": "634b2dc0648fce97",
        "type": "debug",
        "z": "db04ecfcc56afe09",
        "name": "debug 2",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 780,
        "y": 3460,
        "wires": []
    },
    {
        "id": "aa5c8e36.8844d",
        "type": "server",
        "name": "Home Assistant",
        "version": 5,
        "addon": true,
        "rejectUnauthorizedCerts": true,
        "ha_boolean": "y|yes|true|on|home|open",
        "connectionDelay": true,
        "cacheJson": true,
        "heartbeat": false,
        "heartbeatInterval": 30,
        "areaSelector": "friendlyName",
        "deviceSelector": "friendlyName",
        "entitySelector": "friendlyName",
        "statusSeparator": "at: ",
        "statusYear": "hidden",
        "statusMonth": "short",
        "statusDay": "numeric",
        "statusHourCycle": "h23",
        "statusTimeFormat": "h:m",
        "enableGlobalContextStore": true
    }
]

By default, Mustache escapes HTML characters. To avoid this, you can:

  • Use triple curly brackets: {{{payload}}}

  • Or change the data field type to JSONata and use:

    { "title": payload.title, "message": payload.message }
    
  • Alternatively, pass the values via input overrides. Update your function node like this:

    msg.payload = {
      data: {
        title: "Owen's...",
        message: [...].join("\\n")
      }
    };
    return msg;
    
1 Like

Kermit, thank you!!! The very first option worked great. I was beating my head against the wall trying to sort this out. Thanks again, I appreciate the help!