├── .dockerignore ├── .gitignore ├── Dockerfile ├── README.md ├── docker-compose.yml ├── main.py ├── notify.sh ├── requirements.txt └── static ├── JR.mp3 ├── cache └── .gitignore └── doorbell1.mp3 /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | static/*.mp3 4 | .vscode 5 | __pycache__ 6 | /.idea/ 7 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.6 2 | COPY . /app 3 | WORKDIR /app 4 | RUN pip install -r requirements.txt 5 | ENV GRP_NAME "SET YOUR SPEKER GROUP NAME" 6 | ENTRYPOINT ["python"] 7 | CMD ["main.py"] 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # A notification server that sends notifications to Google Home 2 | 3 | This is pretty simple. I had started using [noelportugal's really great node Google Home Notifier](https://github.com/noelportugal/google-home-notifier) but was having some issues with stability. 4 | 5 | I decided to write it in a language i know a bit better - python! yay. Python is your friend. 6 | 7 | The gist is this: 8 | 9 | This is a webservice that has two endpoints: 10 | 11 | - /play/ - plays an mp3 on the google home that is in the static folder 12 | - /say/ - uses googles unofficial google translate TTS service to say a notification 13 | 14 | # use 15 | 16 | ## getting started 17 | 18 | This uses flask and you should just be able to install the requirements: `pip install -r requirements.txt` and then run the webservice `python main.py` 19 | 20 | You will have to edit `main.py` and change the `chromecast_name` to one of your google home device's name. If you have more than 1 google home, I would recommend you put all your google homes into a play group and place the play groups name in the `device_name` variable. 21 | 22 | ## URLs 23 | 24 | `/play/mp3name.mp3` 25 | 26 | This will play mp3name.mp3 over the google homes. I put two mp3s in the static dir for you to try out: JR.mp3 and doorbell1.mp3. Try them: `/play/JR.mp3` or `/play/doorbell1.mp3` 27 | 28 | `/say/?text=Oh My God this is awesome` 29 | 30 | Just pass a GET variable to the `/say/` endpoint and the google homes will say your text. It also caches this so that the second time it will be a bit quicker than the first time. yay. 31 | 32 | You can also do other languages too: 33 | 34 | `/say/?text=猿も木から落ちる&lang=ja` 35 | 36 | ## running for real 37 | 38 | I use docker to run it. It works pretty well. I even included some pretty good docker script that will make it easier. Please check that out for more help. 39 | 40 | # How 41 | 42 | Google homes are just chromecasts! Who knew! You just have to treat them like chromecasts. They show up when you browse for chromecasts via python or any other code library. You can then just send audio their way. 43 | 44 | # TODO 45 | 46 | * Break out the google home bits and make it easy to integrate into other projects and not just a webservice 47 | 48 | 49 | 50 | # HMU 51 | 52 | harper@nata2.org 53 | 54 | @harper on twitter 55 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | home-notifier: 4 | build: 5 | context: . 6 | dockerfile: Dockerfile 7 | container_name: home-notifier 8 | restart: unless-stopped 9 | network_mode: host 10 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, request 2 | import pychromecast 3 | import logging 4 | import os 5 | from gtts import gTTS 6 | from slugify import slugify 7 | from pathlib import Path 8 | from urllib.parse import urlparse 9 | 10 | logging.basicConfig(level=logging.INFO) 11 | logger = logging.getLogger(__name__) 12 | 13 | chromecast_name = os.getenv('GRP_NAME') #set envar to match your speaker or group name 14 | 15 | app = Flask(__name__) 16 | logging.info("Starting up chromecasts") 17 | chromecasts, _ = pychromecast.get_chromecasts() 18 | logging.info("Searching for {}".format(chromecast_name)) 19 | cast = next(cc for cc in chromecasts if cc.cast_info.friendly_name == chromecast_name) 20 | 21 | def play_tts(text, lang='en', slow=False): 22 | tts = gTTS(text=text, lang=lang, slow=slow) 23 | filename = slugify(text+"-"+lang+"-"+str(slow)) + ".mp3" 24 | path = "/static/cache/" 25 | cache_filename = os.path.dirname(__file__) + path + filename 26 | tts_file = Path(cache_filename) 27 | if not tts_file.is_file(): 28 | logging.info(tts) 29 | tts.save(cache_filename) 30 | 31 | urlparts = urlparse(request.url) 32 | mp3_url = "http://" +urlparts.netloc + path + filename 33 | logging.info(mp3_url) 34 | play_mp3(mp3_url) 35 | 36 | 37 | def play_mp3(mp3_url): 38 | print(mp3_url) 39 | cast.wait() 40 | mc = cast.media_controller 41 | mc.play_media(mp3_url, 'audio/mp3') 42 | mc.block_until_active() 43 | 44 | 45 | @app.route('/static/') 46 | def send_static(path): 47 | return send_from_directory('static', path) 48 | 49 | @app.route('/play/') 50 | def play(filename): 51 | urlparts = urlparse(request.url) 52 | mp3 = Path(os.path.dirname(__file__) + "/static/" + filename) 53 | if mp3.is_file(): 54 | play_mp3("http://"+urlparts.netloc+"/static/"+filename) 55 | return filename 56 | else: 57 | return "False" 58 | 59 | @app.route('/say/') 60 | def say(): 61 | text = request.args.get("text") 62 | lang = request.args.get("lang") 63 | if not text: 64 | return False 65 | if not lang: 66 | lang = "en" 67 | play_tts(text, lang=lang) 68 | return text 69 | 70 | if __name__ == '__main__': 71 | app.run(debug=True,host='0.0.0.0') 72 | -------------------------------------------------------------------------------- /notify.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | BASEDIR=$(dirname $0) 4 | cd $BASEDIR 5 | 6 | IMAGE_NAME="harperreed/notifier" 7 | CONTAINER_NAME=notifier 8 | 9 | ACTION=$1 10 | 11 | if [ -z "$ACTION" ]; 12 | then 13 | echo "usage: $0 "; 14 | exit 1; 15 | fi 16 | 17 | # Build 18 | _build() { 19 | docker build --tag="$IMAGE_NAME" . 20 | } 21 | 22 | # Run (first time) 23 | _run() { 24 | docker run -d --name $CONTAINER_NAME --net=host $IMAGE_NAME 25 | } 26 | 27 | # Debugging mode with terminal access 28 | _debug() { 29 | docker run -i -t --entrypoint /bin/bash --name $CONTAINER_NAME --net=host $IMAGE_NAME 30 | } 31 | 32 | # Stop 33 | _stop() { 34 | docker stop $CONTAINER_NAME 35 | } 36 | 37 | # Start (after stopping) 38 | _start() { 39 | docker start $CONTAINER_NAME 40 | } 41 | 42 | # Remove 43 | _remove() { 44 | docker rm $CONTAINER_NAME 45 | } 46 | 47 | # Remove container and create a new one 48 | _rerun() { 49 | _stop 50 | _remove 51 | _run 52 | } 53 | 54 | # Manually open bash 55 | _attach() { 56 | docker exec -ti $CONTAINER_NAME /bin/bash 57 | } 58 | 59 | # Container logs 60 | _logs() { 61 | docker logs $CONTAINER_NAME 62 | } 63 | 64 | # Publish contents 65 | _push() { 66 | docker push $IMAGE_NAME 67 | } 68 | 69 | eval _$ACTION 70 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | gTTS 2 | Flask 3 | pychromecast 4 | unicode_slugify 5 | pathlib 6 | -------------------------------------------------------------------------------- /static/JR.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harperreed/google-home-notifier-python/ed4fef4ea69914b311b1d8b5d633c2c7675516ae/static/JR.mp3 -------------------------------------------------------------------------------- /static/cache/.gitignore: -------------------------------------------------------------------------------- 1 | *.mp3 -------------------------------------------------------------------------------- /static/doorbell1.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harperreed/google-home-notifier-python/ed4fef4ea69914b311b1d8b5d633c2c7675516ae/static/doorbell1.mp3 --------------------------------------------------------------------------------