TileBoard - New dashboard for Homeassistant

A birthday card for TileBoard to users that are intrested.
i use hassio.
the code is modified from a original birthday-card for Lovelace, to be used with TileBoard template

the birthdays-tileboard.js file must be uploaded in /config/www/tileboard/
birthdays-tileboard.js content:

		var bdMarriedSymbol = "mdi-cards-heart";
		var bdCake = "mdi-cake-variant";
		
		// String translations (translate these to your own language) //
		
		var bdTextToday = "Today"; // Today
		var bdTextTomorrow = "Tomorrow"; // Tomorrow
		var bdTextNone = "No birthdays in the next"; // No birthdays during next
		var bdTextDays = "days"; // days
		var bdTextYears = "years"; // years
		var bdTextIn = "in"; // in
		var bdNextMonth= "";
		
///// BIRTHDAY REGISTRY ////////////////////////////////////////////////////
	
		
		//  ,s:1  at the end to add a heart as symbol
		var birthdayList=[
			{name:"Vasilica", day:14, month:7, year:1984},
			{name:"Costel", day:17, month:7, year:1984},
			{name:"Sorin R", day:26, month:7, year:1984},
			{name:"costel R", day:27, month:7, year:1984},
			{name:"Pisica A", day:9, month:8, year:1973},
			{name:"Luca", day:4, month:9, year:2005},
			{name:"Geoege", day:7, month:10, year:1973},
			{name:"Gabita", day:20, month:12, year:1981},
			{name:"Casatorie", day:30, month:8, year:2008, s:1},
		];
//Number of days from today upcomming birthdays will be displayed - default 14
		const numberOfDays = 14; 
		
		var current = new Date();
		var currentMonth = current.getMonth();
		var currentDay = current.getDate();
		var currentYear = current.getFullYear();
		var currentDayTS = new Date(currentYear, currentMonth, currentDay).getTime();
		var oneDay = 24*60*60*1000;
		
		for(var i = 0; i < birthdayList.length; i++) {
			var obj = birthdayList[i];
			if ( ((obj.month-1) < currentMonth) || ( ((obj.month-1) == currentMonth) && (obj.day < currentDay) ) ) {
				// Birthday passed in current year - add one year to throw date to next birthday
				obj.ts = new Date((currentYear+1), (obj.month-1), obj.day).getTime();
				obj.aPlus = 1;
			} else {
				// Birthday to come current year
				obj.ts = new Date(currentYear, (obj.month-1), obj.day).getTime();
				obj.aPlus = 0;
			}
			
			obj.diff = Math.round( Math.abs( (currentDayTS - obj.ts)/(oneDay) ) );
			
			if ( obj.diff > numberOfDays) { obj.ts = 0; }
		}
		
		var sortertListe = birthdayList.sort(
		    function(a, b){return a.ts > b.ts});
		   // (a, b) => (a.ts > b.ts) ? 1 : ((b.ts > a.ts) ? -1 : 0)
		    //); 
		var birthdayToday = "";
		var birthdayNext = "";
        
		var names = [];
        var icons = [];
        var dates = [];
		for(var i = 0; i < sortertListe.length; i++) {
			
			var obj = sortertListe[i];
			if ((obj.month-1) == currentMonth){
			     bdNextMonth="";   
			     } else{
			      bdNextMonth="next month";   
			     }
			if (obj.year > 0) {
				var age = "(" + (currentYear - obj.year + obj.aPlus) + " " + bdTextYears + ")";
			} else {
				var age = "";
			}
			
			var bdSymbol = "";
			if (obj.s == 1) { bdSymbol = " " + bdMarriedSymbol; }
			else { bdSymbol = " " + bdCake; }
			if (((obj.month-1) == currentMonth) && (obj.day == currentDay)) {
				names.push(obj.name + " " + age); 
                icons.push(bdSymbol); 
                dates.push(bdTextToday); 
			} else if (obj.ts !== 0) {
		       if( obj.diff == 1) bdNextMonth = bdTextTomorrow;
		       else bdNextMonth = " on "+ obj.day+" "+bdNextMonth;
				names.push(obj.name + " " + age); 
                icons.push(bdSymbol); 
                dates.push(bdNextMonth);
			}
			
		}
var CONFIG_bds = {    
   bd_name: names,
   bd_date: dates,
   bd_icon: icons,
   
}

in the tileboard index.html file we must put this line:
‘birthdays-tileboard.js’,
before line:
‘config.js’, // <----- NOT config.example.js !!

And the card is something like this:

////////////////////////birthdays                 
                  {
                     position: [2.2, 1], 
                     width: 2,
                     type :TYPES.TEXT_LIST,
                     id:{},
                     title: 'Birthdays',
                     list: [0,1,2,3,4,5].map(function (id) {
                      return {
                      title: CONFIG_bds.bd_name[id],
                      icon: CONFIG_bds.bd_icon[id],
                      value: CONFIG_bds.bd_date[id]
                      }
                     })
                   },

the birtdays well be added in birthdays-tileboard.js, you will see where

hope is usefull for some of you guys.
p.s. not an javascript expert…

2 Likes

There is an error in this line.

a missing semicolon at the end

Not really. Look at the last 10 lines.
It starts with

} else if (obj.ts !== 0) {
else bdNextMonth = " on "+ obj.day+" "+bdNextMonth;
				names.push(obj.name + " " + age); 
                icons.push(bdSymbol); 
              dates.push(bdNextMonth);
			}
			
		}

The curly braces of the if statement should be closed before else and after else there should be another pair of curly braces.

Edit:
Thank you for the information. After giving this a try I prefer it now and yes, makes things much simpler.

Is anyone else having an issue with the camera tile? I have tried both directly to my LTS cam and through motion eye, and no matter what the camera typically has no image. The same link works directly in VLC. Camera works great with Lovelace. I have tried camera and camera_thumbnail. In my yaml I have tried generic platform and mjpeg. Let me know if my code would help.

Same issue here.

Question on using classes:

I’m trying to change the icon color for recycle and trash sensor icon. Since these are on different days of the week I am able to re-use the same position on the page as they show/hide themselves based on the state.

For each tile, I am using a different class as defined in my custom.css. However, both tiles always use the recycle css. Is this the correct way to color icons differently for specific tiles?

Here is my code (I’m using the homekit theme):

/* Trash Can Icon Color */
.-sensor-trash .-theme-homekit .item.-open .item-entity--icon,
.-theme-homekit .item.-on .item-entity--icon {
  color: #10938D;
}

/* Recycle Can Icon Color */
.-sensor-recycle .-theme-homekit .item.-open .item-entity--icon,
.-theme-homekit .item.-on .item-entity--icon {
  color: #13AC20;
}

Tiles:

 {
                     position: [2,0],
                     type: TYPES.SENSOR_ICON,
                     id: "binary_sensor.recycling_day",
                     title: "Recycling Tomorrow",
                     state: false,
                     icon: 'mdi-recycle',
                     classes: ['-sensor-recycle'],
                     customStyles: function (item, entity) {
                        if (this.states['binary_sensor.recycling_day'].state === 'on') {
                           return {
                              'opacity': '.9',
                           };
                        } else {
                           return {
                              'opacity': '0',
                           };
                        }
                     }
                  },
                  {
                     position: [2,0],
                     type: TYPES.SENSOR_ICON,
                     id: "binary_sensor.trash_day",
                     title: "Trash Tomorrow",
                     state: false,
                     icon: 'mdi-trash-can-outline',
                     classes: ['-sensor-trash'],
                     customStyles: function (item, entity) {
                        if (this.states['binary_sensor.trash_day'].state === 'on') {
                           return {
                              'opacity': '.9',
                           };
                        } else {
                           return {
                              'opacity': '0',
                           };
                        }
                     }
                  },

Ok, through more trial and error I think I fixed it. Not sure why but this seems to work for the custom.css

/* Trash Can Icon Color */
.-theme-homekit .item.-on.-sensor-trash .item-entity--icon {
  color: #10938D;
}

/* Recycle Can Icon Color */
.-theme-homekit .item.-on.-sensor-recycle .item-entity--icon {
  color: #13AC20;
}

I had the same issue. I disabled the ping connection in config.js and hope that fixes the constant error messages (though not the problem I suppose). Did anybody else find a real fix for this issue with the new HA update?

Is there a way to pass variables to TTS events? I’m using Fully Kiosk and I am able to send TTS. But if I try putting a sensor state in the TTS message, it just reads the variable name instead of the value.

Example:

event: tileboard
event_data:
  command: tts
  text: The garage temperature is now {{states('sensor.garage_temp')}}

event_data -> event_data_template

position: [2,0],
type: TYPES.SENSOR_ICON,
id: "binary_sensor.recycling_day",
...
hidden: function (item, entity) {
  return entity.state === 'on';
}

Yeah I did that as well and that fixed the issue.

Thank you, that fixed it!

Hi, i am new to both HA and TileBoard but for the time being i am loving both of them… Right now i have 2 little problems…

1- I am trying to integrate spotify properly; my question about this is there a way to integrate playlists or search functions to TileBoard so that user doesnt have to go to Spotify to select the song he/she wants? Also is there a way to pre select source? Right now i am preparing 7 tablets to wall mount on rooms, each room have audio zone setup and on TileBoard each tablet have different index.html and config.js setup, this way i am hoping when someone presses play button on a tablet audio will play only on that room, for that to work properly i need to predefine sources to media player on tablets. I hope that makes sense…

2- I have several smoke alarms, door sensors etc, when an alarm gets triggered i want entire tablet to display only red background with message like “FIRE ALARM” “MOTION DETECTED” etc hiding everything else, is there a way to do this in TileBoard?

I am also a software and web developer so i am open to getting my hands dirty but before starting i want to ask if these already exist…

1 Like
  1. Don’t do separate index.html and config.js. Here is how you can have only separate config files for each tablet:

If all tablets are identical apart from room-specific tiles, you can simply override certain tiles based on the very same conf variable we have just defined.

  1. Send an event from HA and create a fullscreen div on the fly with the HTML content you desire. You can also have timeout, another HA event or even an onclick handler to hide it.

Update:
You could also reuse built-in screensaver for that. Here is how I used to open YouTube streams in fullscreen using custom tile:

function (item, entity) {
        if (CONFIG.screensaver) {
            var cam = ['_qNhxxo9rVU', 'ZqnraG3SsjM', 'ZqnraG3SsjM', 'ECQ0PM8Iqq4', 'DuTc5SOKE98', 'lYRf2jFX6e8', '4EH7ex-MzeE', 'XZT-MenNSLs'];
            var url = 'https://youtube.com/embed/' + cam[Math.floor((Math.random() * cam.length))] + '?autoplay=1&controls=0&rel=0&mute=1';
            
            CONFIG.screensaver.leftTop = [{
                type: SCREENSAVER_ITEMS.CUSTOM_HTML,
                html: '<iframe style="position: absolute; width: 108%; height: 112%; pointer-events: none;" src="' + url + '" frameborder="0" allow="autoplay *; fullscreen *" allowfullscreen="true"></iframe>',
                styles: {position: 'fixed', zIndex: -99, top: 0, left: 0, right: 0, bottom: 0, margin: '-6% -4%'}
            }]
            window.showScreensaver();
        }
    }
1 Like

Thx for the fast reply and ideas, i was going to use single index.html and ip based config.js since all tablets will have static ip’s but your method also makes sense, this way i can also add shortcuts to phones…

As for the full page warning alarm i just had to add this code before alarm tile codes in index.html

<div ng-if="item.type === TYPES.ALARM && entity.state === 'triggered'"  class="alarm_splash"><span>Emergency</span></div>

with the css

@keyframes PulseOpacity{0%{opacity:1;}50%{opacity:0;}100%{opacity:1;}}
.alarm_splash{display:flex;background:#c23847;width:100%;height:100%;position:fixed;top:0px;left:0px;z-index:9999;text-align:center;justify-content:center;align-items:center;font-size:160px;}
.alarm_splash span{animation-name:PulseOpacity;animation-duration:2s;animation-iteration-count:infinite;}

And now when alarm gets triggered all devices show full page red background with “Emergency” text flashing… All i got to do now is replace “Emergency” with device name that triggered the alarm…

Can you please also advice on media playlist and song selection abilities… I want to be able to at least switch between predefined playlists, is there a way to do something like this?

I’m not using Spotify and have no idea how it looks/works. You could use SELECT tile and have some basic automation in in HA to select playlist based on the selection.

I’m new to JS and trying to get dynamic TEXT_LIST populated. If I do the list manually it works:

                     list: [
                        {
                           title: "Test 1",
                           icon: 'mdi-school',
                           value: "Value 1"
                        },
                        {
                           title: "Test 2",
                           icon: 'mdi-school',
                           value: "Value 2"
                        },
                        {
                           title: "Test 3",
                           icon: 'mdi-school',
                           value: "Value 3"
                        },
                     ],

If I try to have it dynamically populated, the text list is empty in TileBoard:

                     list: [0].map(function() {
                        let myList = [];
                        const icon = 'mdi-school';
                        for (i = 1; i <= 3; i++) {
                           myList.push({
                              title: 'Test ' + i,
                              icon: icon,
                              value: 'Value ' + i
                           })
                        }
                        console.log(myList);
                        return myList;
                     }),

Not sure why as when I console log myList, it looks like it’s basically a list of objects similar to when I manually create it. Is this the correct approach?

Thanks!