diff --git a/dist/spotify-playlist-card-hc.js b/dist/spotify-playlist-card-hc.js
index ff4a790..70e6182 100644
--- a/dist/spotify-playlist-card-hc.js
+++ b/dist/spotify-playlist-card-hc.js
@@ -1,12 +1,17 @@
-// Examples:
+// For learning purposes, I studied the following cards:
+// https://github.com/robmarkoski/ha-clockwork-card/blob/master/clockwork-card.js
+
+
+// Examples of using an HA entity's attributes:
+// This example looks into HA entity (sensor.spotifyplaylist), the attribute called 'Unorganized', and sub attribute 'name'
// const entityId = this.config.entity;
// const playlist = hass.states[entityId].attributes;
-// ${playlist['Unorganized']['name']}
-// ${playlist['Unorganized']['image']}
-// ${playlist['Unorganized']['uri']}
+// ${playlist['Unorganized']['name']}
+// ${playlist['Unorganized']['image']}
+// ${playlist['Unorganized']['uri']}
-class SpotifyPlaylistCardHC extends HTMLElement {
+class SpotifyPlaylistCard extends HTMLElement {
constructor() {
super();
@@ -17,30 +22,43 @@ class SpotifyPlaylistCardHC extends HTMLElement {
const root = this.shadowRoot;
if (root.lastChild) root.removeChild(root.lastChild);
+ // Contains values of this Lovelace card configuration
const cardConfig = Object.assign({}, config);
+ // Default values of config options. Uses ternary statements
+ const columns = config.columns ? config.columns : 3;
+ const gradientLevel = config.gradient_level ? config.gradient_level : 0.8;
+ const gridGap = config.grid_gap ? config.grid_gap : '8px';
+ // changes opacity to 0 to hide playlist title
+ const showPlaylistTitles = config.show_playlist_titles ? 1 : 0;
+
+
+ if (!config.entity) {
+ throw new Error('Please define the name of the Spotify Playlist sensor.');
+ }
+
+
const card = document.createElement('div');
const content = document.createElement('div');
const style = document.createElement('style');
-// Ideas: if 'column' and/or 'row' are not defined in options, then use this CSS to automatically organize playlists:
-// 'grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));'
+ // Ideas: if 'column' and/or 'row' are not defined in options, then use this CSS to automatically organize playlists:
+ // 'grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));'
style.textContent = `
.outercontainer {
margin:auto;
display: grid;
- grid-template-columns: 1fr 1fr 1fr;
- grid-gap: 8px;
+ grid-template-columns: repeat(${columns}, 1fr);
+ grid-gap: ${gridGap};
+ }
.grid-item {
position: relative;
- flex-basis: calc(33.333%);
background-repeat: no-repeat;
background-size: cover;
background-position: center center;
border-radius: 3px;
-
}
.grid-item::before {
@@ -49,15 +67,11 @@ class SpotifyPlaylistCardHC extends HTMLElement {
padding-top: 100%;
}
- body {
- padding:0;
- margin:0;
- }
-
.content {
border-radius:0px 0px 3px 3px;
position: absolute;
bottom: 0;
+ opacity: ${showPlaylistTitles};
width:100%;
padding: 20px 10px 10px 10px;
border: 0;
@@ -67,9 +81,9 @@ class SpotifyPlaylistCardHC extends HTMLElement {
overflow: hidden;
text-overflow: ellipsis;
background: rgb(0,0,0);
- background: -moz-linear-gradient(top, rgba(0,0,0,0) 0%, rgba(0,0,0,0.86) 30%, rgba(0,0,0,0.86) 100%);
- background: -webkit-linear-gradient(top, rgba(0,0,0,0) 0%,rgba(0,0,0,0.86) 30%,rgba(0,0,0,0.86) 100%);
- background: linear-gradient(to bottom, rgba(0,0,0,0) 0%,rgba(0,0,0,0.86) 30%,rgba(0,0,0,0.86) 100%);
+ background: -moz-linear-gradient(top, rgba(0,0,0,0) 0%, rgba(0,0,0,${gradientLevel}) 30%, rgba(0,0,0,${gradientLevel}) 100%);
+ background: -webkit-linear-gradient(top, rgba(0,0,0,0) 0%,rgba(0,0,0,${gradientLevel}) 30%,rgba(0,0,0,${gradientLevel}) 100%);
+ background: linear-gradient(to bottom, rgba(0,0,0,0) 0%,rgba(0,0,0,${gradientLevel}) 30%,rgba(0,0,0,${gradientLevel}) 100%);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#00000000', endColorstr='#db000000',GradientType=0 );
}
`;
@@ -86,43 +100,89 @@ class SpotifyPlaylistCardHC extends HTMLElement {
card.appendChild(content);
card.appendChild(style);
root.appendChild(card);
- this._config = cardConfig;
+ this.config = cardConfig;
}
set hass(hass) {
- const config = this._config;
+ const config = this.config;
const root = this.shadowRoot;
const card = root.lastChild;
+ let card_content = ``;
+ let playlist = {};
this.myhass = hass;
- let card_content = `
-
-
-
-
-
-
-
-
-
-
Just Another Long Playlist Name Here
-
-
-
-
- `;
-
-
-
+ try {
+ playlist = hass.states[config.entity].attributes;
+ // Beginning of CSS grid
+ card_content += `
+
+ `;
+
+ for (let entry in playlist) {
+ // Sanity check that attributes in Spotify Playlist sensor are valid playlists
+ if (entry !== "friendly_name" && entry !== "icon" && entry !== "homebridge_hidden") {
+ card_content += `
+
+
${playlist[entry]['name']}
+
+ `;
+ }
+ }
+ // End of CSS grid
+ card_content += `
`;
+ }
+ catch(err) {
+ throw new Error(`Error detected: ${err}`);
+ }
root.lastChild.hass = hass;
root.getElementById('content').innerHTML = card_content;
+
+ // Add a click event to any CSS objects that has an ID: playlist01, playlist02, etc.
+ try {
+ // need to redefine 'playlist' as it was walked through in entirety in prev for loop.
+ playlist = hass.states[config.entity].attributes;
+ const mediaPlayer = config.media_player ? config.media_player : "default";
+ const speakerName = config.speaker_name ? config.speaker_name : "media_player.spotify";
+ const shuffleBoolean = config.shuffle ? config.shuffle : true;
+ const shuffleParameters = { "entity_id": speakerName, "shuffle": shuffleBoolean };
+ let playlistParameters = {};
+
+ // Learn about:
+ // hass.callService: https://developers.home-assistant.io/docs/en/frontend_data.html#hassuser
+ for (let entry in playlist) {
+ if (entry !== "friendly_name" && entry !== "icon" && entry !== "homebridge_hidden") {
+ card.querySelector(`#playlist${playlist[entry]['id']}`).addEventListener('click', event => {
+ if (mediaPlayer == "alexa") {
+ this.myhass.callService('media_player', 'shuffle_set', shuffleParameters);
+ playlistParameters = {"entity_id": speakerName, "media_content_type": "SPOTIFY", "media_content_id": `${playlist[entry]['name']}`};
+ this.myhass.callService('media_player', 'play_media', playlistParameters);
+ }
+ else if (mediaPlayer == "spotcast") {
+ this.myhass.callService('media_player', 'shuffle_set', shuffleParameters);
+ playlistParameters = {"entity_id": speakerName, "uri": `${playlist[entry]['uri']}`, "random_song": shuffleBoolean };
+ this.myhass.callService('spotcast', 'start', playlistParameters);
+ }
+ else {
+ this.myhass.callService('media_player', 'shuffle_set', shuffleParameters);
+ playlistParameters = {"entity_id": speakerName, "media_content_type": "playlist", "media_content_id": `${playlist[entry]['uri']}`};
+ this.myhass.callService('media_player', 'play_media', playlistParameters);
+ }
+ });
+ }
+ }
+ }
+ catch(err) {
+ throw new Error(`Error detected: ${err}`);
+ }
+
+
}
getCardSize() {
return 1;
}
}
-customElements.define('spotify-playlist-card-hc', SpotifyPlaylistCardHC);
\ No newline at end of file
+customElements.define('spotify-playlist-card', SpotifyPlaylistCard);
\ No newline at end of file
diff --git a/dist/spotify-playlist-card.js b/dist/spotify-playlist-card.js
index 12fb515..70e6182 100644
--- a/dist/spotify-playlist-card.js
+++ b/dist/spotify-playlist-card.js
@@ -1,9 +1,14 @@
-// Examples:
+// For learning purposes, I studied the following cards:
+// https://github.com/robmarkoski/ha-clockwork-card/blob/master/clockwork-card.js
+
+
+// Examples of using an HA entity's attributes:
+// This example looks into HA entity (sensor.spotifyplaylist), the attribute called 'Unorganized', and sub attribute 'name'
// const entityId = this.config.entity;
// const playlist = hass.states[entityId].attributes;
-// ${playlist['Unorganized']['name']}
-// ${playlist['Unorganized']['image']}
-// ${playlist['Unorganized']['uri']}
+// ${playlist['Unorganized']['name']}
+// ${playlist['Unorganized']['image']}
+// ${playlist['Unorganized']['uri']}
class SpotifyPlaylistCard extends HTMLElement {
@@ -14,113 +19,76 @@ class SpotifyPlaylistCard extends HTMLElement {
}
setConfig(config) {
- if (!config.entity) {
- throw new Error('Please define an entity.');
- }
const root = this.shadowRoot;
if (root.lastChild) root.removeChild(root.lastChild);
+ // Contains values of this Lovelace card configuration
const cardConfig = Object.assign({}, config);
- if (!cardConfig.title) {
- cardConfig.title = `Playlists`;
- }
-
- if (!config.size) {
- config.size = `15vmin`;
- }
- if (!config.columns) {
- config.columns = 3;
- }
+ // Default values of config options. Uses ternary statements
+ const columns = config.columns ? config.columns : 3;
+ const gradientLevel = config.gradient_level ? config.gradient_level : 0.8;
+ const gridGap = config.grid_gap ? config.grid_gap : '8px';
+ // changes opacity to 0 to hide playlist title
+ const showPlaylistTitles = config.show_playlist_titles ? 1 : 0;
- if (!config.media_player) {
- config.media_player = `default`;
- }
- if (!config.speaker_name) {
- config.speaker_name = `speaker name`;
+ if (!config.entity) {
+ throw new Error('Please define the name of the Spotify Playlist sensor.');
}
+
const card = document.createElement('div');
const content = document.createElement('div');
const style = document.createElement('style');
- style.textContent = `
+ // Ideas: if 'column' and/or 'row' are not defined in options, then use this CSS to automatically organize playlists:
+ // 'grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));'
- button {
- border: 0px;
- padding: 0;
- color: #FFFFFF;
- border: 0;
- width:100%
- font-size: 14px;
- margin: 0;
- background-color: rgba(0,0,0,0.0);
+ style.textContent = `
+ .outercontainer {
+ margin:auto;
+ display: grid;
+ grid-template-columns: repeat(${columns}, 1fr);
+ grid-gap: ${gridGap};
}
- button:hover {
+ .grid-item {
+ position: relative;
+ background-repeat: no-repeat;
+ background-size: cover;
+ background-position: center center;
+ border-radius: 3px;
}
- button img {
- display: block;
- width: 100%;
- border: 2px;
- border-radius: 4px;
- `;
-
- style.textContent += ` height: `;
- style.textContent += config.size;
- style.textContent += `;
- width: `;
- style.textContent += config.size;
- style.textContent += `;
- }`
-
- style.textContent += `
- .grid-container {
- justify-content: center;
- justify-items: center;
- align-items: center;
- display: grid;
- border: 0;
- grid-gap: 8px;
- grid-template-columns: auto`;
- var cssColumns = ' auto'.repeat(config.columns);
- style.textContent += cssColumns;
- style.textContent += `;}
-
- .grid-item {
- border: 0;
- padding: 0;
- position: relative;
+ .grid-item::before {
+ content: '';
+ display: block;
+ padding-top: 100%;
}
- .grid-item-text {
+
+ .content {
+ border-radius:0px 0px 3px 3px;
+ position: absolute;
+ bottom: 0;
+ opacity: ${showPlaylistTitles};
+ width:100%;
+ padding: 20px 10px 10px 10px;
border: 0;
- padding: 18px 10px 10px 10px;
text-align:left;
- position: absolute;
- bottom: 0;
- width: 100%;
+ color: rgba(255,255,255,1);
box-sizing:border-box;
- color: white;
- background: rgb(0,0,0);
- background: linear-gradient(360deg, rgba(0,0,0,1) 0%, rgba(0,0,0,0.5) 69%, rgba(0,0,0,0) 100%);
overflow: hidden;
text-overflow: ellipsis;
- border-radius: 4px;
- }
- .grid-title {
- grid-row: 1 / span 4;
- text-align: center;
- vertical-align: text-top;
- writing-mode: vertical-rl;
- }
- .grid-item ha-icon {
- -ms-transform: rotate(90deg); /* IE 9 */
- -webkit-transform: rotate(90deg); /* Safari */
- transform: rotate(90deg);
- }
+ background: rgb(0,0,0);
+ background: -moz-linear-gradient(top, rgba(0,0,0,0) 0%, rgba(0,0,0,${gradientLevel}) 30%, rgba(0,0,0,${gradientLevel}) 100%);
+ background: -webkit-linear-gradient(top, rgba(0,0,0,0) 0%,rgba(0,0,0,${gradientLevel}) 30%,rgba(0,0,0,${gradientLevel}) 100%);
+ background: linear-gradient(to bottom, rgba(0,0,0,0) 0%,rgba(0,0,0,${gradientLevel}) 30%,rgba(0,0,0,${gradientLevel}) 100%);
+ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#00000000', endColorstr='#db000000',GradientType=0 );
+ }
`;
+
+
content.innerHTML = `
@@ -132,85 +100,85 @@ class SpotifyPlaylistCard extends HTMLElement {
card.appendChild(content);
card.appendChild(style);
root.appendChild(card);
- this._config = cardConfig;
+ this.config = cardConfig;
}
set hass(hass) {
- const config = this._config;
+ const config = this.config;
const root = this.shadowRoot;
const card = root.lastChild;
+ let card_content = ``;
+ let playlist = {};
this.myhass = hass;
- let card_content = ''
- card_content += `
-
-
-
- Spotify Playlists
-
- `;
-
- if (hass.states[config.entity]) {
- const playlist = hass.states[config.entity].attributes;
- let column_count = 0
-
+ try {
+ playlist = hass.states[config.entity].attributes;
+ // Beginning of CSS grid
+ card_content += `
+
+ `;
+
for (let entry in playlist) {
+ // Sanity check that attributes in Spotify Playlist sensor are valid playlists
if (entry !== "friendly_name" && entry !== "icon" && entry !== "homebridge_hidden") {
- card_content += `
`;
- if (config.show_name == true) {
- card_content += `
${playlist[entry]['name']}
`;
- };
- card_content += `
`;
+ card_content += `
+
+
${playlist[entry]['name']}
+
+ `;
}
}
- };
- card_content += `
`;
-// card_content += `
-//
`;
-
+ // End of CSS grid
+ card_content += `
`;
+ }
+ catch(err) {
+ throw new Error(`Error detected: ${err}`);
+ }
root.lastChild.hass = hass;
root.getElementById('content').innerHTML = card_content;
- if (hass.states[config.entity]) {
- const playlist = hass.states[config.entity].attributes;
- const media_player = config.media_player;
- const speaker_name = config.speaker_name;
+ // Add a click event to any CSS objects that has an ID: playlist01, playlist02, etc.
+ try {
+ // need to redefine 'playlist' as it was walked through in entirety in prev for loop.
+ playlist = hass.states[config.entity].attributes;
+ const mediaPlayer = config.media_player ? config.media_player : "default";
+ const speakerName = config.speaker_name ? config.speaker_name : "media_player.spotify";
+ const shuffleBoolean = config.shuffle ? config.shuffle : true;
+ const shuffleParameters = { "entity_id": speakerName, "shuffle": shuffleBoolean };
+ let playlistParameters = {};
+
+ // Learn about:
+ // hass.callService: https://developers.home-assistant.io/docs/en/frontend_data.html#hassuser
for (let entry in playlist) {
if (entry !== "friendly_name" && entry !== "icon" && entry !== "homebridge_hidden") {
card.querySelector(`#playlist${playlist[entry]['id']}`).addEventListener('click', event => {
- if (media_player == "echo") {
- const myPlaylist = {"entity_id": speaker_name, "media_content_type": "playlist", "media_content_id": `${playlist[entry]['uri']}`};
- this.myhass.callService('media_player', 'play_media', myPlaylist);
+ if (mediaPlayer == "alexa") {
+ this.myhass.callService('media_player', 'shuffle_set', shuffleParameters);
+ playlistParameters = {"entity_id": speakerName, "media_content_type": "SPOTIFY", "media_content_id": `${playlist[entry]['name']}`};
+ this.myhass.callService('media_player', 'play_media', playlistParameters);
}
- else if (media_player == "spotcast") {
- const spotcastPlaylist = {"device_name": speaker_name, "uri": `${playlist[entry]['uri']}`};
- this.myhass.callService('spotcast', 'start', spotcastPlaylist);
+ else if (mediaPlayer == "spotcast") {
+ this.myhass.callService('media_player', 'shuffle_set', shuffleParameters);
+ playlistParameters = {"entity_id": speakerName, "uri": `${playlist[entry]['uri']}`, "random_song": shuffleBoolean };
+ this.myhass.callService('spotcast', 'start', playlistParameters);
+ }
+ else {
+ this.myhass.callService('media_player', 'shuffle_set', shuffleParameters);
+ playlistParameters = {"entity_id": speakerName, "media_content_type": "playlist", "media_content_id": `${playlist[entry]['uri']}`};
+ this.myhass.callService('media_player', 'play_media', playlistParameters);
}
});
}
}
}
+ catch(err) {
+ throw new Error(`Error detected: ${err}`);
+ }
+
+
}
getCardSize() {
return 1;