diff --git a/static/img/arduino-1.gif b/static/img/arduino-1.gif new file mode 100644 index 0000000..a869aa9 Binary files /dev/null and b/static/img/arduino-1.gif differ diff --git a/static/pages/Build-your-open-source-multi-room-and-multi-provider-sound-server-with-Platypush-Mopidy-and-Snapcast.md b/static/pages/Build-your-open-source-multi-room-and-multi-provider-sound-server-with-Platypush-Mopidy-and-Snapcast.md index 0244edf..7489db3 100644 --- a/static/pages/Build-your-open-source-multi-room-and-multi-provider-sound-server-with-Platypush-Mopidy-and-Snapcast.md +++ b/static/pages/Build-your-open-source-multi-room-and-multi-provider-sound-server-with-Platypush-Mopidy-and-Snapcast.md @@ -314,3 +314,193 @@ If you use Iris as a web interface to Mopidy you can now head to settings and en will appear in the bottom bar, and you’ll be able to control your music setup from there as well. Time to enjoy your low-cost but powerful multi-room music setup! + +## Build your remote to control the music + +All of us have some unused infrared remote collecting dust somewhere in the living room. In this section I’ll show how +to turn it into a universal remote for controlling your music (and not only) with some Platypush automation. You’ll need +the following: + +- An infrared receiver — they’re usually very cheap. Any of them will do, even though I personally + used [this model](https://www.banggood.in/Universal-IR-Infrared-Receiver-Head-With-Iron-Shell-TL1838-VS1838B-1838-38Khz-p-1204379.html?p=1L111111347088201706). + +- An Arduino or Arduino-compatible device (or an ESP8266, or a RaspberryPi Pico, or any other microcontroller, although + the code may be different). Most of the infrared sensors around communicate over an analog interface, but + the RaspberryPi doesn’t come with an ADC converter. The solution is to plug an Arduino over USB and let it monitor for + changes on the detected infrared signal. + +- A breadboard. + +Once you’ve got all the hardware you can set up your receiver: + +- Plug the infrared receiver to GND and Vcc, and the data PIN to e.g. the Arduino PIN 2, as shown in the figure below: + +![Arduino IR sensor connection](../img/arduino-1.gif) + +- Download and install the Arduino [IRremote library](https://github.com/z3t0/Arduino-IRremote). + +- Prepare a sketch that reads the data from the infrared receiver PIN and writes it over serial interface as a JSON: + +```c +#include + +// When a signal with all bits set to 1 is received it +// usually means that the previous pressed key is still +// being pressed, until a signal with all bits set to +// zero is received. +#define IR_REPEAT 0xFFFFFFFF + +const int RECV_PIN = 2; + +IRrecv irrecv(RECV_PIN); +decode_results results; +unsigned int latest_value = 0; + +void setup(){ + Serial.begin(9600); + irrecv.enableIRIn(); + irrecv.blink13(true); +} + +void send_value(unsigned int value) { + Serial.print("{\"ir\":"); + Serial.print(value, HEX); + Serial.println("}"); +} + +void loop(){ + if (irrecv.decode(&results)){ + if (results.value == IR_REPEAT && latest_value != 0) { + send_value(latest_value); + } else if (results.value && results.value != latest_value) { + send_value(results.value); + } + + latest_value = results.value; + irrecv.resume(); + } +} +``` + +- Compile the sketch and upload it to the Arduino. + +- Open the Arduino serial monitor and verify that you see the JSON string when you press a key on the remote. + +- Enable the serial plugin and backend in your platypush configuration: + +```yaml +serial: + device: /dev/ttyUSB0 + +backend.sensor.serial: + enabled: True +``` + +- Restart platypush, check the output and press a key on your remote. You should see an event in the logs that looks + like this: + +``` +INFO|platypush|Received event: {"type": "event", "target": "hostname", "origin": "hostname", "args": {"type": "platypush.message.event.sensor.SensorDataChangeEvent", "data": {"ir": "4b34d827"}}} +``` + +- Take note of the hexadecimal code reported on the event, that’s the decoded data associated to that specific remote + button. Then add an event hook to deal with the actions to be run when a certain button is pressed: + +```python +from platypush.config import Config +from platypush.event.hook import hook +from platypush.utils import run + +from platypush.message.event.sensor import SensorDataChangeEvent + +@hook(SensorDataChangeEvent) +def on_remote_key_press(event, **context): + ir_code = event.data.get('ir') + if not ir_code: + return + + # Playback control logic + if ir_code == 'code1': + run('music.mpd.play') + elif ir_code == 'code2': + run('music.mpd.pause') + elif ir_code == 'code3': + run('music.mpd.stop') + elif ir_code == 'code5': + run('music.mpd.previous') + elif ir_code == 'code6': + run('music.mpd.next') + # ... + # Multi-room setup logic + elif ir_code == 'code7': + # Un-mute the stream to another host + run('music.snapcast.mute', host=Config.get('device_id'), client='some-client', + mute=False) + elif ir_code == 'code8': + # Mute the stream to another host + run('music.snapcast.mute', host=Config.get('device_id'), client='some-client', + mute=True) +``` + +Congratulations, you’ve just built your own customizable and universal music remote! + +## Voice assistant integration + +A smart music setup isn’t really complete without a voice assistant integration. I’ve covered +[in a previous article](https://blog.platypush.tech/article/Build-your-customizable-voice-assistant-with-Platypush) how +to set up platypush to turn your device into a full-featured Google Assistant. If you’ve managed to get your +assistant up and running, you can add some rules to control your music, play specific content, or synchronize your audio +stream to another room. Let’s see a couple of examples: + +```python +from platypush.config import Config +from platypush.event.hook import hook +from platypush.utils import run + +from platypush.message.event.assistant import SpeechRecognizedEvent + +@hook(SpeechRecognizedEvent, phrase='play (the)? music') +def on_music_play(*args, **context): + run('music.mpd.play') + +@hook(SpeechRecognizedEvent, phrase='stop (the)? music') +def on_music_pause(*args, **context): + run('music.mpd.stop') + +@hook(SpeechRecognizedEvent, phrase='play (the)? radio') +def on_play_radio(*args, **context): + run('music.mpd.play', resource='tunein:station:s13606') + +@hook(SpeechRecognizedEvent, phrase='play playlist ${name}') +def on_play_playlist(event, name=None, **context): + run('music.mpd.load', resource=name) + +@hook(SpeechRecognizedEvent, phrase='play ${title} by ${artist}') +def search_and_play_song(event, title=None, artist=None, **context): + results = run('music.mpd.search', artist=artist, title=title) + if results > 0: + run('music.mpd.play', resource=results[0]['file']) + +@hook(SpeechRecognizedEvent, phrase='play (the)? music to (the)? bedroom') +def sync_music_to_bedroom(event, **context): + run('music.snapcast.mute', host=Config.get('device_id'), client='bedroom', mute=False) + run('music.snapcast.volume', host=Config.get('device_id'), client='bedroom', volume=90) +``` + +## Conclusions + +The current situation when it comes to music streaming and multi-room setup in a home automation environment is still +extremely fragmented. Each commercial solution out there seems more interested in building its own walled garden, and a +proper multi-room setup usually comes with high costs and in most of the cases it won’t be compatible with your existing +speakers. With the ingredients provided in this article you should be able to walk around most of these limitations and: + +- Set up your multi-service music player controllable by any interface you like + +- Set up your multi-room configuration that makes it possible to add a new room by simply adding one more RaspberryPi + +- Use any existing infrared remote to control the music + +- Integrate custom music actions into a voice assistant + +With these foundations in place the only limit to what you can do with your new music set up comes from your own +imagination!