tl;dr: this node-red flow will automatically mute GPDMP commercials. See the code to copy the flow and paste into your own node-red.
I have home assistant and node-red running on my raspi, and I listen to Google Play music quite a bit. Not quite enough to subscribe (since I can’t build my own logic to manipulate playlists as I do in iTunes). I was previously using the web interface and the mini-player Chrome app, but I came across GPMDP (Google Play Music Desktop Player), and was immediately interested.
One big difference is that GPDMP seems to play quite a few more commercials than my web player (not quite sure why, but I suspect it’s due to the extensions I’ve got installed). The commercials are also a whole lot louder than I’d prefer.
Rather than spend my time getting annoyed, I put together a flow in node-red to automatically mute commercials, and then restore the volume once music begins again. Since GPDMP doesn’t have support for the volume_mute option, I built in my own logic to keep track of the set volume level.
It’s not perfect; there’s a bit of a delay between the start of the audio and the home assistant event firing (usually about 2 or 3 seconds), but it’s quite a bit nicer than hearing another ad for yet another podcast I’ll never listen to.
[{"id":"c8ae758d.7a4798","type":"tab","label":"Media Player","disabled":false,"info":""},{"id":"f90cdaa6.b0efe8","type":"function","z":"c8ae758d.7a4798","name":"Media Player","func":"var newState = msg.data.new_state;\nvar thisSong = newState.attributes.media_title;\nvar thisArtist = newState.attributes.media_artist;\nvar thisState = newState.state;\nvar thisVolume = newState.attributes.volume_level;\nvar oldState = msg.data.old_state;\nvar lastSong = oldState.attributes.media_title;\nvar lastArtist = oldState.attributes.media_artist;\nvar lastState = oldState.state;\nvar lastVolume = oldState.attributes.volume_level;\nvar message = thisSong + \" - \" + thisArtist;\nvar priority = null;\nif (thisState != lastState) {\n message += \" ... \" + thisState;\n priority = true;\n}\nif (thisVolume != lastVolume) {\n message = \"Volume: \" + thisVolume * 100 + \"%\";\n priority = true;\n}\nmsg.payload = {\"message\": message, \"priority\":priority};\nif (thisSong != lastSong || \n thisState != lastState ||\n thisVolume != lastVolume) {\n return msg;\n}\n","outputs":1,"noerr":0,"x":365,"y":159,"wires":[["a391efe1.a1c49","bd29e476.814938"]]},{"id":"a71977ec.ec13a8","type":"link out","z":"c8ae758d.7a4798","name":"Media Player Status Output","links":["5c3419cd.56a838"],"x":707,"y":244,"wires":[]},{"id":"a391efe1.a1c49","type":"debug","z":"c8ae758d.7a4798","name":"","active":false,"console":"false","complete":"false","x":606,"y":66,"wires":[]},{"id":"b7a63179.e20f","type":"server-state-changed","z":"c8ae758d.7a4798","name":"GPMDP State Changed","server":"54bdbfb.03d394","entityidfilter":"media_player.gpm_desktop_player","haltifstate":"","x":152,"y":116,"wires":[["f90cdaa6.b0efe8","30f22ea5.defba2"]]},{"id":"30f22ea5.defba2","type":"debug","z":"c8ae758d.7a4798","name":"","active":false,"console":"false","complete":"true","x":354.5,"y":88,"wires":[]},{"id":"bd29e476.814938","type":"switch","z":"c8ae758d.7a4798","name":"","property":"data.new_state.attributes.media_title","propertyType":"msg","rules":[{"t":"regex","v":"music will resume shortly","vt":"str","case":true},{"t":"else"}],"checkall":"true","outputs":2,"x":575,"y":181,"wires":[["79004895.a5e228","e898b638.a16f28"],["a71977ec.ec13a8","eae03bbe.510678"]]},{"id":"196171ae.e6da7e","type":"change","z":"c8ae758d.7a4798","name":"","rules":[{"t":"set","p":"commercial","pt":"flow","to":"false","tot":"bool"},{"t":"set","p":"payload.data.volume_level","pt":"msg","to":"volume_level","tot":"flow"}],"action":"","property":"","from":"","to":"","reg":false,"x":924,"y":204,"wires":[["8d75f937.fed258","155f3d8.e9dfec3"]]},{"id":"79004895.a5e228","type":"change","z":"c8ae758d.7a4798","name":"","rules":[{"t":"set","p":"commercial","pt":"flow","to":"true","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":779,"y":155,"wires":[["8d75f937.fed258"]]},{"id":"eae03bbe.510678","type":"switch","z":"c8ae758d.7a4798","name":"","property":"commercial","propertyType":"flow","rules":[{"t":"true"},{"t":"else"}],"checkall":"true","outputs":2,"x":741,"y":191,"wires":[["196171ae.e6da7e","19b1df69.e4c961"],["15680085.ae1ebf"]]},{"id":"8d75f937.fed258","type":"api-call-service","z":"c8ae758d.7a4798","name":"","server":"54bdbfb.03d394","service_domain":"media_player","service":"volume_set","data":"{ \"entity_id\": \"media_player.gpm_desktop_player\", \"volume_level\":0}","x":1176,"y":184,"wires":[]},{"id":"68781a3b.f9c924","type":"inject","z":"c8ae758d.7a4798","name":"","topic":"","payload":"Your music will resume shortly - Subscribe for all of the music with none of the ads","payloadType":"str","repeat":"","crontab":"","once":false,"x":188,"y":287,"wires":[["7e1ff6c8.c925d8"]]},{"id":"e898b638.a16f28","type":"debug","z":"c8ae758d.7a4798","name":"","active":false,"console":"false","complete":"true","x":761.5,"y":103,"wires":[]},{"id":"155f3d8.e9dfec3","type":"debug","z":"c8ae758d.7a4798","name":"","active":false,"console":"false","complete":"true","x":1126,"y":234,"wires":[]},{"id":"7e1ff6c8.c925d8","type":"change","z":"c8ae758d.7a4798","name":"","rules":[{"t":"set","p":"data","pt":"msg","to":"{\"new_state\":{\"attributes\":{\"media_title\":\"music will resume shortly\"}}}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":356.5,"y":250,"wires":[["bd29e476.814938"]]},{"id":"78fd6b2e.4313f4","type":"catch","z":"c8ae758d.7a4798","name":"","scope":null,"x":206.5,"y":407,"wires":[["8b6cd1eb.43f02"]]},{"id":"8b6cd1eb.43f02","type":"debug","z":"c8ae758d.7a4798","name":"","active":true,"console":"false","complete":"true","x":371,"y":408,"wires":[]},{"id":"53ba1090.ee457","type":"change","z":"c8ae758d.7a4798","name":"","rules":[{"t":"set","p":"data","pt":"msg","to":"{\"new_state\":{\"attributes\":{\"media_title\":\"FAKE SONG TITLE\"}}}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":355,"y":209,"wires":[["bd29e476.814938"]]},{"id":"240c314d.e6d1be","type":"inject","z":"c8ae758d.7a4798","name":"","topic":"","payload":"Your music will resume shortly - Subscribe for all of the music with none of the ads","payloadType":"str","repeat":"","crontab":"","once":false,"x":187,"y":197,"wires":[["53ba1090.ee457"]]},{"id":"15680085.ae1ebf","type":"change","z":"c8ae758d.7a4798","name":"","rules":[{"t":"set","p":"volume_level","pt":"flow","to":"data.new_state.attributes.volume_level","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":953.5,"y":295,"wires":[["7305e575.ddab3c"]]},{"id":"7305e575.ddab3c","type":"debug","z":"c8ae758d.7a4798","name":"","active":false,"console":"false","complete":"true","x":1127,"y":315,"wires":[]},{"id":"e839dd4f.39cad","type":"inject","z":"c8ae758d.7a4798","name":"","topic":"","payload":"{\"data\":{\"volume_level\":0.1}}","payloadType":"json","repeat":"","crontab":"","once":false,"x":951,"y":68,"wires":[["8d75f937.fed258"]]},{"id":"19b1df69.e4c961","type":"debug","z":"c8ae758d.7a4798","name":"","active":false,"console":"false","complete":"true","x":904,"y":254,"wires":[]},{"id":"54bdbfb.03d394","type":"server","z":"","name":"Home Assistant","url":"http://192.168.1.13:8123","pass":"95969562"}]
The flow depends on a fantastic node-red module called node-red-contrib-home-assistant. You’ll want to make sure that’s installed first, then copy the above and import to a new flow. You can additionally get rid of any of the debug nodes if you find them annoying, but I tend to keep them in (and just toggle them off) so I can remember what I was looking at as I was creating the flow.
Let me know if you’ve got any questions/suggestions/thoughts/reactions/etc.
Happy automating!
(I’ve got a few other projects in the works (mostly hardware based). You might notice an output link in this flow, which is related to another node-red automation I’ve got integrated with some hardware. Waiting on parts from China, but I hope to have something cool to share soon.)