definition(
name: "Hubigraphs",
namespace: "tchoward",
author: "Thomas Howard",
description: "Hubitat Graph Creator Parent App",
category: "My Apps",
installOnOpen: true,
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png",
iconX3Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png")
//V 1.0 Ordering, Color and Common API Update
//V 1.8 Smoother sliders, bug fixes
//V 2.0 Support for Time Graphs
//V 2.1 Support for Heatmaps
//V 3.3 Radar Tiles
//V 0.1 Beta - Weather Tiles 2
preferences {
// The parent app preferences are pretty simple: just use the app input for the child app.
page(name: "mainPage", title: "Graph Creator", install: true, uninstall: true, submitOnChange: true)
page(name: "setupOpenWeather", title: "Setup Open Weather", nextPage: "mainPage")
page(name: "setupLongTermStorage", title: "Setup Long Term Storage")
}
def call(Closure code) {
code.setResolveStrategy(Closure.DELEGATE_ONLY);
code.setDelegate(this);
code.call();
}
def getLongTermStorage() {
lts = getChildAppByLabel("Hubigraph Long Term Storage")
}
def getEvents(Map map){
sensor = map.sensor;
attribute = map.attribute;
then = map.start_time;
respEvents = sensor.statesSince(attribute, then, [max: 2000]).collect{[ date: it.date, value: it.value]}
respEvents = respEvents.flatten();
respEvents = respEvents.reverse();
return respEvents;
}
def getData(sensor, attribute, lts, time){
return_data = [];
then = time;
if (lts) {
ltsApp = getChildAppByLabel("Hubigraph Long Term Storage");
return_data = ltsApp.getFileData(sensor, attribute);
if (return_data.size()> 0)
then = return_data[return_data.size()-1].date;
}
events = getEvents(sensor: sensor, attribute: attribute, start_time: then);
return_data = (return_data << events).flatten();
return return_data;
}
def ltsAvailable(sensor, attribute){
lts = getChildAppByLabel("Hubigraph Long Term Storage");
if (lts!=null){
return lts.isStorage(sensor, attribute);
}
else {
return false;
}
}
def mainPage(){
if (latitude && longitude && apikey) {
childDevice = getChildDevice("OPEN_WEATHER${app.id}");
log.debug(childDevice);
if (!childDevice) {
device_name="Open Weather Child Device";
log.debug("Creating Device $device_name");
childDevice = addChildDevice("tchoward", "OpenWeather Hubigraph Driver", "OPEN_WEATHER${app.id}", null,[completedSetup: true, label: device_name])
log.info "Successfully Created Child"
}
if (childDevice) {
childDevice.setupDevice(latitude: latitude, longitude: longitude, apiKey: apikey, pollInterval: open_weather_refresh_rate);
childDevice.pollData();
}
}
dynamicPage(name: "mainPage"){
section {
app(name: "hubiStorage", appName: "Hubigraph Long Term Storage", namespace: "tchoward", title: "Setup Long Term Storage", multiple: true)
}
section {
app(name: "hubiWidget", appName: "Hubigraph Bubble Widget", namespace: "tchoward", title: "Create New Bubble Widget", multiple: true)
app(name: "hubiBarGraph", appName: "Hubigraph Bar Graph", namespace: "tchoward", title: "Create New Bar Graph", multiple: true)
app(name: "hubiRangeBar", appName: "Hubigraph Range Bar", namespace: "tchoward", title: "Create New Range Bar", multiple: true)
app(name: "hubiGraphTime", appName: "Hubigraph Time Line", namespace: "tchoward", title: "Create New Time Line", multiple: true)
app(name: "hubiGauge", appName: "Hubigraph Gauge", namespace: "tchoward", title: "Create New Gauge", multiple: true)
app(name: "hubiTimeGraph", appName: "Hubigraph Time Graph", namespace: "tchoward", title: "Create New Time Graph", multiple: true)
app(name: "hubiHeatMap", appName: "Hubigraph Heat Map", namespace: "tchoward", title: "Create New Heat Map", multiple: true)
app(name: "hubiWeather", appName: "Hubigraph Weather Tile", namespace: "tchoward", title: "Create New Weather Tile", multiple: true)
app(name: "hubiForecast", appName: "Hubigraph Forecast Tile", namespace: "tchoward", title: "Create New Forecast Tile", multiple: true)
app(name: "hubiWeather2", appName: "Hubigraph Weather Tile 2", namespace: "tchoward", title: "Create New Weather Tile 2", multiple: true)
app(name: "hubiRadar", appName: "Hubigraph Radar Tile", namespace: "tchoward", title: "Create New Radar Tile", multiple: true)
}
section {
href name: "setupOpenWeather", title: "Setup Up Open Weather for Weather Tile", description: "", page: "setupOpenWeather"
}
}
}
def setupOpenWeather(){
def updateEnum = ['Manual Poll Only','1 Minute','5 Minutes', '10 Minutes', '15 Minutes', '30 Minutes', '1 Hour', '3 Hours'];
def location = getLocation();
dynamicPage(name: "setupOpenWeather"){
section{
input( type: "enum", name: "open_weather_refresh_rate", title: "Select OpenWeather Update Rate", multiple: false, required: false, options: updateEnum, defaultValue: "5 Minutes");
input( type: "text", name: "latitude", title:"Latitude (Default = Hub Location)", defaultValue: location.latitude);
input( type: "text", name: "longitude", title:"Longitude (Default = Hub Location)", defaultValue: location.longitude);
input( type: "text", name: "apikey", title: "OpenWeather Key", defaultValue:"", submitOnChange: true);
}
}
}
def getOpenWeatherData(){
childDevice = getChildDevice("OPEN_WEATHER${app.id}");
if (!childDevice){
log.debug("Error: No Child Found");
return null;
}
return(childDevice.getWeatherData());
}
def installed() {
log.debug "Installed with settings: ${settings}"
initialize()
}
def updated() {
log.debug "Updated with settings: ${settings}"
unsubscribe()
initialize()
}
def initialize() {
// nothing needed here, since the child apps will handle preferences/subscriptions
// this just logs some messages for demo/information purposes
log.debug "there are ${childApps.size()} child smartapps"
childApps.each {child ->
log.debug "child app: ${child.label}"
}
}
/********************************************************************************************************************************
*********************************************************************************************************************************
****************************************** NEW FORM FUNCTIONS********************************************************************
*********************************************************************************************************************************
*********************************************************************************************************************************/
def hubiForm_container(child, containers, numPerRow=1){
if (numPerRow == 0){
style = """style="margin: 0 !important; padding: 0 !important;"""
numPerRow = 1
} else {
style = "";
}
child.call(){
def html_ =
"""
"""
containers.each{container->
html_ += """
"""
html_ += container;
html_ += """
"""
}
html_ += """
"""
paragraph (html_.replace('\t', '').replace('\n', '').replace(' ', ''));
}
}
def hubiForm_subcontainer(Map map, child){
child.call(){
def containers = map.objects;
def breakdown = map.breakdown;
def html_ =
"""
"""
count = 0;
containers.each{container->
def sz_12 = 12*breakdown[count];
def sz_8 = 8*breakdown[count];
def sz_4 = 4*breakdown[count];
html_ += """
"""
html_ += container;
html_ += """
"""
count++;
}
html_ += """
"""
return (html_.replace('\t', '').replace('\n', '').replace(' ', ''));
}
}
def hubiForm_table(Map map, child){
child.call(){
def header = map.header;
def rows = map.rows;
def footer = map.footer;
def html_ = """
"""
header.each{cell->
html_ += """${cell} | """
}
html+= """
"""
count = 0;
rows.each{row->
html_ += """""";
row.each{cell->
html_ += """${cell} | """;
}
html_ += """
""";
} //rows
html_ += """""";
footer.each{cell->
html_ += """${cell} | """;
}
html_ += """
""";
html_ += """
"""
return (html_.replace('\t', '').replace('\n', '').replace(' ', ''));
}
}
def hubiForm_text(child, text, link=null){
child.call(){
def html_ = "";
if (link != null){
html_ = """${text}""";
} else {
html_ = """${text}""";
}
return html_
}
}
def hubiForm_text_format(Map map, child){
child.call(){
def text = map.text;
def halign = map.horizontal_align ? "text-align: ${map.horizontal_align};" : "";
def valign = map.vertical_align ? "vertical-align: ${map.vertical_align}; " : "";
def size = map.size ? "font-size: ${map.size}px;" : "";
def html_ = """$text
""";
return html_
}
}
def hubiForm_page_button(child, title, page, width, icon=""){
def html_;
child.call(){
html_ = """
""".replace('\t', '').replace('\n', '').replace(' ', '')
}
return html_;
}
def hubiForm_section(child, title, pos, icon="", Closure code) {
child.call(){
def id = title.replace(' ', '_').replace('(', '').replace(')','');
def title_ = title.replace("'", "").replace("`", "");
def titleHTML = """
""";
def modContent = """
""";
modContent = modContent.replace('\t', '').replace('\n', '').replace(' ', '');
section(modContent, code);
}
}
def hubiForm_enum(Map map, child){
child.call(){
def title = map.title;
def var = map.name;
def list = map.list;
def defaultVal = map.default;
def submit_on_change = map.submit_on_change;
if (settings[var] == null){
app.updateSetting ("${var}", defaultVal);
}
def actualVal = settings[var] != null ? "${settings[var]}" : defaultVal;
def submitOnChange = submit_on_change ? "submitOnChange" : "";
def html_ = """
"""
list.each{ item ->
html+= """"""
}
html_ += """
"""
return (html_.replace('\t', '').replace('\n', '').replace(' ', ''));
}
}
def hubiForm_switch(Map map, child){
child.call(){
def title = map.title;
def var = map.name;
def defaultVal = map.default;
def submit_on_change = map.submit_on_change;
def actualVal = settings[var] != null ? settings[var] : defaultVal;
def submitOnChange = submit_on_change ? "submitOnChange" : "";
def html_ = """
"""
return (html_.replace('\t', '').replace('\n', '').replace(' ', ''));
}
}
def hubiForm_text_input(child, title, var, defaultVal, submitOnChange){
child.call(){
settings[var] = settings[var] != null ? settings[var] : defaultVal;
def html_ = """
"""
return html_.replace('\t', '').replace('\n', '').replace(' ', '');
}
}
def hubiForm_font_size(Map map, child){
child.call(){
def title = map.title;
def varname = map.name;
def default_ = map.default;
def min = map.min;
def max = map.max;
def submit_on_change = map.submit_on_change;
def baseId = varname;
def varFontSize = "${varname}_font"
settings[varFontSize] = settings[varFontSize] ? settings[varFontSize] : default_;
submitOnChange = submit_on_change ? "submitOnChange" : "";
def html_ =
"""
|
Font Size: ${settings[varFontSize]}
|
"""
return (html_.replace('\t', '').replace('\n', '').replace(' ', ''));
}
}
def hubiForm_fontvx_size(Map map, child){
child.call(){
def title = map.title;
def varname = map.name;
def default_ = map.default;
def min = map.min;
def max = map.max;
def submit_on_change = map.submit_on_change;
def baseId = varname;
def weight = map.weight ? "font-weight: ${map.weight} !important;" : "";
def icon = null;
def varFontSize = "${varname}_font"
def icon_size = settings[varFontSize] ? 10*settings[varFontSize] : default_*10;
def jq = "";
if (map.icon){
icon = """
cloud
""";
jq = """jQuery('.test').css('font-size', 10*val+"px");"""
} else {
jq = """
jQuery('#${baseId}_font_size_val').css("font-size", 0.5*val+"em");
jQuery('#${baseId}_font_size_val').text(text);
"""
}
settings[varFontSize] = settings[varFontSize] ? settings[varFontSize] : default_;
submitOnChange = submit_on_change ? "submitOnChange" : "";
def html_ =
"""
"""
return (html_.replace('\t', '').replace('\n', '').replace(' ', ''));
}
}
def hubiForm_line_size(Map map, child){
child.call(){
def title = map.title;
def varname = map.name;
def default_ = map.default;
def min = map.min;
def max = map.max;
def submit_on_change = map.submit_on_change;
def baseId = varname;
def varLineSize = "${varname}_line_size"
settings[varLineSize] = settings[varLineSize] ? settings[varLineSize] : default_;
submitOnChange = submit_on_change ? "submitOnChange" : "";
def html_ =
"""
|
Width: ${settings[varLineSize]}
|
"""
return (html_.replace('\t', '').replace('\n', '').replace(' ', ''));
}
}
def hubiForm_slider(Map map, child){
child.call(){
def title = map.title;
def varname = map.name;
def default_ = map.default;
def min = map.min;
def max = map.max;
def units = map.units;
def submit_on_change = map.submit_on_change;
def fontSize;
def varSize = "${varname}"
def baseId = "${varname}";
settings[varSize] = settings[varSize] ? settings[varSize] : default_;
submitOnChange = submit_on_change ? "submitOnChange" : "";
def html_ =
"""
|
${settings[varSize]}${units} |
"""
return (html_.replace('\t', '').replace('\n', '').replace(' ', ''));
}
}
def hubiForm_color(child, title, varname, defaultColorValue, defaultTransparentValue, submit = false){
child.call(){
def varnameColor = "${varname}_color";
def varnameTransparent = "${varname}_color_transparent"
def colorTitle = "${title} Color"
def notTransparentTitle = "Transparent";
def transparentTitle = "${title}: Transparent"
settings[varnameColor] = settings[varnameColor] ? settings[varnameColor]: defaultColorValue;
settings[varnameTransparent] = settings[varnameTransparent] ? settings[varnameTransparent]: defaultTransparentValue;
def isTransparent = settings[varnameTransparent];
def html_ =
"""
${!isTransparent ? """""" : """"""}
${!isTransparent ? """
""" : ""}
""";
return (html_.replace('\t', '').replace('\n', '').replace(' ', ''));
}
}
def hubiForm_graph_preview(child){
child.call(){
if (!state.count_) state.count_ = 7;
def html_ =
"""
"""
return (html_.replace('\t', '').replace('\n', '').replace(' ', ''));
}
}
def hubiForm_sub_section(child, myText=""){
child.call(){
def id = myText.replaceAll("[^a-zA-Z0-9]", "");
def newText = myText.replaceAll("'", "").replaceAll("`", "")
def html_ =
"""
"""
return (html_.replace('\t', '').replace('\n', '').replace(' ', ''));
}
}
def hubiForm_cell(child, containers, numPerRow){
child.call(){
def html_ =
"""
"""
containers.each{container->
html_ += """
"""
html_ += container;
html_ += """
"""
}
html_ += """
"""
return (html_.replace('\t', '').replace('\n', '').replace(' ', ''));
}
}
def hubiForm_list_reorder(child, var, var_color, solid_background="") {
child.call(){
def count_ = 0;
if (settings["${var}"] != null){
list_ = parent.hubiTools_get_order(settings["${var}"]);
//Check List
result_ = true;
count_ = 0;
//check for addition/changes
sensors.each { sensor ->
id = sensor.id;
attributes = settings["attributes_${id}"];
attributes.each { attribute ->
count_ ++;
inner_result = false;
for (i=0; i
attributes = settings["attributes_${sensor.id}"];
attributes.each { attribute ->
settings["${var}"] += /"attribute_${sensor.id}_${attribute}",/
if (settings["attribute_${sensor.id}_${attribute}_${var_color}_color"] == null){
if (solid_background== ""){
settings["attribute_${sensor.id}_${attribute}_${var_color}_color"] = parent.hubiTools_rotating_colors(count_);
} else {
settings["attribute_${sensor.id}_${attribute}_${var_color}_color"] = solid_background;
}
}
count_++;
}
}
settings["${var}"] = settings["${var}"].substring(0, settings["${var}"].length() - 1);
settings["${var}"] += "]";
}
count_ = 0;
order_ = parent.hubiTools_get_order(settings["${var}"]);
order_.each { device_->
deviceName_ = parent.hubiTools_get_name_from_id(device_.id, sensors);
title_ = """${deviceName_}
${device_.attribute}
""";
title_.replace("'", "").replace("`", "");
list_data << [title: title_, var: "attribute_${device_.id}_${device_.attribute}"];
}
/**********************************************/
def var_val_ = settings["${var}"].replace('"', '"');
def html_ =
"""
"""
list_data.each{data->
color_ = settings["${data.var}_${var_color}_color"];
id_ = "${data.var}"
html_ += """
fiber_manual_record
"""
html_ += data.title;
html_ += """
"""
}
html_ += """
"""
html_ = html_.replace('\t', '').replace('\n', '').replace(' ', '');
paragraph (html_);
}
}
/********************************************************************************************************************************
*********************************************************************************************************************************
****************************************** TOOLS ********************************************************************************
*********************************************************************************************************************************
*********************************************************************************************************************************/
def hubiTool_create_tile(child, location="graph") {
child.call(){
log.info "Creating HubiGraph Child Device"
def childDevice = getChildDevice("HUBIGRAPH_${app.id}");
if (!childDevice) {
if (!device_name) device_name="Dummy Device";
log.debug("Creating Device $device_name");
childDevice = addChildDevice("tchoward", "Hubigraph Tile Device", "HUBIGRAPH_${app.id}", null,[completedSetup: true, label: device_name])
log.info "Created HTTP Switch [${childDevice}]"
//Send the html automatically
childDevice.setGraph("${state.localEndpointURL}${location}/?access_token=${state.endpointSecret}");
log.info "Sent setGraph: ${state.localEndpointURL}${location}/?access_token=${state.endpointSecret}"
}
else {
childDevice.label = device_name;
log.info "Label Updated to [${device_name}]"
//Send the html automatically
childDevice.setGraph("${state.localEndpointURL}${location}/?access_token=${state.endpointSecret}");
log.info "Sent setGraph: ${state.localEndpointURL}${location}/?access_token=${state.endpointSecret}"
}
}
}
def hubiTools_validate_order(child, all) {
child.call(){
def order = [];
sensors.eachWithIndex {sensor, idx ->
order << settings["displayOrder_${sensor.id}"];
}
//if we are initialized and need to check
if(state.lastOrder && state.lastOrder[0]) {
def remains = all.findAll { !order.contains(it) }
def dupes = [];
order.each {ord ->
if(order.count(ord) > 1) dupes << ord;
}
sensors.eachWithIndex {sensor, idx ->
if(state.lastOrder[idx] == order[idx] && dupes.contains(settings["displayOrder_${sensor.id}"])) {
settings["displayOrder_${sensor.id}"] = remains[0];
app.updateSetting("displayOrder_${sensor.id}", [value: remains[0], type: "enum"]);
remains.removeAt(0);
}
}
}
//reconstruct order
order = [];
sensors.eachWithIndex {sensor, idx ->
order << settings["displayOrder_${sensor.id}"];
}
state.lastOrder = order;
}
}
def hubiTools_rotating_colors(c){
ret = "#FFFFFF"
color = c % 13;
switch (color){
case 0: return hubiTools_get_color_code("RED"); break;
case 1: return hubiTools_get_color_code("GREEN"); break;
case 2: return hubiTools_get_color_code("BLUE"); break;
case 3: return hubiTools_get_color_code("MAROON"); break;
case 4: return hubiTools_get_color_code("YELLOW"); break;
case 5: return hubiTools_get_color_code("OLIVE"); break;
case 6: return hubiTools_get_color_code("AQUA"); break;
case 7: return hubiTools_get_color_code("LIME"); break;
case 8: return hubiTools_get_color_code("NAVY"); break;
case 9: return hubiTools_get_color_code("FUCHSIA"); break;
case 10: return hubiTools_get_color_code("PURPLE"); break;
case 11: return hubiTools_get_color_code("TEAL"); break;
case 12: return hubiTools_get_color_code("ORANGE"); break;
}
return ret;
}
def hubiTools_get_color_code(input_color){
new_color = input_color.toUpperCase();
switch (new_color){
case "WHITE" : return "#FFFFFF"; break;
case "SILVER" : return "#C0C0C0"; break;
case "GRAY" : return "#808080"; break;
case "BLACK" : return "#000000"; break;
case "RED" : return "#FF0000"; break;
case "MAROON" : return "#800000"; break;
case "YELLOW" : return "#FFFF00"; break;
case "OLIVE" : return "#808000"; break;
case "LIME" : return "#00FF00"; break;
case "GREEN" : return "#008000"; break;
case "AQUA" : return "#00FFFF"; break;
case "TEAL" : return "#008080"; break;
case "BLUE" : return "#0000FF"; break;
case "NAVY" : return "#000080"; break;
case "FUCHSIA" :return "#FF00FF"; break;
case "PURPLE" : return "#800080"; break;
}
}
def hubiTools_get_name_from_id(id, sensors){
def return_val = "Error"
sensors.each { sensor ->
if (id == sensor.id) {
return_val = sensor.displayName;
}
}
return return_val;
}
def hubiTools_get_order(order){
split_ = order.replace('"', '').replace('[', '').replace(']', '').replace("attribute_", "").split(',');
list_ = [];
split_.each{device->
sub_ = device.split('_');
list_ << [id: sub_[0], attribute:sub_[1]];
}
return list_;
}
def hubiTools_check_list(child, sensors, list_){
result = true;
count_ = 0;
//check for addition/changes
sensors.each { sensor ->
id = sensor.id;
attributes = settings["attributes_${sensor.id}"];
attributes.each { attribute ->
count_ ++;
inner_result = false;
for (i=0; i