Groovy Access to Dashboard JSON

Background

I have several dashboards and worked very hard to ensure that all of the mobile formatted dashboards are consistent and that all desktop dashboards are consistent. I always did this by fussing over one and then copying the JSON to the other dashboards. Quite painful when you have a lot of dashboards.

I’m building (trying to) an app that will allow the user to select a Dashboard to use as a layout template and then push the relevant JSON layout pieces to target Dashboards.

What I’ve Done so Far

Parent App created. Allows for multiple template definitions to be saved as children (e.g., mobile template, desktop template, guest template, etc.).

Child App in progress.

  1. Creates a Layout Template child
  2. Allows user to name the Layout Template
  3. Allows user to select the Dashboard to be used as the Layout Template (source)
  4. Allows user to select the Destination Dashboards (targets)
  5. Added the Apply button and stubbed out that code.

What’s Missing

From here I need a way to read the JSON from the source Dashboard and write the relevant JSON to the Target Dashboards. Making the modifications to the JSON shouldn’t be a challenge as far as I can see.

So, does anyone know how to get and put Dashboard JSON?

Happy to share the code if it would be easier. It’s a hot mess at the moment, but will do if requested.

2 Likes

Are you looking to tap into the Event Stream (web socket) that dashboards use?

I've used WebSocket Client on my Mac to monitor the stream. Output (in JSON) looks like:

{
  "installedAppId" : 0,
  "type" : "null",
  "deviceId" : 2644,
  "descriptionText" : "Office WallSwitch was turned on",
  "unit" : "null",
  "hubId" : 0,
  "value" : "on",
  "source" : "DEVICE",
  "name" : "switch",
  "displayName" : "Office WallSwitch"
}
{
  "installedAppId" : 0,
  "type" : "null",
  "deviceId" : 2646,
  "descriptionText" : "MultiSenDomeU (office3) motion is inactive",
  "unit" : "null",
  "hubId" : 0,
  "value" : "inactive",
  "source" : "DEVICE",
  "name" : "motion",
  "displayName" : "MultiSenDomeU (office3)"
}

No, at least, not yet. I'm trying to get the layout JSON.
If you go to the dashboards gear Settings > Advanced > Layout, you can see the layout JSON there. If I can grab that, switch out the tiles and the name, and send it to the target dashboard, the target will be formatted exactly as the source.

{
"localization": "English",
"roundedCorners": 0,
"dateFormat": "MM/DD/YYYY",
"hideLabels": false,
"customColors": [
{
"template": "texttile",
"bgColor": "rgb(7,255,98)",
"iconColor": "",
"state": "default",
"customIcon": ""
},
{
"template": "dashboard",
"bgColor": "rgb(0,0,255)",
"iconColor": "",
"state": "default",
"customIcon": ""
}
],

I'd suggest looking at @thebearmay code of his Hub Information Driver for how he pulls from the hub itself.

https://raw.githubusercontent.com/thebearmay/hubitat/main/hubInfoV3.groovy

Just remember to acknowledge him if you use pieces.

Thanks, I'll check that out.

Wow, that's a master class! Lot's of good stuff in there, and I can see how he's getting into paths. Still don't know how to find the path of the dashboard JSON.

A shot in the dark here but have you seen this post by @sburke781
[RELEASE] Simple CSS Editor - :gear: Custom Apps and Drivers - Hubitat

All good stuff and I'd see that as working handily with what I'm trying to accomplish. I'm looking for a call that seems like it would be similar to the below. Obviously the path is wrong, that is what I'm trying to get to.

try {
def params = [
uri: 'http://127.0.0.1:8080',
path: '/hub/dashboard/Dashboard Name/layout', // don't know the path to call here to get the JSON

Worth a shot @amithalp , but unfortunately my CSS editor is not quite what @diySmartHomeGuy need's in this case.

If I had more time (before work) I could dig into this some more. I had to play around with this when I was trying to fix the Smartly drag and drop feature. I could kind of see how the layout JSON was being read, but never quite got to the bottom of how it was being written.

With a little interpretation of that code (without wanting to share it entirely).... This might help....

In your Groovy code try:

  • Create a HTTP Get Request
  • Set the URL to be the same path as the dashboard (minus the search params in the URL, i.e. the access token), then add /layout at the end of this URL string
  • In the headers:
    • Set the mime-type to application/json
    • Set a header of Authorization to 'Bearer ' + < the-dashboard-access-token >

Now there's a few things in there that I still don't know how you are going to do, like getting the access token, but hopefully it gives you a starting point for the HTTP request structure.

Saving the JSON would take a little more digging by me if you are still stuck.

Simon

Also, really interested in this project. I have grand plans for a dashboard app for various tools, like the CSS editor @amithalp linked earlier. Things like this, or the options it may provide would be a great addition.

Was it the Smartly App that pulled the JSON from the HE dashboard? Although not sure the app is still available..

It was the Smartly Inject, not the web site, you have to copy and paste the layout JSON there. The Drag and Drop (Inject) device added some Javascript to your dashboard that then accessed the layout JSON to allow you to drag and drop the tiles before then writing the changes back to the layout.

When there were some issues recently I chose to take it over, though haven't really done anything in the end as it started to work for people magically again :slight_smile:

2 Likes

Ok... SQUIRL!
Not sure how I missed the Smartly Drag and Drop. Forgive my ADD as I played around with this. Drag and Drop on HE dashboards - grinning ear to ear.

I'll start digging around in this (after the Sunday honey-do is done). In the mean time, here are links to my project FWIW

2 Likes

@sburke781 I'll keep updating on the progress. I'm sure I can get there. Should be pretty clean and simple when finished.

1 Like

@sburke781 This makes sense. I didn't see anything in the .groovy file that would carry that load. The JS file is enormous and de-\n\r'd so not very readable. The trudging continues.

Yeah they must have mini-fied it. I think I was able to reformat it at one point using Notepad++, but the variable names and other abbreviations you can't do anything about.

Use an HTTP GET:

http://<Hub Ip>/apps/api/<Dashboard App AppId>/dashboard/<Dashboard AppId>/layout?access_token=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
4 Likes

Thanks @thebearmay. I see how I can use this to read the JSON. Got it to work in a browser, but not yet from the Groovy code. I'll keep working that in the mean time.

Is there a corresponding PUT command - I'm going to want to write the modified JSON back to the file. Not seeing how that would work.

I have an idea on how that might work, but need to do a couple of packet captures to check my theory. Should have some time in the morning to take a crack at it.

This code should be close:

Store JSON code
def storeJson(String jsonString, String access_token, Integer dashApp-AppId, Integer dash-AppId){
	if(security) cookie = getCookie()  //if using hub security
	Map requestParams =
	[
        uri: "http://127.0.0.1/apps/api/${dashApp-AppId}/dashboard\${dash-AppId}/layout",
        headers: [
            Authorization: "Bearer ${access_token}",
            requestContentType: 'application/json',
		    contentType: 'application/json',
			"Cookie": cookie,
			body: "$jsonString"
        ]
	]
	
	asynchttpPost("postResp", requestParams)
}

def postResp(resp, data){  

}

String getCookie(){
    try{
  	  httpPost(
		[
		uri: "http://127.0.0.1:8080",
		path: "/login",
		query: [ loginRedirect: "/" ],
		body: [
			username: username,
			password: password,
			submit: "Login"
			]
		]
	  ) { resp -> 
		cookie = ((List)((String)resp?.headers?.'Set-Cookie')?.split(';'))?.getAt(0) 
        if(debugEnable)
            log.debug "$cookie"
	  }
    } catch (e){
        cookie = ""
    }
    return "$cookie"
}
2 Likes