[RELEASE] Tile Builder - Build Beautiful Dashboards

If your driver reports two decimal places then you can choose not to modify it and leave it at two. But realistically I've yet to come across any device that would is accurate to two decimal places and in many cases accuracy to one decimal place is iffy.

Grid is the most versatile of the HTML table solutions and does handle the presentation and formatting of all kinds of Hub variables, including those that are not configured with a variable connector.

To display Hub variables in the other Tile Builder products they must be configured with a variable connector to make them appear as a device.

Yes, that is correct.

Yes, I believe so. Rooms Tiles use X,Y positioning of the elements so they all contain either a
"{position:absolute" or a "{position:relative" string.

If you have a look at the State variables in your Tile Builder storage driver you can find the text equivalent where < > is represented as .

The Oval vs Circular is a browser variation and can be ignored.

For the alignment issue:
This is my outside mailbox tile.
image
As seen in Chrome explorer.

he word "Mailbox" is controlled by the class 'EfT' which sets the position 50% in from the left and 85% down from the top. However the alignment is set by the inherited 'Efc' class using the statement {position:absolute;transform:translate(-50%,-50%)}
The -50%,-50% moves it left 50% of its width and up 50% of it's height effectively centering the word "Mailbox" at the exact point %50 X and %85 Y.

Why isn't this working for you? I can't answer that other than to say there is some setting taking precedence. Do you have a way to view the CSS\HTML within the Webview control aking to F12 in Chrome?

Can you tell me which driver you are using and I'll configure a virtual device to test with.

The driver I am using cane be located here - amithalp/Tuta-Avatto2-thermostat (github.com)
Please note that this is the version before adding the code to populate supportedFanModes and coolingSetpoint.

perfect - I'll look for that and only add this extra css if it's there.. that should prevent loading it for the other tiles which won't use it anyway

:+1:

Hmm.. I did find a way to log the entire contents of a Webview. It was encoded (Unicode) so I had to decode it and I think it looks about right.

I do see this for the text "Living Room" in here:
<div class=%"AaT C4 U0 S6%">Living Room</div>

<html><head><style type=%"text/css%">body{color: #fff;}</style><style type=%"text/css%">/* Tile Builder Section 1 - This section controls how ALL tiles display on the Dashboard. */%n%n/* This CSS generated by Tile Builder Rooms version: Tile Builder Rooms v1.1.3 (3/21/24 @ 9:10 AM) */%n/* Make the tile background transparent and allow all the tiles to overflow to adjacent tiles.*/%n[class*='tile']{background-color:rgba(128,128,128,0) !important; overflow:visible !important}%n%n/* Hide all of the Tile Titles. */%n[class*='tile-title']{visibility:hidden}%n%n/* Make sure the contents expand to fill the entire tile and eliminate the gaps between the tiles. If there are still gaps make sure setting Gridgap on Layout tab is set to a 0 between quotation marks! */%n[class*='tile-contents']{width:calc(100% - var(--myRoomGap) ) !important; height:calc(100% - var(--myRoomGap) ) !important}%n%n/* Make sure the image tiles are configured correctly. The image fills the tile and the tiles are place in the far background. */%n.tile.image .inset-auto img {object-fit:fill}%n.tile.image {background-color: rgba(128,128,128,0) !important; z-index:-3 !important}%n%n/* Hide any classes using the 3d_rotation symbol/class and then append some visible text which has the effect of replacing it. 3d_rotation is the first in the picklist so it has been picked for convenience.*/%n[class*='3d_rotation']{visibility:hidden}%n[class*='3d_rotation']::after{color:green; visibility:visible !important;content:'%A0%A0%A0%A0'; transform:translateX(-100%) !important; position: absolute; outline: 2px dotted blue}%n%n/* Tile Builder Section 2 - Tile Classes Start Here */%n:root {--myUnderline-color:purple; --myGreen-color:lime; --myRed-color:red; --myOrange-color:orange; --myRoomGap:0px;}%n%n/* Alignment Control goes here - Prefix a  */%n.a0{transform:translate(0,-50%) !important}%n.a1{text-align:center}%n.a2{text-align:right;transform:translate(-100%,-50%) !important}%n%n/* Size Control goes here - Prefix S  */%n.S0{font-size:33%}%n.S1{font-size:50%}%n.S2{font-size:65%}%n.S3{font-size:75%}%n.S4{font-size:125%}%n.S5{font-size:150%}%n.S6{font-size:200%}%n.S7{font-size:300%}%n.S8{font-size:400%}%n.S9{font-size:500%}%n%n/* Background colors go here - Prefix B. */%n.B0{display:inline-block;background:black}%n.B1{display:inline-block;background:black;border-radius:50%}%n.B2{display:inline-block;background:white}%n.B3{display:inline-block;background:white;border-radius:50%}%n.B4{display:inline-block;background:var(--myGreen-color)}%n.B5{display:inline-block;background:var(--myGreen-color);border-radius:50%}%n.B6{display:inline-block;background:var(--myRed-color)}%n.B7{display:inline-block;background:var(--myRed-color);border-radius:50%}%n.B8{display:inline-block;background:var(--myOrange-color)}%n.B9{display:inline-block;background:var(--myOrange-color);border-radius:50%}%n.B10{display:inline-block;background:yellow}%n.B11{display:inline-block;background:yellow;border-radius:50%}%n.B12{display:inline-block;background:transparent;border-radius:50%}%n%n/* Background Gradients go here */%n.G0{background:linear-gradient(#56ab2f, #a8e063)}%n.G1{background:linear-gradient(#56ab2f, #a8e063);border-radius:50%}%n.G2{background:linear-gradient(#eb3349, #f45c43)}%n.G3{background:linear-gradient(#eb3349, #f45c43);border-radius:50%}%n.G4{background:linear-gradient(#E6C853 4%, #FCF48E 75%)}%n.G5{background:linear-gradient(#E6C853 4%, #FCF48E 75%);border-radius:50%}%n.G6{background:linear-gradient(#36d1dc, #5b86e5)}%n.G7{background:linear-gradient(#36d1dc, #5b86e5);border-radius:50%}%n.G8{background:linear-gradient(#bdc3c7, #2c3e50)}%n.G9{background:linear-gradient(#bdc3c7, #2c3e50);border-radius:50%}%n.G10{background:linear-gradient(limegreen,transparent),linear-gradient(90deg,skyblue,transparent), linear-gradient(-90deg,coral,transparent);background-blend-mode:screen;}%n/* Background Warning (background image) - Prefix W */%n.W0{background-image:repeating-linear-gradient(45deg, red 0px, red 5px, red 0px, yellow 5px, yellow 10px)}%n.W1{background-image:repeating-linear-gradient(45deg, red 0px, red 5px, red 0px, yellow 5px, yellow 10px);border-radius:50%}%n%n/* Animations go here - Prefix A */%n.A0 {animation:blink1 2s ease 0s infinite normal forwards}%n@keyframes blink1{0%,50%,100% {opacity:1}25%,75% {opacity:0.1}}%n%n.A1 {animation:bounce1 2s ease 0s infinite normal forwards}@keyframes bounce1{0%{animation-timing-function:ease-in;opacity:1;transform:translate(-50%,-50%) translateY(-45px)} 24%{opacity:1} 40%{animation-timing-function:ease-in;transform:translate(-50%,-50%) translateY(-24px)} 65%{animation-timing-function:ease-in;transform:translate(-50%,-50%) translateY(-12px)} 82%{animation-timing-function:ease-in;transform:translate(-50%,-50%) translateY(-6px)}  93%{animation-timing-function:ease-in;transform:translate(-50%,-50%) translateY(-4px)} 25%,55%,75%,87% {animation-timing-function:ease-out;transform:translate(-50%,-50%) translateY(0px)}  100%{animation-timing-function:ease-out;opacity:1;transform:translate(-50%,-50%) translateY(0px)}}.A2 {animation:fade1 2s linear 0s infinite alternate forwards}%n@keyframes fade1{0% {opacity:0.25}100% {opacity:1}}%n%n.A3 {animation:glow1 2s ease-in-out infinite alternate}%n@keyframes glow1{from {text-shadow: 0 0 10px #fff, 0 0 25px #fff, 0 0 45px #e60073;}to {text-shadow: 0 0 20px #fff, 0 0 35px #000000;}}%n%n.A4 {animation:ping1 2s ease 0s infinite normal forwards}%n@keyframes ping1{0% {opacity:0.8;transform: translate(-50%,-50%) scale(0.2)} 80%{opacity:0;transform: translate(-50%,-50%) scale(1.5)} 100%{opacity:0; translate(-50%,-50%) transform:scale(2.2)}}%n%n.A5 {animation:pulse1 2s linear 0s infinite normal forwards}%n@keyframes pulse1{0%{transform: translate(-50%,-50%) scale(.75)} 50%{transform: translate(-50%,-50%) scale(1.25)} 100%{transform: translate(-50%,-50%) scale(0.75)}}%n%n.A6{animation:slide1 2s linear 0s infinite alternate-reverse}%n@keyframes slide1{0%{transform: translate(-50%,-50%) translateX(-20px)} 100%{transform: translate(-50%,-50%) translateX(20px)}}%n%n.A7{display:inline-block;animation:spin1 3s linear 0s infinite normal forwards}%n.A8{display:inline-block;animation:spin1 2s linear 0s infinite normal forwards}%n.A9{display:inline-block;animation:spin1 1s linear 0s infinite normal forwards}%n@keyframes spin1 {0% {transform: translate(-50%,-50%) rotate(0deg); transform-origin: center center} 100% {transform: translate(-50%,-50%) rotate(360deg); transform-origin: center center} }%n%n.A10{animation:wiggle1 0.3s linear 0s infinite alternate-reverse}%n@keyframes wiggle1{transform:translate(-50%,-50%);0%, 100% {transform: rotate(-5deg);}50% {transform: rotate(5deg)}}%n%n/* ***** Effects Start Here - Prefix Varies ***** */%n/* Effects Rotations - Prefix R */%n.R0{transform:translate(-50%,-50%) rotate(45deg) !important; transform-origin:center;display:inline-block}%n.R1{transform:translate(-50%,-50%) rotate(315deg) !important;transform-origin:center;display:inline-block}%n.R2{transform:translate(-50%,-50%) rotate(90deg) !important;transform-origin:center;display:inline-block}%n.R3{transform:translate(-50%,-50%) rotate(270deg) !important;transform-origin:center;display:inline-block}%n.R4{transform:translate(-50%,-50%) rotate(180deg) !important;transform-origin:center;display:inline-block}%n%n/* Effects Opacity - Prefix O*/%n.O0{opacity:0}%n.O1{opacity:0.1}%n.O2{opacity:0.2}%n.O3{opacity:0.3}%n.O4{opacity:0.4}%n.O5{opacity:0.5}%n.O6{opacity:0.6}%n.O7{opacity:0.7}%n.O8{opacity:0.8}%n.O9{opacity:0.9}%n%n/* Effects Color - Prefix C*/%n.C0{color:var(--myRed-color) !important}%n.C1{color:var(--myGreen-color) !important}%n.C2{color:var(--myOrange-color) !important}%n.C3{color:yellow !important}%n.C4{color:blue !important}%n.C5{color:black !important}%n.C6{color:white !important}%n.C7{color:gray !important}%n.C8{color:#744735 !important}%n.C9{color:transparent !important}%n%n/* Effects Underline - Prefix U * /%n.U0{text-decoration:underline solid var(--myUnderline-color)}%n.U1{text-decoration:underline dotted var(--myUnderline-color)}%n.U2{text-decoration:underline dashed var(--myUnderline-color)}%n.U3{text-decoration:underline wavy var(--myUnderline-color)}%n%n/* Effects Z-Index - Prefix Z */%n.Z0{z-index:-1}%n.Z1{z-index:1}%n.Z2{z-index:2}%n%n/* Effects Outlines - Prefix o (Lower case is deliberate) */%n.o0{outline:2px solid black;border-radius:50%}%n.o1{outline:2px solid black}%n.o2{outline:2px solid white;border-radius:50%}%n.o3{outline:2px solid white}%n.o4{outline:2px solid var(--myRed-color);border-radius:50%}%n.o5{outline:2px solid var(--myRed-color)}%n.o6{outline:2px solid var(--myGreen-color);border-radius:50%}%n.o7{outline:2px solid var(--myGreen-color)}%n.o8{outline:2px solid var(--myOrange-color);border-radius:50%}%n.o9{outline:2px solid var(--myOrange-color)}%n.o10{outline:2px solid yellow;border-radius:50%}%n.o11{outline:2px solid yellow}%n%n/* Effects Box Shadows */%n.BS0{box-shadow:0px 0px 5px 5px var(--myRed-color)}%n.BS1{box-shadow:0px 0px 5px 5px var(--myGreen-color)}%n.BS2{box-shadow:0px 0px 5px 5px var(--myOrange-color)}%n.BS3{box-shadow:0px 0px 5px 5px Yellow}%n.BS4{box-shadow:0px 0px 5px 5px Blue}%n.BS5{box-shadow:0px 0px 5px 5px Black}%n.BS6{box-shadow:0px 0px 5px 5px White}%n.BS7{box-shadow:0px 0px 5px 5px Gray}%n.BS8{box-shadow:0px 0px 5px 5px #744735}%n%n/* Effects Text Handling goes here */%n.T0{white-space:nowrap}%n.T1{padding:0px !important}%n.T2{padding:5px !important}%n.T3{padding:10px !important}%n.T4{padding:15px !important}%n.T5{letter-spacing:2px}%n.T6{letter-spacing:3px}%n.T7{letter-spacing:5px}%n.T8{text-shadow:5px 5px 10px #f00;}%n.T9{text-shadow:5px 5px 10px #0f0;}%n.T10{white-space:nowrap;word-spacing:10px;}%n%n/* Tile Builder Classes End Here */%n</style></head><body style=%"margin: 0%; padding: 0px;%"><style>.AaC,.AaC>*{position:absolute;transform:translate(-50%,-50%);border:2px dotted red;color:#000}.AaB{width:100%;height:100%;border:20px Inset;border-color:#daa #c88;background:#debF;overflow:visible;box-sizing:border-box;z-index:0}.AaB{top:50%;left:50%;}.AaB >*{display:flex;padding:3px}.AaI,.AaJ{transform:none}.AaT{left:50%;top:50%;color:#000;z-index:0}.Aa1{left:10%;top:20%}.Aa2{left:50%;top:20%}.Aa3{left:90%;top:20%}</style><div class=%"TB AaB AaC%"><div class=%"AaT C4 U0 S6%">Living Room</div><div class=%"Aa1 O5 BS1 G1 S6 A3%">💡</div><div class=%"Aa2 O5 BS1 G1 S6 A3%">💡</div><div class=%"Aa3 O5 BS1 G1 S6 A3%">💡</div></div></body></html>

I think there is something wrong with the CSS\HTML in that code.

If I take the standard Rooms CSS and then add a Rooms tile at the end

<style>
/* This CSS generated by Tile Builder Rooms version: Tile Builder Rooms v1.1.0 (9/21/23 @ 08:24 PM) */
/* Make the tile background transparent and allow all the tiles to overflow to adjacent tiles.*/ 
[class*='tile']{background-color:rgba(128,128,128,0) !important; overflow:visible !important}

/* Hide all of the Tile Titles. */ 
[class*='tile-title']{visibility:hidden}

/* Make sure the contents expand to fill the entire tile and eliminate the gaps between the tiles. If there are still gaps make sure setting Gridgap on Layout tab is set to a 0 between quotation marks! */ 
[class*='tile-contents']{width:calc(100% - var(--myRoomGap) ) !important; height:calc(100% - var(--myRoomGap) ) !important}

/* Make sure the image tiles are configured correctly. The image fills the tile and the tiles are place in the far background. */ 
.tile.image .inset-auto img {object-fit:fill}
.tile.image {background-color: rgba(128,128,128,0) !important; z-index:-3 !important}

/* Hide any classes using the 3d_rotation symbol/class and then append some visible text which has the effect of replacing it. 3d_rotation is the first in the picklist so it has been picked for convenience.*/ 
[class*='3d_rotation']{visibility:hidden}
[class*='3d_rotation']::after{color:green; visibility:visible !important;content:'\00A0\00A0\00A0\00A0'; transform:translateX(-100%) !important; position: absolute; outline: 2px dotted blue}

/* Tile Builder Section 2 - Tile Classes Start Here */
:root {--myUnderline-color:purple; --myGreen-color:lime; --myRed-color:red; --myOrange-color:orange; --myRoomGap:0px;}

/* Alignment Control goes here - Prefix a  */
.a0{transform:translate(0,-50%) !important}
.a1{text-align:center}
.a2{text-align:right;transform:translate(-100%,-50%) !important}

/* Size Control goes here - Prefix S  */
.S0{font-size:33%}
.S1{font-size:50%}
.S2{font-size:65%}
.S3{font-size:75%}
.S4{font-size:125%}
.S5{font-size:150%}
.S6{font-size:200%}
.S7{font-size:300%}
.S8{font-size:400%}
.S9{font-size:500%}

/* Background colors go here - Prefix B. */
.B0{display:inline-block;background:black}
.B1{display:inline-block;background:black;border-radius:50%}
.B2{display:inline-block;background:white}
.B3{display:inline-block;background:white;border-radius:50%}
.B4{display:inline-block;background:var(--myGreen-color)}
.B5{display:inline-block;background:var(--myGreen-color);border-radius:50%}
.B6{display:inline-block;background:var(--myRed-color)}
.B7{display:inline-block;background:var(--myRed-color);border-radius:50%}
.B8{display:inline-block;background:var(--myOrange-color)}
.B9{display:inline-block;background:var(--myOrange-color);border-radius:50%}
.B10{display:inline-block;background:yellow}
.B11{display:inline-block;background:yellow;border-radius:50%}
.B12{display:inline-block;background:transparent;border-radius:50%}

/* Background Gradients go here */
.G0{background:linear-gradient(#56ab2f, #a8e063)}
.G1{background:linear-gradient(#56ab2f, #a8e063);border-radius:50%}
.G2{background:linear-gradient(#eb3349, #f45c43)}
.G3{background:linear-gradient(#eb3349, #f45c43);border-radius:50%}
.G4{background:linear-gradient(#E6C853 4%, #FCF48E 75%)}
.G5{background:linear-gradient(#E6C853 4%, #FCF48E 75%);border-radius:50%}
.G6{background:linear-gradient(#36d1dc, #5b86e5)}
.G7{background:linear-gradient(#36d1dc, #5b86e5);border-radius:50%}
.G8{background:linear-gradient(#bdc3c7, #2c3e50)}
.G9{background:linear-gradient(#bdc3c7, #2c3e50);border-radius:50%}
.G10{background:linear-gradient(limegreen,transparent),linear-gradient(90deg,skyblue,transparent), linear-gradient(-90deg,coral,transparent);background-blend-mode:screen;}
/* Background Warning (background image) - Prefix W */
.W0{background-image:repeating-linear-gradient(45deg, red 0px, red 5px, red 0px, yellow 5px, yellow 10px)}
.W1{background-image:repeating-linear-gradient(45deg, red 0px, red 5px, red 0px, yellow 5px, yellow 10px);border-radius:50%}

/* Animations go here - Prefix A */
.A0 {animation:blink1 2s ease 0s infinite normal forwards} 
@keyframes blink1{0%,50%,100% {opacity:1}25%,75% {opacity:0.1}}

.A1 {animation:bounce1 2s ease 0s infinite normal forwards}@keyframes bounce1{0%{animation-timing-function:ease-in;opacity:1;transform:translate(-50%,-50%) translateY(-45px)} 24%{opacity:1} 40%{animation-timing-function:ease-in;transform:translate(-50%,-50%) translateY(-24px)} 65%{animation-timing-function:ease-in;transform:translate(-50%,-50%) translateY(-12px)} 82%{animation-timing-function:ease-in;transform:translate(-50%,-50%) translateY(-6px)}  93%{animation-timing-function:ease-in;transform:translate(-50%,-50%) translateY(-4px)} 25%,55%,75%,87% {animation-timing-function:ease-out;transform:translate(-50%,-50%) translateY(0px)}  100%{animation-timing-function:ease-out;opacity:1;transform:translate(-50%,-50%) translateY(0px)}}.A2 {animation:fade1 2s linear 0s infinite alternate forwards}
@keyframes fade1{0% {opacity:0.25}100% {opacity:1}}

.A3 {animation:glow1 2s ease-in-out infinite alternate}
@keyframes glow1{from {text-shadow: 0 0 10px #fff, 0 0 25px #fff, 0 0 45px #e60073;}to {text-shadow: 0 0 20px #fff, 0 0 35px #000000;}}

.A4 {animation:ping1 2s ease 0s infinite normal forwards}
@keyframes ping1{0% {opacity:0.8;transform: translate(-50%,-50%) scale(0.2)} 80%{opacity:0;transform: translate(-50%,-50%) scale(1.5)} 100%{opacity:0; translate(-50%,-50%) transform:scale(2.2)}}

.A5 {animation:pulse1 2s linear 0s infinite normal forwards}
@keyframes pulse1{0%{transform: translate(-50%,-50%) scale(.75)} 50%{transform: translate(-50%,-50%) scale(1.25)} 100%{transform: translate(-50%,-50%) scale(0.75)}}

.A6{animation:slide1 2s linear 0s infinite alternate-reverse}
@keyframes slide1{0%{transform: translate(-50%,-50%) translateX(-20px)} 100%{transform: translate(-50%,-50%) translateX(20px)}}

.A7{display:inline-block;animation:spin1 3s linear 0s infinite normal forwards}
.A8{display:inline-block;animation:spin1 2s linear 0s infinite normal forwards}
.A9{display:inline-block;animation:spin1 1s linear 0s infinite normal forwards}
@keyframes spin1 {0% {transform: translate(-50%,-50%) rotate(0deg); transform-origin: center center} 100% {transform: translate(-50%,-50%) rotate(360deg); transform-origin: center center} }

.A10{animation:wiggle1 0.3s linear 0s infinite alternate-reverse}
@keyframes wiggle1{transform:translate(-50%,-50%);0%, 100% {transform: rotate(-5deg);}50% {transform: rotate(5deg)}}

/* ***** Effects Start Here - Prefix Varies ***** */
/* Effects Rotations - Prefix R */
.R0{transform:translate(-50%,-50%) rotate(45deg) !important; transform-origin:center;display:inline-block}
.R1{transform:translate(-50%,-50%) rotate(315deg) !important;transform-origin:center;display:inline-block}
.R2{transform:translate(-50%,-50%) rotate(90deg) !important;transform-origin:center;display:inline-block}
.R3{transform:translate(-50%,-50%) rotate(270deg) !important;transform-origin:center;display:inline-block}
.R4{transform:translate(-50%,-50%) rotate(180deg) !important;transform-origin:center;display:inline-block}

/* Effects Opacity - Prefix O*/
.O0{opacity:0}
.O1{opacity:0.1}
.O2{opacity:0.2}
.O3{opacity:0.3}
.O4{opacity:0.4}
.O5{opacity:0.5}
.O6{opacity:0.6}
.O7{opacity:0.7}
.O8{opacity:0.8}
.O9{opacity:0.9}

/* Effects Color - Prefix C*/
.C0{color:var(--myRed-color) !important}
.C1{color:var(--myGreen-color) !important}
.C2{color:var(--myOrange-color) !important}
.C3{color:yellow !important}
.C4{color:blue !important}
.C5{color:black !important}
.C6{color:white !important}
.C7{color:gray !important}
.C8{color:#744735 !important}
.C9{color:transparent !important}

/* Effects Underline - Prefix U */
.U0{text-decoration:underline solid var(--myUnderline-color)}
.U1{text-decoration:underline dotted var(--myUnderline-color)}
.U2{text-decoration:underline dashed var(--myUnderline-color)}
.U3{text-decoration:underline wavy var(--myUnderline-color)}

/* Effects Z-Index - Prefix Z */
.Z0{z-index:-1}
.Z1{z-index:1}
.Z2{z-index:2}

/* Effects Outlines - Prefix o (Lower case is deliberate) */
.o0{outline:2px solid black;border-radius:50%}
.o1{outline:2px solid black}
.o2{outline:2px solid white;border-radius:50%}
.o3{outline:2px solid white}
.o4{outline:2px solid var(--myRed-color);border-radius:50%}
.o5{outline:2px solid var(--myRed-color)}
.o6{outline:2px solid var(--myGreen-color);border-radius:50%}
.o7{outline:2px solid var(--myGreen-color)}
.o8{outline:2px solid var(--myOrange-color);border-radius:50%}
.o9{outline:2px solid var(--myOrange-color)}
.o10{outline:2px solid yellow;border-radius:50%}
.o11{outline:2px solid yellow}

/* Effects Box Shadows */
.BS0{box-shadow:0px 0px 5px 5px var(--myRed-color)}
.BS1{box-shadow:0px 0px 5px 5px var(--myGreen-color)}
.BS2{box-shadow:0px 0px 5px 5px var(--myOrange-color)}
.BS3{box-shadow:0px 0px 5px 5px Yellow}
.BS4{box-shadow:0px 0px 5px 5px Blue}
.BS5{box-shadow:0px 0px 5px 5px Black}
.BS6{box-shadow:0px 0px 5px 5px White}
.BS7{box-shadow:0px 0px 5px 5px Gray}
.BS8{box-shadow:0px 0px 5px 5px #744735}

/* Effects Text Handling goes here */
.T0{white-space:nowrap}
.T1{padding:0px !important}
.T2{padding:5px !important}
.T3{padding:10px !important}
.T4{padding:15px !important}
.T5{letter-spacing:2px}
.T6{letter-spacing:3px}
.T7{letter-spacing:5px}
.T8{text-shadow:5px 5px 10px #f00;}
.T9{text-shadow:5px 5px 10px #0f0;}
.T10{white-space:nowrap;word-spacing:10px;}

/* Tile Builder Classes End Here */
</style>

<head><style>.AaC,.AaC>*{position:absolute;transform:translate(-50%,-50%);border:2px dotted red;color:#000}.AaB{width:100%;height:100%;border:20px Inset;border-color:#daa #c88;background:#debF;overflow:visible;box-sizing:border-box;z-index:0}.AaB{top:50%;left:50%;}.AaB >*{display:flex;padding:3px}.AaI,.AaJ{transform:none}.AaT{left:50%;top:50%;color:#000;z-index:0}.Aa1{left:10%;top:20%}.Aa2{left:50%;top:20%}.Aa3{left:90%;top:20%}</style><div class=%"TB AaB AaC%"><div class=%"AaT C4 U0 S6%">Living Room</div><div class=%"Aa1 O5 BS1 G1 S6 A3%">💡</div><div class=%"Aa2 O5 BS1 G1 S6 A3%">💡</div><div class=%"Aa3 O5 BS1 G1 S6 A3%">💡</div></div></body></html>

then it displays correctly in my browser.

This is from the Hubitat dashboard with a background image applied with some controls layered on top (blue dotted boxes), neither of which will be displayed in your tile either.
image

If I were to give you some advice it would be to make the tile work by hand editing it and then work backwards into the programming of it. That is how I would approach it.

Thanks again for the help.. I might try a different approach. Instead of trying to 'inject' css into a WebView that's already displaying the tile contents, maybe I'll just combine the css and tile contents first and try displaying that.. I'll give it a shot and let you know how it goes.


A somewhat unrelated question for you.. if the attribute length is > 1024 I see you're creating a .html file and saving it to the FileManager. Then, the tile attribute will have a link to that file.. something like <iframe src=http://192.168.0.200/local/tile_123.html

That won't work when the user is remote though. I don't think there's any way to directly access the Hub filemanager remotely either unless I'm missing something. Anyway, not sure if you've already looked at any way to get this to work remotely.. 1 thought that comes to mind is creating an endpoint in the Hubitat TB app. I'm pretty sure that could be accessed both locally (http://192.168.0.200/APP-ID/generate/tile1 and remotely (https://cloud.hubitat.com/api/CLOUD-TOKEN/apps/APP-ID/generate/tile1) access options.

I don't know much about writing apps & drivers so you probably already looked at it.. but, I did add an endpoint once and it seemed pretty easy to add. I don't see much documentation on it -- this was the best I could find.

1 Like

You'll need to be on 2.3.9.xxx to serve a text file up through the cloud interface; image files won't work currently but should soon - I have pre-beta capability demonstration app sitting on my dev hub that I could push to github when I get home in a couple of hours if anyone is interested.

1 Like

Thanks, I have not really looked at endpoints. You are correct that files over 1,024 will not display on the dashboard via the internet, unless you have a VPN.

But I will have a look at it just to see what is possible.

Thanks for chiming in. Definitely interested in alternatives to ease the local\internet dilemma.

What is the performance like? Is it truly serving up a file from the file system or is it more of a dynamic text stream?

Be great to have a solution that works both local and remote that doesn't have some downside.

You’ll have to wait for the next release for the image side of this to work but the text files work as of 2.3.9.158

https://raw.githubusercontent.com/thebearmay/hubitat/main/apps/hubProxy.groovy

Purely a work in progress proof of concept, but has potential I think.

Mine are all text so no problem there. I did little bit of testing and the early results are very promising. It works well with only one issue I can see.

This image is the same file side by side on the dashboard. You can see the fidelity is pretty good but there is something about the encoding that causes the extended ascii\eMojis not to load correctly.

One thing I have found is that generally loading files in an iFrame on the dashboard tends to be a bit jerky vs loading a changed attribute which is silky smooth. What I'm thinking is whether it is possible to just load the text returned from the endpoint without it having to exist as a file. My hope is this will be faster and smoother and I would be able to eliminate the use of files. Any idea whether this is possible?

Thanks for your excellent work on this.

Is this currently working for you once you added the coolingSetpoint?

Ran across that a couple of years ago, it's due to the way that File Manager serves up the content. I have a non-standard way of retrieving the content (is anyone suprised :rofl:) that I used in another app that I could look to as providing an alternate character set. If you don't mind can paste a few of the emojis that are giving you issue so that I can test?

Yes it does

Here are a couple: :droplet: and :bulb:

May have the fix for you. Reimport the code, and turn on the Alternate Read option. (Although I get the same results using either read - might be the beta version I'm running if your experience differs.)

1 Like

Will try this afternoon. Thanks for the quick turnaround.

Edit: Unfortunately I still get the same issue even with this enabled.
image

Don't sweat it, I probably won't get to this until next week at the very best.

I have updated the Tile Builder Thermostat app to handle Thermostats that having a missing cooling or heating setpoint. I'll release it in the next few days.

Thanks for bringing the error to my attention.

1 Like

Hey Jean, this is what I had to change to make the Unicode characters display correctly in my case.
image

1 Like