Voice powered home automation is the future! I made my lights smart, my air conditioners smart, my TVs smart, my doors smart.. but something was missing.. I found myself unable to move my services around my "smart" devices... as far as I know there are no available solutions for this so I thought, why not do it myself?
Check it out in the video below:
Let's get started (Source code for the entire project found at the end as well as a diagram and explanation of how this project works):
So what can you do? You can move between devices any file VLC can play, and you can also do the same for Netflix and Spotify! The content will continue to play from the same time it was paused!
Supports both Windows and Android!
Step 1: Set static IPs for your desired devices, this project supports 3 distinct devices out of the box (The Arduino/ESP8266 doesn't need an address reservation)
Every router is a bit different, but the procedure for doing that is usually as follows:
- Launch a web browser from a computer or mobile device that is connected to your router’s network.
- Enter your router IP (if you don't know your router IP you can get it by launching CMD, typing ipconfig and get the default gateway for your network adapter)
- Enter the router user name and password.If you haven't changed the username/passwords, its usually admin for both.
- Search for something along the lines of Setup > LAN Setup.
- Look for any setting that says Address Reservation, click the Add button.
- In the IP Address field, type the IP address to assign to the computer or server.Choose an IP address from the router’s LAN subnet, such as 192.168.1.x.
- Type the MAC address of the computer or server. (You can usually find this in the router as well under Connected Devices/Attached Devices)
- Click the Apply button.The reserved address is entered into the table (some routers and devices may require a restart/reconnect to get the new IPs)
Step 2: Generate a (free) Blynk authentication code in order for our Arduino to be connected to the cloud and Alexa.
Android: https://play.google.com/store/apps/details?id=cc.blynk
iOS: https://itunes.apple.com/us/app/blynk-control-arduino-raspberry/id808760481?ls=1&mt=8
- Install and launch the app.
- Create a new account in Blynk App.
- Create a New Project. Then choose the board and connection you will use.
- After the project was created, Blynk will send you an Auth Token over email.
- Check your email inbox and find the Auth Token
Step 3: Install/Unzip Streamy on your Windows devices and/or Android devices and VLC Media player (only if you want to play files)
- Get VLC Media player / Spotify / Netflix from Google Play Store / Apple App Store)
Step 4: Configure Windows app (if you don't want to use Windows devices you can skip)
- Launch Streamy (If the program fails to launch you need to install .NET Framework 4.5 and 4.6 )
- Under Blynk/Video services click configure, paste in your Blynk authentication code from step 2, click OK.
- If you want to use VLC media player for files, follow these instructions, if not you can skip ahead.
- NOTE: If you just installed VLC for the first time, please run it at least once before the next step.
- Click Configure button under VLC, if all went well you will see this:
- Now the last thing we need to do here is to share the folder you wish to play the files from, so the file will be accessible across the network, to do that, go to the Network and Sharing Center, enable in your profile (or all of them) "Turn on file and printer sharing"
- Under "All Networks", check "Turn off password protected sharing"
- Click Save changes.
- Now go to the folder you want to share (you can share the specific folder or you can share the parent directory as well, for example let's say my file is C:/Streamy/Folder1/File, I can share Streamy, or I can share Folder1 etc... both will work)
- Right click on the folder you select, choose Properties
- Go to Sharing tab
- Click Share, Type Everyone (Guest might also work), Click Add
- Click Share, click Done, Click Close
- NOTE: If VLC still asks for a username/password when you try to stream a file, it's because Windows didn't actually disable password protected sharing, in this case you can follow this guide, I had to try all 3 methods in order for it to work for me.
Step 5: Configure Android app (if you don't want to use Android devices you can skip)
- Download and install APK
- Run Streamy for Android
- Paste in the Authentication code for blynk here, click Save
- Click "Streamy Service" and in the next window "Start foreground service", Done!
Step 6: Arduino
Connect your Sparkfun/Other Wifi shield, this is pretty easy so no schematics needed, just connect it on top of your Arduino
Open your Arduino IDE and Install the latest Blynk library for Arduino:
Download: https://github.com/blynkkk/blynk-library/releases/latest
- Unzip the Blynk_Release_vXX.zip archive. You will notice that archive contains several folders and several libraries.
- Copy all of these libraries to your sketchbook folder of Arduino IDE. To find the location of your sketchbook folder, go to top menu in Arduino IDE: Windows: File → Preferences
If you have any trouble, Blynk offers an install guide with a video here: http://help.blynk.cc/getting-started-library-auth-token-code-examples/how-to-install-blynk-library-for-arduino
Copy and paste the following code to your IDE:
(Sparkfun Arduino WiFi shield or other Arduino WiFi shield(untested) )
/*************************************************************
Download latest Blynk library here:
https://github.com/blynkkk/blynk-library/releases/latest
Blynk is a platform with iOS and Android apps to control
Arduino, Raspberry Pi and the likes over the Internet.
You can easily build graphic interfaces for all your
projects by simply dragging and dropping widgets.
Downloads, docs, tutorials: http://www.blynk.cc
Sketch generator: http://examples.blynk.cc
Blynk community: http://community.blynk.cc
Follow us: http://www.fb.com/blynkapp
http://twitter.com/blynk_app
Blynk library is licensed under MIT license
This example code is in public domain.
Virtual Pins for reference:
V2 -> IPs of potential VLC locations
V3 -> Alexa location and service response
V4 -> Netflix last show ID
V5 -> Location of video file for VLC player
Service IDs for reference:
1 -> Netflix
2 -> Spotify
3 -> VLC
*************************************************************/
#define BLYNK_PRINT Serial
#include <ESP8266_Lib.h>
//For Wifi shields use this:
#include <BlynkSimpleShieldEsp8266.h>
//For other ESP8266 (untested) use the following line instead:
//#include <BlynkSimpleEsp8266.h>
// You should get Auth Token in the Blynk App.
// Go to the Project Settings (nut icon).
char auth[] = "";
// Your WiFi credentials.
// Set password to "" for open networks.
char ssid[] = "";
char pass[] = "";
//Define your device IPs, !!if you have less leave them as 0.0.0.0!!
char livingroom[] = "0.0.0.0"; //ID 4
char bedroom[] = "0.0.0.0"; //ID 5
char computer[] = "0.0.0.0"; //ID 6
String all = (String)livingroom + "." + bedroom + "." + computer;
// Hardware Serial on Mega, Leonardo, Micro...
//#define EspSerial Serial1
// or Software Serial on Uno, Nano...
#include <SoftwareSerial.h>
//Most Wifi shields will use port 2,3, Sparkfun wifi shield uses ports 8,9
//SoftwareSerial EspSerial(2, 3); // RX, TX
SoftwareSerial EspSerial(8, 9); // RX, TX
// Set your ESP8266 baud rate:
#define ESP8266_BAUD 9600
ESP8266 wifi(&EspSerial);
//Blynk writes here whenever a value is updated in the cloud
BLYNK_WRITE(V3)
{
uint8_t buffer[256] = {0};
int pinValue = param.asInt(); // assigning incoming value from pin V3 to a variable
String val = String(pinValue);
char service = val.charAt(0);
char location = val.charAt(1);
Serial.print(service);
Serial.print(location);
//Update cloud with potential IPs
Blynk.virtualWrite(V2,all);
//Parse all locations and services
if (location == '4' && livingroom != "0.0.0.0")
{
static uint8_t mux_id = 0;
if (wifi.createTCP(mux_id, livingroom, 13000)) {
Serial.println("create tcp ");
Serial.print(mux_id);
Serial.println(" ok");
} else {
Serial.println("create tcp ");
Serial.print(mux_id);
Serial.println(" err");
}
if (service == '1')
{
if (wifi.send(mux_id, (const uint8_t*)"1-EOF-", 6)) {
Serial.println("send ok");
} else {
Serial.println("send err");
}
if (wifi.releaseTCP(mux_id)) {
Serial.print("release tcp ");
Serial.print(mux_id);
Serial.println(" ok");
} else {
Serial.print("release tcp ");
Serial.print(mux_id);
Serial.println(" err");
restartAP();
}
}
else if (service == '2')
{
if (wifi.send(mux_id, (const uint8_t*)"2-EOF-", 6)) {
Serial.println("send ok");
} else {
Serial.println("send err");
}
if (wifi.releaseTCP(mux_id)) {
Serial.print("release tcp ");
Serial.print(mux_id);
Serial.println(" ok");
} else {
Serial.print("release tcp ");
Serial.print(mux_id);
Serial.println(" err");
restartAP();
}
}
else if (service == '3')
{
if (wifi.send(mux_id, (const uint8_t*)"3-EOF-", 6)) {
Serial.println("send ok");
} else {
Serial.println("send err");
}
if (wifi.releaseTCP(mux_id)) {
Serial.print("release tcp ");
Serial.print(mux_id);
Serial.println(" ok");
} else {
Serial.print("release tcp ");
Serial.print(mux_id);
Serial.println(" err");
restartAP();
}
}
}
else if (location == '5' && bedroom != "0.0.0.0")
{
static uint8_t mux_id = 0;
if (wifi.createTCP(mux_id, bedroom, 13000)) {
Serial.println("create tcp ");
Serial.print(mux_id);
Serial.println(" ok");
} else {
Serial.println("create tcp ");
Serial.print(mux_id);
Serial.println(" err");
}
if (service == '1')
{
if (wifi.send(mux_id, (const uint8_t*)"1-EOF-", 6)) {
Serial.println("send ok");
} else {
Serial.println("send err");
}
delay(1000);
if (wifi.releaseTCP(mux_id)) {
Serial.print("release tcp ");
Serial.print(mux_id);
Serial.println(" ok");
} else {
Serial.print("release tcp ");
Serial.print(mux_id);
Serial.println(" err");
restartAP();
}
}
else if (service == '2')
{
if (wifi.send(mux_id, (const uint8_t*)"2-EOF-", 6)) {
Serial.println("send ok");
} else {
Serial.println("send err");
}
if (wifi.releaseTCP(mux_id)) {
Serial.print("release tcp ");
Serial.print(mux_id);
Serial.println(" ok");
} else {
Serial.print("release tcp ");
Serial.print(mux_id);
Serial.println(" err");
restartAP();
}
}
else if (service == '3')
{
if (wifi.send(mux_id, (const uint8_t*)"3-EOF-", 6)) {
Serial.println("send ok");
} else {
Serial.println("send err");
}
if (wifi.releaseTCP(mux_id)) {
Serial.print("release tcp ");
Serial.print(mux_id);
Serial.println(" ok");
} else {
Serial.print("release tcp ");
Serial.print(mux_id);
Serial.println(" err");
restartAP();
}
}
}
else if (location == '6' && computer != "0.0.0.0")
{
static uint8_t mux_id = 0;
if (wifi.createTCP(mux_id, computer, 13000)) {
Serial.println("create tcp ");
Serial.print(mux_id);
Serial.println(" ok");
} else {
Serial.println("create tcp ");
Serial.print(mux_id);
Serial.println(" err");
}
if (service == '1')
{
if (wifi.send(mux_id, (const uint8_t*)"1-EOF-", 6)) {
Serial.println("send ok");
} else {
Serial.println("send err");
}
if (wifi.releaseTCP(mux_id)) {
Serial.print("release tcp ");
Serial.print(mux_id);
Serial.println(" ok");
} else {
Serial.print("release tcp ");
Serial.print(mux_id);
Serial.println(" err");
restartAP();
}
}
else if (service == '2')
{
if (wifi.send(mux_id, (const uint8_t*)"2-EOF-", 6)) {
Serial.println("send ok");
} else {
Serial.println("send err");
}
if (wifi.releaseTCP(mux_id)) {
Serial.print("release tcp ");
Serial.print(mux_id);
Serial.println(" ok");
} else {
Serial.print("release tcp ");
Serial.print(mux_id);
Serial.println(" err");
restartAP();
}
}
else if (service == '3')
{
if (wifi.send(mux_id, (const uint8_t*)"3-EOF-", 6)) {
Serial.println("send ok");
} else {
Serial.println("send err");
}
if (wifi.releaseTCP(mux_id)) {
Serial.print("release tcp ");
Serial.print(mux_id);
Serial.println(" ok");
} else {
Serial.print("release tcp ");
Serial.print(mux_id);
Serial.println(" err");
restartAP();
}
}
}
}
//fallback in case TCP connection does not close
void restartAP()
{
if (wifi.leaveAP()) {
Serial.print("leave AP success\r\n");
Serial.print("IP: ");
Serial.println(wifi.getLocalIP().c_str());
} else {
Serial.print("leaveAP failure\r\n");
}
if (wifi.joinAP(ssid, pass)) {
Serial.print("Join AP success\r\n");
Serial.print("IP: ");
Serial.println(wifi.getLocalIP().c_str());
} else {
Serial.print("Join AP failure\r\n");
}
}
void setup()
{
// Debug console
Serial.begin(9600);
delay(10);
// Set ESP8266 baud rate
EspSerial.begin(ESP8266_BAUD);
delay(10);
if (wifi.joinAP(ssid, pass)) {
Serial.print("Join AP success\r\n");
Serial.print("IP: ");
Serial.println(wifi.getLocalIP().c_str());
} else {
Serial.print("Join AP failure\r\n");
}
delay(10);
if (wifi.enableMUX()) {
Serial.print("mux enabled");
Blynk.config(wifi, auth);
} else {
Serial.print("Software will probably not work, multiple conections (MUX) needs to be enabled");
Blynk.begin(auth, wifi, ssid, pass);
}
}
void loop()
{
Blynk.run();
}
- Set your Blynk Auth code
- Set your wifi SSID and password
- Set your device IPs
- Set your ESP8266 baud rate (9600 is for Sparkfun WiFi shield), if you are using a different WiFi shield, use the appropriate Software/Hardware serial from the code with the matching ports for your shield)
- Open the serial monitor (Tools -> Serial Monitor or Ctrl+Shift+M, Make sure the baud rate matches your WiFi shield's buad rate), upload the code to your Arduino, if all went well you will see this something like this within a few seconds:
Step 7: Load up and configure Alexa skill and AWS Lambda function
I will split this guide into two steps, setting up the skill and then setting up the Lambda function
Skill:
- Sign up for an amazon developer account at: https://developer.amazon.com
- Make sure you are under Alexa tab, click Add new skill
- In the "Skill type" select Custom Interaction model
- Select Language: English
- Name: Streamy
- Invocation name: Streamy
- Click Next
- In the interaction model, under Slot Types click Add +, add "location" slot and add your locations, I have PC, bedroom and living room (Except PC, all in lower-case letters), also add any synonyms you want
- Add another slot type called "service", add these exact slot values (spotify, netflix, VLC) (except for VLC, all in lower-case letters), for synonyms you can add whatever you like, I recommend starting with the synonyms that I have added below (Ignore my duplicates)
- From Intents panel on the side left side, click Add+, add an intent called Play
- On the right side panel under "Intent Slots" add 2 new slots, "service" and "location", map them to the slot types we created before (see the image below)
- Add the following sample utterances:
Stream {service} at my {location}
Stream {service} on my {location}
Stream {service} in my {location}
Play {service} at my {location}
Play {service} on my {location}
Play {service} in my {location}
- On the left side under Play intent click service
- Enable the "is this slot required to fulfill the intent", fill in the Prompt with "What do you want to play?"
- Again on the left panel click on location this time
- Enable "is this slot required to fulfill the intent", fill in the prompt with "Where do you want to play?"
- From the top menu click save model and then build model
- Go back to "Skill Information"
- Save the" Application ID" somewhere handy (The ID for this skill), we'll need it soon.
- Leave this window open, we'll come here later.
Lambda Function:
- Navigate to AWS Lambda: https://console.aws.amazon.com/lambda/home?region=us-east-1#/
- Click create function
- Select "Blueprints"
- In the filter bar, type "Color"
- select "Alexa-skills-kit-color-expert-python" and click Continue
- In name, put Streamy
- Under role, You can select "create new role from template(s)", enter any name for the role and under Policy templates select "S3 object read-only permissions"
- Scroll down to Alexa-skills-kit, select Enable and paste in the skill ID that we saved earlier
- Scroll down more and click Create Function
- Your next screen should look something like this
- If you don't have "Alexa Skills Kit", add the trigger for "Alexa Skills Kit" on the left side where it says "Add triggers"
- Scroll down to the "lambda_function.py", and overwrite everything in it with this code below (I recommend copying this Python code from the github link.)
- If you want to use Netflix, fill in your username and password here (your username would be your account email in this case).
- Fill in BLYNKIP with the correct IP, to find the correct IP for you, open up CMD and type: ping blynk-cloud.com , the IP that you see there is what you should paste back here
- Fill in your BLYNKAUTH with the blynk authentication code from step one.
from __future__ import print_function
import urllib2 as urlreq
from botocore.vendored import requests
from HTMLParser import HTMLParser
import re
#Variables
#Fill in your Netflix username and password here
USERNAME = ""
PASSWORD = ""
# PLEASE SET
# Europe -> 139.59.206.133 US -> 45.55.96.146 Middle east -> 188.166.206.43
# To make sure the IP is correct, open your CMD/Terminal and ping
BLYNKIP = '45.55.96.146'
BLYNKAUTH = ''
authenticity_token = ''
lastwatchid = ''
#HTML Parser for getting the login page for video service
class LoginParser(HTMLParser):
def __init__(self):
HTMLParser.__init__(self)
self.recording = 0
self.data = []
def handle_starttag(self, tag, attributes):
global authenticity_token
if tag == 'authURL':
return
if self.recording:
self.recording += 1
return
for name, value in attributes:
if value=='authURL':
for value,authURL in attributes:
if ('value' in value):
authenticity_token = authURL
else:
return
self.recording = 1
def handle_endtag(self, tag):
if tag == 'div' and self.recording:
self.recording -= 1
def handle_data(self, data):
if self.recording:
self.data.append(data)
#HTML Parser for scraping the last watched show from video service
class lastwatchedparser(HTMLParser):
def __init__(self):
HTMLParser.__init__(self)
self.recording = 0
self.data = []
def handle_starttag(self, tag, attributes):
global lastwatchid
if tag == 'div':
return
if self.recording:
self.recording += 1
return
for name, value in attributes:
if ('/title/' in value):
lastwatchid += value
else:
return
self.recording = 1
def handle_endtag(self, tag):
if tag == 'div' and self.recording:
self.recording -= 1
def handle_data(self, data):
if self.recording:
self.data.append(data)
# --------------- Helpers that build all of the responses ----------------------
def build_speechlet_response(title, output, reprompt_text, should_end_session):
return {
'outputSpeech': {
'type': 'PlainText',
'text': output
},
'card': {
'type': 'Simple',
'title': "SessionSpeechlet - " + title,
'content': "SessionSpeechlet - " + output
},
'reprompt': {
'outputSpeech': {
'type': 'PlainText',
'text': reprompt_text
}
},
'shouldEndSession': should_end_session
}
def build_response(session_attributes, speechlet_response):
return {
'version': '1.0',
'sessionAttributes': session_attributes,
'response': speechlet_response
}
# --------------- Functions that control the skill's behavior ------------------
def get_welcome_response():
""" If we wanted to initialize the session to have some attributes we could
add those here
"""
session_attributes = {}
card_title = "Welcome"
speech_output = "Welcome to Streamy"
# If the user either does not reply to the welcome message or says something
# that is not understood, they will be prompted again with this text.
reprompt_text = "Would you like to stream something?"
should_end_session = False
return build_response(session_attributes, build_speechlet_response(
card_title, speech_output, reprompt_text, should_end_session))
def handle_session_end_request():
card_title = "Session Ended"
speech_output = "Thanks for trying out Streamy"
# Setting this to true ends the session and exits the skill.
should_end_session = True
return build_response({}, build_speechlet_response(
card_title, speech_output, None, should_end_session))
def playintent(intent, session):
card_title = intent['name']
session_attributes = {}
should_end_session = False
serviceslot = intent['slots']['service']['resolutions']['resolutionsPerAuthority'][0]['values'][0]['value']['name']
locationslot = intent['slots']['location']['resolutions']['resolutionsPerAuthority'][0]['values'][0]['value']['name']
if (serviceslot == None or serviceslot == ""):
serviceslot = intent['slots']['service']['value']
if (locationslot == None or locationslot == ""):
locationslot = intent['slots']['location']['value']
target = ""
if (serviceslot == 'netflix'):
LOGIN_URL = "https://www.netflix.com/login"
URL = "https://www.netflix.com/WiViewingActivity"
session_requests = requests.session()
target = '1'
# Get login csrf token
result = session_requests.get(LOGIN_URL)
parser = LoginParser()
parser.feed(result.text)
# Create payload
payload = {
"action": "loginAction",
"authURL": authenticity_token,
"email": USERNAME,
"flow": "websiteSignUp",
"mode": "login",
"nextPage": "",
"password": PASSWORD,
"rememberMe": "true",
"showPassword": "",
"withFields": "email,password,rememberMe,nextPage,showPassword"
}
# Perform login
result = session_requests.post(LOGIN_URL, data = payload, headers = dict(referer = URL))
# Scrape url
result = session_requests.get(URL, headers = dict(referer = LOGIN_URL))
parser = lastwatchedparser()
parser.feed(result.text)
#Update blynk
lastwatchedurl = "http://"+BLYNKIP+"/"+BLYNKAUTH+"/update/V4?value=" + lastwatchid[7:15]
#Method 1 of sending the request
#lastreq = urlreq.Request(lastwatchedurl)
#urlreq.urlopen(lastreq).read()
#Method 2 of sending the request, I found this to be more reliable
update_nfx = requests.session()
nfxresult = update_nfx.get(lastwatchedurl)
elif (serviceslot == 'spotify'):
target = '2'
elif (serviceslot == 'VLC'):
target = '3'
if (locationslot == 'living room'):
target += '4'
elif (locationslot == 'bedroom'):
target += '5'
elif (locationslot == 'PC'):
target += '6'
finalurl = "http://"+BLYNKIP+"/"+BLYNKAUTH+"/update/V3?value=" + target
#Method 1 of sending the request
#req = urlreq.Request(finalurl)
#urlreq.urlopen(req).read()
#Method 2 of sending the request
updatefinalurl = requests.session()
finalresults = updatefinalurl.get(finalurl)
session_attributes = target
speech_output = "Playing"
reprompt_text = None
should_end_session = True
return build_response(session_attributes, build_speechlet_response(
card_title, speech_output, reprompt_text, should_end_session))
# --------------- Events ------------------
def on_session_started(session_started_request, session):
""" Called when the session starts """
print("on_session_started requestId=" + session_started_request['requestId']
+ ", sessionId=" + session['sessionId'])
def on_launch(launch_request, session):
""" Called when the user launches the skill without specifying what they
want
"""
print("on_launch requestId=" + launch_request['requestId'] +
", sessionId=" + session['sessionId'])
# Dispatch to your skill's launch
return get_welcome_response()
def on_intent(intent_request, session):
""" Called when the user specifies an intent for this skill """
print("on_intent requestId=" + intent_request['requestId'] +
", sessionId=" + session['sessionId'])
intent = intent_request['intent']
intent_name = intent_request['intent']['name']
# Dispatch to your skill's intent handlers
if intent_name == "Play":
return playintent(intent, session)
elif intent_name == "AMAZON.HelpIntent":
return get_welcome_response()
elif intent_name == "AMAZON.CancelIntent" or intent_name == "AMAZON.StopIntent":
return handle_session_end_request()
else:
raise ValueError("Invalid intent")
def on_session_ended(session_ended_request, session):
""" Called when the user ends the session.
Is not called when the skill returns should_end_session=true
"""
print("on_session_ended requestId=" + session_ended_request['requestId'] +
", sessionId=" + session['sessionId'])
# add cleanup logic here
# --------------- Main handler ------------------
def lambda_handler(event, context):
""" Route the incoming request based on type (LaunchRequest, IntentRequest,
etc.) The JSON body of the request is provided in the event parameter.
"""
print("event.session.application.applicationId=" +
event['session']['application']['applicationId'])
"""
Uncomment this if statement and populate with your skill's application ID to
prevent someone else from configuring a skill that sends requests to this
function.
"""
# if (event['session']['application']['applicationId'] !=
# "amzn1.echo-sdk-ams.app.[unique-value-here]"):
# raise ValueError("Invalid Application ID")
if event['session']['new']:
on_session_started({'requestId': event['request']['requestId']},
event['session'])
if event['request']['type'] == "LaunchRequest":
return on_launch(event['request'], event['session'])
elif event['request']['type'] == "IntentRequest":
return on_intent(event['request'], event['session'])
elif event['request']['type'] == "SessionEndedRequest":
return on_session_ended(event['request'], event['session'])
- Scroll down and increase the timeout to at least 30 seconds.
- Scroll to the top and click Save
- Above the save button, copy the arn code (starts with arn:aws....)
- Go back to the Alexa skill we started making earlier, under the configuration tab, select "AWS Lambda ARN (Amazon Resource Name"
- in "Default" field, fill in the ARN code you previously copied from the lambda function.
- Provide Geographical region endpoints - No
- Account Linking - No
- The rest leave empty, click Next
- In the test tab, enable skill testing like this:
- Scroll to the end, click next.
- Alexa skill done! (Don't need to fill in the rest of the tabs, that's for publishing the skill)
That's it! Now you can say "Alexa, open Streamy" and
"Play Netflix in my bedroom"
"Stream this file to my PC"
"Play this song in my living room" (Don't forget to pause Spotify before)
Have fun!
Project Diagram and Explanation:
The idea was to use an Arduino as a cloud connected smart hub for directing the services and locations I want to use to my devices, when I started I had dabbled a bit with the Arduino but nothing like this, also I have never created an Android application so this was my first time. Basically I used 4 programming languages to make this project: AWS Lambda -> Python, Android -> Java, Windows -> C#, Arduino -> C/C++.
So first I created an Alexa skill and Lambda function to process voice commands and to get some data from the services (such as the last played movie/tv show from content providers), then I send the values to Blynk.
The Arduino device connects to serial WiFi and listens through the Blynk library for any updates. Once updated it gets the new value (For example, the value 34 signifies 3-> VLC, 4->Living room), it sends the value 3 to the living room device.
On both the Android and Windows app, an asynchronous TCP server is running and waiting for commands from the Arduino.
once a command is received, it checks which service we want to use and opens up the program with the parameters (for example, for VLC we need to specify a path for the file, and the start time in order for the video to continue playing from the same place, for Netflix, I need to provide the last watched ID of the show/movie, for Spotify I just need to start playing the song as the last one played is selected)
Source code:
Arduino and AWS Lambda: https://github.com/nirkons/StreamyLibs
Android app: https://github.com/nirkons/StreamyAndroid
Windows app: https://github.com/nirkons/StreamyWindows
*(Note: All services are interchangeable between devices, except for VLC which works Windows->Windows and Windows->Android but not a local file playing on Android (for example, on the SD card) to Windows at the time of this release)
Comments