Help with numbers to text (TTS)

(I know.... Stupid question)

I've been a bit bitten by the TTS bug.

When it says the time I am getting different results at different times how the time is said.

Exert of code:

[{"id":"943ee548ae140c50","type":"change","z":"235f16ee6e459f2c","g":"00f37121fa1e88a9","name":"TimeStamp","rules":[{"t":"set","p":"payload","pt":"msg","to":"","tot":"date"}],"action":"","property":"","from":"","to":"","reg":false,"x":4020,"y":3880,"wires":[["c8db866e84b556ab"]]},{"id":"c8db866e84b556ab","type":"moment","z":"235f16ee6e459f2c","g":"00f37121fa1e88a9","name":"","topic":"","input":"payload","inputType":"msg","inTz":"Australia/Sydney","adjAmount":0,"adjType":"days","adjDir":"add","format":"HH:mm:ss","locale":"en_AU","output":"payload","outputType":"msg","outTz":"Australia/Sydney","x":4210,"y":3880,"wires":[["78a97b936514a7b7"]]},{"id":"78a97b936514a7b7","type":"function","z":"235f16ee6e459f2c","g":"00f37121fa1e88a9","name":"function 40","func":"let x = msg.payload.split(\":\")\n\n//node.warn(x);\n\nmsg.hour = x[0];\nmsg.minute = x[1];\nmsg.second = x[2];\n\nif (msg.minute == 0)\n{\n    msg.minute = \" hundred \";\n    //return msg;\n}\n\nif (msg.second == 0)\n{\n    msg.second = \"exactly\";\n} else\nmsg.second = \" and \" + x[2] + \" seconds\";\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":4410,"y":3880,"wires":[["945027a34e67375e"]]},{"id":"945027a34e67375e","type":"template","z":"235f16ee6e459f2c","g":"00f37121fa1e88a9","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"The time is {{hour}} {{minute}} {{second}}","output":"str","x":4560,"y":3880,"wires":[["91699b85b4e6a7d6","0a1c7d0838510fc2"]]}]

Given it is 07:45:20 I hear:

The time is oh 7 four five and twenty seconds

I don't get why the 20 is said as twenty yet the 45 is said as four five.

Someone - please.

Not seeing any TTS node in your code

But if it was there - what node would it be?

Try using Jsonata to convert to words

 $split($$.payload, ":").$formatInteger($number($), "w")

Example flow

[{"id":"8cc3059d5fbd5f22","type":"inject","z":"d1395164b4eec73e","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"$moment().tz(\"Australia/Sydney\").format(\"H:m:s\")","payloadType":"jsonata","x":270,"y":5580,"wires":[["3da5cca44220f42b"]]},{"id":"3da5cca44220f42b","type":"change","z":"d1395164b4eec73e","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"$split($$.payload, \":\").$formatInteger($number($), \"w\")","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":400,"y":5580,"wires":[["1df5c7c0bd2ce8a9","b050c494fdc4aac8"]]},{"id":"1df5c7c0bd2ce8a9","type":"function","z":"d1395164b4eec73e","name":"function 40","func":"let x = msg.payload\n\n//node.warn(x);\n\nmsg.hour = x[0];\nmsg.minute = x[1];\nmsg.second = x[2];\n\nif (msg.minute == \"zero\")\n{\n    msg.minute = \" hundred \";\n    //return msg;\n}\n\nif (msg.second == \"zero\")\n{\n    msg.second = \"exactly\";\n} else\nmsg.second = \" and \" + x[2] + \" seconds\";\n\nreturn msg;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":540,"y":5580,"wires":[["3ccd929a0eb9c875"]]},{"id":"b050c494fdc4aac8","type":"debug","z":"d1395164b4eec73e","name":"debug 2487","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":530,"y":5500,"wires":[]},{"id":"3ccd929a0eb9c875","type":"template","z":"d1395164b4eec73e","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"The time is {{hour}} {{minute}} {{second}}","output":"str","x":690,"y":5580,"wires":[["d86ad42663068fde"]]},{"id":"d86ad42663068fde","type":"debug","z":"d1395164b4eec73e","name":"debug 2486","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":630,"y":5620,"wires":[]}]

For the sake of sharing, this is the bit with the TTS code:

[{"id":"6f171251266fb16d","type":"group","z":"235f16ee6e459f2c","name":"TTS","style":{"stroke":"#000000","fill":"#7fb7df","label":true,"color":"#ffC000"},"nodes":["8889470f9a47deb2","ec3cd9d8ff86edb8","3d9635bc.53c14a","17d1372ec4e2fa56","eddc469dd44ddef0","b803161ae495cc15","4fc8e8dcbbe54ad3","8da5e4f1394ecad2","e3445ae13936d183","2899787677c5f0b9","4bdbf5745e5c2a3f","fd06db60ec34cd35","1eb57714d4e51236","81963dcf419e6fbe","f97489d19613ea8c"],"x":4204,"y":1179,"w":642,"h":252},{"id":"8889470f9a47deb2","type":"debug","z":"235f16ee6e459f2c","g":"6f171251266fb16d","name":"Voice message","active":true,"tosidebar":false,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":4560,"y":1220,"wires":[]},{"id":"ec3cd9d8ff86edb8","type":"trigger","z":"235f16ee6e459f2c","g":"6f171251266fb16d","name":"","op1":"","op2":" ","op1type":"pay","op2type":"str","duration":"5","extend":false,"overrideDelay":false,"units":"s","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":4390,"y":1220,"wires":[["8889470f9a47deb2"]]},{"id":"3d9635bc.53c14a","type":"ttsultimate","z":"235f16ee6e459f2c","g":"6f171251266fb16d","name":"","voice":"en-AU","ssml":false,"sonosipaddress":"192.168.1.109","sonosvolume":"30","sonoshailing":"0","config":"557d8082.eb5a8","property":"payload","propertyType":{},"rules":[],"playertype":"noplayer","speakingrate":"","speakingpitch":"","unmuteIfMuted":false,"elevenlabsStability":"","elevenlabsSimilarity_boost":"","x":4400,"y":1280,"wires":[["17d1372ec4e2fa56","e5cf171d06085ea9"],[]]},{"id":"17d1372ec4e2fa56","type":"switch","z":"235f16ee6e459f2c","g":"6f171251266fb16d","name":"","property":"filesArray","propertyType":"msg","rules":[{"t":"nnull"}],"checkall":"true","repair":false,"outputs":1,"x":4550,"y":1280,"wires":[["fd06db60ec34cd35"]]},{"id":"eddc469dd44ddef0","type":"function","z":"235f16ee6e459f2c","g":"6f171251266fb16d","name":"get voice message","func":"msg.payload = msg.filesArray[0].file;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":4345,"y":1340,"wires":[["b803161ae495cc15"]],"l":false},{"id":"b803161ae495cc15","type":"function","z":"235f16ee6e459f2c","g":"6f171251266fb16d","name":"MUST HAVE for volume","func":"const x = flow.get(\"default_volume\");\n\nif (msg.volume == undefined)\n{\n    msg.volume = x;\n}\nif (msg.volume == 0)\n    msg.volume = x;\n\n//  If too loud limit to 1.6\nif (msg.volume >= 1.7)\n    msg.volume = 1.6;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":4455,"y":1340,"wires":[["4fc8e8dcbbe54ad3","fe118f6a19b8d45f"]],"l":false},{"id":"4fc8e8dcbbe54ad3","type":"template","z":"235f16ee6e459f2c","g":"6f171251266fb16d","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{{{payload}}} -af \"volume={{volume}}\" -f wav - | DISPLAY=:0 aplay","output":"str","x":4560,"y":1340,"wires":[["8da5e4f1394ecad2"]]},{"id":"8da5e4f1394ecad2","type":"exec","z":"235f16ee6e459f2c","g":"6f171251266fb16d","command":"ffmpeg -i","addpay":"payload","append":"","useSpawn":"false","timer":"","winHide":false,"oldrc":false,"name":"","x":4720,"y":1340,"wires":[[],[],["4bdbf5745e5c2a3f"]]},{"id":"e3445ae13936d183","type":"junction","z":"235f16ee6e459f2c","g":"6f171251266fb16d","x":4290,"y":1320,"wires":[["ec3cd9d8ff86edb8","3d9635bc.53c14a","1eb57714d4e51236","81963dcf419e6fbe"]]},{"id":"2899787677c5f0b9","type":"link out","z":"235f16ee6e459f2c","g":"6f171251266fb16d","name":"next message","mode":"link","links":["70681aaf5f80084c"],"x":4765,"y":1390,"wires":[]},{"id":"4bdbf5745e5c2a3f","type":"function","z":"235f16ee6e459f2c","g":"6f171251266fb16d","name":"next message","func":"let msg1 = {\"flush\":1};\nreturn msg1;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":4675,"y":1390,"wires":[["2899787677c5f0b9"]],"l":false},{"id":"fd06db60ec34cd35","type":"change","z":"235f16ee6e459f2c","g":"6f171251266fb16d","name":"","rules":[{"t":"set","p":"volume","pt":"msg","to":"volume","tot":"flow"}],"action":"","property":"","from":"","to":"","reg":false,"x":4740,"y":1280,"wires":[["eddc469dd44ddef0"]]},{"id":"1eb57714d4e51236","type":"change","z":"235f16ee6e459f2c","g":"6f171251266fb16d","name":"","rules":[{"t":"set","p":"volume","pt":"flow","to":"volume","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":4740,"y":1220,"wires":[[]]},{"id":"81963dcf419e6fbe","type":"link out","z":"235f16ee6e459f2c","g":"6f171251266fb16d","name":"link out 51","mode":"link","links":["a4cfa2cbe080abbc"],"x":4345,"y":1380,"wires":[]},{"id":"f97489d19613ea8c","type":"link in","z":"235f16ee6e459f2c","g":"6f171251266fb16d","name":"TTS - Otherwise","links":["a7de867f4916b788","91699b85b4e6a7d6","18a5cef1083a1bf7"],"x":4245,"y":1320,"wires":[["e3445ae13936d183"]]},{"id":"557d8082.eb5a8","type":"ttsultimate-config","name":"googletranslate","noderedipaddress":"127.0.0.1","noderedport":"1980","purgediratrestart":"leave","ttsservice":"googletranslate","TTSRootFolderPath":""}]

UPDATE:

Now it is 16:27:53

With the code I posted and I use the code - as is/was - I get:

The time is sixteen twenty seven and fifty three seconds.

So what has changed from this morning when I got (for minutes) four five and not fourty five?

Thanks, but I am having slight difficulty understanding exactly how to do what you did.

Looking: you changed 2 nodes. Ok.

The initial inject node injects .... the timestamp but with formatting and (maybe) some tricks.

I didn't realise that at first and only did the change node.
Putting that in my flow (the original part posted) it didn't work.

So I'm stuck how to actually do what you did.
I'm guessing: what you have in the inject node, I put in a change node.

It then begs the question: Could I put it like this:
(I'll try anyway)

[{"id":"3da5cca44220f42b","type":"change","z":"7879609.4fb06a","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"$moment().tz(\"Australia/Sydney\").format(\"H:m:s\")","tot":"jsonata"},{"t":"set","p":"payload","pt":"msg","to":"$split($$.payload, \":\").$formatInteger($number($), \"w\")","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":1470,"y":520,"wires":[["1df5c7c0bd2ce8a9","b050c494fdc4aac8"]]}]

Or for quicker:

(Oh, sorry)

And so I don't need the moment node that way too?

I added a change node and altered the function node. You can add the change node after your moment node and replace your function node. Your moment node should format "H:m:s".

Or

Remove your moment node add the change node you created and replace the function node.

Yeah, sorry.

I did see that.

Got it working, but... (Sorry).....

Some values come through as (eg) four one and not forty one sometimes in the minutes but the seconds for the same value I get forty one.

So if (example) if it is 12:41:41 I would get twelve four one forty one.

I am going to have to do more digging into which values they are.

What I may do is make a 0 to 59 inject node and inject those into the TTS node and see what comes out.

But thanks.

This is the code now as I have it.

[{"id":"694c22c904cc66e4","type":"change","z":"235f16ee6e459f2c","g":"00f37121fa1e88a9","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"$moment().tz(\"Australia/Sydney\").format(\"H:m:s\")","tot":"jsonata"},{"t":"set","p":"payload","pt":"msg","to":"$split($$.payload, \":\").$formatInteger($number($), \"w\")","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":4040,"y":3890,"wires":[["1df5c7c0bd2ce8a9"]]},{"id":"1df5c7c0bd2ce8a9","type":"function","z":"235f16ee6e459f2c","g":"00f37121fa1e88a9","name":"new time reader","func":"let x = msg.payload\n\n//node.warn(x);\n\nmsg.hour = x[0];\nmsg.minute = x[1];\nmsg.second = x[2];\n\nif (msg.minute == \"zero\")\n{\n    msg.minute = \" hundred \";\n    //return msg;\n}\n\nif (msg.second == \"zero\")\n{\n    msg.second = \"exactly\";\n} else\nmsg.second = \" and \" + x[2] + \" seconds\";\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":4250,"y":3890,"wires":[["945027a34e67375e"]]},{"id":"945027a34e67375e","type":"template","z":"235f16ee6e459f2c","g":"00f37121fa1e88a9","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"The time is {{hour}} {{minute}} {{second}}","output":"str","x":4430,"y":3910,"wires":[["91699b85b4e6a7d6","0a1c7d0838510fc2"]]}]

Show the debug output of the text when you get four one or any reading that says individual numbers instead of combined.

You can inject the text 21:21:21 or similar direct into a change node with out the moment expression.

1 Like

That's hard to do as it is not always done.

It happened this morning but now (in the afternoon) it doesn't seem to be doing it.

Though it is now 19:28 and I have to wait for about 12 minutes for 19:41 to be sure.

But I will put a debug there to get the output.

Well, this is what I get now (for example):

The time is nineteen sixteen and nineteen seconds

Which is what I want.
:slight_smile:
(That is out of the template node) BTW

Read edited post above.

Sorry.... Hang on.

BTW, just now I am running a 10 second time loop. Making it say the time every 10 seconds.

It is working perfectly as expected.

I'll now try some early hour (low value) times.

UPDATE

Ok, good.

I scrolled up and got the same time from this morning.

Wow!

Ok, interesting.

Ok, code:

[{"id":"c3d7cb2c20cbf96d","type":"inject","z":"235f16ee6e459f2c","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"07:45:18","payloadType":"str","x":4270,"y":3650,"wires":[["5f8667b94e1c2705"]]},{"id":"5f8667b94e1c2705","type":"change","z":"235f16ee6e459f2c","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"$split($$.payload, \":\").$formatInteger($number($), \"w\")","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":4440,"y":3760,"wires":[["1df5c7c0bd2ce8a9"]]},{"id":"1df5c7c0bd2ce8a9","type":"function","z":"235f16ee6e459f2c","name":"new time reader","func":"let x = msg.payload\n\n//node.warn(x);\n\nmsg.hour = x[0];\nmsg.minute = x[1];\nmsg.second = x[2];\n\nif (msg.minute == \"zero\")\n{\n    msg.minute = \" hundred \";\n    //return msg;\n}\n\nif (msg.second == \"zero\")\n{\n    msg.second = \"exactly\";\n} else\nmsg.second = \" and \" + x[2] + \" seconds\";\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":4250,"y":3890,"wires":[["945027a34e67375e"]]},{"id":"945027a34e67375e","type":"template","z":"235f16ee6e459f2c","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"The time is {{hour}} {{minute}} {{second}}","output":"str","x":4430,"y":3910,"wires":[["91699b85b4e6a7d6","0a1c7d0838510fc2"]]},{"id":"0a1c7d0838510fc2","type":"debug","z":"235f16ee6e459f2c","name":"The time","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":4500,"y":4020,"wires":[]},{"id":"78a97b936514a7b7","type":"function","z":"235f16ee6e459f2c","name":"function 40","func":"let x = msg.payload.split(\":\")\n\n//node.warn(x);\n\nmsg.hour = x[0];\nmsg.minute = x[1];\nmsg.second = x[2];\n\nif (msg.minute == 0)\n{\n    msg.minute = \" hundred \";\n    //return msg;\n}\n\nif (msg.second == 0)\n{\n    msg.second = \"exactly\";\n} else\nmsg.second = \" and \" + x[2] + \" seconds\";\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":4260,"y":3940,"wires":[["945027a34e67375e"]]},{"id":"91a46ecaa2d71009","type":"inject","z":"235f16ee6e459f2c","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"07:45:18","payloadType":"str","x":4100,"y":4130,"wires":[["78a97b936514a7b7"]]}]

The top part is yours, the bottom is the original.

the top gets me the time in this format:
The time is seven forty-five and eighteen seconds

The bottom gives me:
The time is 07 45 and 18 seconds

And it is said like:
The time is oh seven four five and eighteen seconds

So - yes, your way works - but what's going on with the parsing of the numbers?
07 is said as oh seven which is acceptable.
45 becomes four five
and
18 is eighteen.....

So that confuses me what is going on there.

My moment format of H:m:s does not output 01,02,03..11, 12 ,etc it outputs 1,2,3... 11 12,etc

The JSONata expression $number($) will take 01 and output 1 etc.

Off out, wont be back for 6 hours, catch you later.

1 Like

Thanks very much for the help.

Greatly appreciated.

I'm stuck though on why the original way the 45 and 18 aren't parsed the same way.

I may need to dig into the TTS part, but to me that is clutching at straws.

Not looked at any of the code, but it would make sense to me, that the bottom text is spoken that way. Since it is a list of numbers, which could just as easily be a phone number, where you would expect that way of speaking, but the 18 is between two words and so makes sense to say that as a word.

With the top text there is no ambiguity as its all words.

Well yes, and no.

If the numbers were 4 5 then I'd expect four five but it is 45.
So I'd expect forty five to be said.