diff --git a/smartapps/smartthings/examples/every-element.src/every-element.groovy b/smartapps/smartthings/examples/every-element.src/every-element.groovy index 82ff69a..417ae1c 100644 --- a/smartapps/smartthings/examples/every-element.src/every-element.groovy +++ b/smartapps/smartthings/examples/every-element.src/every-element.groovy @@ -1,7 +1,7 @@ /** * Every Element * - * Copyright 2014 SmartThings + * Copyright 2015 SmartThings * * 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: @@ -14,349 +14,555 @@ * */ definition( - name: "Every Element", - namespace: "smartthings/examples", - author: "SmartThings", - description: "Every element demonstration app", - category: "SmartThings Internal", - iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png", - iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png" + name: "Every Element", + namespace: "smartthings/examples", + author: "SmartThings", + description: "Every element demonstration app", + category: "SmartThings Internal", + iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png", + iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png" ) preferences { - page(name: "firstPage") - page(name: "inputPage") - page(name: "appPage") - page(name: "labelPage") - page(name: "modePage") - page(name: "paragraphPage") - page(name: "iconPage") - page(name: "hrefPage") - page(name: "buttonsPage") - page(name: "imagePage") - page(name: "videoPage") - page(name: "deadEnd", title: "Nothing to see here, move along.", content: "foo") - page(name: "flattenedPage") + // landing page + page(name: "firstPage") + + // PageKit + page(name: "buttonsPage") + page(name: "imagePage") + page(name: "inputPage") + page(name: "inputBooleanPage") + page(name: "inputIconPage") + page(name: "inputImagePage") + page(name: "inputDevicePage") + page(name: "inputCapabilityPage") + page(name: "inputRoomPage") + page(name: "inputModePage") + page(name: "inputSelectionPage") + page(name: "inputHubPage") + page(name: "inputContactBookPage") + page(name: "inputTextPage") + page(name: "inputTimePage") + page(name: "appPage") + page(name: "hrefPage") + page(name: "paragraphPage") + page(name: "videoPage") + page(name: "labelPage") + page(name: "modePage") + + // Every element helper pages + page(name: "deadEnd", title: "Nothing to see here, move along.", content: "foo") + page(name: "flattenedPage") } def firstPage() { - dynamicPage(name: "firstPage", title: "Where to first?", install: true, uninstall: true) { - section() { - href(page: "inputPage", title: "Element: 'input'") - href(page: "appPage", title: "Element: 'app'") - href(page: "labelPage", title: "Element: 'label'") - href(page: "modePage", title: "Element: 'mode'") - href(page: "paragraphPage", title: "Element: 'paragraph'") - href(page: "iconPage", title: "Element: 'icon'") - href(page: "hrefPage", title: "Element: 'href'") - href(page: "buttonsPage", title: "Element: 'buttons'") - href(page: "imagePage", title: "Element: 'image'") - href(page: "videoPage", title: "Element: 'video'") - } - section() { - href(page: "flattenedPage", title: "All of the above elements on a single page") - } - } + dynamicPage(name: "firstPage", title: "Where to first?", install: true, uninstall: true) { + section { + href(page: "appPage", title: "Element: 'app'") + href(page: "buttonsPage", title: "Element: 'buttons'") + href(page: "hrefPage", title: "Element: 'href'") + href(page: "imagePage", title: "Element: 'image'") + href(page: "inputPage", title: "Element: 'input'") + href(page: "labelPage", title: "Element: 'label'") + href(page: "modePage", title: "Element: 'mode'") + href(page: "paragraphPage", title: "Element: 'paragraph'") + href(page: "videoPage", title: "Element: 'video'") + } + section { + href(page: "flattenedPage", title: "All of the above elements on a single page") + } + } } def inputPage() { - dynamicPage(name: "inputPage", title: "Every 'input' type") { - section("enum") { - input(type: "enum", name: "enumRefresh", title: "submitOnChange:true", required: false, multiple: true, options: ["one", "two", "three"], submitOnChange: true) - if (enumRefresh) { - paragraph "${enumRefresh}" - } - input(type: "enum", name: "enumSegmented", title: "style:segmented", required: false, multiple: true, options: ["one", "two", "three"], style: "segmented") - input(type: "enum", name: "enum", title: "required:false, multiple:false", required: false, multiple: false, options: ["one", "two", "three"]) - input(type: "enum", name: "enumRequired", title: "required:true", required: true, multiple: false, options: ["one", "two", "three"]) - input(type: "enum", name: "enumMultiple", title: "multiple:true", required: false, multiple: true, options: ["one", "two", "three"]) - input(type: "enum", name: "enumWithImage", title: "This element has an image and a long title.", description: "I am setting long title and descriptions to test the offset", required: false, multiple: true, options: ["one", "two", "three"], image: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png") - input(type: "enum", name: "enumWithGroupedOptions", title: "groupedOptions", description: "This enum has grouped options", required: false, multiple: true, groupedOptions: [ - [ - title : "the group title that is displayed", - order : 0, // the order of the group; 0-based - image : "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png", // not yet supported - values: [ - [ - key : "the value that will be placed in SmartApp settings.", // such as a device id - value: "the title of the selectable option that is displayed", // such as a device name - order: 0 // the order of the option - ] - ] - ], - [ - title : "the second group title that is displayed", - order : 1, // the order of the group; 0-based - image : null, // not yet supported - values: [ - [ - key : "some_device_id", - value: "some_device_name", - order: 1 // the order of the option. This option will appear second in the list even though it is the first option defined in this map - ], - [ - key : "some_other_device_id", - value: "some_other_device_name", - order: 0 // the order of the option. This option will appear first in the list even though it is not the first option defined in this map - ] - ] - ] - ]) - } - section("text") { - input(type: "text", name: "text", title: "required:false, multiple:false", required: false, multiple: false) - input(type: "text", name: "textRequired", title: "required:true", required: true, multiple: false) - input(type: "text", name: "textWithImage", title: "This element has an image and a long title.", description: "I am setting long title and descriptions to test the offset", required: false, image: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png") - } - section("number") { - input(type: "number", name: "number", title: "required:false, multiple:false", required: false, multiple: false) - input(type: "number", name: "numberRequired", title: "required:true", required: true, multiple: false) - input(type: "number", name: "numberWithImage", title: "This element has an image and a long title.", description: "I am setting long title and descriptions to test the offset", required: false, image: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png") - } - section("boolean") { - input(type: "boolean", name: "boolean", title: "required:false, multiple:false", required: false, multiple: false) - input(type: "boolean", name: "booleanWithImage", title: "This element has an image and a long title.", description: "I am setting long title and descriptions to test the offset", required: false, image: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png") - } - section("password") { - input(type: "password", name: "password", title: "required:false, multiple:false", required: false, multiple: false) - input(type: "password", name: "passwordRequired", title: "required:true", required: true, multiple: false) - input(type: "password", name: "passwordWithImage", title: "This element has an image and a long title.", description: "I am setting long title and descriptions to test the offset", required: false, image: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png") - } - section("phone") { - input(type: "phone", name: "phone", title: "required:false, multiple:false", required: false, multiple: false) - input(type: "phone", name: "phoneRequired", title: "required:true", required: true, multiple: false) - input(type: "phone", name: "phoneWithImage", title: "This element has an image and a long title.", description: "I am setting long title and descriptions to test the offset", required: false, image: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png") - } - section("email") { - input(type: "email", name: "email", title: "required:false, multiple:false", required: false, multiple: false) - input(type: "email", name: "emailRequired", title: "required:true", required: true, multiple: false) - input(type: "email", name: "emailWithImage", title: "This element has an image and a long title.", description: "I am setting long title and descriptions to test the offset", required: false, image: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png") - } - section("decimal") { - input(type: "decimal", name: "decimal", title: "required:false, multiple:false", required: false, multiple: false) - input(type: "decimal", name: "decimalRequired", title: "required:true", required: true, multiple: false) - input(type: "decimal", name: "decimalWithImage", title: "This element has an image and a long title.", description: "I am setting long title and descriptions to test the offset", required: false, image: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png") - } - section("mode") { - input(type: "mode", name: "mode", title: "required:false, multiple:false", required: false, multiple: false) - input(type: "mode", name: "modeRequired", title: "required:true", required: true, multiple: false) - input(type: "mode", name: "modeMultiple", title: "multiple:true", required: false, multiple: true) - input(type: "mode", name: "iconWithImage", title: "This element has an image and a long title.", description: "I am setting long title and descriptions to test the offset", required: false, multiple: true, image: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png") - } - section("icon") { - input(type: "icon", name: "icon", title: "required:false, multiple:false", required: false, multiple: false) - input(type: "icon", name: "iconRequired", title: "required:true", required: true, multiple: false) - input(type: "icon", name: "iconWithImage", title: "This element has an image and a long title.", description: "I am setting long title and descriptions to test the offset", required: false, image: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png") - } - section("capability") { - input(type: "capability.switch", name: "capability", title: "required:false, multiple:false", required: false, multiple: false) - input(type: "capability.switch", name: "capabilityRequired", title: "required:true", required: true, multiple: false) - input(type: "capability.switch", name: "capabilityMultiple", title: "multiple:true", required: false, multiple: true) - input(type: "capability.switch", name: "capabilityWithImage", title: "This element has an image and a long title.", description: "I am setting long title and descriptions to test the offset", required: false, multiple: true, image: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png") - } - section("hub") { - input(type: "hub", name: "hub", title: "required:false, multiple:false", required: false, multiple: false) - input(type: "hub", name: "hubRequired", title: "required:true", required: true, multiple: false) - input(type: "hub", name: "hubMultiple", title: "multiple:true", required: false, multiple: true) - input(type: "hub", name: "hubWithImage", title: "This element has an image and a long title.", description: "I am setting long title and descriptions to test the offset", required: false, multiple: true, image: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png") - } - section("device") { - input(type: "device.switch", name: "device", title: "required:false, multiple:false", required: false, multiple: false) - input(type: "device.switch", name: "deviceRequired", title: "required:true", required: true, multiple: false) - input(type: "device.switch", name: "deviceMultiple", title: "multiple:true", required: false, multiple: true) - input(type: "device.switch", name: "deviceWithImage", title: "This element has an image and a long title.", description: "I am setting long title and descriptions to test the offset", required: false, multiple: true, image: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png") - } - section("time") { - input(type: "time", name: "time", title: "required:false, multiple:false", required: false, multiple: false) - input(type: "time", name: "timeRequired", title: "required:true", required: true, multiple: false) - input(type: "time", name: "timeWithImage", title: "This element has an image and a long title.", description: "I am setting long title and descriptions to test the offset", required: false, image: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png") - } - section("contact-book") { - input("recipients", "contact", title: "Notify", description: "Send notifications to") { - input(type: "phone", name: "phone", title: "Send text message to", required: false, multiple: false) - input(type: "boolean", name: "boolean", title: "Send push notification", required: false, multiple: false) - } - } - } + dynamicPage(name: "inputPage", title: "Links to every 'input' element") { + section { + href(page: "inputBooleanPage", title: "to boolean page") + href(page: "inputIconPage", title: "to icon page") + href(page: "inputImagePage", title: "to image page") + href(page: "inputSelectionPage", title: "to selection page") + href(page: "inputTextPage", title: "to text page") + href(page: "inputTimePage", title: "to time page") + } + section("subsets of selection input") { + href(page: "inputDevicePage", title: "to device selection page") + href(page: "inputCapabilityPage", title: "to capability selection page") + href(page: "inputRoomPage", title: "to room selection page") + href(page: "inputModePage", title: "to mode selection page") + href(page: "inputHubPage", title: "to hub selection page") + href(page: "inputContactBookPage", title: "to contact-book selection page") + } + } +} + +def inputBooleanPage() { + dynamicPage(name: "inputBooleanPage") { + section { + paragraph "The `required` and `multiple` attributes have no effect because the value will always be either `true` or `false`" + } + section { + input(type: "boolean", name: "booleanWithoutDescription", title: "without description", description: null) + input(type: "boolean", name: "booleanWithDescription", title: "with description", description: "This has a description") + } + section("defaultValue: 'true'") { + input(type: "boolean", name: "booleanWithDefaultValue", title: "", description: "", defaultValue: "true") + } + section("with image") { + input(type: "boolean", name: "booleanWithoutDescriptionWithImage", title: "without description", image: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png", description: null) + input(type: "boolean", name: "booleanWithDescriptionWithImage", title: "with description", description: "This has a description", image: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png") + } + } +} +def inputIconPage() { + dynamicPage(name: "inputIconPage") { + section { + paragraph "`description` is not displayed for icon elements" + paragraph "`multiple` has no effect because you can only choose a single icon" + } + section("required: true") { + input(type: "icon", name: "iconRequired", title: "without description", required: true) + input(type: "icon", name: "iconRequiredWithDescription", title: "with description", description: "this is a description", required: true) + } + section("with image") { + paragraph "The image specified will be replaced after an icon is selected" + input(type: "icon", name: "iconwithImage", title: "without description", required: false, image: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png") + } + } +} +def inputImagePage() { + dynamicPage(name: "inputImagePage") { + section { + paragraph "This only exists in DeviceTypes. Someone should do something about that. (glares at MikeDave)" + paragraph "Go to the device preferences of a Mobile Presence device to see it in action" + paragraph "If you try to set the value of this, it will not behave as it would in Device Preferences" + input(type: "image", title: "This is kind of what it looks like", required: false) + } + } +} + + +def optionsGroup(List groups, String title) { + def group = [values:[], order: groups.size()] + group.title = title ?: "" + groups << group + return groups +} +def addValues(List groups, String key, String value) { + def lastGroup = groups[-1] + lastGroup["values"] << [ + key: key, + value: value, + order: lastGroup["values"].size() + ] + return groups +} +def listToMap(List original) { + original.inject([:]) { result, v -> + result[v] = v + return result + } +} +def addGroup(List groups, String title, values) { + if (values instanceof List) { + values = listToMap(values) + } + + values.inject(optionsGroup(groups, title)) { result, k, v -> + return addValues(result, k, v) + } + return groups +} +def addGroup(values) { + addGroup([], null, values) +} +/* Example usage of options builder + +// Creating grouped options + def newGroups = [] + addGroup(newGroups, "first group", ["foo", "bar", "baz"]) + addGroup(newGroups, "second group", [zero: "zero", one: "uno", two: "dos", three: "tres"]) + +// simple list + addGroup(["a", "b", "c"]) + +// simple map + addGroup(["a": "yes", "b": "no", "c": "maybe"])​​​​ +*/ + + +def inputSelectionPage() { + + def englishOptions = ["One", "Two", "Three"] + def spanishOptions = ["Uno", "Dos", "Tres"] + def groupedOptions = [] + addGroup(groupedOptions, "English", englishOptions) + addGroup(groupedOptions, "Spanish", spanishOptions) + + dynamicPage(name: "inputSelectionPage") { + + section("options variations") { + paragraph "tap these elements and look at the differences when selecting an option" + input(type: "enum", name: "selectionSimple", title: "Simple options", description: "no separators in the selectable options", groupedOptions: addGroup(englishOptions + spanishOptions)) + input(type: "enum", name: "selectionGrouped", title: "Grouped options", description: "separate groups of options with headers", groupedOptions: groupedOptions) + } + + section("list vs map") { + paragraph "These should be identical in UI, but are different in code and will produce different settings" + input(type: "enum", name: "selectionList", title: "Choose a device", description: "settings will be something like ['Device1 Label']", groupedOptions: addGroup(["Device1 Label", "Device2 Label"])) + input(type: "enum", name: "selectionMap", title: "Choose a device", description: "settings will be something like ['device1-id']", groupedOptions: addGroup(["device1-id": "Device1 Label", "device2-id": "Device2 Label"])) + } + + section("segmented") { + paragraph "segmented should only work if there are either 2 or 3 options to choose from" + input(type: "enum", name: "selectionSegmented1", style: "segmented", title: "1 option", groupedOptions: addGroup(["One"])) + input(type: "enum", name: "selectionSegmented4", style: "segmented", title: "4 options", groupedOptions: addGroup(["One", "Two", "Three", "Four"])) + + paragraph "multiple and required will have no effect on segmented selection elements. There will always be exactly 1 option selected" + input(type: "enum", name: "selectionSegmented2", style: "segmented", title: "2 options", options: ["One", "Two"]) + input(type: "enum", name: "selectionSegmented3", style: "segmented", title: "3 options", options: ["One", "Two", "Three"]) + + paragraph "specifying defaultValue still works with segmented selection elements" + input(type: "enum", name: "selectionSegmentedWithDefault", title: "defaulted to 'two'", groupedOptions: addGroup(["One", "Two", "Three"]), defaultValue: "Two") + } + + section("required: true") { + input(type: "enum", name: "selectionRequired", title: "This is required", description: "It should look different when nothing is selected", groupedOptions: addGroup(["only option"]), required: true) + } + + section("multiple: true") { + input(type: "enum", name: "selectionMultiple", title: "This allows multiple selections", description: "It should look different when nothing is selected", groupedOptions: addGroup(["an option", "another option", "no way, one more?"]), multiple: true) + } + + section("with image") { + input(type: "enum", name: "selectionWithImage", title: "This has an image", description: "and a description", groupedOptions: addGroup(["an option", "another option", "no way, one more?"]), image: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png") + } + } +} +def inputTextPage() { + dynamicPage(name: "inputTextPage", title: "Every 'text' variation") { + section("style and functional differences") { + input(type: "text", name: "textRequired", title: "required: true", description: "This should look different when nothing has been entered", required: true) + input(type: "text", name: "textWithImage", title: "with image", description: "This should look different when nothing has been entered", image: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png", required: false) + } + section("text") { + input(type: "text", name: "text", title: "This has an alpha-numeric keyboard", description: "no special formatting", required: false) + } + section("password") { + input(type: "password", name: "password", title: "This has an alpha-numeric keyboard", description: "masks value", required: false) + } + section("email") { + input(type: "email", name: "email", title: "This has an email-specific keyboard", description: "no special formatting", required: false) + } + section("phone") { + input(type: "phone", name: "phone", title: "This has a numeric keyboard", description: "formatted for phone numbers", required: false) + } + section("decimal") { + input(type: "decimal", name: "decimal", title: "This has an numeric keyboard with decimal point", description: "no special formatting", required: false) + } + section("number") { + input(type: "number", name: "number", title: "This has an numeric keyboard without decimal point", description: "no special formatting", required: false) + } + + section("specified ranges") { + paragraph "You can limit number and decimal inputs to a specific range." + input(range: "50..150", type: "decimal", name: "decimalRange50..150", title: "only values between 50 and 150 will pass validation", description: "no special formatting", required: false) + paragraph "Negative limits will add a negative symbol to the keyboard." + input(range: "-50..50", type: "number", name: "numberRange-50..50", title: "only values between -50 and 50 will pass validation", description: "no special formatting", required: false) + paragraph "Specify * to not limit one side or the other." + input(range: "*..0", type: "decimal", name: "decimalRange*..0", title: "only negative values will pass validation", description: "no special formatting", required: false) + input(range: "*..*", type: "number", name: "numberRange*..*", title: "only positive values will pass validation", description: "no special formatting", required: false) + paragraph "If you don't specify a range, it defaults to 0..*" + } + } +} +def inputTimePage() { + dynamicPage(name: "inputTimePage") { + section { + input(type: "time", name: "timeWithDescription", title: "a time picker", description: "with a description", required: false) + input(type: "time", name: "timeWithoutDescription", title: "without a description", description: null, required: false) + input(type: "time", name: "timeRequired", title: "required: true", required: true) + } + } +} + +/// selection subsets +def inputDevicePage() { + + dynamicPage(name: "inputDevicePage") { + + section("required: true") { + input(type: "device.switch", name: "deviceRequired", title: "This is required", description: "It should look different when nothing is selected") + } + + section("multiple: true") { + input(type: "device.switch", name: "deviceMultiple", title: "This is required", description: "It should look different when nothing is selected", multiple: true) + } + + section("with image") { + input(type: "device.switch", name: "deviceRequired", title: "This has an image", description: "and a description", image: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png") + } + } +} +def inputCapabilityPage() { + + dynamicPage(name: "inputCapabilityPage") { + + section("required: true") { + input(type: "capability.switch", name: "capabilityRequired", title: "This is required", description: "It should look different when nothing is selected") + } + + section("multiple: true") { + input(type: "capability.switch", name: "capabilityMultiple", title: "This is required", description: "It should look different when nothing is selected", multiple: true) + } + + section("with image") { + input(type: "capability.switch", name: "capabilityRequired", title: "This has an image", description: "and a description", image: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png") + } + } +} +def inputRoomPage() { + + dynamicPage(name: "inputRoomPage") { + + section("required: true") { + input(type: "room", name: "roomRequired", title: "This is required", description: "It should look different when nothing is selected") + } + + section("multiple: true") { + input(type: "room", name: "roomMultiple", title: "This is required", description: "It should look different when nothing is selected", multiple: true) + } + + section("with image") { + input(type: "room", name: "roomRequired", title: "This has an image", description: "and a description", image: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png") + } + } +} +def inputModePage() { + + dynamicPage(name: "inputModePage") { + + section("required: true") { + input(type: "mode", name: "modeRequired", title: "This is required", description: "It should look different when nothing is selected") + } + + section("multiple: true") { + input(type: "mode", name: "modeMultiple", title: "This is required", description: "It should look different when nothing is selected", multiple: true) + } + + section("with image") { + input(type: "mode", name: "modeRequired", title: "This has an image", description: "and a description", image: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png") + } + } +} +def inputHubPage() { + + dynamicPage(name: "inputHubPage") { + + section("required: true") { + input(type: "hub", name: "hubRequired", title: "This is required", description: "It should look different when nothing is selected") + } + + section("multiple: true") { + input(type: "hub", name: "hubMultiple", title: "This is required", description: "It should look different when nothing is selected", multiple: true) + } + + section("with image") { + input(type: "hub", name: "hubRequired", title: "This has an image", description: "and a description", image: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png") + } + } +} +def inputContactBookPage() { + + dynamicPage(name: "inputContactBookPage") { + + section("required: true") { + input(type: "contact", name: "contactRequired", title: "This is required", description: "It should look different when nothing is selected") + } + + section("multiple: true") { + input(type: "contact", name: "contactMultiple", title: "This is required", description: "It should look different when nothing is selected", multiple: true) + } + + section("with image") { + input(type: "contact", name: "contactRequired", title: "This has an image", description: "and a description", image: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png") + } + } } def appPage() { - dynamicPage(name: "appPage", title: "Every 'app' type") { - section { - paragraph "These won't work unless you create a child SmartApp to link to... Sorry." - } - section("app") { - app( - name: "app", - title: "required:false, multiple:false", - required: false, - multiple: false, - namespace: "Steve", - appName: "Child SmartApp" - ) - app(name: "appRequired", title: "required:true", required: true, multiple: false, namespace: "Steve", appName: "Child SmartApp") - app(name: "appComplete", title: "state:complete", required: false, multiple: false, namespace: "Steve", appName: "Child SmartApp", state: "complete") - app(name: "appWithImage", title: "This element has an image and a long title.", description: "I am setting long title and descriptions to test the offset", required: false, multiple: false, image: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png", namespace: "Steve", appName: "Child SmartApp") - } - section("multiple:true") { - app(name: "appMultiple", title: "multiple:true", required: false, multiple: true, namespace: "Steve", appName: "Child SmartApp") - } - section("multiple:true with image") { - app(name: "appMultipleWithImage", title: "This element has an image and a long title.", description: "I am setting long title and descriptions to test the offset", required: false, multiple: true, image: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png", namespace: "Steve", appName: "Child SmartApp") - } - } + dynamicPage(name: "appPage", title: "Every 'app' type") { + section { + paragraph "These won't work unless you create a child SmartApp to link to... Sorry." + } + section("app") { + app( + name: "app", + title: "required:false, multiple:false", + required: false, + multiple: false, + namespace: "Steve", + appName: "Child SmartApp" + ) + app(name: "appRequired", title: "required:true", required: true, multiple: false, namespace: "Steve", appName: "Child SmartApp") + app(name: "appComplete", title: "state:complete", required: false, multiple: false, namespace: "Steve", appName: "Child SmartApp", state: "complete") + app(name: "appWithImage", title: "This element has an image and a long title.", description: "I am setting long title and descriptions to test the offset", required: false, multiple: false, image: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png", namespace: "Steve", appName: "Child SmartApp") + } + section("multiple:true") { + app(name: "appMultiple", title: "multiple:true", required: false, multiple: true, namespace: "Steve", appName: "Child SmartApp") + } + section("multiple:true with image") { + app(name: "appMultipleWithImage", title: "This element has an image and a long title.", description: "I am setting long title and descriptions to test the offset", required: false, multiple: true, image: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png", namespace: "Steve", appName: "Child SmartApp") + } + } } def labelPage() { - dynamicPage(name: "labelPage", title: "Every 'Label' type") { - section("label") { - label(name: "label", title: "required:false, multiple:false", required: false, multiple: false) - label(name: "labelRequired", title: "required:true", required: true, multiple: false) - label(name: "labelWithImage", title: "This element has an image and a long title.", description: "I am setting long title and descriptions to test the offset", required: false, image: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png") - } - } + dynamicPage(name: "labelPage", title: "Every 'Label' type") { + section("label") { + paragraph "The difference between a label element and a text input element is that the label element will effect the SmartApp directly by setting the label. An input element will place the set value in the SmartApp's settings." + paragraph "There are 3 here as an example. Never use more than 1 label element on a page." + label(name: "label", title: "required:false, multiple:false", required: false, multiple: false) + label(name: "labelRequired", title: "required:true", required: true, multiple: false) + label(name: "labelWithImage", title: "This element has an image and a long title.", description: "I am setting long title and descriptions to test the offset", required: false, image: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png") + } + } } def modePage() { - dynamicPage(name: "modePage", title: "Every 'mode' type") { // TODO: finish this - section("mode") { - mode(name: "mode", title: "required:false, multiple:false", required: false, multiple: false) - mode(name: "modeRequired", title: "required:true", required: true, multiple: false) - mode(name: "modeMultiple", title: "multiple:true", required: false, multiple: true) - mode(name: "modeWithImage", title: "This element has an image and a long title.", description: "I am setting long title and descriptions to test the offset", required: false, multiple: true, image: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png") - } - } + dynamicPage(name: "modePage", title: "Every 'mode' type") { // TODO: finish this + section("mode") { + paragraph "The difference between a mode element and a mode input element is that the mode element will effect the SmartApp directly by setting the modes it executes in. A mode input element will place the set value in the SmartApp's settings." + paragraph "Another difference is that you can select 'All Modes' when choosing which mode the SmartApp should execute in. This is the same as selecting no modes. When a SmartApp does not have modes specified, it will execute in all modes." + paragraph "There are 4 here as an example. Never use more than 1 mode element on a page." + mode(name: "mode", title: "required:false, multiple:false", required: false, multiple: false) + mode(name: "modeRequired", title: "required:true", required: true, multiple: false) + mode(name: "modeMultiple", title: "multiple:true", required: false, multiple: true) + mode(name: "modeWithImage", title: "This element has an image and a long title.", description: "I am setting long title and descriptions to test the offset", required: false, multiple: true, image: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png") + } + } } def paragraphPage() { - dynamicPage(name: "paragraphPage", title: "Every 'paragraph' type") { - section("paragraph") { - paragraph "This us how you should make a paragraph element" - paragraph image: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png", "This is a long description, blah, blah, blah." - } - } -} - -def iconPage() { - dynamicPage(name: "iconPage", title: "Every 'icon' type") { // TODO: finish this - section("icon") { - icon(name: "icon", title: "required:false, multiple:false", required: false, multiple: false) - icon(name: "iconRequired", title: "required:true", required: true, multiple: false) - icon(name: "iconWithImage", title: "This element has an image and a long title.", description: "I am setting long title and descriptions to test the offset", required: false, image: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png") - } - } + dynamicPage(name: "paragraphPage", title: "Every 'paragraph' type") { + section("paragraph") { + paragraph "This is how you should make a paragraph element" + paragraph image: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png", "This is a long description, blah, blah, blah." + } + } } def hrefPage() { - dynamicPage(name: "hrefPage", title: "Every 'href' type") { - section("page") { - href(name: "hrefPage", title: "required:false, multiple:false", required: false, multiple: false, page: "deadEnd") - href(name: "hrefPageRequired", title: "required:true", required: true, multiple: false, page: "deadEnd", description: "Don't make hrefs required") - href(name: "hrefPageComplete", title: "state:complete", required: false, multiple: false, page: "deadEnd", state: "complete") - href(name: "hrefPageWithImage", title: "This element has an image and a long title.", description: "I am setting long title and descriptions to test the offset", required: false, image: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png", page: "deadEnd",) - } - section("external") { - href(name: "hrefExternal", title: "required:false, multiple:false", required: false, multiple: false, style: "external", url: "http://smartthings.com/") - href(name: "hrefExternalRequired", title: "required:true", required: true, multiple: false, style: "external", url: "http://smartthings.com/", description: "Don't make hrefs required") - href(name: "hrefExternalComplete", title: "state:complete", required: false, multiple: true, style: "external", url: "http://smartthings.com/", state: "complete") - href(name: "hrefExternalWithImage", title: "This element has an image and a long title.", description: "I am setting long title and descriptions to test the offset", required: false, image: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png", url: "http://smartthings.com/") - } - section("embedded") { - href(name: "hrefEmbedded", title: "required:false, multiple:false", required: false, multiple: false, style: "embedded", url: "http://smartthings.com/") - href(name: "hrefEmbeddedRequired", title: "required:true", required: true, multiple: false, style: "embedded", url: "http://smartthings.com/", description: "Don't make hrefs required") - href(name: "hrefEmbeddedComplete", title: "state:complete", required: false, multiple: true, style: "embedded", url: "http://smartthings.com/", state: "complete") - href(name: "hrefEmbeddedWithImage", title: "This element has an image and a long title.", description: "I am setting long title and descriptions to test the offset", required: false, image: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png", url: "http://smartthings.com/") - } - } + dynamicPage(name: "hrefPage", title: "Every 'href' variation") { + section("stylistic differences") { + href(page: "deadEnd", title: "state: 'complete'", description: "gives the appearance of an input that has been filled out", state: "complete") + href(page: "deadEnd", title: "with image", image: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png") + href(page: "deadEnd", title: "with image and description", description: "and state: 'complete'", image: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png", state: "complete") + } + section("functional differences") { + href(page: "deadEnd", title: "to a page within the app") + href(url: "http://www.google.com", title: "to a url using all defaults") + href(url: "http://www.google.com", title: "external: true", description: "takes you outside the app", external: true) + } + } } def buttonsPage() { - dynamicPage(name: "buttonsPage", title: "Every 'button' type") { - section("buttons") { - buttons(name: "buttons", title: "required:false, multiple:false", required: false, multiple: false, buttons: [ - [label: "foo", action: "foo"], - [label: "bar", action: "bar"] - ]) - buttons(name: "buttonsRequired", title: "required:true", required: true, multiple: false, buttons: [ - [label: "foo", action: "foo"], - [label: "bar", action: "bar"] - ]) - buttons(name: "buttonsWithImage", title: "This element has an image and a long title.", description: "I am setting long title and descriptions to test the offset", required: false, image: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png", buttons: [ - [label: "foo", action: "foo"], - [label: "bar", action: "bar"] - ]) - } - section("Colored Buttons") { - buttons(name: "buttonsColoredSpecial", title: "special strings", description: "SmartThings highly recommends using these colors", buttons: [ - [label: "complete", action: "bar", backgroundColor: "complete"], - [label: "required", action: "bar", backgroundColor: "required"] - ]) - buttons(name: "buttonsColoredHex", title: "hex values work", buttons: [ - [label: "bg: #000dff", action: "foo", backgroundColor: "#000dff"], - [label: "fg: #ffac00", action: "foo", color: "#ffac00"], - [label: "both fg and bg", action: "foo", color: "#ffac00", backgroundColor: "#000dff"] - ]) - buttons(name: "buttonsColoredString", title: "strings work too", buttons: [ - [label: "green", action: "foo", backgroundColor: "green"], - [label: "red", action: "foo", backgroundColor: "red"], - [label: "both fg and bg", action: "foo", color: "red", backgroundColor: "green"] - ]) - } - } + dynamicPage(name: "buttonsPage", title: "Every 'button' type") { + section("Simple Buttons") { + paragraph "If there are an odd number of buttons, the last button will span the entire view area." + buttons(name: "buttons1", title: "1 button", buttons: [ + [label: "foo", action: "foo"] + ]) + buttons(name: "buttons2", title: "2 buttons", buttons: [ + [label: "foo", action: "foo"], + [label: "bar", action: "bar"] + ]) + buttons(name: "buttons3", title: "3 buttons", buttons: [ + [label: "foo", action: "foo"], + [label: "bar", action: "bar"], + [label: "baz", action: "baz"] + ]) + buttons(name: "buttonsWithImage", title: "This element has an image and a long title.", description: "I am setting long title and descriptions to test the offset", image: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png", buttons: [ + [label: "foo", action: "foo"], + [label: "bar", action: "bar"] + ]) + } + section("Colored Buttons") { + buttons(name: "buttonsColoredSpecial", title: "special strings", description: "SmartThings highly recommends using these colors", buttons: [ + [label: "complete", action: "bar", backgroundColor: "complete"], + [label: "required", action: "bar", backgroundColor: "required"] + ]) + buttons(name: "buttonsColoredHex", title: "hex values work", buttons: [ + [label: "bg: #000dff", action: "foo", backgroundColor: "#000dff"], + [label: "fg: #ffac00", action: "foo", color: "#ffac00"], + [label: "both fg and bg", action: "foo", color: "#ffac00", backgroundColor: "#000dff"] + ]) + buttons(name: "buttonsColoredString", title: "strings work too", buttons: [ + [label: "green", action: "foo", backgroundColor: "green"], + [label: "red", action: "foo", backgroundColor: "red"], + [label: "both fg and bg", action: "foo", color: "red", backgroundColor: "green"] + ]) + } + } } def imagePage() { - dynamicPage(name: "imagePage", title: "Every 'image' type") { // TODO: finish thise - section("image") { - image "http://f.cl.ly/items/1k1S0A0m3805402o3O12/20130915-191127.jpg" - image(name: "imageWithImage", title: "This element has an image and a long title.", description: "I am setting long title and descriptions to test the offset", required: false, image: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png") - } - } + dynamicPage(name: "imagePage", title: "Every 'image' type") { // TODO: finish thise + section("image") { + image "http://f.cl.ly/items/1k1S0A0m3805402o3O12/20130915-191127.jpg" + image(name: "imageWithMultipleImages", title: "This element has an image and a long title.", description: "I am setting long title and descriptions to test the offset", required: false, images: ["https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png", "http://f.cl.ly/items/1k1S0A0m3805402o3O12/20130915-191127.jpg"]) + } + } } def videoPage() { - dynamicPage(name: "imagePage", title: "Every 'image' type") { // TODO: finish this - section("video") { - // TODO: update this when there is a videoElement method - element(name: "videoElement", element: "video", type: "video", title: "this is a video!", description: "I am setting long title and descriptions to test the offset", required: false, image: "http://ec2-54-161-144-215.compute-1.amazonaws.com:8081/jesse/cam1/54aafcd1c198347511c26321.jpg", video: "http://ec2-54-161-144-215.compute-1.amazonaws.com:8081/jesse/cam1/54aafcd1c198347511c2631f.mp4") - } - } + dynamicPage(name: "videoPage", title: "Every 'video' type") { // TODO: finish this + section("video") { + // TODO: update this when there is a videoElement method + element(name: "videoElement", element: "video", type: "video", title: "this is a video!", description: "I am setting long title and descriptions to test the offset", required: false, image: "http://f.cl.ly/items/0w0D1p0K2D0d190F3H3N/Image%202015-12-14%20at%207.57.27%20AM.jpg", video: "http://f.cl.ly/items/3O2L03471l2K3E3l3K1r/Zombie%20Kid%20Likes%20Turtles.mp4") + } + } } def flattenedPage() { - def allSections = [] - firstPage().sections.each { section -> - section.body.each { hrefElement -> - if (hrefElement.page != "flattenedPage") { - allSections += "${hrefElement.page}"().sections - } - } - } - def flattenedPage = dynamicPage(name: "flattenedPage", title: "All elements in one page!") {} - flattenedPage.sections = allSections - return flattenedPage + def allSections = [] + firstPage().sections[0].body.each { hrefElement -> + if (hrefElement.name != "inputPage") { + // inputPage is a bunch of hrefs + allSections += "${hrefElement.page}"().sections + } + } + // collect the input elements + inputPage().sections.each { section -> + section.body.each { hrefElement -> + allSections += "${hrefElement.page}"().sections + } + } + def flattenedPage = dynamicPage(name: "flattenedPage", title: "All elements in one page!") {} + flattenedPage.sections = allSections + return flattenedPage } def foo() { - dynamicPage(name: "deadEnd") { - - } + dynamicPage(name: "deadEnd") { + section { } + } } def installed() { - log.debug "Installed with settings: ${settings}" + log.debug "Installed with settings: ${settings}" - initialize() + initialize() } def updated() { - log.debug "Updated with settings: ${settings}" + log.debug "Updated with settings: ${settings}" - unsubscribe() - initialize() + unsubscribe() + initialize() } def initialize() { - // TODO: subscribe to attributes, devices, locations, etc. + // TODO: subscribe to attributes, devices, locations, etc. }