@@ -501,6 +501,23 @@ class HouseCard extends LitElement {
501501 this . _sizeGridCanvas ( ) ;
502502 }
503503
504+ /**
505+ * Returns the tight bounding box of all rooms on a floor, in grid cells.
506+ * Falls back to the full floor dimensions if there are no rooms.
507+ * { minCol, minRow, usedCols, usedRows }
508+ */
509+ _floorBounds ( floor ) {
510+ const rooms = floor ?. rooms || [ ] ;
511+ if ( ! rooms . length ) {
512+ return { minCol : 0 , minRow : 0 , usedCols : floor ?. cols || 1 , usedRows : floor ?. rows || 1 } ;
513+ }
514+ const minCol = Math . min ( ...rooms . map ( r => r . col ) ) ;
515+ const minRow = Math . min ( ...rooms . map ( r => r . row ) ) ;
516+ const maxCol = Math . max ( ...rooms . map ( r => r . col + r . width ) ) ;
517+ const maxRow = Math . max ( ...rooms . map ( r => r . row + r . height ) ) ;
518+ return { minCol, minRow, usedCols : maxCol - minCol , usedRows : maxRow - minRow } ;
519+ }
520+
504521 _sizeGridCanvas ( ) {
505522 const wrapper = this . shadowRoot ?. querySelector ( '.grid-wrapper' ) ;
506523 const canvas = this . shadowRoot ?. querySelector ( '.grid-canvas' ) ;
@@ -511,12 +528,13 @@ class HouseCard extends LitElement {
511528 const wW = wrapper . clientWidth ;
512529 const wH = wrapper . clientHeight ;
513530 if ( ! wW || ! wH ) return ;
531+ // Use trimmed bounding box for sizing so empty rows/cols don't waste space
532+ const { usedCols, usedRows } = this . _floorBounds ( floor ) ;
514533 // Visual height after rotateX(38deg) = DOM_height × cos(38°)
515- // DOM_height = canvas_width × rows/cols
534+ // DOM_height = canvas_width × usedRows/usedCols
516535 // Constrain so visual height ≤ wrapper height:
517- // canvas_width ≤ wH × cols / (rows × cos(38°))
518- const cos38 = Math . cos ( 38 * Math . PI / 180 ) ; // ≈ 0.788
519- const maxW = Math . floor ( Math . min ( wW , wH * floor . cols / ( floor . rows * cos38 ) ) ) ;
536+ const cos38 = Math . cos ( 38 * Math . PI / 180 ) ; // ≈ 0.788
537+ const maxW = Math . floor ( Math . min ( wW , wH * usedCols / ( usedRows * cos38 ) ) ) ;
520538 canvas . style . width = maxW + 'px' ;
521539 }
522540
@@ -774,7 +792,7 @@ class HouseCard extends LitElement {
774792 return out ;
775793 }
776794
777- _renderThermalLayer ( floor ) {
795+ _renderThermalLayer ( floor , minCol = 0 , minRow = 0 , usedCols = null , usedRows = null ) {
778796 const mode = this . _getHeatmapMode ( ) ;
779797 if ( mode === 'off' ) return '' ;
780798
@@ -783,17 +801,23 @@ class HouseCard extends LitElement {
783801 const range = floor . temperature_range || this . _config . temperature_range || [ 16 , 26 ] ;
784802 const humFloor = this . _config . humidity_floor ?? 50 ;
785803
804+ const vbCols = usedCols ?? floor . cols ;
805+ const vbRows = usedRows ?? floor . rows ;
806+
786807 const enriched = ( floor . rooms || [ ] )
787808 . filter ( r => r . heatmap !== false )
788809 . map ( r => {
789810 const t = parseFloat ( this . _getEntityState ( r . entities ?. temperature ) ?. state ) ;
790811 const h = parseFloat ( this . _getEntityState ( r . entities ?. humidity ) ?. state ) ;
791- return { ...r , _temp : isNaN ( t ) ? null : t , _hum : isNaN ( h ) ? null : h } ;
812+ // Offset room coordinates to the trimmed origin
813+ return { ...r ,
814+ col : r . col - minCol , row : r . row - minRow ,
815+ _temp : isNaN ( t ) ? null : t , _hum : isNaN ( h ) ? null : h } ;
792816 } ) ;
793817
794818 return svg `
795819 < svg class ="thermal-svg "
796- viewBox ="0 0 ${ floor . cols * 100 } ${ floor . rows * 100 } "
820+ viewBox ="0 0 ${ vbCols * 100 } ${ vbRows * 100 } "
797821 preserveAspectRatio ="none "
798822 xmlns ="http://www.w3.org/2000/svg ">
799823
@@ -1180,25 +1204,28 @@ class HouseCard extends LitElement {
11801204 if ( ! floor || ! floor . cols || ! floor . rows ) {
11811205 return html `< div class ="no-floor "> Floor not configured</ div > ` ;
11821206 }
1183- const mode = this . _getHeatmapMode ( ) ;
1184- const rooms = floor . rooms || [ ] ;
1185- const cellWPct = 100 / floor . cols ;
1186- const cellHPct = 100 / floor . rows ;
1207+ const mode = this . _getHeatmapMode ( ) ;
1208+ const rooms = floor . rooms || [ ] ;
11871209 const sunStyle = this . _getSunStyle ( ) ;
11881210
1211+ // Trim canvas to the tight bounding box of actual rooms
1212+ const { minCol, minRow, usedCols, usedRows } = this . _floorBounds ( floor ) ;
1213+ const cellWPct = 100 / usedCols ;
1214+ const cellHPct = 100 / usedRows ;
1215+
11891216 return html `
11901217 < div class ="grid-wrapper ">
11911218 < div class ="grid-canvas "
1192- style ="aspect-ratio:${ floor . cols } /${ floor . rows } ; ${ sunStyle } "
1219+ style ="aspect-ratio:${ usedCols } /${ usedRows } ; ${ sunStyle } "
11931220 data-heatmap ="${ mode } ">
1194- ${ this . _renderThermalLayer ( floor ) }
1195- ${ rooms . map ( room => this . _renderRoom ( room , cellWPct , cellHPct ) ) }
1221+ ${ this . _renderThermalLayer ( floor , minCol , minRow , usedCols , usedRows ) }
1222+ ${ rooms . map ( room => this . _renderRoom ( room , cellWPct , cellHPct , minCol , minRow ) ) }
11961223 </ div >
11971224 </ div >
11981225 ` ;
11991226 }
12001227
1201- _renderRoom ( room , cellWPct , cellHPct ) {
1228+ _renderRoom ( room , cellWPct , cellHPct , minCol = 0 , minRow = 0 ) {
12021229 const lightOn = room . entities ?. light ? this . _isLightOn ( room . entities . light ) : false ;
12031230 const occupied = room . entities ?. occupancy ? this . _isOccupied ( room . entities . occupancy ) : false ;
12041231 const temp = room . entities ?. temperature ? this . _getTemperature ( room . entities . temperature ) : null ;
@@ -1208,8 +1235,8 @@ class HouseCard extends LitElement {
12081235 const detections = this . _getDetections ( room . entities ?. detections ) ;
12091236
12101237 const gap = 0.6 ;
1211- const left = `${ room . col * cellWPct + gap } %` ;
1212- const top = `${ room . row * cellHPct + gap } %` ;
1238+ const left = `${ ( room . col - minCol ) * cellWPct + gap } %` ;
1239+ const top = `${ ( room . row - minRow ) * cellHPct + gap } %` ;
12131240 const width = `${ room . width * cellWPct - gap * 2 } %` ;
12141241 const height = `${ room . height * cellHPct - gap * 2 } %` ;
12151242
0 commit comments