TileBoard - New dashboard for Homeassistant

I managed a workaround for this. In custom.css I globally set the title bar to 0px for all the pop-up-tile entries and copied the popup–close entries from the old main.css to the new custom.css as popup-close. I can now see the entire window and can close it as well.

Added these to custom.css:

.camera-popup-title,
.door-entry-popup-title,
.iframe-popup-title,
.history-popup-title {
  height: 0px;
}
.camera-popup-close,
.door-entry-popup-close,
.iframe-popup-close,
.history-popup-close {
  position: absolute;
  text-align: center;
  right: 15px;
  top: 15px;
  color: rgba(255, 255, 255, 0.6);
  background: rgba(0, 0, 0, 0.2);
  width: 70px;
  height: 70px;
  line-height: 70px;
  font-size: 50px;
  z-index: 3;
  cursor: pointer;
}

EDIT/UPDATE:

For those who want to keep the title bar as is but fix the image being cut off at the bottom, the height of the below elements need to be changed from 100% to calc(100% - 50px). I just added these to custom.css to override what’s in main.css. The only issue is the image is slightly smaller since you’re losing 50px of height.

.camera-popup--camera,
.door-entry-popup--camera,
.iframe-popup--camera {
  height: calc(100% - 50px);
}

.camera-popup--iframe,
.door-entry-popup--iframe,
.iframe-popup--iframe {
  height: calc(100% - 50px);
}

just wondering :slight_smile: is is possible to divide config file into smaller pieces ? currently my config is 3,5k lines long and it would be much more convenient to work with it if I could e.g. have each room in separate file.

Splitting the configuration:

Define vars in separate JS files, then merge them into CONFIG. like this:

{
         position: [0, 1],
         width: 3,
         height: 1,
         type: TYPES.CUSTOM,
         id: {},
         icons: angular.merge(weather_icons,{}),
         customHtml: angular.merge(weather_forecast_list_html_lovalace,{}),
         provider: 'weather.gismeteo',
         forecast_mode: 'hourly',
         filters: angular.merge(weather_forecast_list_filters),
},

Hi

I’m looking for a way to open a configurable popup using the action or secondaryAction of any tile. By configurable I mean that the popup layout could contain other tiles, like in door entry tile. Only without the camera component and callable from any tile type.

For example, a long press on a weather tile (over secondaryAction) could open a popup with more detailed weather info. Or a security sensor tile showing if any door or window is currently open (over a template sensor) could open a popup that gives detailed info on every individual window and door status in the house. I’m targeting a phone with this, so screen space is a premium. I’d like to avoid an additional dedicated page for this. And I think that opening additional context sensitive info by long pressing a tile is pretty intuitive.

So anyone know if this can be achieved with stock TileBoard in some way I overlooked, or did anyone implemented this already ? If not I’m going to code it myself, but I’d rather not reinvent the wheel if it was already done :slight_smile:

thanks !

Anyone found a way to replace the side/bottm menu buttons from mdi to an actual image ?

(Post Edited)

Found out that the Climate button didnt really worked (unable to set preset_mode on
generic Zwave climate controller and on Sensibo), Not sure if it was done for a specific controller.
climate fan climate temp climate
Modifications i have done to support a more generic way:
main.js:

 $scope.climateFanSpeed = function (item, entity) {
      var value = entity.attributes.fan_mode;
      return value;
   };

   $scope.setClimateFanMode = function ($event, item, entity, option) {
      $event.preventDefault();
      $event.stopPropagation();

      sendItemData(item, {
         type: "call_service",
         domain: "climate",
         service: "set_fan_mode",
         service_data: {
            entity_id: item.id,
            fan_mode: option
         }
      });

      $scope.closeActiveSelect();

      return false;
   };

$scope.setClimateMode = function ($event, item, entity, option) {
      $event.preventDefault();
      $event.stopPropagation();

      sendItemData(item, {
         type: "call_service",
         domain: "climate",
         service: "set_hvac_mode",
         service_data: {
            entity_id: item.id,
            hvac_mode: option
         }
      });

      $scope.closeActiveSelect();

      return false;
   };

on the index.html

       <div ng-if="item.type === TYPES.CLIMATE"
           class="item-entity-container">
         <div>
            <div class="item-button -center-right"
                 ng-if="entity.attributes.temperature && entity.state !== 'off'"
                 ng-click="increaseClimateTemp($event, item, entity)">
               <i class="mdi mdi-arrow-up-bold"></i>
            </div>
            <div class="item-button -bottom-right"
                 ng-if="entity.attributes.temperature && entity.state !== 'off'"
                 ng-click="decreaseClimateTemp($event, item, entity)">
               <i class="mdi mdi-arrow-down-bold"></i>
            </div>
         </div>

	<!-- Climate Mode -->
	  <div class="item-climate">
		 <div class="item-climate--mode" ng-if="entity.attributes.temperature"
              ng-click="openSelect(item)">
				<span ng-bind="climateTarget(item, entity)"></span>
				<span ng-if="(_unit = entityUnit(item, entity))"
                     class="item-climate--target--unit" ng-bind="_unit"></span>
         </div>
         <div ng-if="selectOpened(item)" class="item-select"
              ng-style="itemSelectStyles(entity, entity.attributes.hvac_modes)">
            <div class="item-select--option"
                 ng-repeat="option in entity.attributes.hvac_modes track by $index"
                 ng-class="{'-active': option === entity.state}"
                 ng-click="setClimateMode($event, item, entity, option)">
				<span ng-bind="option"></span>
            </div>
         </div>
	  </div>

    <!-- Climate Fan Mode -->
		 <div class="item-climate--mode" ng-if="entity.attributes.temperature"
              ng-click="openSelect(seconditem)">
	   	      <span ng-bind="entity.attributes.fan_mode"></span>
         </div>
		 <div ng-if="selectOpened(seconditem)" class="item-select"
			  ng-style="itemSelectStyles(entity, entity.attributes.fan_modes)">
            <div class="item-select--option"
                 ng-repeat="option in entity.attributes.fan_modes track by $index"
                 ng-class="{'-active': option === entity.attributes.fan_mode}"
                 ng-click="setClimateFanMode($event, item, entity, option)">
               <span ng-bind="option"></span>
            </div>
         </div>
      </div>

To-Do (If Possible, New to This…)
Rather having a list, It would be nice to do it as Alarm Button
since mdi icons available for all AC Modes, And AC Speeds.
future

Interestingly, Home Assistant 0.106.3 has a Coronavirus integration. Although perhaps a little overboard, it might be interesting to incorporate this into a Tileboard page. Any advice on how to do that?

I just updated to see what it gives you. It’s just 4 sensors. Confirmed, current, recovered and deaths. A TYPES.TEXT_LIST tile would probably be easiest. You could do 4 sensor tiles for each. You could dump the data into a grafana graph and use the images as a background. If you want images or graphs, it might just be easier to use images from an internet site that’s actively updating stats then use those with camera tiles.

I just added this to my Tileboard. I added a value function to add the thousands separator…

{
    position: [0, 3],
    width: 2,
    height: .75,
    title: 'US SARS-Cov-2 Information',
    id: {},
    type: TYPES.TEXT_LIST,
    state: false,
    list: [
    {
        title: 'US Confirmed Cases',
        //value: 'sensor.us_coronavirus_confirmed.state'
        value: function () {
            return Number(this.states['sensor.us_coronavirus_confirmed'].state).toLocaleString();
        }
    },
    {
        title: 'US Current Cases',
        //value: '&sensor.us_coronavirus_current.state'
        value: function () {
            return Number(this.states['sensor.us_coronavirus_current'].state).toLocaleString();
        }
    },
    {
        title: 'US Current Deaths',
        //value: '&sensor.us_coronavirus_deaths.state'
        value: function () {
            return Number(this.states['sensor.us_coronavirus_deaths'].state).toLocaleString();
        }
    },
    {
        title: 'US Current Recovered',
        //value: '&sensor.us_coronavirus_recovered.state'
        value: function () {
            return Number(this.states['sensor.us_coronavirus_recovered'].state).toLocaleString();
        }
    }, ]
},

{
    position: [0, 3],
    width: 2,
    height: .75,
    title: 'Worldwide SARS-Cov-2 Information',
    id: {},
    type: TYPES.TEXT_LIST,
    state: false,
    list: [
    {
        title: 'Worldwide Confirmed Cases',
        //value: 'sensor.worldwide_coronavirus_confirmed.state'
        value: function () {
            return Number(this.states['sensor.worldwide_coronavirus_confirmed'].state).toLocaleString();
        }
    },
    {
        title: 'Worldwide Current Cases',
        //value: '&sensor.worldwide_coronavirus_current.state'
        value: function () {
            return Number(this.states['sensor.worldwide_coronavirus_current'].state).toLocaleString();
        }
    },
    {
        title: 'Worldwide Current Deaths',
        //value: '&sensor.worldwide_coronavirus_deaths.state'
        value: function () {
            return Number(this.states['sensor.worldwide_coronavirus_deaths'].state).toLocaleString();
        }
    },
    {
        title: 'Worldwide Current Recovered',
        //value: '&sensor.worldwide_coronavirus_recovered.state'
        value: function () {
            return Number(this.states['sensor.worldwide_coronavirus_recovered'].state).toLocaleString();
        }
    }, ]
},

Thanks, I implemented it as you provided, although you have a minor typo. You have both the US and Worldwide statistics going to the same tile ( position: [0, 3] ) so they overwrite each other. I fixed that and it works great.

I’ve been trying to add some sort of light brightness popup which is similar to what you are trying to do.

I try try and code something myself but didn’t get very far, but if you to manage to do better than me then I’d be interested to see how it’s done. :slight_smile:

Excellent. I have them in two separate groups on the same page which is why the position is the same. I also have a custom css that I didn’t include that makes the text size smaller to fit on the .75 height tiles. You probably had to increase the height a bit for standard size text to fit.

Can someone please assist with how to script a function to detect which page is currently open?
I have searched the github page and through this thread but couldn’t find anything.

I’m on it. This is what I have so far. I added a generic fully customizable popup that can be added to any tile (the tile layout and contents can change from tile to tile, to display context sensitive information). I still have some positioning problems, so I will have to dive into the CSS/less files to fix this. I’ll try to find some time this upcoming weekend to finish this.

Here’s a very early example for a popup that appears when tapping a weather tile and shows more detailed weather data and forecast. The margins and tile positioning is still wrong, but it works.

I don’t see a way to get it directly. There’s an activePage variable in main.js that keeps track of it. You could create a global variable $scope.activePage in main.js and set it to page in in the $scope.openPage function.

1 Like

isn’t it simple to use cover_toggle type and have icon change based on garage door statue closed vs open?

this is my code for garage doors in Tileboard.

              {
                 position: [0, 0], // [x, y]
                 width: 1,
                 type: TYPES.COVER_TOGGLE,
                 title: 'Garage Door 1',
                 id: 'cover.double',
                 states: {open: 'Opened', closed: 'Closed', unknown: 'Unknown'},
                 icons: {open: "mdi-garage-open", closed: "mdi-garage", unknown: "mdi-garage-alert"},
                 state: false, // hidding state
                 customStyles: function(item, entity){
                    if (entity.state === 'closed') {return {'backgroundColor': '#27B80D',};}
                    else if (entity.state === 'open') {return {'backgroundColor': '#B80D0D',};}
                    else {return { 'backgroundColor': '#FFA100',};}
                   }
              },

image

Can someone help me here on what i am doing wrong?
I have added another selection box for the fan speed on the A/C,
Everything looked ok, Works fine.
But when i added another Climate Tile I noticed, that if clicking on the fan mode,
will open the Selection box on all 3 Fans (rather only on the selected active one).
This is not the case with the A/C Mode, It works perfect).

Fan Mode:
bad

AC Mode:
good

Code:

      <div ng-if="item.type === TYPES.CLIMATE"
           class="item-entity-container">
         <div>
            <div class="item-button -bottom-left"
                 ng-if="entity.attributes.temperature && entity.state !== 'off'"
                 ng-click="increaseClimateTemp($event, item, entity)">
               <i class="mdi mdi-arrow-up-bold"></i>
            </div>
         <div class="item-button -center-left"
                 ng-if="entity.attributes.temperature && entity.state !== 'off'"
                 ng-click="decreaseClimateTemp($event, item, entity)">
               <i class="mdi mdi-arrow-down-bold"></i>
            </div>
         </div>

	  <div class="item-climate">
	   <div class="item-climate--target">
          <span ng-bind="climateTarget(item, entity)"></span>
          <span ng-if="(_unit = entityUnit(item, entity))"
                class="item-climate--target--unit" ng-bind="_unit"></span>
	   </div>
	<!-- Climate Mode (Head,Dry, Cool, Fan Only...) -->
       <div class="item-climate--mode" ng-if="entity.attributes.temperature"
	        ng-click="openSelect(item)">
          <span ng-bind="entity.state"></span>
       </div>
	<!-- Climate Fan Mode (Low, Medium High....) -->
       <div class="item-climate--mode" ng-if="entity.attributes.temperature"
	        ng-click="openSelect(seconditem)">
          <span ng-bind="entity.attributes.fan_mode"></span>		  
       </div>
	   </div>
    
    <!-- Climate Mode (Head,Dry, Cool, Fan Only...) -->
	<div ng-if="selectOpened(item)" class="item-select"
         ng-style="itemSelectStyles(entity, entity.attributes.hvac_modes)">	  

        <div class="item-select--option"
             ng-repeat="option in entity.attributes.hvac_modes track by $index"
             ng-class="{'-active': option === entity.state}"
             ng-click="setClimateMode($event, item, entity, option)">
           <span ng-bind="option"></span>
        </div>
		</div>
		
    <!-- Climate Fan Mode (Low, Medium High....) -->
	<div ng-if="selectOpened(seconditem)" class="item-select"
         ng-style="itemSelectStyles(entity, entity.attributes.fan_modes)">	  

        <div class="item-select--option"
             ng-repeat="option in entity.attributes.fan_modes track by $index"
             ng-class="{'-active': option === entity.attributes.fan_mode}"
             ng-click="setClimateMode($event, item, entity, option)">
           <span ng-bind="option"></span>
        </div>
    </div>
    </div>

how to work with more than one js file? example leaving a page in each file. I followed the link below, but I don’t know how to make the other file, the error.

I’m not sure there’s a easy way to do that because you’d be trying to break a single variable up.

I use what @diggerdanh posted here: https://community.home-assistant.io/t/tileboard-new-dashboard-for-homeassistant/57173/2152

You can then create the tiles using one liners and you can pass multiple elements and the function will parse through them all.

I use angular.merge with mine as I need to set variables in a function to what I pass to the build tile function then merge the objects together. I was able to reduce my code by at least 2000 lines using this method. When I get back, I’ll post sample code.

Hi, how did you blur the background of the popup?

Sample code that I use to display 16 camera tiles.

tiles = {};

tiles.cameras_house = {
    height: .75,
    width: 1.08,
    type: TYPES.CAMERA,
    id: {},
    bgSize: 'cover',
    state: false,
}

function buildCameraTile( thumb, video, tileDefinition, options ){                    
    var z = {
        fullscreen: {
            type: TYPES.CAMERA,
            id: {},
            bgSize: 'contain',
            filter: function () {
                return video;
            },
        },
        filter: function () {
            return thumb;
        }
    };
                
    var tile = angular.copy( tileDefinition );
    Object.keys( options ).forEach( k => {
       tile[ k ] = options[ k ]
    } );

    angular.merge( tile , z );

    return tile;
};
...
buildCameraTile( 'http://192.168.10.12/TileBoard/CameraThumbs/camera05.jpg', 'http://192.168.6.10:8090/channel05.mjpeg', tiles.cameras_house, { position: [0, 0], title: '05 A/C Units' } ),
buildCameraTile( 'http://192.168.10.12/TileBoard/CameraThumbs/camera14.jpg', 'http://192.168.6.10:8090/channel14.mjpeg', tiles.cameras_house, { position: [0, .75], title: '14 Attached Garage' } ),
buildCameraTile( 'http://192.168.10.12/TileBoard/CameraThumbs/camera15.jpg', 'http://192.168.6.10:8090/channel15.mjpeg', tiles.cameras_house, { position: [0, 1.5], title: '15 Basement' } ),
...

You can also pass additional name/value pairs that your tile supports: { position: [0, 1.5], title: ‘15 Basement’ , refresh: 30000 , name: value, etc: ‘etc’ }

1 Like