Skip to content

Commit

Permalink
Merge pull request #288 from evancohen/dev
Browse files Browse the repository at this point in the history
Adding a functioning keyword spotter to master. Fixes #170
  • Loading branch information
evancohen authored Jun 11, 2016
2 parents 8278b97 + 8cf3ee3 commit 1b5733b
Show file tree
Hide file tree
Showing 23 changed files with 717 additions and 241 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ npm-debug.log
.DS_Store
bower_components
fb-token.json
speech/snowboydecoder.pyc
speech/snowboydetect.pyc
15 changes: 13 additions & 2 deletions config.example.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
var config = {

// Lenguage for the mirror
language : "en", //must also manually update locales/X.js bower component in index.html
language : "en-US",

// Keyword Spotting (Hotword Detection)
speech : {
keyword : "Smart Mirror",
model : "smart_mirror.pmdl", // The name of your model
sensitivity : 0.5, // Keyword getting too many false positives or not detecting? Change this.
continuous: false // After a keyword is detected keep listening until speech is not heard
},
layout: "main",
greeting : ["Hi, sexy!", "Greetings, commander"], // An array of greetings to randomly choose from

Expand All @@ -24,7 +32,7 @@ var config = {
*/

// forcast.io
forcast : {
forecast : {
key : "", // Your forcast.io api key
units : "auto" // See forcast.io documentation if you are getting the wrong units
},
Expand Down Expand Up @@ -75,3 +83,6 @@ var config = {
}]
}
};

// DO NOT REMOVE
if (typeof module !== 'undefined') {module.exports = config;}
13 changes: 11 additions & 2 deletions css/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,18 @@ dt {
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%; }
height: 100%;
transition: all 0.3s ease;}

.listening {
box-shadow: inset 0px -40px 40px -40px #fff
}

.not-listening {
box-shadow: inset 0px 0px 0px 0px #fff
}

.listening, .interim-result {
.interim-result {
font-family: 'Open Sans', sans-serif; }

.top-left {
Expand Down
29 changes: 15 additions & 14 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<script src="js/services/timebox.js"></script>
<script src="js/services/soundcloud.js"></script>
<script src="js/services/search.js"></script>
<script src="js/services/annyang.js"></script>
<script src="js/services/speech.js"></script>
<script src="js/services/weather.js"></script>
<script src="js/services/comic.js"></script>
<script src="js/services/map.js"></script>
Expand Down Expand Up @@ -66,26 +66,26 @@
<div class="top-right">
<div class="weather">
<div class="weather-today">
<span class="icon dimmed wi {{currentForcast.wi}}"></span>
<span class="icon dimmed wi {{currentForecast.wi}}"></span>
<canvas id="icon_weather_current" width="90" height="70"></canvas>
<span class="tempreture">{{currentForcast.temperature}}&deg;</span>
<span class="tempreture" ng-show="currentForecast.temperature">{{currentForecast.temperature}}&deg;</span>
</div>
<div class="weather-week-descriptor">
<span>{{hourlyForcast.summary}}</span>
<span>{{weeklyForcast.summary}}</span>
<span>{{hourlyForecast.summary}}</span>
<span>{{weeklyForecast.summary}}</span>
</div>
<div class="weather-week" ng-repeat="forcast in weeklyForcast.data" ng-if="$index > 0">
<div class="weather-week" ng-repeat="forecast in weeklyForecast.data" ng-if="$index > 0">
<div class="weather-week-day">
<span class="day light-grey">{{forcast.day}}</span>
<canvas id="icon_weather_{{forcast.counter}}" width="33" height="25"></canvas>
<span class="icon-small dimmed wi wi-fw {{forcast.wi}}"></span>
<span class="tempreture tempreture-min">{{forcast.temperatureMin}}&deg;</span>
<span class="tempreture tempreture-max">{{forcast.temperatureMax}}&deg;</span>
<span class="day light-grey">{{forecast.day}}</span>
<canvas id="icon_weather_{{forecast.counter}}" width="33" height="25"></canvas>
<span class="icon-small dimmed wi wi-fw {{forecast.wi}}"></span>
<span class="tempreture tempreture-min">{{forecast.temperatureMin}}&deg;</span>
<span class="tempreture tempreture-max">{{forecast.temperatureMax}}&deg;</span>
</div>
</div>
<!-- Workaround: -->
<div style="display: none;" ng-repeat="forcast in weeklyForcast.data" ng-if="$index > 0">
<span ng-init="iconLoad('icon_weather_'+forcast.counter, forcast.iconAnimation)"></span>
<div style="display: none;" ng-repeat="forecast in weeklyForecast.data" ng-if="$index > 0">
<span ng-init="iconLoad('icon_weather_'+forecast.counter, forecast.iconAnimation)"></span>
</div>
</div>
<div class="traffic" ng-repeat="traffic in trips">
Expand All @@ -96,7 +96,7 @@
</div>
</div>
</div>
<div class="container">
<div class="container" ng-class="(listening == true)?'listening':'not-listening'">
<div class="middle-center">
<h1>{{greeting}}</h1>
<h3 ng-show="!(user.name === undefined)">{{user.name}}</h3>
Expand All @@ -105,6 +105,7 @@ <h3 ng-show="!(user.name === undefined)">{{user.name}}</h3>
<iframe class="video animate-grow" ng-src="{{video}}"/></iframe>
</div>
<div class="contents-box sc-container animate-grow" ng-show="focus == 'sc'">
<audio id="player" controls="" autoplay="" preload autobuffer style="display: none"></audio>
<div><canvas id="visualizer" width="150" height="150"></canvas></div>
<img class="sc" ng-src="{{scThumb}}"/>
<img class="scWaveform" ng-src="{{scWaveform}}"/>
Expand Down
15 changes: 6 additions & 9 deletions js/app.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
// Warn the user if they have not filled out config.js or if it has an error
if(typeof config == 'undefined'){
alert("'config.js' is missing or contains an error!");
}

// Bootstrap Angular
(function(angular) {
'use strict';


var language = (typeof config.language != 'undefined')?config.language.substring(0, 2).toLowerCase(): 'en';

angular.module('SmartMirror', ['ngAnimate', 'tmh.dynamicLocale', 'pascalprecht.translate'])
.config(function(tmhDynamicLocaleProvider) {
var locale = config.language.toLowerCase();
tmhDynamicLocaleProvider.localeLocationPattern('bower_components/angular-i18n/angular-locale_' + locale + '.js');
console.log(config)
tmhDynamicLocaleProvider.localeLocationPattern('bower_components/angular-i18n/angular-locale_' + language + '.js');
})

.config(['$translateProvider', function ($translateProvider) {
Expand All @@ -24,7 +21,7 @@ if(typeof config == 'undefined'){
// Avoiding the duplicity of the locale for the default language, xx-YY -> xx
// We are considering only the language
// Please refer https://github.com/evancohen/smart-mirror/pull/179 for further discussion
var language = config.language.substring(0, 2);
var language = (typeof config.language != 'undefined')?config.language.substring(0, 2): 'en';
$translateProvider.preferredLanguage(language);
}])

Expand Down
104 changes: 51 additions & 53 deletions js/controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
'use strict';

function MirrorCtrl(
AnnyangService,
SpeechService,
GeolocationService,
WeatherService,
FitbitService,
Expand Down Expand Up @@ -35,11 +35,9 @@
}

//set lang
$scope.locale = config.language;
tmhDynamicLocale.set(config.language.toLowerCase());
moment.locale(config.language);
moment.locale((typeof config.language != 'undefined')?config.language.substring(0, 2).toLowerCase(): 'en');
console.log('moment local', moment.locale());

//Update the time
function updateTime(){
$scope.date = new moment();
Expand Down Expand Up @@ -70,15 +68,15 @@
GeolocationService.getLocation({enableHighAccuracy: true}).then(function(geoposition){
console.log("Geoposition", geoposition);
WeatherService.init(geoposition).then(function(){
$scope.currentForcast = WeatherService.currentForcast();
$scope.weeklyForcast = WeatherService.weeklyForcast();
$scope.hourlyForcast = WeatherService.hourlyForcast();
console.log("Current", $scope.currentForcast);
console.log("Weekly", $scope.weeklyForcast);
console.log("Hourly", $scope.hourlyForcast);
$scope.currentForecast = WeatherService.currentForecast();
$scope.weeklyForecast = WeatherService.weeklyForecast();
$scope.hourlyForecast = WeatherService.hourlyForecast();
console.log("Current", $scope.currentForecast);
console.log("Weekly", $scope.weeklyForecast);
console.log("Hourly", $scope.hourlyForecast);

var skycons = new Skycons({"color": "#aaa"});
skycons.add("icon_weather_current", $scope.currentForcast.iconAnimation);
skycons.add("icon_weather_current", $scope.currentForecast.iconAnimation);

skycons.play();

Expand Down Expand Up @@ -108,7 +106,7 @@
FitbitService.profileSummary(function(response){
$scope.fbDailyAverage = response;
});

FitbitService.todaySummary(function(response){
$scope.fbToday = response;
});
Expand All @@ -118,7 +116,7 @@
$interval(refreshMirrorData, 1500000);

var greetingUpdater = function () {
if(!Array.isArray(config.greeting) && typeof config.greeting.midday != 'undefined') {
if(typeof config.greeting != 'undefined' && !Array.isArray(config.greeting) && typeof config.greeting.midday != 'undefined') {
var hour = moment().hour();
var greetingTime = "midday";

Expand Down Expand Up @@ -149,8 +147,10 @@
});
};

refreshTrafficData();
$interval(refreshTrafficData, config.traffic.reload_interval * 60000);
if(typeof config.traffic != 'undefined'){
refreshTrafficData();
$interval(refreshTrafficData, config.traffic.reload_interval * 60000);
}

var refreshComic = function () {
console.log("Refreshing comic");
Expand All @@ -174,7 +174,7 @@
var textId = 'commands.'+commandId+'.text';
var descId = 'commands.'+commandId+'.description';
$translate([voiceId, textId, descId]).then(function (translations) {
AnnyangService.addCommand(translations[voiceId], commandFunction);
SpeechService.addCommand(translations[voiceId], commandFunction);
if (translations[textId] != '') {
var command = {"text": translations[textId], "description": translations[descId]};
$scope.commands.push(command);
Expand All @@ -185,11 +185,11 @@
// List commands
addCommand('list', function() {
console.debug("Here is a list of commands...");
console.log(AnnyangService.commands);
console.log(SpeechService.commands);
$scope.focus = "commands";
});


// Go back to default view
addCommand('home', defaultView);

Expand All @@ -207,7 +207,7 @@
console.debug("Boop Boop. Showing debug info...");
$scope.debug = true;
});

// Show map
addCommand('map_show', function() {
console.debug("Going on an adventure?");
Expand All @@ -217,7 +217,7 @@
$scope.focus = "map";
});
});

// Hide everything and "sleep"
addCommand('map_location', function(location) {
console.debug("Getting map of", location);
Expand Down Expand Up @@ -250,12 +250,6 @@
//SoundCloud search and play
addCommand('sc_play', function(query) {
SoundCloudService.searchSoundCloud(query).then(function(response){
SC.stream('/tracks/' + response[0].id).then(function(player){
player.play();
sound = player;
playing = true;
});

if (response[0].artwork_url){
$scope.scThumb = response[0].artwork_url.replace("-large.", "-t500x500.");
} else {
Expand All @@ -264,27 +258,23 @@
$scope.scWaveform = response[0].waveform_url;
$scope.scTrack = response[0].title;
$scope.focus = "sc";
SoundCloudService.startVisualizer();
SoundCloudService.play();
});
});

//SoundCloud stop
addCommand('sc_pause', function() {
sound.pause();
SoundCloudService.stopVisualizer();
SoundCloudService.pause();
$scope.focus = "default";
});
//SoundCloud resume
addCommand('sc_resume', function() {
sound.play();
SoundCloudService.startVisualizer();
SoundCloudService.play();
$scope.focus = "sc";
});
//SoundCloud replay
addCommand('sc_replay', function() {
sound.seek(0);
sound.play();
SoundCloudService.startVisualizer();
SoundCloudService.replay();
$scope.focus = "sc";
});

Expand Down Expand Up @@ -344,7 +334,7 @@

//Show fitbit stats (registered only if fitbit is configured in the main config)
if ($scope.fitbitEnabled) {
AnnyangService.addCommand('show my walking', function() {
SpeechService.addCommand('show my walking', function() {
refreshFitbitData();
});
}
Expand Down Expand Up @@ -411,22 +401,30 @@
});

var resetCommandTimeout;
//Track when the Annyang is listening to us
AnnyangService.start(function(listening){
$scope.listening = listening;
}, function(interimResult){
$scope.interimResult = interimResult;
$timeout.cancel(resetCommandTimeout);
}, function(result){
if(typeof result != 'undefined'){
$scope.interimResult = result[0];
resetCommandTimeout = $timeout(restCommand, 5000);
}
}, function(error){
console.log(error);
if(error.error == "network"){
$scope.speechError = "Google Speech Recognizer is down :(";
AnnyangService.abort();
//Register callbacks for Annyang and the Keyword Spotter
SpeechService.registerCallbacks({
listening : function(listening){
$scope.listening = listening;
},
interimResult : function(interimResult){
$scope.interimResult = interimResult;
$timeout.cancel(resetCommandTimeout);
},
result : function(result){
if(typeof result != 'undefined'){
$scope.interimResult = result[0];
resetCommandTimeout = $timeout(restCommand, 5000);
}
},
error : function(error){
console.log(error);
if(error.error == "network"){
$scope.speechError = "Google Speech Recognizer: Network Error (Speech quota exceeded?)";
SpeechService.abort();
} else {
// Even if it isn't a network error, stop making requests
SpeechService.abort();
}
}
});
};
Expand Down
Loading

0 comments on commit 1b5733b

Please sign in to comment.