Initial commit
Still a lot to do here, such as better error handling and handling multiple teams matching the name typed in, but it's a start.
This commit is contained in:
61
.gitignore
vendored
Normal file
61
.gitignore
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Typescript v1 declaration files
|
||||
typings/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
|
||||
# package lock
|
||||
package-lock.json
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
166
MMM-ShiftStats.js
Normal file
166
MMM-ShiftStats.js
Normal file
@ -0,0 +1,166 @@
|
||||
shiftStats = {};
|
||||
|
||||
Module.register("MMM-ShiftStats",{
|
||||
defaults: {
|
||||
apiKey: null,
|
||||
type: 'Regular Season',
|
||||
teamName: null,
|
||||
sport: null,
|
||||
mode: 'standings',
|
||||
maxGames: 6,
|
||||
teamNameClass: "light",
|
||||
updateInterval: 12 * 60 * 60 * 1000
|
||||
},
|
||||
// search for team name, look for newest team, pick playoffs if there are games, otherwise reg season, otherwise exhibition?
|
||||
// teamSearch response comes with references.season which should get you the info you need. season has multiple season.stats
|
||||
// which has .Exhibition and etc. which contains games_played.
|
||||
start: function() {
|
||||
this.sendSocketNotification('SHIFTSTATS_CONFIG', this.config);
|
||||
this.sendSocketNotification('SHIFTSTATS_UPDATE');
|
||||
},
|
||||
|
||||
getStyles: function() {
|
||||
return ["shiftstats.css"];
|
||||
},
|
||||
|
||||
getDom: function() {
|
||||
if (!shiftStats.standings || !shiftStats.games) {
|
||||
let wrapper = document.createElement("div");
|
||||
wrapper.innerHTML = 'Loading...';
|
||||
wrapper.className += "dimmed light small";
|
||||
|
||||
return wrapper;
|
||||
} else {
|
||||
let table = document.createElement('table');
|
||||
table.className = "small";
|
||||
if (this.config.colored) {
|
||||
table.className += " colored";
|
||||
}
|
||||
|
||||
if (this.config.mode == 'games') {
|
||||
table = showGames(table, this.config)
|
||||
} else {
|
||||
table = showStandings(table)
|
||||
}
|
||||
|
||||
return table;
|
||||
}
|
||||
},
|
||||
|
||||
socketNotificationReceived: function(notification, payload) {
|
||||
if (notification === 'SHIFTSTATS_STANDINGS') {
|
||||
shiftStats = payload;
|
||||
this.updateDom();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function showGames(table, config) {
|
||||
let now = new Date()
|
||||
let mostRecent = []
|
||||
shiftStats.games.games.forEach((game) => {
|
||||
if (new Date(game.datetime) < now) {
|
||||
mostRecent.push({
|
||||
game: game,
|
||||
home_team: shiftStats.games.references.team.find(team => team.id == game.home_team_id),
|
||||
away_team: shiftStats.games.references.team.find(team => team.id == game.away_team_id),
|
||||
})
|
||||
if (mostRecent.length > config.maxGames) {
|
||||
mostRecent.shift()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
let row
|
||||
let cell
|
||||
mostRecent.forEach((game) => {
|
||||
row = document.createElement('tr')
|
||||
|
||||
cell = document.createElement('td')
|
||||
cell.innerHTML = game.home_team.name
|
||||
cell.className = config.teamNameClass
|
||||
row.appendChild(cell)
|
||||
|
||||
cell = document.createElement('td')
|
||||
cell.innerHTML = game.game.stats.home_score
|
||||
cell.className = 'center'
|
||||
row.appendChild(cell)
|
||||
|
||||
cell = document.createElement('td')
|
||||
cell.innerHTML = game.game.stats.away_score
|
||||
cell.className = 'center'
|
||||
row.appendChild(cell)
|
||||
|
||||
cell = document.createElement('td')
|
||||
cell.innerHTML = game.away_team.name
|
||||
cell.className = `left ${config.teamNameClass}`
|
||||
row.appendChild(cell)
|
||||
|
||||
table.appendChild(row)
|
||||
})
|
||||
|
||||
return table
|
||||
}
|
||||
|
||||
function showStandings(table) {
|
||||
let row = document.createElement('tr')
|
||||
|
||||
let cell = document.createElement('th')
|
||||
cell.innerHTML = 'Team'
|
||||
row.appendChild(cell)
|
||||
|
||||
cell = document.createElement('th')
|
||||
cell.className = 'center'
|
||||
cell.innerHTML = 'W'
|
||||
row.appendChild(cell)
|
||||
|
||||
cell = document.createElement('th')
|
||||
cell.className = 'center'
|
||||
cell.innerHTML = 'L'
|
||||
row.appendChild(cell)
|
||||
|
||||
cell = document.createElement('th')
|
||||
cell.className = 'center'
|
||||
cell.innerHTML = 'OTW'
|
||||
row.appendChild(cell)
|
||||
|
||||
cell = document.createElement('th')
|
||||
cell.className = 'center'
|
||||
cell.innerHTML = 'OTL'
|
||||
row.appendChild(cell)
|
||||
|
||||
table.appendChild(row)
|
||||
|
||||
shiftStats.standings.teams.forEach((team) => {
|
||||
row = document.createElement('tr')
|
||||
|
||||
cell = document.createElement('td')
|
||||
cell.innerHTML = team.name
|
||||
cell.className = 'light'
|
||||
row.appendChild(cell)
|
||||
|
||||
cell = document.createElement('td')
|
||||
cell.innerHTML = team.stats['Regular Season'].wins
|
||||
cell.className = 'center'
|
||||
row.appendChild(cell)
|
||||
|
||||
cell = document.createElement('td')
|
||||
cell.innerHTML = team.stats['Regular Season'].losses
|
||||
cell.className = 'center'
|
||||
row.appendChild(cell)
|
||||
|
||||
cell = document.createElement('td')
|
||||
cell.innerHTML = team.stats['Regular Season'].otw
|
||||
cell.className = 'center'
|
||||
row.appendChild(cell)
|
||||
|
||||
cell = document.createElement('td')
|
||||
cell.innerHTML = team.stats['Regular Season'].otl
|
||||
cell.className = 'center'
|
||||
row.appendChild(cell)
|
||||
|
||||
table.appendChild(row)
|
||||
})
|
||||
|
||||
return table
|
||||
}
|
41
README.md
Normal file
41
README.md
Normal file
@ -0,0 +1,41 @@
|
||||
# MMM-ShiftStats
|
||||
A [MagicMirror²](https://github.com/MichMich/MagicMirror) module used to display stats from any DigitalShift site ([HockeyShift](https://hockeyshift.com), [SoccerShift](https://soccershift.com), [LacrosseShift](https://lacrosseshift.com), [FootballShift](http://footballshift.com), [BasketballShift](https://basketballshift.com), and [BaseballShift](http://baseballshift.com)).
|
||||
|
||||
## Installation
|
||||
1. Navigate into your MagicMirror's `modules` folder and execute `git clone https://github.com/parnic/MMM-ShiftStats.git`.
|
||||
2. `cd MMM-ShiftStats`
|
||||
3. Execute `npm install` to install the node dependencies.
|
||||
4. Add the module inside `config.js` placing it where you prefer.
|
||||
|
||||
## Config
|
||||
|Option|Type|Description|Default|
|
||||
|---|---|---|---|
|
||||
|apiKey|`string`|Your API key. If not supplied, the HockeyShift Android app's key is used by default.||
|
||||
|teamName|`string`|(REQUIRED) The name of the team you want to track.||
|
||||
|sport|`string`|(REQUIRED) The name of the sport you want to track (e.g. `'hockey'`, `'soccer'`).||
|
||||
|mode|`string`|What mode the module should run in. Valid values: `'standings'`, `'games'`|`'standings'`|
|
||||
|maxGames|`number`|When in `games` mode, how many games should be shown (it will show this many most recent games).|`6`|
|
||||
|teamNameClass|`string`|CSS class to apply to displayed team names.|`'light'`|
|
||||
|updateInterval|`number`|How frequently, in milliseconds, to update the info.|`12 * 60 * 60 * 1000` (every 12 hours)|
|
||||
|
||||
Here is an example of an entry in config.js
|
||||
```
|
||||
{
|
||||
module: 'MMM-ShiftStats',
|
||||
header: 'Standings',
|
||||
position: 'top_left',
|
||||
config: {
|
||||
teamName: 'Bears',
|
||||
sport: 'Hockey'
|
||||
}
|
||||
},
|
||||
```
|
||||
|
||||
## Screenshot
|
||||

|
||||
|
||||
## Notes
|
||||
Pull requests are very welcome! If you'd like to see any additional functionality, don't hesitate to let me know.
|
||||
|
||||
## Dependencies
|
||||
This uses a Node.JS library I created for interfacing with DigitalShift sites: [node-shiftstats](https://github.com/parnic/node-shiftstats), so feel free to check that out for more information.
|
59
node_helper.js
Normal file
59
node_helper.js
Normal file
@ -0,0 +1,59 @@
|
||||
let NodeHelper = require('node_helper')
|
||||
let ShiftStats = require('node-shiftstats')
|
||||
|
||||
module.exports = NodeHelper.create({
|
||||
start: function() {
|
||||
this.setTimer(12 * 60 * 60 * 1000)
|
||||
},
|
||||
|
||||
doUpdate: async function() {
|
||||
console.log("doupdate")
|
||||
if (!this.config) {
|
||||
return
|
||||
}
|
||||
|
||||
const s = new ShiftStats(this.config.apiKey)
|
||||
await s.login()
|
||||
let teams = await s.teamSearch(this.config.sport, this.config.teamName)
|
||||
// standings will be sorted in the order of the type we requested (regular season, playoffs, ...) but also includes
|
||||
// stats for other types. consider sorting by the type we want first. see property division_rank
|
||||
let standings = await s.divisionStandings(teams.references.division[0].id, this.config.type)
|
||||
let games = sortGames(await s.divisionGamesList(teams.references.division[0].id), this.config.type)
|
||||
this.sendSocketNotification('SHIFTSTATS_STANDINGS', {standings: standings, games: games})
|
||||
},
|
||||
|
||||
setTimer: function(updateInterval) {
|
||||
var update = true
|
||||
update = typeof this.updateInterval === 'undefined' || this.updateInterval != updateInterval
|
||||
this.updateInterval = updateInterval
|
||||
|
||||
if (update) {
|
||||
if (typeof this.timer !== 'undefined') {
|
||||
clearInterval(this.timer)
|
||||
}
|
||||
|
||||
this.timer = setInterval(() => {
|
||||
this.doUpdate()
|
||||
}, this.updateInterval)
|
||||
}
|
||||
},
|
||||
|
||||
socketNotificationReceived: function(notification, payload) {
|
||||
if (notification === 'SHIFTSTATS_CONFIG') {
|
||||
this.config = payload
|
||||
this.setTimer(this.config.updateInterval)
|
||||
}
|
||||
if (notification === 'SHIFTSTATS_UPDATE') {
|
||||
this.doUpdate()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
function sortGames(games, type) {
|
||||
games.games = games.games.filter(game => game.type == type)
|
||||
games.games.sort((a, b) => {
|
||||
return new Date(a.datetime) - new Date(b.datetime)
|
||||
})
|
||||
|
||||
return games
|
||||
}
|
12
package.json
Normal file
12
package.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "magic-mirror-module-shiftstate",
|
||||
"version": "1.0.0",
|
||||
"description": "Show data from DigitalShift sites",
|
||||
"main": "MMM-ShiftStats.js",
|
||||
"author": "Chris Pickett",
|
||||
"repository": "https://github.com/parnic/MMM-ShiftStats.git",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"node-shiftstats": "^1.0.0"
|
||||
}
|
||||
}
|
BIN
screenshot.png
Normal file
BIN
screenshot.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 185 KiB |
11
shiftstats.css
Normal file
11
shiftstats.css
Normal file
@ -0,0 +1,11 @@
|
||||
.MMM-ShiftStats td {
|
||||
padding: 2px 5px 2px 5px;
|
||||
}
|
||||
|
||||
.MMM-ShiftStats td.center, th.center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.MMM-ShiftStats td.left {
|
||||
text-align: left;
|
||||
}
|
Reference in New Issue
Block a user