/** * Hubigraph Timeline Child App * * Copyright 2020, but let's behonest, you'll copy it * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License * for the specific language governing permissions and limitations under the License. * */ // Hubigraph Gauge Change Log // V 0.1 Intial release // V 1.0 Released (not Beta) Cleanup and Preview Enabled // V 1.5 Ordering, Color and Common API Update // V 1.8 Smoother sliders, bug fixes import groovy.json.JsonOutput import java.text.DecimalFormat; def ignoredEvents() { return [ 'lastReceive' , 'reachable' , 'buttonReleased' , 'buttonPressed', 'lastCheckinDate', 'lastCheckin', 'buttonHeld' ] } def version() { return "v0.22" } definition( name: "Hubigraph Gauge", namespace: "tchoward", author: "Thomas Howard", description: "Hubigraph Gauge", category: "", parent: "tchoward:Hubigraphs", 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", ) preferences { section ("test"){ page(name: "mainPage", install: true, uninstall: true) page(name: "deviceSelectionPage", nextPage: "mainPage") page(name: "attributeConfigurationPage", nextPage: "mainPage") page(name: "graphSetupPage", nextPage: "mainPage") page(name: "enableAPIPage") page(name: "disableAPIPage") } mappings { path("/graph/") { action: [ GET: "getGraph" ] } } path("/getData/") { action: [ GET: "getData" ] } path("/getOptions/") { action: [ GET: "getOptions" ] } path("/getSubscriptions/") { action: [ GET: "getSubscriptions" ] } } def call(Closure code) { code.setResolveStrategy(Closure.DELEGATE_ONLY); code.setDelegate(this); code.call(); } def extractNumber( String input ) { val = input.findAll( /-?\d+\.\d*|-?\d*\.\d+|-?\d+/ )*.toDouble() val[0] } def deviceSelectionPage() { def supported_attrs; dynamicPage(name: "deviceSelectionPage") { parent.hubiForm_section(this,"Device Selection", 1){ input "sensor_", "capability.*", title: "Sensor", multiple: false, required: true, submitOnChange: true container = []; if (sensor_) { attributes_ = sensor_.getSupportedAttributes(); final_attrs = []; attributes_.each{attribute_-> final_attrs += attribute_.getName(); } container << parent.hubiForm_sub_section(this,"""${sensor_.getDisplayName()}""") if (final_attrs == []){ container<< parent.hubiForm_text(this, "No supported Numerical Attributes
Please select a different Sensor"); parent.hubiForm_container(this, container, 1); } else { input( type: "enum", name: "attribute_", title: "Attribute for Gauge", required: true, multiple: false, options: final_attrs, defaultValue: "1", submitOnChange: true) } } } if (attribute_){ state_ = sensor_.currentState(attribute_); if (state_ != null) { currentValue = state_.value; parent.hubiForm_section(this, "Min Max Value", 1){ container = []; container<< parent.hubiForm_text(this, "Current Value = $currentValue"); container << parent.hubiForm_text_input (this, "Minimum Value for Gauge", "minValue_", "0", false); container << parent.hubiForm_text_input (this, "Maximum Value for Gauge", "maxValue_", "100", false); parent.hubiForm_container(this, container, 1); } } else { container = []; paragraph "No recent valid events
Please select a different Attribute" parent.hubiForm_container(this, container, 1); } } } } def graphSetupPage(){ def fontEnum = [["1":"1"], ["2":"2"], ["3":"3"], ["4":"4"], ["5":"5"], ["6":"6"], ["7":"7"], ["8":"8"], ["9":"9"], ["10":"10"], ["11":"11"], ["12":"12"], ["13":"13"], ["14":"14"], ["15":"15"], ["16":"16"], ["17":"17"], ["18":"18"], ["19":"19"], ["20":"20"]]; def highlightEnum = [[0:"0"], [1:"1"], [2:"2"], [3:"3"]]; def num_; dynamicPage(name: "graphSetupPage") { parent.hubiForm_section(this,"General Options", 1){ container = []; container << parent.hubiForm_text_input (this, "Gauge Title", "gauge_title", "Gauge Title", false); container << parent.hubiForm_text_input (this, "Gauge Units", "gauge_units", "Units", false); container << parent.hubiForm_text_input (this, "Gauge Number Formatting
Example", "gauge_number_format", "##.#", false); container << parent.hubiForm_slider (this, title: "Select Number of Highlight Areas on Gauge", name: "num_highlights", default_value: 3, min: 0, max: 3, units: " highlights", submit_on_change: true); parent.hubiForm_container(this, container, 1); } if (num_highlights == null){ settings["num_highlights"] = 3; num_ = 3; } else { num_ = num_highlights.toInteger(); } if (num_ > 0){ parent.hubiForm_section(this,"HighLight Regions", 1){ container = []; for (i=0; i
""" return html; } // Create a formatted date object string for Google Charts Timeline def getDateString(date) { def dateObj = Date.parse("yyyy-MM-dd HH:mm:ss.SSS", date.toString()) //def dateObj = date def year = dateObj.getYear() + 1900 def dateString = "new Date(${year}, ${dateObj.getMonth()}, ${dateObj.getDate()}, ${dateObj.getHours()}, ${dateObj.getMinutes()}, ${dateObj.getSeconds()})" dateString } // Events come in Date format def getDateStringEvent(date) { def dateObj = date def yyyy = dateObj.getYear() + 1900 def MM = String.format("%02d", dateObj.getMonth()+1); def dd = String.format("%02d", dateObj.getDate()); def HH = String.format("%02d", dateObj.getHours()); def mm = String.format("%02d", dateObj.getMinutes()); def ss = String.format("%02d", dateObj.getSeconds()); def dateString = /$yyyy-$MM-$dd $HH:$mm:$ss.000/; dateString } def initializeAppEndpoint() { if (!state.endpoint) { try { def accessToken = createAccessToken() if (accessToken) { state.endpoint = getApiServerUrl() state.localEndpointURL = fullLocalApiServerUrl("") state.remoteEndpointURL = fullApiServerUrl("") state.endpointSecret = accessToken } } catch(e) { log.debug("Error: $e"); state.endpoint = null } } return state.endpoint } def getColorCode(code){ switch (code){ case "Maroon": ret = "#800000"; break; case "Red": ret = "#FF0000"; break; case "Orange": ret = "#FFA500"; break; case "Yellow": ret = "#FFFF00"; break; case "Olive": ret = "#808000"; break; case "Green": ret = "#008000"; break; case "Purple": ret = "#800080"; break; case "Fuchsia": ret = "#FF00FF"; break; case "Lime": ret = "#00FF00"; break; case "Teal": ret = "#008080"; break; case "Aqua": ret = "#00FFFF"; break; case "Blue": ret = "#0000FF"; break; case "Navy": ret = "#000080"; break; case "Black": ret = "#000000"; break; case "Gray": ret = "#808080"; break; case "Silver": ret = "#C0C0C0"; break; case "White": ret = "#FFFFFF"; break; case "Transparent": ret = "transparent"; break; } } //oauth endpoints def getGraph() { return render(contentType: "text/html", data: getGauge()); } def getData() { def data = buildData(); return render(contentType: "text/json", data: JsonOutput.toJson([ "value": data ])); } def getOptions() { return render(contentType: "text/json", data: JsonOutput.toJson(getChartOptions())); } def getSubscriptions() { def subscriptions = [ "id": sensor_.idAsLong, "attribute": attribute_ ]; return render(contentType: "text/json", data: JsonOutput.toJson(subscriptions)); }