Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,40 @@
MagicMirror
===========

##Configuration

The super magic interface of my personal Magic Mirror. More information about this project can be found on my [blog](http://michaelteeuw.nl/tagged/magicmirror).

Runs as a php script on a web server with basically no external dependencies. Can use socket.io for XBEE integration, but isn't required for basic functionality.

Modify js/config.js to change some general variables (language, wather location, compliments, news feed RSS) and calendar.php to add your own ICS calendar

##Code

###[main.js](js/main.js)

This file initiates the separate pieces of functionality that will appear in the view. It also includes various utility functions that are used to update what is visible.

###[Calendar](js/calendar)

Parsing functionality for the Calendar that retrieves and updates the calendar based on the interval set at the top of the [calendar.js](js/calendar/calendar.js) file. This was actually a straight pull from the original main.js file but the parsing code may deserve an upgrade.

###[Compliments](js/compliments)

Functionality related to inserting compliments into the view and rotating them based on a specific interval set at the top of the [compliments.js](js/compliments/compliments.js) file.

###[News](js/news)

Takes an array of news feeds (or a single string) from the config file and retrieves each one so that it can be displayed in a loop based on the interval set at the top of the [news.js](js/news/news.js) file.

###[Time](js/time)

Updates the time on the screen on one second interval (I think).

###[Version](js/version)

Checks the git version and refreshes if a new version has been pulled.

###[Weather](js/weather)

Takes the user's inserted location, language, unit type, and OpenWeatherMap API key and grabs the five day weather forecast from OpenWeatherMap.
8 changes: 7 additions & 1 deletion index.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,15 @@
<script src="js/jquery.js"></script>
<script src="js/jquery.feedToJSON.js"></script>
<script src="js/ical_parser.js"></script>
<script src="js/moment-with-langs.min.js"></script>
<script src="js/moment-with-locales.min.js"></script>
<script src="js/config.js"></script>
<script src="js/rrule.js"></script>
<script src="js/version/version.js" type="text/javascript"></script>
<script src="js/calendar/calendar.js" type="text/javascript"></script>
<script src="js/compliments/compliments.js" type="text/javascript"></script>
<script src="js/weather/weather.js" type="text/javascript"></script>
<script src="js/time/time.js" type="text/javascript"></script>
<script src="js/news/news.js" type="text/javascript"></script>
<script src="js/main.js?nocache=<?php echo md5(microtime()) ?>"></script>
<!-- <script src="js/socket.io.min.js"></script> -->

Expand Down
132 changes: 132 additions & 0 deletions js/calendar/calendar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
var calendar = {
eventList: [],
calendarLocation: '.calendar',
updateInterval: 1000,
updateDataInterval: 60000,
fadeInterval: 1000,
intervalId: null,
dataIntervalId: null
}

calendar.updateData = function (callback) {

new ical_parser("calendar.php", function(cal) {
var events = cal.getEvents();
this.eventList = [];

for (var i in events) {
var e = events[i];
for (var key in e) {
var value = e[key];
var seperator = key.search(';');
if (seperator >= 0) {
var mainKey = key.substring(0,seperator);
var subKey = key.substring(seperator+1);

var dt;
if (subKey == 'VALUE=DATE') {
//date
dt = new Date(value.substring(0,4), value.substring(4,6) - 1, value.substring(6,8));
} else {
//time
dt = new Date(value.substring(0,4), value.substring(4,6) - 1, value.substring(6,8), value.substring(9,11), value.substring(11,13), value.substring(13,15));
}

if (mainKey == 'DTSTART') e.startDate = dt;
if (mainKey == 'DTEND') e.endDate = dt;
}
}

if (e.startDate == undefined){
//some old events in Gmail Calendar is "start_date"
//FIXME: problems with Gmail's TimeZone
var days = moment(e.DTSTART).diff(moment(), 'days');
var seconds = moment(e.DTSTART).diff(moment(), 'seconds');
var startDate = moment(e.DTSTART);
} else {
var days = moment(e.startDate).diff(moment(), 'days');
var seconds = moment(e.startDate).diff(moment(), 'seconds');
var startDate = moment(e.startDate);
}

//only add fututre events, days doesn't work, we need to check seconds
if (seconds >= 0) {
if (seconds <= 60*60*5 || seconds >= 60*60*24*2) {
var time_string = moment(startDate).fromNow();
}else {
var time_string = moment(startDate).calendar()
}
if (!e.RRULE) {
this.eventList.push({'description':e.SUMMARY,'seconds':seconds,'days':time_string});
}
e.seconds = seconds;
}

// Special handling for rrule events
if (e.RRULE) {
var options = new RRule.parseString(e.RRULE);
options.dtstart = e.startDate;
var rule = new RRule(options);

// TODO: don't use fixed end date here, use something like now() + 1 year
var dates = rule.between(new Date(), new Date(2016,11,31), true, function (date, i){return i < 10});
for (date in dates) {
var dt = new Date(dates[date]);
var days = moment(dt).diff(moment(), 'days');
var seconds = moment(dt).diff(moment(), 'seconds');
var startDate = moment(dt);
if (seconds >= 0) {
if (seconds <= 60*60*5 || seconds >= 60*60*24*2) {
var time_string = moment(dt).fromNow();
} else {
var time_string = moment(dt).calendar()
}
this.eventList.push({'description':e.SUMMARY,'seconds':seconds,'days':time_string});
}
}
}
};

this.eventList = this.eventList.sort(function(a,b){return a.seconds-b.seconds});

if (callback !== undefined && Object.prototype.toString.call(callback) === '[object Function]') {
callback(this.eventList);
}

}.bind(this));

}

calendar.updateCalendar = function (eventList) {

table = $('<table/>').addClass('xsmall').addClass('calendar-table');
opacity = 1;

for (var i in eventList) {
var e = eventList[i];

var row = $('<tr/>').css('opacity',opacity);
row.append($('<td/>').html(e.description).addClass('description'));
row.append($('<td/>').html(e.days).addClass('days dimmed'));
table.append(row);

opacity -= 1 / eventList.length;
}

$(this.calendarLocation).updateWithText(table, this.fadeInterval);

}

calendar.init = function () {

this.updateData(this.updateCalendar.bind(this));

this.intervalId = setInterval(function () {
this.updateCalendar(this.eventList)
}.bind(this), this.updateInterval);

this.dataIntervalId = setInterval(function () {
this.updateData(this.updateCalendar.bind(this));
}.bind(this), this.updateDataInterval);

}
66 changes: 66 additions & 0 deletions js/compliments/compliments.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
var compliments = {
complimentLocation: '.compliment',
currentCompliment: '',
complimentList: {
'morning': config.compliments.morning,
'afternoon': config.compliments.afternoon,
'evening': config.compliments.evening
},
updateInterval: config.compliments.interval || 30000,
fadeInterval: config.compliments.fadeInterval || 4000,
intervalId: null
};

/**
* Changes the compliment visible on the screen
*/
compliments.updateCompliment = function () {

var _list = [];

var hour = moment().hour();

if (hour >= 3 && hour < 12) {
// Morning compliments
_list = compliments.complimentList['morning'];
} else if (hour >= 12 && hour < 17) {
// Afternoon compliments
_list = compliments.complimentList['afternoon'];
} else if (hour >= 17 || hour < 3) {
// Evening compliments
_list = compliments.complimentList['evening'];
} else {
// Edge case in case something weird happens
// This will select a compliment from all times of day
Object.keys(compliments.complimentList).forEach(function (_curr) {

_list = _list.concat(compliments.complimentList[_curr]);

});
}

// Search for the location of the current compliment in the list
var _spliceIndex = _list.indexOf(compliments.currentCompliment);

// If it exists, remove it so we don't see it again
if (_spliceIndex !== -1) {
_list = _list.slice(_spliceIndex, 1);
}

// Randomly select a location
var _location = Math.floor(Math.random() * _list.length);
compliments.currentCompliment = _list[_location];

$('.compliment').updateWithText(compliments.currentCompliment, compliments.fadeInterval);

}

compliments.init = function () {

this.updateCompliment();

this.intervalId = setInterval(function () {
this.updateCompliment();
}.bind(this), this.updateInterval)

}
58 changes: 30 additions & 28 deletions js/config.js
Original file line number Diff line number Diff line change
@@ -1,37 +1,39 @@
// for navigator language
// var lang = window.navigator.language;
// you can change the language
var lang = 'nl';

//change weather params here:
//units: metric or imperial
var weatherParams = {
'q':'Baarn,Netherlands',
'units':'metric',
'lang':lang,
'APPID':'YOUR_FREE_OPENWEATHER_API_KEY'
};

var feed = 'http://feeds.nos.nl/nosjournaal?format=rss';
//var feed = 'http://www.nu.nl/feeds/rss/achterklap.rss';
//var feed = 'http://www.nu.nl/feeds/rss/opmerkelijk.rss';
//var feed = 'http://www.nytimes.com/services/xml/rss/nyt/HomePage.xml';

// compliments:
var morning = [
var config = {
lang: 'nl',
time: {
timeFormat: 12
},
weather: {
//change weather params here:
//units: metric or imperial
params: {
q: 'Baarn,Netherlands',
units: 'metric',
// if you want a different lang for the weather that what is set above, change it here
lang: 'nl',
APPID: 'YOUR_FREE_OPENWEATHER_API_KEY'
}
},
compliments: {
interval: 2000,
fadeInterval: 4000,
morning: [
'Good morning, handsome!',
'Enjoy your day!',
'How was your sleep?'
];

var afternoon = [
],
afternoon: [
'Hello, beauty!',
'You look sexy!',
'Looking good today!'
];

var evening = [
],
evening: [
'Wow, you look hot!',
'You look nice!',
'Hi, sexy!'
];
]
},
news: {
feed: 'http://www.nytimes.com/services/xml/rss/nyt/HomePage.xml'
}
}
Loading