I had not seen this post until now, but I had the same issue on finding out when my dryer was finished with a Shelly Pro Plug.
The way I solved it was to use the /aenergy/by_minute
values that are returned. This has three values that are the amount of energy used for the previous three minutes. I used this to see if they all were less than a set threshold and report finished if so.
[{"id":"5eb9edf0837f1d1f","type":"change","z":"48f70e83376cab74","name":"Set Shelly Power Details","rules":[{"t":"set","p":"shellyDevices","pt":"flow","to":"{\"fcb467bee8f8\":{\"name\":\"Dryer\",\"threshold\":350,\"type\":\"I\",\"alertrecip\":\"user1\"}}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":290,"y":40,"wires":[[]]},{"id":"4f381a724fdd8d5e","type":"link in","z":"48f70e83376cab74","name":"Trigger - Startup","links":["6c98517f4e521057"],"x":115,"y":40,"wires":[["5eb9edf0837f1d1f"]],"icon":"font-awesome/fa-thumb-tack"},{"id":"e9c51bcd0ea74790","type":"link in","z":"48f70e83376cab74","name":"Shelly Power Plug","links":["28c4ec9d0ee61629"],"x":35,"y":40,"wires":[["fe62ef257cfd476a"]],"icon":"font-awesome/fa-power-off"},{"id":"287d959b0926e5af","type":"switch","z":"48f70e83376cab74","name":"Device\\n Select","property":"topic","propertyType":"msg","rules":[{"t":"eq","v":"Dryer","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":350,"y":160,"wires":[["ef823703414f6ee5"]]},{"id":"ef823703414f6ee5","type":"link out","z":"48f70e83376cab74","name":"Dryer","mode":"link","links":["64390ea627f84088","7e621ecc334d4472","ac3601ddfd865bc6","e7d03d4fe4c40dc2","dccee15310f12204","1c22c361dc9976a5"],"x":570,"y":160,"wires":[],"icon":"font-awesome/fa-power-off","l":true},{"id":"cf03708640fd8d83","type":"rbe","z":"48f70e83376cab74","name":"Alerts","func":"rbei","gap":"","start":"","inout":"out","septopics":true,"property":"payload","topi":"topic","x":350,"y":280,"wires":[["71856f01b40aac56"]]},{"id":"fe62ef257cfd476a","type":"delay","z":"48f70e83376cab74","name":"1/1s","pauseType":"rate","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":55,"y":160,"wires":[["564cb4447e1156e1"]],"l":false},{"id":"71856f01b40aac56","type":"link out","z":"48f70e83376cab74","name":"PushOver","mode":"link","links":["a750115b04e3749b"],"x":560,"y":280,"wires":[],"icon":"node-red-node-pushover/pushover.png","l":true},{"id":"564cb4447e1156e1","type":"function","z":"48f70e83376cab74","name":"Format Data","func":"var func = global.get(\"func\");\nconst state = {\n \"0\": \"Powered Off\",\n \"1\": \"Powered On\",\n \"R\": \"Running\",\n \"F\": \"Finished\",\n \"X\": \"Unknown State\"\n}\n\nvar now = new Date();\nvar time = func.formatDate(now).split(\" - \")[1];\nvar deviceList = flow.get(\"shellyDevices\") || [];\nif (deviceList.length == 0) { return null }\n\nvar mac = (msg.topic.split(\"/\")[1]);\nvar device = deviceList[mac];\nvar deviceName = device.name;\nvar deviceType = device.type;\nvar threshold = device.threshold;\nvar alertrecip = device.alertrecip;\n\nmsg = {\n topic: deviceName,\n threshold: threshold,\n history: [0,0,0],\n output: msg.payload.output,\n apower: msg.payload.apower, // will be renamed to msg.payload\n payload: msg.payload\n}\n\nif (msg.payload.aenergy && msg.payload.aenergy.by_minute) {\n msg.history = msg.payload.aenergy.by_minute\n}\n\n// There are two possible deviceType values:\n// \n// I: Intermittent : Devices that are ONLY powered on when they are required (Dryer, Washer, TV)\n// C: Constant : Devices that are ALWAYS powered on and reporting usage (Freezer, Rack)\n// \n// ###############################################\n\nif (deviceType == \"C\") {\n // START DEVICETYPE == \"C\" //\n msg = msg;\n // END DEVICETYPE == \"C\" //\n}\nelse if (deviceType == \"I\") {\n // START DEVICETYPE == \"I\" //\n var deviceData = flow.get(`p${msg.topic}`) || {opState: state[\"X\"], startTime: \"\"};\n msg.opState = deviceData.opState;\n msg.started = deviceData.startTime;\n msg.updated = time;\n msg.duration = \"-\";\n\n // Calculate duration (which may be zero)\n if ((msg.opState != state[\"0\"]) || (msg.started != \"\")) {\n var tUpdated = new Date(\"1970-01-01T\" + msg.updated).getTime();\n var tStarted = new Date(\"1970-01-01T\" + msg.started).getTime();\n var tDuration = ((tUpdated - tStarted) / 1000);\n msg.duration = (func.convertTime(tDuration, \"short\"))\n }\n\n if (msg.output == false) {\n msg.started = \"\";\n msg.opState = state[\"0\"]\n flow.set(`p${msg.topic}`, {opState: msg.opState, startTime: \"\", date: now});\n }\n else if (msg.output == true) {\n if (deviceData.startTime == \"\") { // Newly Powered On\n msg.started = time;\n msg.opState = state[\"1\"]\n flow.set(`p${msg.topic}`, {opState: msg.opState, startTime: msg.started, date: now});\n }\n else {\n // Continuing Power Usage - what is the power output (apower)\n var aMinute = msg.history;\n if ((aMinute[0] > msg.threshold) && (aMinute[1] > msg.threshold) && (aMinute[2] > msg.threshold)) { msg.opState = state[\"R\"] }\n\n if (msg.opState == state[\"1\"]) {\n if (aMinute[0] > msg.threshold) { msg.opState = state[\"R\"] }\n }\n else if (tDuration > 3600) {\n if ((aMinute[1] < msg.threshold) && (aMinute[2] < msg.threshold)) { msg.opState = state[\"F\"] }\n }\n else {\n if ((aMinute[0] < msg.threshold) && (aMinute[1] < msg.threshold)) { msg.opState = state[\"F\"] }\n if ((aMinute[0] < msg.threshold) && (aMinute[2] < msg.threshold)) { msg.opState = state[\"F\"] }\n if ((aMinute[0] < msg.threshold) && (aMinute[1] < msg.threshold) && (aMinute[2] < msg.threshold)) { msg.opState = state[\"F\"] }\n }\n\n flow.set(`p${msg.topic}`, {opState: msg.opState, startTime: deviceData.startTime, date: now});\n }\n }\n\n if (msg.started == \"\") { msg.started = \"--:--:--\" }\n // END DEVICETYPE == \"I\" //\n}\nelse {\n // START UNKNOWN DEVICETYPE //\n msg.topic = \"Power Error\"\n msg.opState = `Unknown 'deviceType' Value: ${deviceType}`\n // END UNKNOWN DEVICETYPE //\n}\n\nmsg.payload = msg.apower;\n\nreturn [\n msg,\n {topic: msg.topic, payload: msg.opState, device: alertrecip}\n]\n","outputs":2,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":170,"y":160,"wires":[["287d959b0926e5af"],["cf03708640fd8d83"]]},{"id":"2a092c751585d595","type":"group","z":"48f70e83376cab74","name":"fcb467bee8f8 - Dryer","style":{"stroke":"#ffC000","fill":"#ffefbf","fill-opacity":"0.25","label":true,"color":"#000000"},"nodes":["8c3bb350d5c6e7b1","e8c28d583cb5bcad","9fb570b8f92cf4b1","64390ea627f84088","8377cf2e9b5c2f5a","028d04f6be5e8cd4","ee414198db5be446","9eefd1d93fb09536","91a83caff4e558df"],"x":14,"y":699,"w":612,"h":122},{"id":"8c3bb350d5c6e7b1","type":"ui_text","z":"48f70e83376cab74","g":"2a092c751585d595","group":"b3dc5ac5c3026ddc","order":2,"width":3,"height":1,"name":"Watts","label":"","format":"{{msg.payload}} W","layout":"row-center","className":"","style":false,"font":"","fontSize":16,"color":"#000000","x":530,"y":740,"wires":[]},{"id":"e8c28d583cb5bcad","type":"ui_text","z":"48f70e83376cab74","g":"2a092c751585d595","group":"b3dc5ac5c3026ddc","order":3,"width":2,"height":1,"name":"Started","label":"{{msg.started}}","format":"","layout":"row-center","className":"","style":false,"font":"","fontSize":16,"color":"#000000","x":540,"y":780,"wires":[]},{"id":"9fb570b8f92cf4b1","type":"ui_chart","z":"48f70e83376cab74","g":"2a092c751585d595","name":"Chart","group":"b3dc5ac5c3026ddc","order":6,"width":7,"height":5,"label":"","chartType":"line","legend":"false","xformat":"HH:mm","interpolate":"linear","nodata":"Powered Off","dot":false,"ymin":"0","ymax":"1000","removeOlder":"1","removeOlderPoints":"","removeOlderUnit":"3600","cutout":0,"useOneColor":false,"useUTC":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"outputs":1,"useDifferentColor":false,"className":"","x":230,"y":780,"wires":[[]]},{"id":"64390ea627f84088","type":"link in","z":"48f70e83376cab74","g":"2a092c751585d595","name":"Dryer","links":["ef823703414f6ee5","d8840cb1b67a45d2"],"x":55,"y":760,"wires":[["e8c28d583cb5bcad","8c3bb350d5c6e7b1","8377cf2e9b5c2f5a","028d04f6be5e8cd4","ee414198db5be446","91a83caff4e558df"]],"icon":"font-awesome/fa-power-off"},{"id":"8377cf2e9b5c2f5a","type":"change","z":"48f70e83376cab74","g":"2a092c751585d595","name":"Set opState","rules":[{"t":"set","p":"payload","pt":"msg","to":"opState","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":135,"y":740,"wires":[["9eefd1d93fb09536"]],"l":false},{"id":"028d04f6be5e8cd4","type":"ui_text","z":"48f70e83376cab74","g":"2a092c751585d595","group":"b3dc5ac5c3026ddc","order":5,"width":2,"height":1,"name":"Updated","label":"{{msg.updated}}","format":"","layout":"row-center","className":"","style":false,"font":"","fontSize":16,"color":"#000000","x":380,"y":780,"wires":[]},{"id":"ee414198db5be446","type":"ui_text","z":"48f70e83376cab74","g":"2a092c751585d595","group":"b3dc5ac5c3026ddc","order":4,"width":3,"height":1,"name":"Duration","label":"{{msg.duration}}","format":"","layout":"row-center","className":"","style":false,"font":"","fontSize":16,"color":"#000000","x":380,"y":740,"wires":[]},{"id":"9eefd1d93fb09536","type":"ui_led","z":"48f70e83376cab74","g":"2a092c751585d595","order":1,"group":"b3dc5ac5c3026ddc","width":4,"height":1,"label":"{{msg.opState}}","labelPlacement":"left","labelAlignment":"left","colorForValue":[{"color":"#000000","value":"Off","valueType":"str"},{"color":"#007fff","value":"Ready","valueType":"str"},{"color":"#00ff00","value":"Running","valueType":"str"},{"color":"#ff0000","value":"Finished","valueType":"str"}],"allowColorForValueInMessage":false,"shape":"circle","showGlow":false,"name":"State","x":230,"y":740,"wires":[]},{"id":"91a83caff4e558df","type":"function","z":"48f70e83376cab74","g":"2a092c751585d595","name":"History & Status","func":"var aMinute = msg.history;\nvar data = `${msg.updated} | ${parseInt(aMinute[0])} / ${parseInt(aMinute[1])} / ${parseInt(aMinute[2])} | ${msg.opState}`;\n\nnode.status({\n fill: \"blue\",\n shape: \"dot\",\n text: data\n});\n\nif (msg.opState == \"Powered Off\") { return {payload: []}; }\nreturn msg;\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":135,"y":780,"wires":[["9fb570b8f92cf4b1"]],"icon":"node-red-contrib-home-assistant-websocket/ha-current-state.svg","l":false},{"id":"b3dc5ac5c3026ddc","type":"ui_group","name":"Garage - Dryer","tab":"9777d11ed41b8896","order":2,"disp":true,"width":"7","collapse":false,"className":""},{"id":"9777d11ed41b8896","type":"ui_tab","name":"Shelly Power","icon":"power_settings_new","order":7,"disabled":false,"hidden":false}]
The input is from a MQTT-In node for the plug.
This is also modular in that you can add many more plugs and have them reporting correctly as well as different types of usage. "Intermediate" for things like washers/dryers or "Permanent" for things like servers.
Names, thresholds and alert recipients are all device specific in the top change node called "Set Shelly Power Details"
Hope this helps