Compare commits

..

22 Commits

Author SHA1 Message Date
Ralph Estes
e54e18bbea MSA-1315: power 2016-05-26 18:27:35 -05:00
Jason Botello
aa3a18f421 Merge pull request #865 from SmartThingsCommunity/MSA-1255-1
MSA-1255: Gidjit - Smart Launcher with SmartThings support
2016-05-25 15:13:55 -05:00
Vinay Rao
fc6b14b85e Merge pull request #921 from SmartThingsCommunity/staging
Rolling down staging hotfix to master
2016-05-24 16:27:08 -07:00
Vinay Rao
f131fb71cf Merge pull request #918 from SmartThingsCommunity/production
Rolling down production hotfix to staging
2016-05-24 13:51:26 -07:00
Vinay Rao
9c27ed6cb7 Merge pull request #913 from SmartThingsCommunity/revert-904-revert-901-DVCSMP-1739-revert-untested-code
Revert "DVCSMP-1739 Revert "Undo DVCSMP-1739 Revert "make sure reformat the storeGraphData"""
2016-05-23 16:05:09 -07:00
Dwight Liu
35edaa19c7 Revert "DVCSMP-1739 Revert "Undo DVCSMP-1739 Revert "make sure reformat the storeGraphData""" 2016-05-23 14:38:40 -07:00
Vinay Rao
dc09201866 Merge pull request #912 from larsfinander/DVCSMP-1770_Add_MSR_to_zwave-switch_staging
DVCSMP-1770 Add MSR to zwave-switch and configure at pairing
2016-05-23 14:22:40 -07:00
Lars Finander
6afcbf8f70 DVCSMP-1770 Add MSR to zwave-switch and configure at pairing
-Make ZWave switch and Dimmer switch behave the same and save MSR and
manufacturer name in data
2016-05-23 14:13:10 -07:00
rohandesai
2894d52efa Merge pull request #909 from SmartThingsCommunity/MSA-1285-3
MSA-1285: May 2016 Submission
2016-05-20 13:39:33 -07:00
Will Price
a21f9f177c MSA-1285: Updated submission, already reviewed, just cleaning up the pull request. 2016-05-20 14:22:19 -05:00
rohandesai
02f968b8cb Merge pull request #889 from SmartThingsCommunity/MSA-1278-2
MSA-1278: Updates to dynamic object execution for security compliance
2016-05-20 10:30:48 -07:00
Juan Pablo Risso
b105d9d80e DVCSMP-1775 - Call poll() every 5 minutes to reduce duplicated calls (#902)
* DVCSMP-1775 - Call poll() every 5 minutes to reduce duplicated calls

Call poll() every 5 minutes instead of discovery() to reduce duplicated
getActivityList() call.

* Better error logging
2016-05-19 13:58:44 -04:00
Chelsea Okey
9bfad5d6a4 Merge pull request #908 from ch3lsea/INTL-527
INTL-527 Temperature Notifications Should Respect Location Temp Scale
2016-05-19 10:41:01 -05:00
chelseaokey
85a335d365 INTL-527 Temperature Notifications Should Respect Location Temp Scale 2016-05-19 09:59:45 -05:00
Jim Anderson
40e6778e31 Merge pull request #898 from jimmyjames/add-tiles-readme
DOCS-284 adding README for example/test tiles
2016-05-18 13:58:45 -05:00
Vinay Rao
bd0ccd0c21 Merge pull request #903 from SmartThingsCommunity/staging
Rolling up staging to production Deploy May 17
2016-05-17 09:58:29 -07:00
Jim Anderson
26a0f6f939 DOCS-284 adding README for example/test tiles 2016-05-13 21:10:54 -05:00
Matthew Hartley
56eef9cf22 MSA-1278: The Prempoint SmartApp is a service bridge to allow Prempoint users to import, share, and manage devices that are controlled by their SmartThings hub. 2016-05-12 14:28:40 -05:00
Vinay Rao
566425c531 Merge pull request #869 from SmartThingsCommunity/staging
Rolling up staging to production 05-10
2016-05-10 13:40:49 -07:00
Matthew Page
ab2ba8104d MSA-1255: The following app submitted will be an endpoint for our IOS app Gidjit.
Gidjit is like having a home screen based on your environment. It scans your surroundings looking for home devices, personal electronic devices, times and locations of interest, and more. It will present a list of corresponding launcher panels that allow you to quickly access actions like open an app, control/monitor a device, execute a SmartThings Routine, or link to the control/launch screen of a device. For example based on the users location they will quickly be able to access a preset routine. We are really looking forward to having SmartThings in integration with the app. 

Link: https://itunes.apple.com/us/app/gidjit-smart-launcher/id961388659?mt=8

The app currently will not have the SmartThing's support until the next version that will be released when this is hopefully approved, as we will also have to go through the Apple approval process allowing them to test the features.

Thank you for your time!

Matt
2016-05-07 16:51:30 -05:00
Vinay Rao
973c16f088 Merge pull request #842 from SmartThingsCommunity/staging
Rolling up changes to production from staging 2016-05-03 Release
2016-05-03 10:20:53 -07:00
Vinay Rao
b05d956d95 Merge pull request #830 from SmartThingsCommunity/staging
Rolling up changes to production from staging 2016-04-26 Release
2016-04-26 10:59:05 -07:00
9 changed files with 1180 additions and 44 deletions

View File

@@ -0,0 +1,394 @@
/**
* Aeon HEM1
*
* 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.
*
* Aeon Home Energy Meter v1 (US)
*
* Version: v3.1
*
* Updates:
* -------
* 02-15-2016 : Removed posting to the Activity Feed in the phone app and event log.
* 02-17-2016 : Fixed preferences for kWh cost from string to number.
* 02-20-2016 : Enabled battery reporting (parameter 103, value 1), and documented the parameters better.
* 02-21-2016 : Made certain configuration parameters changeable via device preferences instead of having to tweak code all the time.
* 02-22-2016 : Fixed kWh cost entry in Preferences not allowing decimals.
* 02-27-2016 : Changed date formats to be MM-dd-yyyy h:mm a.
* 02-29-2016 : Changed reportType variable from 0 to 1.
* 03-11-2016 : Due to ST's v2.1.0 app totally hosing up SECONDARY_CONTROL, implemented a workaround to display that info in a separate tile.
* 03-19-2016 : Added clarity for preferences.
* 03-21-2016 : Fixed issue when resetting energy would also reset watts.
* 03-25-2016 : Removed the \n from the two tiles for resetting watta and energy due to rendering issues on iOS
*
*/
metadata {
definition (name: "My Aeon Home Energy Monitor", namespace: "jscgs350", author: "SmartThings")
{
capability "Energy Meter"
capability "Power Meter"
capability "Configuration"
capability "Sensor"
capability "Refresh"
capability "Polling"
capability "Battery"
attribute "energy", "string"
attribute "energyDisp", "string"
attribute "energyOne", "string"
attribute "energyTwo", "string"
attribute "power", "string"
attribute "powerDisp", "string"
attribute "powerOne", "string"
attribute "powerTwo", "string"
command "reset"
command "configure"
command "resetmaxmin"
fingerprint deviceId: "0x2101", inClusters: " 0x70,0x31,0x72,0x86,0x32,0x80,0x85,0x60"
}
// tile definitions
tiles(scale: 2) {
multiAttributeTile(name:"powerDisp", type: "lighting", width: 6, height: 4, decoration: "flat", canChangeIcon: true, canChangeBackground: true){
tileAttribute ("device.powerDisp", key: "PRIMARY_CONTROL") {
attributeState "default", action: "refresh", label: '${currentValue}', icon: "st.switches.light.on", backgroundColor: "#79b821"
}
tileAttribute ("statusText", key: "SECONDARY_CONTROL") {
// attributeState "statusText", label:'${currentValue}'
attributeState "statusText", label:''
}
}
valueTile("energyDisp", "device.energyDisp", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
state("default", label: '${currentValue}', backgroundColor:"#ffffff")
}
valueTile("energyOne", "device.energyOne", width: 6, height: 2, inactiveLabel: false, decoration: "flat") {
state("default", label: '${currentValue}', backgroundColor:"#ffffff")
}
valueTile("energyTwo", "device.energyTwo", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
state("default", label: '${currentValue}', backgroundColor:"#ffffff")
}
standardTile("refresh", "device.power", width: 3, height: 2, inactiveLabel: false, decoration: "flat") {
state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
}
standardTile("configure", "device.power", width: 3, height: 2, inactiveLabel: false, decoration: "flat") {
state "configure", label:'', action:"configure", icon:"st.secondary.configure"
}
valueTile("battery", "device.battery", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
state "battery", label:'${currentValue}%\nbattery', unit:""
}
valueTile("statusText", "statusText", inactiveLabel: false, decoration: "flat", width: 6, height: 2) {
state "statusText", label:'${currentValue}', backgroundColor:"#ffffff"
}
valueTile("min", "powerOne", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
state "default", label:'Min:\n${currentValue}', backgroundColor:"#ffffff"
}
valueTile("max", "powerTwo", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
state "default", label:'Max:\n${currentValue}', backgroundColor:"#ffffff"
}
standardTile("resetmaxmin", "device.energy", width: 3, height: 2, inactiveLabel: false, decoration: "flat") {
state "default", label:'Reset Watts', action:"resetmaxmin", icon:"st.secondary.refresh-icon"
}
standardTile("reset", "device.energy", width: 3, height: 2, inactiveLabel: false, decoration: "flat") {
state "default", label:'Reset Energy', action:"reset", icon:"st.secondary.refresh-icon"
}
main (["powerDisp"])
details(["powerDisp", "statusText", "battery", "energyDisp", "energyTwo", "energyOne", "resetmaxmin", "resetenergy", "reset", "refresh", "configure"])
}
preferences {
input "kWhCost", "string",
title: "Enter your cost per kWh (or just use the default, or use 0 to not calculate):",
defaultValue: 0.16,
required: false,
displayDuringSetup: true
input "reportType", "number",
title: "ReportType: Send watt/kWh data on a time interval (0), or on a change in wattage (1)? Enter a 0 or 1:",
defaultValue: 1,
required: false,
displayDuringSetup: true
input "wattsChanged", "number",
title: "For ReportType = 1, Don't send unless watts have changed by this many watts: (range 0 - 32,000W)",
defaultValue: 50,
required: false,
displayDuringSetup: true
input "wattsPercent", "number",
title: "For ReportType = 1, Don't send unless watts have changed by this percent: (range 0 - 99%)",
defaultValue: 10,
required: false,
displayDuringSetup: true
input "secondsWatts", "number",
title: "For ReportType = 0, Send Watts data every how many seconds? (range 0 - 65,000 seconds)",
defaultValue: 15,
required: false,
displayDuringSetup: true
input "secondsKwh", "number",
title: "For ReportType = 0, Send kWh data every how many seconds? (range 0 - 65,000 seconds)",
defaultValue: 60,
required: false,
displayDuringSetup: true
input "secondsBattery", "number",
title: "Send battery data every how many seconds? (range 0 - 65,000 seconds)",
defaultValue: 900,
required: false,
displayDuringSetup: true
}
}
def updated() {
log.debug "updated (kWhCost: ${kWhCost}, reportType: ${reportType}, wattsChanged: ${wattsChanged}, wattsPercent: ${wattsPercent}, secondsWatts: ${secondsWatts}, secondsKwh: ${secondsKwh}, secondsBattery: ${secondsBattery})"
response(configure())
}
def parse(String description) {
// log.debug "Parse received ${description}"
def result = null
def cmd = zwave.parse(description, [0x31: 1, 0x32: 1, 0x60: 3, 0x80: 1])
if (cmd) {
result = createEvent(zwaveEvent(cmd))
}
// if (result) log.debug "Parse returned ${result}"
def statusTextmsg = ""
statusTextmsg = "Min was ${device.currentState('powerOne')?.value}\nMax was ${device.currentState('powerTwo')?.value}"
sendEvent("name":"statusText", "value":statusTextmsg)
// log.debug statusTextmsg
return result
}
def zwaveEvent(physicalgraph.zwave.commands.meterv1.MeterReport cmd) {
//log.debug "zwaveEvent received ${cmd}"
def dispValue
def newValue
def timeString = new Date().format("MM-dd-yyyy h:mm a", location.timeZone)
if (cmd.meterType == 33) {
if (cmd.scale == 0) {
newValue = cmd.scaledMeterValue
if (newValue != state.energyValue) {
dispValue = String.format("%5.2f",newValue)+"\nkWh"
sendEvent(name: "energyDisp", value: dispValue as String, unit: "", displayed: false)
state.energyValue = newValue
BigDecimal costDecimal = newValue * ( kWhCost as BigDecimal)
def costDisplay = String.format("%3.2f",costDecimal)
sendEvent(name: "energyTwo", value: "Cost\n\$${costDisplay}", unit: "", displayed: false)
[name: "energy", value: newValue, unit: "kWh", displayed: false]
}
} else if (cmd.scale == 1) {
newValue = cmd.scaledMeterValue
if (newValue != state.energyValue) {
dispValue = String.format("%5.2f",newValue)+"\nkVAh"
sendEvent(name: "energyDisp", value: dispValue as String, unit: "", displayed: false)
state.energyValue = newValue
[name: "energy", value: newValue, unit: "kVAh", displayed: false]
}
}
else if (cmd.scale==2) {
newValue = Math.round( cmd.scaledMeterValue ) // really not worth the hassle to show decimals for Watts
if (newValue != state.powerValue) {
dispValue = newValue+"w"
sendEvent(name: "powerDisp", value: dispValue as String, unit: "", displayed: false)
if (newValue < state.powerLow) {
dispValue = newValue+"w"+" on "+timeString
sendEvent(name: "powerOne", value: dispValue as String, unit: "", displayed: false)
state.powerLow = newValue
}
if (newValue > state.powerHigh) {
dispValue = newValue+"w"+" on "+timeString
sendEvent(name: "powerTwo", value: dispValue as String, unit: "", displayed: false)
state.powerHigh = newValue
}
state.powerValue = newValue
[name: "power", value: newValue, unit: "W", displayed: false]
}
}
}
}
def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
def map = [:]
map.name = "battery"
map.unit = "%"
if (cmd.batteryLevel == 0xFF) {
map.value = 1
map.descriptionText = "${device.displayName} has a low battery"
map.isStateChange = true
} else {
map.value = cmd.batteryLevel
sendEvent(name: "battery", value: map.value as String, displayed: false)
}
return map
}
def zwaveEvent(physicalgraph.zwave.Command cmd) {
// Handles all Z-Wave commands we aren't interested in
log.debug "Unhandled event ${cmd}"
[:]
}
def refresh() {
log.debug "Refreshed ${device.name}"
delayBetween([
zwave.meterV2.meterGet(scale: 0).format(),
zwave.meterV2.meterGet(scale: 2).format()
])
}
def poll() {
refresh()
}
def reset() {
log.debug "${device.name} reset kWh/Cost values"
def timeString = new Date().format("MM-dd-yyyy h:mm a", location.timeZone)
sendEvent(name: "energyOne", value: "Energy Data (kWh/Cost) Reset On:\n"+timeString, unit: "")
sendEvent(name: "energyDisp", value: "", unit: "")
sendEvent(name: "energyTwo", value: "Cost\n--", unit: "")
def cmd = delayBetween( [
zwave.meterV2.meterReset().format(),
zwave.meterV2.meterGet(scale: 0).format(),
zwave.meterV2.meterGet(scale: 2).format()
])
cmd
}
def resetmaxmin() {
log.debug "${device.name} reset max/min values"
state.powerHigh = 0
state.powerLow = 99999
def timeString = new Date().format("MM-dd-yyyy h:mm a", location.timeZone)
sendEvent(name: "energyOne", value: "Watts Data (min/max) Reset On:\n"+timeString, unit: "")
sendEvent(name: "powerOne", value: "", unit: "")
sendEvent(name: "powerTwo", value: "", unit: "")
def cmd = delayBetween( [
zwave.meterV2.meterGet(scale: 0).format(),
zwave.meterV2.meterGet(scale: 2).format()
])
cmd
}
def configure() {
log.debug "${device.name} configuring..."
if (reportType == 0) {
log.debug "Setting reportType to ${reportType} per user request."
} else if (reportType == 1) {
log.debug "Setting reportType to ${reportType} per user request."
}
else {
def reportType = 1
log.debug "Setting reportType to ${reportType} because an invalid value was provided."
}
if (wattsChanged < 0) {
def wattsChanged = 50
log.debug "Setting wattsChanged to ${wattsChanged} (device default) because an invalid value was provided."
} else if (wattsChanged < 32001) {
log.debug "Setting wattsChanged to ${wattsChanged} per user request."
}
else {
def wattsChanged = 50
log.debug "Setting wattsChanged to ${wattsChanged} (device default) because an invalid value was provided."
}
if (wattsPercent < 0) {
def wattsPercent = 10
log.debug "Setting wattsPercent to ${wattsPercent} (device default) because an invalid value was provided."
} else if (wattsPercent < 100) {
log.debug "Setting wattsPercent to ${wattsPercent} per user request."
}
else {
def wattsPercent = 10
log.debug "Setting wattsPercent to ${wattsPercent} (device default) because an invalid value was provided."
}
if (secondsWatts < 0) {
def secondsWatts = 600
log.debug "Setting secondsWatts to ${secondsWatts} (device default) because an invalid value was provided."
} else if (secondsWatts < 65000) {
log.debug "Setting secondsWatts to ${secondsWatts} per user request."
}
else {
def secondsWatts = 600
log.debug "Setting secondsWatts to ${secondsWatts} (device default) because an invalid value was provided."
}
if (secondsKwh < 0) {
def secondsKwh = 600
log.debug "Setting secondsKwh to ${secondsKwh} (device default) because an invalid value was provided."
} else if (secondsKwh < 65000) {
log.debug "Setting secondsKwh to ${secondsKwh} per user request."
}
else {
def secondsKwh = 600
log.debug "Setting secondsKwh to ${secondsKwh} (device default) because an invalid value was provided."
}
if (secondsBattery < 0) {
def secondsBattery = 3600
log.debug "Setting secondsBattery to ${secondsBattery} (device default) because an invalid value was provided."
} else if (secondsBattery < 65000) {
log.debug "Setting secondsBattery to ${secondsBattery} per user request."
}
else {
def secondsBattery = 3600
log.debug "Setting secondsBattery to ${secondsBattery} (device default) because an invalid value was provided."
}
def cmd = delayBetween([
// Performs a complete factory reset. Use this all by itself and comment out all others below. Once reset, comment this line out and uncomment the others to go back to normal
// zwave.configurationV1.configurationSet(parameterNumber: 255, size: 4, scaledConfigurationValue: 1).format()
// Send data based on a time interval (0), or based on a change in wattage (1). 0 is default and enables parameters 111, 112, and 113. 1 enables parameters 4 and 8.
zwave.configurationV1.configurationSet(parameterNumber: 3, size: 1, scaledConfigurationValue: reportType).format(),
// If parameter 3 is 1, don't send unless watts have changed by 50 <default> for the whole device.
zwave.configurationV1.configurationSet(parameterNumber: 4, size: 2, scaledConfigurationValue: wattsChanged).format(),
// If parameter 3 is 1, don't send unless watts have changed by 10% <default> for the whole device.
zwave.configurationV1.configurationSet(parameterNumber: 8, size: 1, scaledConfigurationValue: wattsPercent).format(),
// Defines the type of report sent for Reporting Group 1 for the whole device. 1->Battery Report, 4->Meter Report for Watt, 8->Meter Report for kWh
zwave.configurationV1.configurationSet(parameterNumber: 101, size: 4, scaledConfigurationValue: 4).format(), //watts
// If parameter 3 is 0, report every XX Seconds (for Watts) for Reporting Group 1 for the whole device.
zwave.configurationV1.configurationSet(parameterNumber: 111, size: 4, scaledConfigurationValue: secondsWatts).format(),
// Defines the type of report sent for Reporting Group 2 for the whole device. 1->Battery Report, 4->Meter Report for Watt, 8->Meter Report for kWh
zwave.configurationV1.configurationSet(parameterNumber: 102, size: 4, scaledConfigurationValue: 8).format(), //kWh
// If parameter 3 is 0, report every XX seconds (for kWh) for Reporting Group 2 for the whole device.
zwave.configurationV1.configurationSet(parameterNumber: 112, size: 4, scaledConfigurationValue: secondsKwh).format(),
// Defines the type of report sent for Reporting Group 3 for the whole device. 1->Battery Report, 4->Meter Report for Watt, 8->Meter Report for kWh
zwave.configurationV1.configurationSet(parameterNumber: 103, size: 4, scaledConfigurationValue: 1).format(), //battery
// If parameter 3 is 0, report every XX seconds (for battery) for Reporting Group 2 for the whole device.
zwave.configurationV1.configurationSet(parameterNumber: 113, size: 4, scaledConfigurationValue: secondsBattery).format()
])
cmd
}

View File

@@ -94,11 +94,11 @@ def parse(String description) {
def cmd = zwave.parse(description, [0x31: 1, 0x32: 1, 0x60: 3])
if (cmd) {
result = createEvent(zwaveEvent(cmd))
log.debug "Parse returned ${result?.descriptionText}"
storeGraphData(result.name, result.value)
} else {
log.debug "zwave.parse returned null command. Cannot create event"
}
log.debug "Parse returned ${result?.descriptionText}"
storeGraphData(result.name, result.value)
return result
}

View File

@@ -0,0 +1,42 @@
# Device Tiles Examples and Reference
This package contains examples of Device tiles, organized by tile type.
## Purpose
Each Device Handler shows example usages of a specific tile, and is meant to represent the variety of permutations that a tile can be configured.
The various tiles can be used by QA to test tiles on all supported mobile devices, and by developers as a reference implementation.
## Installation
1. Self-publish the Device Handlers in this package.
2. Self-publish the Device Tile Controller SmartApp. The SmartApp can be found [here](https://github.com/SmartThingsCommunity/SmartThingsPublic/blob/master/smartapps/smartthings/tile-ux/device-tile-controller.src/device-tile-controller.groovy).
3. Install the SmartApp from the Marketplace, under "My Apps".
4. Select the simulated devices you want to install and press "Done".
The simulated devices can then be found in the "Things" view of "My Home" in the mobile app.
You may wish to create a new room for these simulated devices for easy access.
## Usage
Each simulated device can be interacted with like other devices.
You can use the mobile app to interact with the tiles to see how they look and behave.
## Troubleshooting
If you get an error when installing the simulated devices using the controller SmartApp, ensure that you have published all the Device Handlers for yourself.
Also check live logging to see if there is a specific tile that is causing installation issues.
## FAQ
*Question: A tile isn't behaving as expected. What should I do?*
QA should create a JIRA ticket for any issues or inconsistencies of tiles across devices.
Developers may file a support ticket, and reference the specific tile and issue observed.
*Question: I'd like to contribute an example tile usage that would be helpful for testing and reference purposes. Can I do that?*
We recommend that you open an issue in the SmartThingsPublic repository describing the example tile and usage.
That way we can discuss with you the proposed change, and then if appropriate you can create a PR associated to the issue.

View File

@@ -0,0 +1,252 @@
/**
* Gidjit Hub
*
* Copyright 2016 Matthew Page
*
* 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.
*
*/
definition(
name: "Gidjit Hub",
namespace: "com.gidjit.smartthings.hub",
author: "Matthew Page",
description: "Act as an endpoint so user's of Gidjit can quickly access and control their devices and execute routines. Users can do this quickly as Gidjit filters these actions based on their environment",
category: "Convenience",
iconUrl: "http://www.gidjit.com/appicon.png",
iconX2Url: "http://www.gidjit.com/appicon@2x.png",
iconX3Url: "http://www.gidjit.com/appicon@3x.png",
oauth: [displayName: "Gidjit", displayLink: "www.gidjit.com"])
preferences {
section ("Allow Gidjit to have access, there by allowing you to quickly control and monitor the following devices") {
input "switches", "capability.switch", title: "Control/Monitor your switches", multiple: true, required: false
input "thermostats", "capability.thermostat", title: "Control/Monitor your thermostats", multiple: true, required: false
input "windowShades", "capability.windowShade", title: "Control/Monitor your window shades", multiple: true, required: false //windowShade
//input "bulbs", "capability.colorControl", title: "Control your lights", multiple: true, required: false //windowShade
}
}
mappings {
path("/structureinfo") {
action: [
GET: "structureInfo"
]
}
path("/helloactions") {
action: [
GET: "helloActions"
]
}
path("/helloactions/:label") {
action: [
PUT: "executeAction"
]
}
path("/switch/:id/:command") {
action: [
PUT: "updateSwitch"
]
}
path("/thermostat/:id/:command") {
action: [
PUT: "updateThermostat"
]
}
path("/windowshade/:id/:command") {
action: [
PUT: "updateWindowShade"
]
}
path("/acquiredata/:id") {
action: [
GET: "acquiredata"
]
}
}
def installed() {
log.debug "Installed with settings: ${settings}"
initialize()
}
def updated() {
log.debug "Updated with settings: ${settings}"
unsubscribe()
initialize()
}
def initialize() {
// subscribe to attributes, devices, locations, etc.
}
def helloActions() {
def actions = location.helloHome?.getPhrases()*.label
if(!actions) {
return []
}
return actions
}
def executeAction() {
def actions = location.helloHome?.getPhrases()*.label
def a = actions?.find() { it == params.label }
if (!a) {
httpError(400, "invalid label $params.label")
return
}
location.helloHome?.execute(params.label)
}
/* this is the primary function called to query at the structure and its devices */
def structureInfo() { //list all devices
def list = [:]
def currId = location.id
list[currId] = [:]
list[currId].name = location.name
list[currId].id = location.id
list[currId].temperatureScale = location.temperatureScale
list[currId].devices = [:]
def setValues = {
if (params.brief) {
return [id: it.id, name: it.displayName]
}
def newList = [id: it.id, name: it.displayName, suppCapab: it.capabilities.collect {
"$it.name"
}, suppAttributes: it.supportedAttributes.collect {
"$it.name"
}, suppCommands: it.supportedCommands.collect {
"$it.name"
}]
return newList
}
switches?.each {
list[currId].devices[it.id] = setValues(it)
}
thermostats?.each {
list[currId].devices[it.id] = setValues(it)
}
windowShades?.each {
list[currId].devices[it.id] = setValues(it)
}
return list
}
/* This function returns all of the current values of the specified Devices attributes */
def acquiredata() {
def resp = [:]
if (!params.id) {
httpError(400, "invalid id $params.id")
return
}
def dev = switches.find() { it.id == params.id } ?: windowShades.find() { it.id == params.id } ?:
thermostats.find() { it.id == params.id }
if (!dev) {
httpError(400, "invalid id $params.id")
return
}
def att = dev.supportedAttributes
att.each {
resp[it.name] = dev.currentValue("$it.name")
}
return resp
}
void updateSwitch() {
// use the built-in request object to get the command parameter
def command = params.command
def sw = switches.find() { it.id == params.id }
if (!sw) {
httpError(400, "invalid id $params.id")
return
}
switch(command) {
case "on":
if ( sw.currentSwitch != "on" ) {
sw.on()
}
break
case "off":
if ( sw.currentSwitch != "off" ) {
sw.off()
}
break
default:
httpError(400, "$command is not a valid")
}
}
void updateThermostat() {
// use the built-in request object to get the command parameter
def command = params.command
def therm = thermostats.find() { it.id == params.id }
if (!therm || !command) {
httpError(400, "invalid id $params.id")
return
}
def passComm = [
"off",
"heat",
"emergencyHeat",
"cool",
"fanOn",
"fanAuto",
"fanCirculate",
"auto"
]
def passNumParamComm = [
"setHeatingSetpoint",
"setCoolingSetpoint",
]
def passStringParamComm = [
"setThermostatMode",
"setThermostatFanMode",
]
if (command in passComm) {
therm."$command"()
} else if (command in passNumParamComm && params.p1 && params.p1.isFloat()) {
therm."$command"(Float.parseFloat(params.p1))
} else if (command in passStringParamComm && params.p1) {
therm."$command"(params.p1)
} else {
httpError(400, "$command is not a valid command")
}
}
void updateWindowShade() {
// use the built-in request object to get the command parameter
def command = params.command
def ws = windowShades.find() { it.id == params.id }
if (!ws || !command) {
httpError(400, "invalid id $params.id")
return
}
def passComm = [
"open",
"close",
"presetPosition",
]
if (command in passComm) {
ws."$command"()
} else {
httpError(400, "$command is not a valid command")
}
}
// TODO: implement event handlers

View File

@@ -0,0 +1,345 @@
/**
* SmartThings service for Prempoint
*
* Author: Prempoint Inc. (c) 2016
*
*/
definition(
name: "Prempoint",
namespace: "prempoint.com",
author: "Prempoint Inc.",
description: "SmartThings service for Prempoint",
category: "Connections",
iconUrl: "http://www.prempoint.com/images/social_app_emblem_50x50.png",
iconX2Url: "http://www.prempoint.com/images/social_app_emblem_100x100.png",
iconX3Url: "http://www.prempoint.com/images/social_app_emblem_150x150.png",
oauth: [displayName: "Prempoint", displayLink: "http://www.prempoint.com/"])
preferences {
section("Allow Prempoint to Control & Access These Things...") {
input "switches", "capability.switch", title: "Which Switches?", multiple: true, required: false
input "locks", "capability.lock", title: "Which Locks?", multiple: true, required: false
input "garagedoors", "capability.garageDoorControl", title: "Which Garage Doors?", multiple: true, required: false
//input "doors", "capability.doorControl", title: "Which Doors?", multiple: true, required: false
input "cameras", "capability.imageCapture", title: "Which Cameras?", multiple: true, required: false
}
}
mappings {
path("/list") {
action: [
GET: "listDevices"
]
}
path("/switches") {
action: [
GET: "listSwitches"
]
}
path("/switches/:id") {
action: [
GET: "showSwitch"
]
}
path("/switches/:id/:command") {
action: [
GET: "updateSwitch"
]
}
path("/switches/:id/:command/:level") {
action: [
GET: "updateSwitch"
]
}
path("/locks") {
action: [
GET: "listLocks"
]
}
path("/locks/:id") {
action: [
GET: "showLock"
]
}
path("/locks/:id/:command") {
action: [
GET: "updateLock"
]
}
path("/doors/:id") {
action: [
GET: "showDoor"
]
}
path("/doors/:id/:command") {
action: [
GET: "updateDoor"
]
}
path("/garagedoors/:id") {
action: [
GET: "showGarageDoor"
]
}
path("/garagedoors/:id/:command") {
action: [
GET: "updateGarageDoor"
]
}
path("/cameras/:id") {
action: [
GET: "showCamera"
]
}
path("/cameras/:id/:command") {
action: [
GET: "updateCamera"
]
}
}
def installed() {}
def updated() {}
def listDevices() {
log.debug "entering listDevices"
//return listSwitches() + listLocks() + listGarageDoors() + listDoors() + listCameras()
return listSwitches() + listLocks() + listGarageDoors() + listCameras()
}
//switches
def listSwitches() {
log.debug "entering listSwitches"
switches.collect{showDevice(it,"switch")}
}
def showSwitch() {
log.debug "entering showSwitches"
show(switches, "switch")
}
def updateSwitch() {
log.debug "entering updateSwitches"
update(switches, "switch")
}
//locks
def listLocks() {
log.debug "entering listLocks"
locks.collect{showDevice(it,"lock")}
}
def showLock() {
log.debug "entering showLock"
show(locks, "lock")
}
def updateLock() {
log.debug "entering updateLock"
update(locks, "lock")
}
//doors
def listDoors() {
log.debug "entering listDoors"
locks.collect{showDevice(it,"door")}
}
def showDoor() {
log.debug "entering showDoors"
show(doors, "door")
}
def updateDoor() {
log.debug "entering updateDoor"
update(doors, "door")
}
//garagedoors
def listGarageDoors() {
log.debug "entering listGarageDoors"
locks.collect{showDevice(it,"garagedoor")}
}
def showGarageDoor() {
log.debug "entering showGarageDoors"
show(garagedoors, "garagedoor")
}
def updateGarageDoor() {
log.debug "entering updateGarageDoor"
update(gargedoors, "garagedoor")
}
//cameras
def listCameras() {
log.debug "entering listCameras"
cameras.collect{showDevice(it,"image")}
}
def showCamera() {
log.debug "entering showCameras"
show(cameras, "camera")
}
def updateCamera() {
log.debug "entering updateCamera"
update(cameras, "camera")
}
def deviceHandler(evt) {}
private update(devices, type) {
def rc = null
//def command = request.JSON?.command
def command = params.command
log.debug "update, request: params: ${params}, devices: $devices.id type=$type command=$command"
// Process the command.
if (command)
{
def dev = devices.find { it.id == params.id }
if (!dev) {
httpError(404, "Device not found: $params.id")
} else if (type == "switch") {
switch(command) {
case "on":
rc = dev.on()
break
case "off":
rc = dev.off()
break
default:
httpError(400, "Device command=$command is not a valid for device=$it.id $dev")
}
} else if (type == "lock") {
switch(command) {
case "lock":
rc = dev.lock()
break
case "unlock":
rc = dev.unlock()
break
default:
httpError(400, "Device command=$command is not a valid for device:=$it.id $dev")
}
} else if (type == "door") {
switch(command) {
case "open":
rc = dev.open()
break
case "close":
rc = dev.close()
break
default:
httpError(400, "Device command=$command is not a valid for device=$it.id $dev")
}
} else if (type == "garagedoor") {
switch(command) {
case "open":
rc = dev.open()
break
case "close":
rc = dev.close()
break
default:
httpError(400, "Device command=$command is not a valid for device=$it.id $dev")
}
} else if (type == "camera") {
switch(command) {
case "take":
rc = dev.take()
log.debug "Device command=$command device=$it.id $dev current image=$it.currentImage"
break
default:
httpError(400, "Device command=$command is not a valid for device=$it.id $dev")
}
}
log.debug "executed device=$it.id $dev command=$command rc=$rc"
// Check that the device is a switch that is currently on, supports 'setLevel"
// and that a level was specified.
int level = params.level ? params.level as int : -1;
if ((type == "switch") && (dev.currentValue('switch') == "on") && hasLevel(dev) && (level != -1)) {
log.debug "device about to setLevel=$level"
dev.setLevel(level);
}
// Show the device info if necessary.
if (rc == null) {
rc = showDevice(dev, type)
}
}
return rc
}
private show(devices, type) {
def dev = devices.find { it.id == params.id }
if (!dev) {
httpError(404, "Device not found")
} else {
// Show the device info.
showDevice(dev, type)
}
}
private showDevice(it, type) {
def props = null
// Get the current state for the device type.
def state = [it.currentState(type)]
// Check that whether the a switch device with level support is located and update the returned device type.
def devType = type
if (type == "switch" && hasLevel(it)) {
// Assign "switchWithLevel" to device type.
devType = "switchWithLevel"
// Add the level state.
def levelState = it.currentState("level")
if (levelState) {
state.add(levelState)
}
}
log.debug "device label=$it.label type=$devType"
// Assign the device item properties if appropriate.
if (it) {
props = [id: it.id, label: it.label, type: devType, state: state]
// Add the hub information to the device properties
// if appropriate.
if (it.hub) {
props.put("location", it.hub.hub.location)
}
if (it.currentImage) {
props.put("currentImage", it.currentImage)
}
}
return props
}
private hasLevel(device) {
// Default return value.
def rc = false;
// Get the device supported commands.
def supportedCommands = device.supportedCommands
// Check to see if the "setLevel" was found and assign
// the appropriate return value.
if (supportedCommands) {
// Find the "setLevel" command.
rc = supportedCommands.toString().indexOf("setLevel") != -1
}
log.debug "hasLevel device label=$device.label supportedCommands=$supportedCommands rc=$rc"
return rc
}

View File

@@ -21,6 +21,12 @@ preferences()
section("Allow Simple Control to Monitor and Control These Things...")
{
input "switches", "capability.switch", title: "Which Switches?", multiple: true, required: false
input "locks", "capability.lock", title: "Which Locks?", multiple: true, required: false
input "thermostats", "capability.thermostat", title: "Which Thermostats?", multiple: true, required: false
input "doorControls", "capability.doorControl", title: "Which Door Controls?", multiple: true, required: false
input "colorControls", "capability.colorControl", title: "Which Color Controllers?", multiple: true, required: false
input "musicPlayers", "capability.musicPlayer", title: "Which Music Players?", multiple: true, required: false
input "switchLevels", "capability.switchLevel", title: "Which Adjustable Switches?", multiple: true, required: false
}
page(name: "mainPage", title: "Simple Control Setup", content: "mainPage", refreshTimeout: 5)
@@ -31,12 +37,17 @@ preferences()
mappings {
path("/devices") {
action: [
GET: "getDevices"
]
}
path("/:deviceType/devices") {
action: [
GET: "getDevices",
POST: "handleDevicesWithIDs"
]
}
path("/device/:id") {
}
path("/device/:deviceType/:id") {
action: [
GET: "getDevice",
POST: "updateDevice"
@@ -93,33 +104,40 @@ def handleDevicesWithIDs()
//log.debug("ids: ${ids}")
def command = data?.command
def arguments = data?.arguments
def type = params?.deviceType
//log.debug("device type: ${type}")
if (command)
{
def success = false
def statusCode = 404
//log.debug("command ${command}, arguments ${arguments}")
for (devId in ids)
{
def device = allDevices.find { it.id == devId }
if (device) {
if (arguments) {
//log.debug("device: ${device}")
// Check if we have a device that responds to the specified command
if (validateCommand(device, type, command)) {
if (arguments) {
device."$command"(*arguments)
} else {
device."$command"()
}
success = true
}
else {
device."$command"()
}
statusCode = 200
} else {
//log.debug("device not found ${devId}")
statusCode = 403
}
}
if (success)
def responseData = "{}"
switch (statusCode)
{
render status: 200, data: "{}"
}
else
{
render status: 404, data: '{"msg": "Device not found"}'
case 403:
responseData = '{"msg": "Access denied. This command is not supported by current capability."}'
break
case 404:
responseData = '{"msg": "Device not found"}'
break
}
render status: statusCode, data: responseData
}
else
{
@@ -164,25 +182,101 @@ def updateDevice()
def data = request.JSON
def command = data?.command
def arguments = data?.arguments
def type = params?.deviceType
//log.debug("device type: ${type}")
//log.debug("updateDevice, params: ${params}, request: ${data}")
if (!command) {
render status: 400, data: '{"msg": "command is required"}'
} else {
def statusCode = 404
def device = allDevices.find { it.id == params.id }
if (device) {
if (arguments) {
device."$command"(*arguments)
// Check if we have a device that responds to the specified command
if (validateCommand(device, type, command)) {
if (arguments) {
device."$command"(*arguments)
}
else {
device."$command"()
}
statusCode = 200
} else {
device."$command"()
statusCode = 403
}
render status: 204, data: "{}"
} else {
render status: 404, data: '{"msg": "Device not found"}'
}
def responseData = "{}"
switch (statusCode)
{
case 403:
responseData = '{"msg": "Access denied. This command is not supported by current capability."}'
break
case 404:
responseData = '{"msg": "Device not found"}'
break
}
render status: statusCode, data: responseData
}
}
/**
* Validating the command passed by the user based on capability.
* @return boolean
*/
def validateCommand(device, deviceType, command) {
//log.debug("validateCommand ${command}")
def capabilityCommands = getDeviceCapabilityCommands(device.capabilities)
//log.debug("capabilityCommands: ${capabilityCommands}")
def currentDeviceCapability = getCapabilityName(deviceType)
//log.debug("currentDeviceCapability: ${currentDeviceCapability}")
if (capabilityCommands[currentDeviceCapability]) {
return command in capabilityCommands[currentDeviceCapability] ? true : false
} else {
// Handling other device types here, which don't accept commands
httpError(400, "Bad request.")
}
}
/**
* Need to get the attribute name to do the lookup. Only
* doing it for the device types which accept commands
* @return attribute name of the device type
*/
def getCapabilityName(type) {
switch(type) {
case "switches":
return "Switch"
case "locks":
return "Lock"
case "thermostats":
return "Thermostat"
case "doorControls":
return "Door Control"
case "colorControls":
return "Color Control"
case "musicPlayers":
return "Music Player"
case "switchLevels":
return "Switch Level"
default:
return type
}
}
/**
* Constructing the map over here of
* supported commands by device capability
* @return a map of device capability -> supported commands
*/
def getDeviceCapabilityCommands(deviceCapabilities) {
def map = [:]
deviceCapabilities.collect {
map[it.name] = it.commands.collect{ it.name.toString() }
}
return map
}
def listSubscriptions()
{
//log.debug "listSubscriptions()"
@@ -361,7 +455,13 @@ def agentDiscovery(params=[:])
}
section("Allow Simple Control to Monitor and Control These Things...")
{
input "switches", "capability.switch", title: "Which Switches?", multiple: true, required: false
input "switches", "capability.switch", title: "Which Switches?", multiple: true, required: false
input "locks", "capability.lock", title: "Which Locks?", multiple: true, required: false
input "thermostats", "capability.thermostat", title: "Which Thermostats?", multiple: true, required: false
input "doorControls", "capability.doorControl", title: "Which Door Controls?", multiple: true, required: false
input "colorControls", "capability.colorControl", title: "Which Color Controllers?", multiple: true, required: false
input "musicPlayers", "capability.musicPlayer", title: "Which Music Players?", multiple: true, required: false
input "switchLevels", "capability.switchLevel", title: "Which Adjustable Switches?", multiple: true, required: false
}
}
}
@@ -672,5 +772,3 @@ def List getRealHubFirmwareVersions()
}

View File

@@ -73,7 +73,8 @@ def temperatureHandler(evt) {
// TODO: Send "Temperature back to normal" SMS, turn switch off
} else {
log.debug "Temperature dropped below $tooCold: sending SMS to $phone1 and activating $mySwitch"
send("${temperatureSensor1.displayName} is too cold, reporting a temperature of ${evt.value}${evt.unit?:"F"}")
def tempScale = location.temperatureScale ?: "F"
send("${temperatureSensor1.displayName} is too cold, reporting a temperature of ${evt.value}${evt.unit?:tempScale}")
switch1?.on()
}
}

View File

@@ -73,7 +73,8 @@ def temperatureHandler(evt) {
// TODO: Send "Temperature back to normal" SMS, turn switch off
} else {
log.debug "Temperature rose above $tooHot: sending SMS to $phone1 and activating $mySwitch"
send("${temperatureSensor1.displayName} is too hot, reporting a temperature of ${evt.value}${evt.unit?:"F"}")
def tempScale = location.temperatureScale ?: "F"
send("${temperatureSensor1.displayName} is too hot, reporting a temperature of ${evt.value}${evt.unit?:tempScale}")
switch1?.on()
}
}

View File

@@ -339,7 +339,7 @@ def initialize() {
state.aux = 0
if (selectedhubs || selectedactivities) {
addDevice()
runEvery5Minutes("discovery")
runEvery5Minutes("poll")
}
}
@@ -394,9 +394,9 @@ def discovery() {
}
} catch (java.net.SocketTimeoutException e) {
log.warn "Connection to the hub timed out. Please restart the hub and try again."
state.resethub = true
state.resethub = true
} catch (e) {
log.warn "Hostname in certificate didn't match. Please try again later."
log.info "Logitech Harmony - Error: $e"
}
return null
}
@@ -474,7 +474,7 @@ def activity(dni,mode) {
def poll() {
// GET THE LIST OF ACTIVITIES
if (state.HarmonyAccessToken) {
getActivityList()
getActivityList()
def Params = [auth: state.HarmonyAccessToken]
def url = "https://home.myharmony.com/cloudapi/state?${toQueryString(Params)}"
try {
@@ -520,14 +520,17 @@ def poll() {
return "Poll completed $map - $state.hubs"
}
} catch (groovyx.net.http.HttpResponseException e) {
if (e.statusCode == 401) { // token is expired
state.remove("HarmonyAccessToken")
return "Harmony Access token has expired"
}
} catch(Exception e) {
log.trace e
}
}
if (e.statusCode == 401) { // token is expired
state.remove("HarmonyAccessToken")
log.warn "Harmony Access token has expired"
}
} catch (java.net.SocketTimeoutException e) {
log.warn "Connection to the hub timed out. Please restart the hub and try again."
state.resethub = true
} catch (e) {
log.info "Logitech Harmony - Error: $e"
}
}
}