Timer issue how to stop it when it gets to zero

i have used this flow to create a timer for my aquarium controller which tells me how long the feed mode has left to run but the issue i have is i want it to stop when it gets to zero. just started with node red and new to java any help please
here is the flow
[{"id":"5b1333cd.ff8c1c","type":"tab","label":"Flow 8","disabled":false,"info":""},{"id":"cbf730b3.8951a","type":"inject","z":"5b1333cd.ff8c1c","name":"Setup","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":true,"onceDelay":0.1,"x":190,"y":100,"wires":[["3e33495e.6fd176"]]},{"id":"3e33495e.6fd176","type":"change","z":"5b1333cd.ff8c1c","name":"feed change rule","rules":[{"t":"set","p":"gameState","pt":"global","to":"stopped","tot":"str"},{"t":"set","p":"timeRemaining","pt":"global","to":"900000","tot":"str"},{"t":"set","p":"currentTime","pt":"global","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":390,"y":100,"wires":[[]]},{"id":"88d6ccfa.9cc92","type":"inject","z":"5b1333cd.ff8c1c","name":"Loop","topic":"","payload":"","payloadType":"date","repeat":"1","crontab":"","once":true,"onceDelay":0.1,"x":190,"y":160,"wires":[["25d07727.458328"]]},{"id":"25d07727.458328","type":"function","z":"5b1333cd.ff8c1c","name":"Update Time Remaining","func":"var currentTime = msg.payload;\nvar lastTickTime = global.get(\"lastTickTime\") || currentTime;\nvar timeRemaining = global.get(\"timeRemaining\") ||0;\nvar gameState = global.get(\"gameState\")\n\nif (gameState == \"playing\") {\n \n timeRemaining -= (currentTime - lastTickTime);\n \n global.set(\"timeRemaining\", timeRemaining);\n}\nmsg.timeRemaining = timeRemaining;\nglobal.set(\"lastTickTime\", currentTime);\n\nnode.status({fill:\"green\", shape:\"dot\", text: timeRemaining});\n\nreturn msg;","outputs":1,"noerr":0,"x":420,"y":160,"wires":[["a9fdd00a.4c7ce"]]},{"id":"a9fdd00a.4c7ce","type":"function","z":"5b1333cd.ff8c1c","name":"Format time as hh:mm:ss","func":"var t = msg.timeRemaining / 1000;\nvar h = Math.floor(t/3600);\nvar m = Math.floor(t%3600 / 60);\nvar s = Math.floor(t%3600 % 60);\n//Ensure Leading zeroes\nmsg.formattedTimeRemaining = (\"0\" + m).slice(-2) + \":\" + (\"0\" + s).slice(-2);\n\nreturn msg;","outputs":1,"noerr":0,"x":670,"y":160,"wires":[["20214b58.f93344"]]},{"id":"20214b58.f93344","type":"ui_text","z":"5b1333cd.ff8c1c","group":"2e2d67c3.babf78","order":4,"width":0,"height":0,"name":"","label":"Feed Time Remaining","format":"{{msg.formattedTimeRemaining }}","layout":"row-spread","x":960,"y":160,"wires":[]},{"id":"9cd50c2a.76d22","type":"inject","z":"5b1333cd.ff8c1c","name":"Feed","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":190,"y":260,"wires":[["e4e00ab0.434728"]]},{"id":"e4e00ab0.434728","type":"change","z":"5b1333cd.ff8c1c","name":"","rules":[{"t":"set","p":"gameState","pt":"global","to":"playing","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":400,"y":260,"wires":[[]]},{"id":"bd63158a.eefb48","type":"ui_button","z":"5b1333cd.ff8c1c","name":"","group":"2e2d67c3.babf78","order":2,"width":0,"height":0,"passthru":false,"label":"Feed","tooltip":"","color":"","bgcolor":"","icon":"","payload":"true","payloadType":"bool","topic":"","x":190,"y":220,"wires":[["e4e00ab0.434728"]]},{"id":"c32fa9b.ac0a958","type":"inject","z":"5b1333cd.ff8c1c","name":"Stop","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":190,"y":360,"wires":[["d414c5b8.ff8258"]]},{"id":"d414c5b8.ff8258","type":"change","z":"5b1333cd.ff8c1c","name":"","rules":[{"t":"set","p":"gameState","pt":"global","to":"stopped","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":400,"y":360,"wires":[[]]},{"id":"256d1ffb.712cd","type":"inject","z":"5b1333cd.ff8c1c","name":"Reset","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":190,"y":440,"wires":[["3a75bb96.ddbd04"]]},{"id":"3a75bb96.ddbd04","type":"change","z":"5b1333cd.ff8c1c","name":"","rules":[{"t":"set","p":"timeRemaining","pt":"global","to":"900000","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":410,"y":440,"wires":[[]]},{"id":"561f3292.b913bc","type":"ui_button","z":"5b1333cd.ff8c1c","name":"","group":"2e2d67c3.babf78","order":2,"width":0,"height":0,"passthru":false,"label":"Stop","tooltip":"","color":"","bgcolor":"","icon":"","payload":"","payloadType":"str","topic":"","x":190,"y":320,"wires":[["d414c5b8.ff8258"]]},{"id":"40091059.8755e","type":"ui_button","z":"5b1333cd.ff8c1c","name":"","group":"2e2d67c3.babf78","order":3,"width":0,"height":0,"passthru":false,"label":"Reset","tooltip":"","color":"","bgcolor":"","icon":"","payload":"","payloadType":"str","topic":"","x":190,"y":400,"wires":[["3a75bb96.ddbd04"]]},{"id":"2e2d67c3.babf78","type":"ui_group","z":"","name":"Default","tab":"b51dbdfc.c7bf3","order":2,"disp":true,"width":"6","collapse":false},{"id":"b51dbdfc.c7bf3","type":"ui_tab","z":"","name":"Home","icon":"dashboard"}]

You should post your code between 3 sets of backticks, so it appears like your unmodified code below:

[{"id":"5b1333cd.ff8c1c","type":"tab","label":"Flow 8","disabled":false,"info":""},{"id":"cbf730b3.8951a","type":"inject","z":"5b1333cd.ff8c1c","name":"Setup","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":true,"onceDelay":0.1,"x":190,"y":100,"wires":[["3e33495e.6fd176"]]},{"id":"3e33495e.6fd176","type":"change","z":"5b1333cd.ff8c1c","name":"feed change rule","rules":[{"t":"set","p":"gameState","pt":"global","to":"stopped","tot":"str"},{"t":"set","p":"timeRemaining","pt":"global","to":"900000","tot":"str"},{"t":"set","p":"currentTime","pt":"global","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":390,"y":100,"wires":[[]]},{"id":"88d6ccfa.9cc92","type":"inject","z":"5b1333cd.ff8c1c","name":"Loop","topic":"","payload":"","payloadType":"date","repeat":"1","crontab":"","once":true,"onceDelay":0.1,"x":190,"y":160,"wires":[["25d07727.458328"]]},{"id":"25d07727.458328","type":"function","z":"5b1333cd.ff8c1c","name":"Update Time Remaining","func":"var currentTime = msg.payload;\nvar lastTickTime = global.get(\"lastTickTime\") || currentTime;\nvar timeRemaining = global.get(\"timeRemaining\") ||0;\nvar gameState = global.get(\"gameState\")\n\nif (gameState == \"playing\") {\n \n timeRemaining -= (currentTime - lastTickTime);\n \n global.set(\"timeRemaining\", timeRemaining);\n}\nmsg.timeRemaining = timeRemaining;\nglobal.set(\"lastTickTime\", currentTime);\n\nnode.status({fill:\"green\", shape:\"dot\", text: timeRemaining});\n\nreturn msg;","outputs":1,"noerr":0,"x":420,"y":160,"wires":[["a9fdd00a.4c7ce"]]},{"id":"a9fdd00a.4c7ce","type":"function","z":"5b1333cd.ff8c1c","name":"Format time as hh:mm:ss","func":"var t = msg.timeRemaining / 1000;\nvar h = Math.floor(t/3600);\nvar m = Math.floor(t%3600 / 60);\nvar s = Math.floor(t%3600 % 60);\n//Ensure Leading zeroes\nmsg.formattedTimeRemaining = (\"0\" + m).slice(-2) + \":\" + (\"0\" + s).slice(-2);\n\nreturn msg;","outputs":1,"noerr":0,"x":670,"y":160,"wires":[["20214b58.f93344"]]},{"id":"20214b58.f93344","type":"ui_text","z":"5b1333cd.ff8c1c","group":"2e2d67c3.babf78","order":4,"width":0,"height":0,"name":"","label":"Feed Time Remaining","format":"{{msg.formattedTimeRemaining }}","layout":"row-spread","x":960,"y":160,"wires":[]},{"id":"9cd50c2a.76d22","type":"inject","z":"5b1333cd.ff8c1c","name":"Feed","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":190,"y":260,"wires":[["e4e00ab0.434728"]]},{"id":"e4e00ab0.434728","type":"change","z":"5b1333cd.ff8c1c","name":"","rules":[{"t":"set","p":"gameState","pt":"global","to":"playing","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":400,"y":260,"wires":[[]]},{"id":"bd63158a.eefb48","type":"ui_button","z":"5b1333cd.ff8c1c","name":"","group":"2e2d67c3.babf78","order":2,"width":0,"height":0,"passthru":false,"label":"Feed","tooltip":"","color":"","bgcolor":"","icon":"","payload":"true","payloadType":"bool","topic":"","x":190,"y":220,"wires":[["e4e00ab0.434728"]]},{"id":"c32fa9b.ac0a958","type":"inject","z":"5b1333cd.ff8c1c","name":"Stop","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":190,"y":360,"wires":[["d414c5b8.ff8258"]]},{"id":"d414c5b8.ff8258","type":"change","z":"5b1333cd.ff8c1c","name":"","rules":[{"t":"set","p":"gameState","pt":"global","to":"stopped","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":400,"y":360,"wires":[[]]},{"id":"256d1ffb.712cd","type":"inject","z":"5b1333cd.ff8c1c","name":"Reset","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":190,"y":440,"wires":[["3a75bb96.ddbd04"]]},{"id":"3a75bb96.ddbd04","type":"change","z":"5b1333cd.ff8c1c","name":"","rules":[{"t":"set","p":"timeRemaining","pt":"global","to":"900000","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":410,"y":440,"wires":[[]]},{"id":"561f3292.b913bc","type":"ui_button","z":"5b1333cd.ff8c1c","name":"","group":"2e2d67c3.babf78","order":2,"width":0,"height":0,"passthru":false,"label":"Stop","tooltip":"","color":"","bgcolor":"","icon":"","payload":"","payloadType":"str","topic":"","x":190,"y":320,"wires":[["d414c5b8.ff8258"]]},{"id":"40091059.8755e","type":"ui_button","z":"5b1333cd.ff8c1c","name":"","group":"2e2d67c3.babf78","order":3,"width":0,"height":0,"passthru":false,"label":"Reset","tooltip":"","color":"","bgcolor":"","icon":"","payload":"","payloadType":"str","topic":"","x":190,"y":400,"wires":[["3a75bb96.ddbd04"]]},{"id":"2e2d67c3.babf78","type":"ui_group","z":"","name":"Default","tab":"b51dbdfc.c7bf3","order":2,"disp":true,"width":"6","collapse":false},{"id":"b51dbdfc.c7bf3","type":"ui_tab","z":"","name":"Home","icon":"dashboard"}]

A simple option would to just prevent the value going below zero, for example...

msg.timeRemaining = Math.max(0, timeRemaining);

However, you might want to actually do something when it gets to zero too.

Thank you michaelblight
That does the trick and the display stops at Zero, I have modified the flow so that when i click on the feed button it resets the timer back to 15:00 minutes and a delay node to start the timer after it has been reset. what I have noticed was the timer keeps running even through the display stops at zero. would be good if I could stop the timer without clicking the stop button once the time remaining is zero. The timer is for display only as I do the switching parts with other nodes which I will share the full flow once this is sorted.
I did not realize i needed to add any ticks to show the code so i will add them to the latest code in case it helps anyone who is trying to do anything like this in future.
the flow is now as follows
If you have any idea how to improve the flow please feel free to comment as i am really new to all this

[{"id":"44c34c7d.cf1744","type":"tab","label":"Flow 8","disabled":false,"info":""},{"id":"da658601.5656a8","type":"inject","z":"44c34c7d.cf1744","name":"Setup","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":true,"onceDelay":0.1,"x":190,"y":100,"wires":[["e52580f7.20e65"]]},{"id":"e52580f7.20e65","type":"change","z":"44c34c7d.cf1744","name":"feed change rule","rules":[{"t":"set","p":"gameState","pt":"global","to":"stopped","tot":"str"},{"t":"set","p":"timeRemaining","pt":"global","to":"9000","tot":"str"},{"t":"set","p":"currentTime","pt":"global","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":730,"y":100,"wires":[[]]},{"id":"91e6295d.ba7a38","type":"inject","z":"44c34c7d.cf1744","name":"Loop","topic":"","payload":"","payloadType":"date","repeat":"1","crontab":"","once":true,"onceDelay":0.1,"x":190,"y":160,"wires":[["7c858e7.0fcdb7"]]},{"id":"7c858e7.0fcdb7","type":"function","z":"44c34c7d.cf1744","name":"Update Time Remaining","func":"var currentTime = msg.payload;\nvar lastTickTime = global.get(\"lastTickTime\") || currentTime;\nvar timeRemaining = global.get(\"timeRemaining\") ||0;\nvar gameState = global.get(\"gameState\")\n\nif (gameState == \"playing\") {\n \n timeRemaining -= (currentTime - lastTickTime);\n \n global.set(\"timeRemaining\", timeRemaining);\n}\nmsg.timeRemaining = Math.max(0, timeRemaining);\nglobal.set(\"lastTickTime\", currentTime);\n\nnode.status({fill:\"green\", shape:\"dot\", text: timeRemaining});\n\nreturn msg;","outputs":1,"noerr":0,"x":420,"y":160,"wires":[["65eaa1e8.234bf"]]},{"id":"65eaa1e8.234bf","type":"function","z":"44c34c7d.cf1744","name":"Format time as hh:mm:ss","func":"var t = msg.timeRemaining / 1000;\nvar h = Math.floor(t/3600);\nvar m = Math.floor(t%3600 / 60);\nvar s = Math.floor(t%3600 % 60);\n//Ensure Leading zeroes\nmsg.formattedTimeRemaining = (\"0\" + m).slice(-2) + \":\" + (\"0\" + s).slice(-2);\n\nreturn msg;","outputs":1,"noerr":0,"x":670,"y":160,"wires":[["6973ce17.38986"]]},{"id":"6973ce17.38986","type":"ui_text","z":"44c34c7d.cf1744","group":"2e2d67c3.babf78","order":4,"width":0,"height":0,"name":"","label":"Feed Time Remaining","format":"{{msg.formattedTimeRemaining }}","layout":"row-spread","x":960,"y":160,"wires":[]},{"id":"6cfe64c1.611c5c","type":"inject","z":"44c34c7d.cf1744","name":"Feed","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":190,"y":260,"wires":[["80a46d0a.d2e59"]]},{"id":"80a46d0a.d2e59","type":"change","z":"44c34c7d.cf1744","name":"","rules":[{"t":"set","p":"gameState","pt":"global","to":"playing","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":740,"y":240,"wires":[[]]},{"id":"1d3b4be3.a52cf4","type":"ui_button","z":"44c34c7d.cf1744","name":"","group":"2e2d67c3.babf78","order":2,"width":0,"height":0,"passthru":false,"label":"Feed","tooltip":"","color":"","bgcolor":"","icon":"","payload":"true","payloadType":"bool","topic":"","x":190,"y":220,"wires":[["6aef830c.ff505c","972b0d68.54b9d"]]},{"id":"acac7862.b97408","type":"inject","z":"44c34c7d.cf1744","name":"Stop","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":190,"y":360,"wires":[["b3529805.d89428"]]},{"id":"b3529805.d89428","type":"change","z":"44c34c7d.cf1744","name":"","rules":[{"t":"set","p":"gameState","pt":"global","to":"stopped","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":740,"y":340,"wires":[[]]},{"id":"5f887281.8db1ec","type":"inject","z":"44c34c7d.cf1744","name":"Reset","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":190,"y":440,"wires":[["6aef830c.ff505c"]]},{"id":"6aef830c.ff505c","type":"change","z":"44c34c7d.cf1744","name":"","rules":[{"t":"set","p":"timeRemaining","pt":"global","to":"900000","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":750,"y":440,"wires":[[]]},{"id":"4dea90f1.cd0d1","type":"ui_button","z":"44c34c7d.cf1744","name":"","group":"2e2d67c3.babf78","order":2,"width":0,"height":0,"passthru":false,"label":"Stop","tooltip":"","color":"","bgcolor":"","icon":"","payload":"","payloadType":"str","topic":"","x":190,"y":320,"wires":[["b3529805.d89428"]]},{"id":"9ca36d77.b036c","type":"ui_button","z":"44c34c7d.cf1744","name":"","group":"2e2d67c3.babf78","order":3,"width":0,"height":0,"passthru":false,"label":"Reset","tooltip":"","color":"","bgcolor":"","icon":"","payload":"","payloadType":"str","topic":"","x":190,"y":400,"wires":[["6aef830c.ff505c"]]},{"id":"972b0d68.54b9d","type":"delay","z":"44c34c7d.cf1744","name":"","pauseType":"delay","timeout":"1","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":360,"y":220,"wires":[["80a46d0a.d2e59"]]},{"id":"2e2d67c3.babf78","type":"ui_group","z":"","name":"Default","tab":"b51dbdfc.c7bf3","order":2,"disp":true,"width":"6","collapse":false},{"id":"b51dbdfc.c7bf3","type":"ui_tab","z":"","name":"Home","icon":"dashboard"}]