[RELEASE] HD+ - Android Dashboard

@jpage4500 heya - I'm back seeking ideas -
I recently have been experimenting with a tool called Tile Builder and its been very useful - the modules of TB allow me to condense large arrays of tiles into more useful, manageable layouts without me having to customize the tile drivers that the original devs create. I've literally cut 20+ tiles out of my dashboards and created a more cohesive layout and design I like.
The author of the app relies on his insane knowledge of CSS. To that end, he extends HE tiles by burying custom CSS inside the HE dashboard - and I really dislike HE dashboard to put it mildly.
I use your app exclusively for production environment - but I am dabbling on HE dashboard as well. TB - Rooms has a custom CSS you cut and paste over. How can I implement this?
I considered hidden tiles that have the css class stuff, tried external files via http calls, - I can't think of a method.
Is this going to prompt a new Tile type from you which is Imho to big an ask, or can you think of a way I can implement with the tools already built? Thanks for your time!

Not sure if this is of value : here is Tile Builder on HE and the equivalent on HD+


image

Shu

Can you send me the JSON that's returned via MakerAPI for this device? If it's > 1024 characters you'd also have to send me the html that's returned in the http call as well since I won't be able to see that.

I have to imagine that it can be displayed in a web tile but I can try and figure out if there's other ways to display it too

not sure how to pull the json from bluestacks - I think this is it. my text editor says its 958 in length.
{
"date": "2023-09-22T19:05:48",
"device_id": "246",
"isStateChange": "true",
"label": "Tile Builder Storage Device 2",
"name": "tile2",
"source": "DEVICE",
"value": "\u003chead\u003e\u003cstyle\u003e.BbC,.BbC\u003e*{position:absolute;transform:translate(-50%,-50%);color:#000}.BbB{width:100%;height:100%;border:10px Inset;border-color:#daa #c88;background:#debF;overflow:visible;box-sizing:border-box}.BbB{top:50%;left:50%;}.BbB \u003e*{display:flex;padding:3px}.BbT{left:50%;top:50%;color:#000;z-index:-2}.Bb1{left:10%;top:10%}.Bb2{left:20%;top:20%}.Bb3{left:50%;top:50%}\u003c/style\u003e\u003c/head\u003e\u003cdiv class\u003d\u0027TB BbB BbC\u0027\u003e\u003cdiv class\u003d\u0027BbT \u0027\u003eRoom Name\u003c/div\u003e\u003cdiv class\u003d\u0027Bb1 B5\u0027\u003e🔋\u003c/div\u003e\u003cdiv class\u003d\u0027Bb2 B5\u0027\u003e🅰\u003c/div\u003e\u003cdiv class\u003d\u0027Bb3 \u0027\u003e❓\u003c/div\u003e\u003c/div\u003e"
}
Not sure the html text grab will display here here is direct from the tile as well as a screensnap of that.
[head][style].qqC,.qqC]{position:absolute;transform:translate(-50%,-50%);color:#000}.qqB{width:100%;height:100%;background:#deb0;overflow:hidden;z-index:2}.qqB{top:50%;left:50%;}.qqB ]{display:flex;padding:1px}.qqI,.qqJ{transform:none}.qqT{left:50%;top:80%;color:#000;z-index:-2}.qqI{top:70%;color:#000;left:calc(50% + 20% / 2);transform:translateX(-50%) !important}.qq1{left:55%;top:35%}.qq2{left:90%;top:10%}.qq3{left:90%;top:40%}.qq4{left:90%;top:50%}[/style][/head][div class='TB qqB qqC'][div class='qqT a1 BS5 S3']Guest Bathroom [/div][div class='qqI B10 BS3 S3']:level_slider:99% [br]:thermometer:74.5° [br]:droplet:50% RH[/div][div class='qq1 ']:bulb:[br]:bulb:[br]:bulb:[/div][div class='qq2 ']:person_standing:[/div][div class='qq3 B5 S1']:electric_plug:[/div][div class='qq4 o0 B5 S1']:thermometer:[/div][/div]

/* 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}

Finally - here is the css the TB author wants embedded in the HE dashboard css - where his magic comes from so to speak.

/* 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;}

^^ FWIW - if you put all of the html/code into a code block it will look much better. Use 3 backtick characters to start and end the code block (example)

Sorry, I forgot to include this link to get the JSON for this device. Basically you get it from the Hub's MakerAPI page. That's what HD+ gets to display the devices

I'm not sure where all of that CSS code comes from either - it must be getting pulled in using a link back to the hub I have to imagine. Anyway, hopefully with the full JSON response I'll try to figure it out

apologies on the messy post. I used to know how to streamline!

The css chunk - in Tile Builder - Rooms it looks like this (the bolded text is the key:)

So on any HE dashboard (I don't use HE dashboards generally) its that advanced thingy - its also the currently suggested way for background images...

I'm starting to understand how this works although I could use some help getting it created -- I guess I just need to spend more time trying to figure it out.

Anyway, if I can get something basic working I'll look into how to support these room tiles in HD+. Ultimately I think I'll need to inject css into the WebView (html renderer)

it was my thought too. at least at the individual tile. I'm ever hopeful!

@jpage4500: Just wanted to install and setup HD+ on a different device. If I press "Discover hub" I get the error message "Login failure. Maker API not found".

Maker API is installed and running fine.

Any ideas?
Thanks

The Discover button uses UPNP to try and 'find' your hub on the network. Typically this works great for me on any device but once and a while I feel like the Hub stops responding to these UPNP requests and requires a reboot. It might be a coincidence but I've seen it happen after an Internet outage where I reboot my router but not my Hubitat..

There's a free app which shows you what devices 'reply' back to this UPNP request on your network. You should see the Hub in there

EDIT: Actually, I updated to the latest Hubitat version and 'Discover' isn't working for me either. It finds the Hub but not the app ID for MakerAPI.. something probably changed in the HTML so I'll look into and try to fix it!

1 Like

Perfect thanks man, that's amazing. Can't use Hubitat without HD+.

2 Likes

I fixed it and it'll be in the next version. In the meantime if this happens it should still set the IP address of the Hub. If you click on "MANUAL ENTRY" -> "ADVANCED" and enter the Maker API App ID and hit LOGIN

1 Like

I cannot get HD+ to talk to the "My Location" device. And I am not sure what else to do to troubleshoot this. I have the "My Location" device I installed from the Hubitat Package Manager. The Phone App has all the permissions it needs including activity. I set the location to "home" on the driver and in the app. Setting my GPS in the HD+ App was difficult. I had to copy and paste the GPS numbers from google maps because it gave me no way to set them from the app. It showed my location but would put the pin somewhere else far. But I did work through that. There is that option to point the location thing at a switch. I created a virtual switch called "home" and pointed it at that. This also is not getting triggered.

I would appreciate any troubleshooting steps I might take.

Hi @TheFatherMind, there is a special driver for the location stuff, it's in HPM now called myLocation, which it looks like you have. But you need to create a virtual device using that driver. Then in HD+ set the location device to this one and you should be all set, it will not work with a virtual switch.

NOTE: If you want to use the presence feature then be sure to exactly match (case and all) the name of your location zone in HD+ to the 'presence' preference in the driver. If the driver detects an exact match then presence is set.

yes yes I have done ALL of that.
It is the Virtual Device using the "My Location" driver that is not working, as stated I installed that from the HPM and it said it was by Mark Weninger for HD+ in the description. NOTHING in the event history... Just always says I am away. As to the Virtual Switch.. I did that because the location setting seems to want me to select a device just to the right of the longitude and latitude. I did not understand the point of that and it was not seeing the virtual presence device in the list so I created a virtual switch to point it at. I had set the Virtual Device to "home" in lower case just as had it set in the app. I suppose I can put together screenshots of all this and link an album.

OK, it sounds like you're close. I think there may just be a cross up with the location and presence features.

There are 2 ways to do presence, your choice, based on your needs...

In the HD+ presence tracking area where you setup all your zones you have the ability to select an individual virtual presence device for each zone which will be present or away based on that specific zone. Also in this area you can name the zones. This area/feature does not have any affect or connection to the myLocation device.

In the Location Tracking area in HD+ you can set 1 device, the myLocation device. This will provide lat/lon updates, and other info, as well as presence for 1 zone (if desired). For this to work the name of the zone location (set in the HD+ presence tracking section) must match the setting in the myLocation driver called 'Presence Zone Name'. If they exactly match then you're deemed 'present'.

It does not reflect it in the pictures but I did delete the location and set it again. This time with no "Home" button. Also, oddly, when I set it the second time it did grab the GPS location and pin it for me with no trouble. I also set the "home" setting from the "Merlins Phone HD" icon on the HD+ desktop as well. But still is not reporting that I am home.








Maybe this is the problem... what version of HD+ are you using? In the 'more settings' section I don't see 'Location Tracking' listed. The latest is v1.0.2176, this is what the more settings screen should look like.

1 Like

Also still getting the keyboard popup randomly on my pad. Any progress on that error?

I was on version 1.0.2094 that I got from Play Store.
I upgraded to the apk at the top of this post. That resolved the problem.

2 Likes