Offline detection for Z2M devices with last_seen

Hi, can you explain how it works? Notify sensors that have a last_seen greater than 36 hours?

Would it be possible to store the number of reported devices somewhere? For instance in a helper?

All, Iā€™ve been looking at this blueprint, but as Iā€™m using NodeRed for most of my automations. I went into this direction and made an easy flow which you can connect to telegram or whatever. Iā€™ve taken 4 hours to see if a device becomes unresponsive. This doesnā€™t take away the work that has been done in this blueprint and last_seen attribute that needs to be added as described in Offline detection for Z2M devices with last_seen - #28 by Kosy.

Hope this helps some folks out.

[
    {
        "id": "f9d73f2633b55f9c",
        "type": "inject",
        "z": "2114eb300a6fb5f4",
        "name": "",
        "props": [
            {
                "p": "payload"
            }
        ],
        "repeat": "",
        "crontab": "00 18 * * *",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 130,
        "y": 180,
        "wires": [
            [
                "10fe19a7a5243af1"
            ]
        ]
    },
    {
        "id": "10fe19a7a5243af1",
        "type": "ha-get-entities",
        "z": "2114eb300a6fb5f4",
        "name": "Sensors battery",
        "server": "3c172c5d.c09a84",
        "version": 0,
        "rules": [
            {
                "property": "attributes.device_class",
                "logic": "is",
                "value": "battery",
                "valueType": "str"
            },
            {
                "property": "entity_id",
                "logic": "is_not",
                "value": ".+(_battery_low)$",
                "valueType": "re"
            }
        ],
        "output_type": "array",
        "output_empty_results": false,
        "output_location_type": "msg",
        "output_location": "payload",
        "output_results_count": 1,
        "x": 320,
        "y": 180,
        "wires": [
            [
                "e5c70db55c0ac57b"
            ]
        ]
    },
    {
        "id": "85a9a621be0dcbe4",
        "type": "debug",
        "z": "2114eb300a6fb5f4",
        "name": "Zigbee Unresponsive Sensors",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 1690,
        "y": 240,
        "wires": []
    },
    {
        "id": "e5c70db55c0ac57b",
        "type": "function",
        "z": "2114eb300a6fb5f4",
        "name": "function 1",
        "func": "let sensors = msg.payload;\nlet timeNow = Date.now();\nlet timeNowlocal = new Date (timeNow);\nlet unresponsive = [];\n\nfor (let i=0; i < sensors.length; i++) {\n  let lastSeen = sensors[i].attributes.last_seen;\n  let lastSeenlocal = new Date (lastSeen);\n  if ((lastSeenlocal.getTime()+14400000) <= timeNowlocal.getTime()) {\n    unresponsive.push(sensors[i]);\n  };\n};\nmsg.payload = unresponsive;\nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 540,
        "y": 60,
        "wires": [
            [
                "5d2a83801b256c7d"
            ]
        ]
    },
    {
        "id": "34eb35f1abf167a6",
        "type": "sort",
        "z": "2114eb300a6fb5f4",
        "name": "Sort by entity_id",
        "order": "ascending",
        "as_num": false,
        "target": "",
        "targetType": "seq",
        "msgKey": "",
        "msgKeyType": "elem",
        "seqKey": "payload.entity_id",
        "seqKeyType": "msg",
        "x": 780,
        "y": 180,
        "wires": [
            [
                "0e0801a0c2050c43"
            ]
        ]
    },
    {
        "id": "203a6d03c5869ff0",
        "type": "join",
        "z": "2114eb300a6fb5f4",
        "name": "",
        "mode": "custom",
        "build": "string",
        "property": "payload",
        "propertyType": "msg",
        "key": "topic",
        "joiner": "\\n",
        "joinerType": "str",
        "accumulate": false,
        "timeout": "",
        "count": "",
        "reduceRight": false,
        "reduceExp": "",
        "reduceInit": "",
        "reduceInitType": "",
        "reduceFixup": "",
        "x": 1170,
        "y": 180,
        "wires": [
            [
                "90dbba0c598e01fb"
            ]
        ]
    },
    {
        "id": "0e0801a0c2050c43",
        "type": "function",
        "z": "2114eb300a6fb5f4",
        "name": "Prep message",
        "func": "var sensor = msg.payload.attributes.friendly_name.replace(\" Battery\",\"\");\nmsg.payload = sensor;\nreturn msg;",
        "outputs": 1,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 1000,
        "y": 180,
        "wires": [
            [
                "203a6d03c5869ff0"
            ]
        ]
    },
    {
        "id": "5d2a83801b256c7d",
        "type": "split",
        "z": "2114eb300a6fb5f4",
        "name": "",
        "splt": "\\n",
        "spltType": "str",
        "arraySplt": 1,
        "arraySpltType": "len",
        "stream": false,
        "addname": "",
        "x": 590,
        "y": 180,
        "wires": [
            [
                "34eb35f1abf167a6"
            ]
        ]
    },
    {
        "id": "90dbba0c598e01fb",
        "type": "function",
        "z": "2114eb300a6fb5f4",
        "name": "Prep Notification",
        "func": "let message =  \"Unresponsive Sensors: \\n\" + msg.payload\nmsg.payload = message;\nreturn msg;",
        "outputs": 1,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 1390,
        "y": 180,
        "wires": [
            [
                "85a9a621be0dcbe4",
                "1b9bed5908900dd1"
            ]
        ]
    },
    {
        "id": "3c172c5d.c09a84",
        "type": "server",
        "name": "Home Assistant Main",
        "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
    }
]

For people who want to use this blueprint as a sensor (so that you can show the list on a dashboard card), just put this in the template config file : (I choose 6 hours, you can change it)

`# Z2MQTT last seen

  • trigger:
    • platform: time_pattern
      hours: /1
      sensor:
    • name: z2mqtt last seen
      unique_id: z2mqtt_last_seen
      state: >
      {% set result = namespace(sensors=[]) %}
      {% for state in states.sensor | rejectattr(ā€˜attributes.device_classā€™, ā€˜undefinedā€™) | selectattr(ā€˜attributes.device_classā€™, ā€˜==ā€™, ā€˜timestampā€™) %}
      {% if ā€˜last_seenā€™ in state.entity_id and (states(state.entity_id) == ā€˜unavailableā€™ or ((as_timestamp(now()) - as_timestamp(states(state.entity_id))) > (6 * 60 * 60))) %}
      {% set result.sensors = result.sensors + [state.name | regex_replace(find=ā€™ last seenā€™, replace=ā€™ā€™) ~ ā€™ (ā€™ ~ relative_time(strptime(states(state.entity_id), ā€˜%Y-%m-%dT%H:%M:%S%zā€™, ā€˜unavailableā€™)) ~ ā€˜)ā€™] %}
      {% endif %}
      {% endfor %}
      {{ result.sensors | join(ā€™\nā€™) }}`
1 Like

Would it be possible / would it make sense to differenciate by device class, e.g.

  • get an alert if a router is offline longer than one hour
  • get an alert if a device if offline longer than six hours
1 Like

I found this automation.
But itā€™s no longer working - itā€™s a convenient idea
Or the notification in the form - {{sensors}} will not go through

what do you mean by template config file? pls help a noob.

Works fine!
When creating the automation based on the BP, youā€™ll have to add an action, a notification for example. Key is to add {{sensors}} in order to get the sensor names

service: notify.persistent_notification
data:
  title: Sensors last_seen exceeded 4hrs
  message: |
    Sensors to check:
    {{sensors}}
    Message Timestamp: {{ now().strftime("%d-%m-%Y %H:%M.%S") }}
    Automation: [Devices, Z2M - last_seen exceeds 4hrs]

2 Likes

@Mr_Groch
Hello,
Just a quick post to say thank you.
I was looking for something that can help to monitor my Z2M devices and your blueprint is perfectly doing the job :slight_smile:
@peter_kawa : You service example allow to do a quick test, thanks for sharing.

Thanks !

1 Like

Will be good if the blue print could be updated and support Excluded Sensors via Labels.
@Mr_Groch any chance you can add this?

1 Like

Hi Peter.

Can you help me with not being told which devices are offline in z2m? I just get this message as you can see in the picture. I have used the same yaml code as you, but with my own mobile phone :slight_smile:

Hi J,

I donā€™t know what goes wrong here.

Hereā€™s my yaml and a screenshot, I hope it helps!

alias: Devices, z2m - 18:00 chk last_seen exceeded 24h
description: ""
use_blueprint:
  path: Mr-Groch/offline-notification-for-sensors-with-last_seen.yaml
  input:
    hours: 24
    actions:
      - service: notify.mobile_app_s9_pe_hass_vm
        metadata: {}
        data:
          title: Zigbee device(s) silent
          message: |
            Sensors last_seen exceeded 24 hours: {{sensors}}
            @ {{ now().strftime("%d-%m-%Y %H:%M.%S") }}
            [Devices, z2m - 18:00 chk last_seen exceeded 24h]
      - service: notify.persistent_notification
        data:
          title: Zigbee device(s) silent
          message: |
            Sensors last_seen exceeded 24 hours: {{sensors}}
            @ {{ now().strftime("%d-%m-%Y %H:%M.%S") }}
            [Devices, z2m - 18:00 chk last_seen exceeded 24h]
    time: "18:00:00"

Itā€™s weird, it wonā€™t send a message about the devices that are offline, it just sends the message with the text written in it. It is as if there is no connection from HA to z2m regarding last seen status. It is switched on in z2m.

all I get is
This template listens for the following state changed events:

  • Domain: sensor

what have I missed

Hi, just wanted to say thank you !
Regards.

Hi, Iā€™ve added this to my Z2M configuration file so ā€˜Last seenā€™ sensor is enabled by default, but after restarting z2m multiple times, it still doesnā€™t show.

Any ideas? Thanks

device_options:
  homeassistant:
    last_seen:
      enabled_by_default: true

Have you also done :

advanced:
  last_seen: ISO_8601_local

Yeh, but I switched it back to ISO_8601, should it be set to local?

Edit: I got it partially working, it only applies to newly paired devices. I had to manually enable last_seen for existing ones.

Hi!

I would really like to use that blueprint, but ever get this error:

Error rendering variables: ValueError: Template error: as_timestamp got invalid input 'unknown' when rendering template '{% set result = namespace(sensors=[]) %} {% for state in states.sensor | rejectattr('attributes.device_class', 'undefined') | selectattr('attributes.device_class', '==', 'timestamp') %} {% if 'last_seen' in state.entity_id and not state.entity_id in exclude.entity_id and (states(state.entity_id) == 'unavailable' or ((as_timestamp(now()) - as_timestamp(states(state.entity_id))) > ((hours | int) * 60 * 60))) %} {% set result.sensors = result.sensors + [state.name | regex_replace(find=' last seen', replace='') ~ ' (' ~ relative_time(strptime(states(state.entity_id), '%Y-%m-%dT%H:%M:%S%z', 'unavailable')) ~ ')'] %} {% endif %} {% endfor %} {{ result.sensors | join(', ') }}' but no default was specified

I know itā€™s something with defaults, but could not find the right place.
Please help - Thanks

Chances are you installed it and type run, but donā€™t have all the required inputs.
May I suggest checking the inputs against those required then triggering the blueprint the way it was intended.

Note: I donā€™t know this blueprint personally, but I am getting this from the error you posted. Please forgive me if Iā€™m wrong.)