Compare commits

..

1 Commits

Author SHA1 Message Date
이진희
7a92ba3fe5 MSA-993: 테스트 2016-03-23 02:47:25 -05:00
25 changed files with 307 additions and 759 deletions

View File

@@ -1,18 +1,15 @@
import java.nio.charset.StandardCharsets
import java.nio.file.Paths
import com.smartthings.deployment.slack.FileUpload
import com.smartthings.deployment.slack.Message
apply plugin: 'groovy'
apply plugin: 'smartthings-executable-deployment'
apply plugin: 'smartthings-slack'
apply plugin: 'smartthings-hipchat'
buildscript {
dependencies {
classpath "com.smartthings.deployment:executable-deployment-scripts:1.0.7"
classpath "com.smartthings.deployment:executable-deployment-scripts:1.0.3"
}
repositories {
mavenLocal()
jcenter()
maven {
credentials {
@@ -32,43 +29,7 @@ repositories {
dependencies {
}
slackSendMessage {
String branch = project.hasProperty('branch') ? project.property('branch') : 'unknown'
String token = project.hasProperty('slackToken') ? project.property('slackToken') : null
String webhookUrl = project.hasProperty('slackWebhookUrl') ? project.property('slackWebhookUrl') : null
String channel = project.hasProperty('slackChannel') ? project.property('slackChannel') : null
String drinks = 'https://dl.dropboxusercontent.com/s/m1z5mpd3c83lwev/minion_beer.jpeg?dl=0'
String wolverine = 'https://dl.dropboxusercontent.com/s/4lbjqzvm2v033u9/minion_wolverine.jpg?dl=0'
String beach = 'https://dl.dropboxusercontent.com/s/rqrfgxk53gfng69/minion_beach.png?dl=0'
String iconUrl
String color
String messageText
String username
switch (branch) {
case 'master':
username = 'Hickory'
iconUrl = wolverine
color = '#35D0F2'
messageText = 'Began deployment of _SmartThingsPublic[master]_ branch to the _Dev_ environments.'
break
case 'staging':
username = 'Dickory'
iconUrl = beach
color = '#FFDE20'
messageText = 'Began deployment of _SmartThingsPublic[staging]_ branch to the _Staging_ environments.'
break
case 'production':
username = 'Dock'
iconUrl = drinks
color = '#FF1D23'
messageText = 'Began deployment of _SmartThingsPublic[production]_ branch to the _Prod_ environments.'
break
default:
username = 'Hickory'
iconUrl = wolverine
color = '#35D0F2'
messageText = "Began deployment of an _SmartThingsPublic[${branch}]_ branch. Have no idea what's going on."
}
hipchatShareFile {
List<String> archives = []
File rootDir = new File("${project.buildDir}/archives")
if (rootDir.exists()) {
@@ -81,25 +42,19 @@ slackSendMessage {
}
}
}
Date date = new Date()
String fileDate = date.format('yyyy-MM-dd_HH-mm-ss', TimeZone.getTimeZone('GMT'))
// Required Task Arguments.
file = new FileUpload(
data: archives.join('\n').getBytes(StandardCharsets.UTF_8),
filename: "deployment-notes-${fileDate}.txt",
title: 'Deployment Notes',
channels: channel,
token: token,
color: color
)
message = new Message(
webhookUrl: webhookUrl,
username: username,
asUser: true,
iconUrl: iconUrl,
channel: channel,
fallback: 'Deployment Notification',
text: messageText
)
// Set task properties
data = archives.join('\n').getBytes(StandardCharsets.UTF_8)
fileName = 'deployment-notes.txt'
contentType = 'text/html'
}
hipchatSendNotification {
String branch = project.hasProperty('branch') ? project.property('branch') : 'unknown'
message = "Began executable deploy of SmartThingsPublic(${branch})."
if (branch == 'master') {
message += ' (dev shards)'
}
color = branch == 'master' ? 'yellow' : 'red'
notify = true
}

View File

@@ -15,11 +15,13 @@ deployment:
develop:
branch: master
commands:
- ./gradlew deployArchives -PsmartThingsArtifactoryUserName="$ARTIFACTORY_USERNAME" -PsmartThingsArtifactoryPassword="$ARTIFACTORY_PASSWORD" -Ps3Buckets="$S3_BUCKETS_DEV"
- ./gradlew slackSendMessage -PsmartThingsArtifactoryUserName="$ARTIFACTORY_USERNAME" -PsmartThingsArtifactoryPassword="$ARTIFACTORY_PASSWORD" -Pbranch="$CIRCLE_BRANCH" -PslackToken="$SLACK_TOKEN" -PslackWebhookUrl="$SLACK_WEBHOOK_URL" -PslackChannel="$SLACK_CHANNEL" --stacktrace
- ./gradlew deployArchives -PsmartThingsArtifactoryUserName=$ARTIFACTORY_USERNAME -PsmartThingsArtifactoryPassword=$ARTIFACTORY_PASSWORD -Ps3BucketName=$S3_BUCKET_NAME_PREPROD_DEV
- ./gradlew hipchatSendNotification -PsmartThingsArtifactoryUserName=$ARTIFACTORY_USERNAME -PsmartThingsArtifactoryPassword=$ARTIFACTORY_PASSWORD -Pbranch=$CIRCLE_BRANCH
- ./gradlew hipchatShareFile -PsmartThingsArtifactoryUserName=$ARTIFACTORY_USERNAME -PsmartThingsArtifactoryPassword=$ARTIFACTORY_PASSWORD
stage:
branch: staging
commands:
- ./gradlew deployArchives -PsmartThingsArtifactoryUserName="$ARTIFACTORY_USERNAME" -PsmartThingsArtifactoryPassword="$ARTIFACTORY_PASSWORD" -Ps3Buckets="$S3_BUCKETS_STAGE"
- ./gradlew slackSendMessage -PsmartThingsArtifactoryUserName="$ARTIFACTORY_USERNAME" -PsmartThingsArtifactoryPassword="$ARTIFACTORY_PASSWORD" -Pbranch="$CIRCLE_BRANCH" -PslackToken="$SLACK_TOKEN" -PslackWebhookUrl="$SLACK_WEBHOOK_URL" -PslackChannel="$SLACK_CHANNEL" --stacktrace
- ./gradlew deployArchives -PsmartThingsArtifactoryUserName=$ARTIFACTORY_USERNAME -PsmartThingsArtifactoryPassword=$ARTIFACTORY_PASSWORD -Ps3BucketName=$S3_BUCKET_NAME_PREPROD_STAGING
- ./gradlew hipchatSendNotification -PsmartThingsArtifactoryUserName=$ARTIFACTORY_USERNAME -PsmartThingsArtifactoryPassword=$ARTIFACTORY_PASSWORD -Pbranch=$CIRCLE_BRANCH
- ./gradlew hipchatShareFile -PsmartThingsArtifactoryUserName=$ARTIFACTORY_USERNAME -PsmartThingsArtifactoryPassword=$ARTIFACTORY_PASSWORD

View File

@@ -0,0 +1,42 @@
/**
* 내가만든장치
*
* Copyright 2016 이진희
*
* 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.
*
*/
metadata {
definition (name: "내가만든장치", namespace: "내가만든장치", author: "이진희") {
capability "Health Check"
capability "Test Capability"
}
simulator {
// TODO: define status and reply messages here
}
tiles {
// TODO: define your main and details tiles here
}
}
// parse events into attributes
def parse(String description) {
log.debug "Parsing '${description}'"
// TODO: handle 'checkInterval' attribute
}
// handle commands
def ping() {
log.debug "Executing 'ping'"
// TODO: handle 'ping' command
}

View File

@@ -1,194 +0,0 @@
/**
* Iris Smart Fob
*
* Copyright 2015 Mitch Pond
* Presence code adapted from SmartThings Arrival Sensor HA device type
*
* 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.
*
*/
metadata {
definition (name: "Iris Smart Fob", namespace: "mitchpond", author: "Mitch Pond") {
capability "Battery"
capability "Button"
capability "Configuration"
capability "Presence Sensor"
capability "Sensor"
//fingerprint endpointId: "01", profileId: "0104", inClusters: "0000,0001,0003,0007,0020,0B05", outClusters: "0003,0006,0019", model:"3450-L", manufacturer: "CentraLite"
}
preferences{
input ("holdTime", "number", title: "Minimum time in seconds for a press to count as \"held\"",
defaultValue: 3, displayDuringSetup: false)
input "checkInterval", "enum", title: "Presence timeout (minutes)",
defaultValue:"2", options: ["2", "3", "5"], displayDuringSetup: false
input "logging", "bool", title: "Enable debug logging",
defaultValue: false, displayDuringSetup: false
}
tiles(scale: 2) {
standardTile("presence", "device.presence", width: 4, height: 4, canChangeBackground: true) {
state "present", label: "Present", labelIcon:"st.presence.tile.present", backgroundColor:"#53a7c0"
state "not present", labelIcon:"st.presence.tile.not-present", backgroundColor:"#ffffff"
}
standardTile("button", "device.button", decoration: "flat", width: 2, height: 2) {
state "default", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#ffffff"
}
valueTile("battery", "device.battery", decoration: "flat", width: 2, height: 2) {
state "battery", label:'${currentValue}% battery', unit:""
}
main (["presence"])
details(["presence","button","battery"])
}
}
def parse(String description) {
def descMap = zigbee.parseDescriptionAsMap(description)
logIt descMap
state.lastCheckin = now()
logIt "lastCheckin = ${state.lastCheckin}"
handlePresenceEvent(true)
def results = []
if (description?.startsWith('catchall:'))
results = parseCatchAllMessage(descMap)
else if (description?.startsWith('read attr -'))
results = parseReportAttributeMessage(descMap)
else logIt(descMap, "trace")
return results;
}
def updated() {
startTimer()
configure()
}
def configure(){
logIt "Configuring Smart Fob..."
[
"zdo bind 0x${device.deviceNetworkId} 1 1 6 {${device.zigbeeId}} {}", "delay 200",
"zdo bind 0x${device.deviceNetworkId} 2 1 6 {${device.zigbeeId}} {}", "delay 200",
"zdo bind 0x${device.deviceNetworkId} 3 1 6 {${device.zigbeeId}} {}", "delay 200",
"zdo bind 0x${device.deviceNetworkId} 4 1 6 {${device.zigbeeId}} {}", "delay 200",
"zdo bind 0x${device.deviceNetworkId} 1 1 1 {${device.zigbeeId}} {}", "delay 200"
] +
zigbee.configureReporting(0x0001,0x0020,0x20,20,20,0x01)
}
def parseCatchAllMessage(descMap) {
if (descMap?.clusterId == "0006" && descMap?.command == "01") //button pressed
handleButtonPress(descMap.sourceEndpoint as int)
else if (descMap?.clusterId == "0006" && descMap?.command == "00") //button released
handleButtonRelease(descMap.sourceEndpoint as int)
else logIt("Parse: Unhandled message: ${descMap}","trace")
}
def parseReportAttributeMessage(descMap) {
if (descMap?.cluster == "0001" && descMap?.attrId == "0020") createBatteryEvent(getBatteryLevel(descMap.value))
else logIt descMap
}
private createBatteryEvent(percent) {
logIt "Battery level at " + percent
return createEvent([name: "battery", value: percent])
}
//this method determines if a press should count as a push or a hold and returns the relevant event type
private handleButtonRelease(button) {
logIt "lastPress state variable: ${state.lastPress}"
def sequenceError = {logIt("Uh oh...missed a message? Dropping this event.", "error"); state.lastPress = null; return []}
if (!state.lastPress) return sequenceError()
else if (state.lastPress.button != button) return sequenceError()
def currentTime = now()
def startOfPress = state.lastPress?.time
def timeDif = currentTime - startOfPress
def holdTimeMillisec = (settings.holdTime?:3).toInteger() * 1000
state.lastPress = null //we're done with this. clear it to make error conditions easier to catch
if (timeDif < 0)
//likely a message sequence issue or dropped packet. Drop this press and wait for another.
return sequenceError()
else if (timeDif < holdTimeMillisec)
return createButtonEvent(button,"pushed")
else
return createButtonEvent(button,"held")
}
private handleButtonPress(button) {
state.lastPress = [button: button, time: now()]
}
private createButtonEvent(button,action) {
logIt "Button ${button} ${action}"
return createEvent([
name: "button",
value: action,
data:[buttonNumber: button],
descriptionText: "${device.displayName} button ${button} was ${action}",
isStateChange: true,
displayed: true])
}
private getBatteryLevel(rawValue) {
def intValue = Integer.parseInt(rawValue,16)
def min = 2.1
def max = 3.0
def vBatt = intValue / 10
return ((vBatt - min) / (max - min) * 100) as int
}
private handlePresenceEvent(present) {
def wasPresent = device.currentState("presence")?.value == "present"
if (!wasPresent && present) {
logIt "Sensor is present"
startTimer()
} else if (!present) {
logIt "Sensor is not present"
stopTimer()
}
def linkText = getLinkText(device)
def eventMap = [
name: "presence",
value: present ? "present" : "not present",
linkText: linkText,
descriptionText: "${linkText} has ${present ? 'arrived' : 'left'}",
]
logIt "Creating presence event: ${eventMap}"
sendEvent(eventMap)
}
private startTimer() {
logIt "Scheduling periodic timer"
schedule("0 * * * * ?", checkPresenceCallback)
}
private stopTimer() {
logIt "Stopping periodic timer"
unschedule()
}
def checkPresenceCallback() {
def timeSinceLastCheckin = (now() - state.lastCheckin) / 1000
def theCheckInterval = (checkInterval ? checkInterval as int : 2) * 60
logIt "Sensor checked in ${timeSinceLastCheckin} seconds ago"
if (timeSinceLastCheckin >= theCheckInterval) {
handlePresenceEvent(false)
}
}
// ****** Utility functions ******
private logIt(str, logLevel = 'debug') {if (settings.logging) log."$logLevel"(str) }

View File

@@ -1,16 +1,16 @@
#==============================================================================
# Copyright 2016 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
# 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
# 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.
#==============================================================================
# Purpose: Arrival Sensor HA i18n Translation File
@@ -28,7 +28,6 @@
'''Presence timeout (minutes)'''.ko=시간 초과. 스마트폰 위치 정보
'''Tap to set'''.ko=눌러서 설정
'''Arrival Sensor'''.ko=도착알림 센서
'''${currentValue}% battery'''.ko=${currentValue}% 배터리
# Events / Notifications
'''{{ linkText }} battery was {{ value }}'''.ko={{ linkText }}남아있는 배터리는 {{ value }}입니다.
'''{{ linkText }} has arrived'''.ko={{ linkText }}집에 도착했습니다.

View File

@@ -1,227 +0,0 @@
/**
* Hue Bloom
*
* Philips Hue Type "Color Light"
*
* Author: SmartThings
*/
// for the UI
metadata {
// Automatically generated. Make future change here.
definition (name: "Hue Bloom", namespace: "smartthings", author: "SmartThings") {
capability "Switch Level"
capability "Actuator"
capability "Color Control"
capability "Switch"
capability "Refresh"
capability "Sensor"
command "setAdjustedColor"
command "reset"
command "refresh"
}
simulator {
// TODO: define status and reply messages here
}
tiles (scale: 2){
multiAttributeTile(name:"rich-control", type: "lighting", width: 6, height: 4, canChangeIcon: true){
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
attributeState "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#00A0DC", nextState:"turningOff"
attributeState "off", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#C6C7CC", nextState:"turningOn"
attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#00A0DC", nextState:"turningOff"
attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#C6C7CC", nextState:"turningOn"
}
tileAttribute ("device.level", key: "SLIDER_CONTROL") {
attributeState "level", action:"switch level.setLevel", range:"(0..100)"
}
tileAttribute ("device.level", key: "SECONDARY_CONTROL") {
attributeState "level", label: 'Level ${currentValue}%'
}
tileAttribute ("device.color", key: "COLOR_CONTROL") {
attributeState "color", action:"setAdjustedColor"
}
}
standardTile("reset", "device.reset", height: 2, width: 2, inactiveLabel: false, decoration: "flat") {
state "default", label:"Reset Color", action:"reset", icon:"st.lights.philips.hue-single"
}
standardTile("refresh", "device.refresh", height: 2, width: 2, inactiveLabel: false, decoration: "flat") {
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
}
main(["rich-control"])
details(["rich-control", "colorTempSliderControl", "colorTemp", "reset", "refresh"])
}
}
// parse events into attributes
def parse(description) {
log.debug "parse() - $description"
def results = []
def map = description
if (description instanceof String) {
log.debug "Hue Bulb stringToMap - ${map}"
map = stringToMap(description)
}
if (map?.name && map?.value) {
results << createEvent(name: "${map?.name}", value: "${map?.value}")
}
results
}
// handle commands
void on() {
log.trace parent.on(this)
sendEvent(name: "switch", value: "on")
}
void off() {
log.trace parent.off(this)
sendEvent(name: "switch", value: "off")
}
void nextLevel() {
def level = device.latestValue("level") as Integer ?: 0
if (level <= 100) {
level = Math.min(25 * (Math.round(level / 25) + 1), 100) as Integer
}
else {
level = 25
}
setLevel(level)
}
void setLevel(percent) {
log.debug "Executing 'setLevel'"
if (verifyPercent(percent)) {
parent.setLevel(this, percent)
sendEvent(name: "level", value: percent, descriptionText: "Level has changed to ${percent}%")
sendEvent(name: "switch", value: "on")
}
}
void setSaturation(percent) {
log.debug "Executing 'setSaturation'"
if (verifyPercent(percent)) {
parent.setSaturation(this, percent)
sendEvent(name: "saturation", value: percent, displayed: false)
}
}
void setHue(percent) {
log.debug "Executing 'setHue'"
if (verifyPercent(percent)) {
parent.setHue(this, percent)
sendEvent(name: "hue", value: percent, displayed: false)
}
}
void setColor(value) {
log.debug "setColor: ${value}, $this"
def events = []
def validValues = [:]
if (verifyPercent(value.hue)) {
events << createEvent(name: "hue", value: value.hue, displayed: false)
validValues.hue = value.hue
}
if (verifyPercent(value.saturation)) {
events << createEvent(name: "saturation", value: value.saturation, displayed: false)
validValues.saturation = value.saturation
}
if (value.hex != null) {
if (value.hex ==~ /^\#([A-Fa-f0-9]){6}$/) {
events << createEvent(name: "color", value: value.hex)
validValues.hex = value.hex
} else {
log.warn "$value.hex is not a valid color"
}
}
if (verifyPercent(value.level)) {
events << createEvent(name: "level", value: value.level, descriptionText: "Level has changed to ${value.level}%")
validValues.level = value.level
}
if (value.switch == "off" || (value.level != null && value.level <= 0)) {
events << createEvent(name: "switch", value: "off")
validValues.switch = "off"
} else {
events << createEvent(name: "switch", value: "on")
validValues.switch = "on"
}
if (!events.isEmpty()) {
parent.setColor(this, validValues)
}
events.each {
sendEvent(it)
}
}
void reset() {
log.debug "Executing 'reset'"
def value = [level:100, saturation:56, hue:23]
setAdjustedColor(value)
parent.poll()
}
void setAdjustedColor(value) {
if (value) {
log.trace "setAdjustedColor: ${value}"
def adjusted = value + [:]
adjusted.hue = adjustOutgoingHue(value.hue)
// Needed because color picker always sends 100
adjusted.level = null
setColor(adjusted)
} else {
log.warn "Invalid color input"
}
}
void setColorTemperature(value) {
if (value) {
log.trace "setColorTemperature: ${value}k"
parent.setColorTemperature(this, value)
sendEvent(name: "colorTemperature", value: value)
sendEvent(name: "switch", value: "on")
} else {
log.warn "Invalid color temperature"
}
}
void refresh() {
log.debug "Executing 'refresh'"
parent.manualRefresh()
}
def adjustOutgoingHue(percent) {
def adjusted = percent
if (percent > 31) {
if (percent < 63.0) {
adjusted = percent + (7 * (percent -30 ) / 32)
}
else if (percent < 73.0) {
adjusted = 69 + (5 * (percent - 62) / 10)
}
else {
adjusted = percent + (2 * (100 - percent) / 28)
}
}
log.info "percent: $percent, adjusted: $adjusted"
adjusted
}
def verifyPercent(percent) {
if (percent == null)
return false
else if (percent >= 0 && percent <= 100) {
return true
} else {
log.warn "$percent is not 0-100"
return false
}
}

View File

@@ -1,8 +1,6 @@
/**
* Hue Bulb
*
* Philips Hue Type "Extended Color Light"
*
* Author: SmartThings
*/
@@ -71,13 +69,11 @@ metadata {
def parse(description) {
log.debug "parse() - $description"
def results = []
def map = description
if (description instanceof String) {
log.debug "Hue Bulb stringToMap - ${map}"
map = stringToMap(description)
}
if (map?.name && map?.value) {
results << createEvent(name: "${map?.name}", value: "${map?.value}")
}
@@ -107,104 +103,62 @@ void nextLevel() {
}
void setLevel(percent) {
log.debug "Executing 'setLevel'"
if (verifyPercent(percent)) {
parent.setLevel(this, percent)
sendEvent(name: "level", value: percent, descriptionText: "Level has changed to ${percent}%")
sendEvent(name: "switch", value: "on")
}
log.debug "Executing 'setLevel'"
parent.setLevel(this, percent)
sendEvent(name: "level", value: percent, descriptionText: "Level has changed to ${percent}%")
}
void setSaturation(percent) {
log.debug "Executing 'setSaturation'"
if (verifyPercent(percent)) {
parent.setSaturation(this, percent)
sendEvent(name: "saturation", value: percent, displayed: false)
}
log.debug "Executing 'setSaturation'"
parent.setSaturation(this, percent)
sendEvent(name: "saturation", value: percent, displayed: false)
}
void setHue(percent) {
log.debug "Executing 'setHue'"
if (verifyPercent(percent)) {
parent.setHue(this, percent)
sendEvent(name: "hue", value: percent, displayed: false)
}
log.debug "Executing 'setHue'"
parent.setHue(this, percent)
sendEvent(name: "hue", value: percent, displayed: false)
}
void setColor(value) {
log.debug "setColor: ${value}, $this"
def events = []
def validValues = [:]
if (verifyPercent(value.hue)) {
events << createEvent(name: "hue", value: value.hue, displayed: false)
validValues.hue = value.hue
}
if (verifyPercent(value.saturation)) {
events << createEvent(name: "saturation", value: value.saturation, displayed: false)
validValues.saturation = value.saturation
}
if (value.hex != null) {
if (value.hex ==~ /^\#([A-Fa-f0-9]){6}$/) {
events << createEvent(name: "color", value: value.hex)
validValues.hex = value.hex
} else {
log.warn "$value.hex is not a valid color"
}
}
if (verifyPercent(value.level)) {
events << createEvent(name: "level", value: value.level, descriptionText: "Level has changed to ${value.level}%")
validValues.level = value.level
}
if (value.switch == "off" || (value.level != null && value.level <= 0)) {
events << createEvent(name: "switch", value: "off")
validValues.switch = "off"
} else {
events << createEvent(name: "switch", value: "on")
validValues.switch = "on"
}
if (!events.isEmpty()) {
parent.setColor(this, validValues)
}
events.each {
sendEvent(it)
}
log.debug "setColor: ${value}, $this"
parent.setColor(this, value)
if (value.hue) { sendEvent(name: "hue", value: value.hue, displayed: false)}
if (value.saturation) { sendEvent(name: "saturation", value: value.saturation, displayed: false)}
if (value.hex) { sendEvent(name: "color", value: value.hex)}
if (value.level) { sendEvent(name: "level", value: value.level, descriptionText: "Level has changed to ${value.level}%")}
sendEvent(name: "switch", value: "on")
}
void reset() {
log.debug "Executing 'reset'"
log.debug "Executing 'reset'"
def value = [level:100, saturation:56, hue:23]
setAdjustedColor(value)
parent.poll()
parent.poll()
}
void setAdjustedColor(value) {
if (value) {
if (value) {
log.trace "setAdjustedColor: ${value}"
def adjusted = value + [:]
adjusted.hue = adjustOutgoingHue(value.hue)
// Needed because color picker always sends 100
adjusted.level = null
setColor(adjusted)
} else {
log.warn "Invalid color input"
}
}
void setColorTemperature(value) {
if (value) {
if (value) {
log.trace "setColorTemperature: ${value}k"
parent.setColorTemperature(this, value)
sendEvent(name: "colorTemperature", value: value)
sendEvent(name: "switch", value: "on")
} else {
log.warn "Invalid color temperature"
}
}
}
void refresh() {
log.debug "Executing 'refresh'"
parent.manualRefresh()
log.debug "Executing 'refresh'"
parent.manualRefresh()
}
def adjustOutgoingHue(percent) {
@@ -223,14 +177,3 @@ def adjustOutgoingHue(percent) {
log.info "percent: $percent, adjusted: $adjusted"
adjusted
}
def verifyPercent(percent) {
if (percent == null)
return false
else if (percent >= 0 && percent <= 100) {
return true
} else {
log.warn "$percent is not 0-100"
return false
}
}

View File

@@ -1,8 +1,6 @@
/**
* Hue Lux Bulb
*
* Philips Hue Type "Dimmable Light"
*
* Author: SmartThings
*/
// for the UI
@@ -25,10 +23,10 @@ metadata {
tiles(scale: 2) {
multiAttributeTile(name:"rich-control", type: "lighting", canChangeIcon: true){
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
attributeState "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#00A0DC", nextState:"turningOff"
attributeState "off", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#C6C7CC", nextState:"turningOn"
attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#00A0DC", nextState:"turningOff"
attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#C6C7CC", nextState:"turningOn"
attributeState "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
attributeState "off", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
}
tileAttribute ("device.level", key: "SLIDER_CONTROL") {
attributeState "level", action:"switch level.setLevel", range:"(0..100)"
@@ -70,24 +68,19 @@ def parse(description) {
// handle commands
void on() {
log.trace parent.on(this)
parent.on(this)
sendEvent(name: "switch", value: "on")
}
void off() {
log.trace parent.off(this)
parent.off(this)
sendEvent(name: "switch", value: "off")
}
void setLevel(percent) {
log.debug "Executing 'setLevel'"
if (percent != null && percent >= 0 && percent <= 100) {
parent.setLevel(this, percent)
sendEvent(name: "level", value: percent)
sendEvent(name: "switch", value: "on")
} else {
log.warn "$percent is not 0-100"
}
parent.setLevel(this, percent)
sendEvent(name: "level", value: percent)
}
void refresh() {

View File

@@ -28,8 +28,4 @@
'''{{ device.displayName }} is On'''.ko={{ device.displayName }}켜졌습니다.
'''{{ device.displayName }} is Off'''.ko={{ device.displayName }}꺼졌습니다.
'''{{ device.displayName }} power is {{ value }} Watts'''.ko={{ device.displayName }} 전원은 {{ value }}와트입니다
'''On'''.ko=켜짐
'''Off'''.ko=꺼짐
'''Turning On'''.ko=켜기
'''Turning Off'''.ko=끄기
#==============================================================================

View File

@@ -65,10 +65,10 @@ metadata {
tiles(scale: 2) {
multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
attributeState "on", label: 'On', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821", nextState: "turningOff"
attributeState "off", label: 'Off', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff", nextState: "turningOn"
attributeState "turningOn", label: 'Turning On', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821", nextState: "turningOff"
attributeState "turningOff", label: 'Turning Off', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff", nextState: "turningOn"
attributeState "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821", nextState: "turningOff"
attributeState "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff", nextState: "turningOn"
attributeState "turningOn", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821", nextState: "turningOff"
attributeState "turningOff", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff", nextState: "turningOn"
}
tileAttribute ("power", key: "SECONDARY_CONTROL") {
attributeState "power", label:'${currentValue} W'

View File

@@ -22,8 +22,6 @@
#==============================================================================
# Korean (ko)
# Device Preferences
'''Dry'''.ko=건조
'''Wet'''.ko=누수
'''dry'''.ko=건조
'''wet'''.ko=누수
'''battery'''.ko=배터리
@@ -33,7 +31,6 @@
'''Adjust temperature by this many degrees'''.ko=몇 도씩 온도를 조절하십시오
'''Give your device a name'''.ko=기기 이름 바꾸기
'''Water Leak Sensor'''.ko=누수센서
'''${currentValue}% battery'''.ko=${currentValue}% 배터리
# Events descriptionText
'''{{ device.displayName }} is dry'''.ko={{ device.displayName }}가 건조
'''{{ device.displayName }} is wet'''.ko={{ device.displayName }}누수

View File

@@ -30,9 +30,6 @@
'''Adjust temperature by this many degrees'''.ko=몇 도씩 온도를 조절하십시오
'''Give your device a name'''.ko=기기 이름 바꾸기
'''Motion Sensor'''.ko=모션 센서
'''motion'''.ko=동작 감지
'''no motion'''.ko=동작 없음
'''${currentValue}% battery'''.ko=${currentValue}% 배터리
# Events descriptionText
'''{{ device.displayName }} detected motion'''.ko={{ device.displayName }} 가 움직임을 감지하였습니다.
'''{{ device.displayName }} motion has stopped'''.ko={{ device.displayName }}움직임이 중단되었습니다

View File

@@ -43,8 +43,3 @@
'''{{ device.displayName }} battery was {{ value }}%'''.ko={{ device.displayName }}남아있는 배터리는 {{ value }}%입니다.
'''Updating device to garage sensor'''.ko=기기-차고 센서 업데이트 중
'''Updating device to open/close sensor'''.ko=기기-열림/닫힘 센서 업데이트 중
'''Inactive'''.ko=비활성
'''Active'''.ko=활성
'''Open'''.ko=열림
'''Closed'''.ko=닫힘
'''${currentValue}% battery'''.ko=${currentValue}% 배터리

View File

@@ -83,19 +83,19 @@ metadata {
tiles(scale: 2) {
multiAttributeTile(name:"status", type: "generic", width: 6, height: 4){
tileAttribute ("device.status", key: "PRIMARY_CONTROL") {
attributeState "open", label:'Open', icon:"st.contact.contact.open", backgroundColor:"#ffa81e"
attributeState "closed", label:'Closed', icon:"st.contact.contact.closed", backgroundColor:"#79b821"
attributeState "open", label:'${name}', icon:"st.contact.contact.open", backgroundColor:"#ffa81e"
attributeState "closed", label:'${name}', icon:"st.contact.contact.closed", backgroundColor:"#79b821"
attributeState "garage-open", label:'Open', icon:"st.doors.garage.garage-open", backgroundColor:"#ffa81e"
attributeState "garage-closed", label:'Closed', icon:"st.doors.garage.garage-closed", backgroundColor:"#79b821"
}
}
standardTile("contact", "device.contact", width: 2, height: 2) {
state("open", label:'Open', icon:"st.contact.contact.open", backgroundColor:"#ffa81e")
state("closed", label:'Closed', icon:"st.contact.contact.closed", backgroundColor:"#79b821")
state("open", label:'${name}', icon:"st.contact.contact.open", backgroundColor:"#ffa81e")
state("closed", label:'${name}', icon:"st.contact.contact.closed", backgroundColor:"#79b821")
}
standardTile("acceleration", "device.acceleration", width: 2, height: 2) {
state("active", label:'Active', icon:"st.motion.acceleration.active", backgroundColor:"#53a7c0")
state("inactive", label:'Inactive', icon:"st.motion.acceleration.inactive", backgroundColor:"#ffffff")
state("active", label:'${name}', icon:"st.motion.acceleration.active", backgroundColor:"#53a7c0")
state("inactive", label:'${name}', icon:"st.motion.acceleration.inactive", backgroundColor:"#ffffff")
}
valueTile("temperature", "device.temperature", width: 2, height: 2) {
state("temperature", label:'${currentValue}°',

View File

@@ -203,7 +203,7 @@ private List parseContactMessage(String description) {
parts.each { part ->
part = part.trim()
if (part.startsWith('contactState:')) {
results.addAll(getContactResult(part, description))
results << getContactResult(part, description)
}
else if (part.startsWith('accelerationState:')) {
results << getAccelerationResult(part, description)
@@ -316,7 +316,7 @@ private List getContactResult(part, description) {
results
}
private Map getAccelerationResult(part, description) {
private getAccelerationResult(part, description) {
def name = "acceleration"
def value = part.endsWith("1") ? "active" : "inactive"
def linkText = getLinkText(device)
@@ -335,7 +335,7 @@ private Map getAccelerationResult(part, description) {
]
}
private Map getTempResult(part, description) {
private getTempResult(part, description) {
def name = "temperature"
def temperatureScale = getTemperatureScale()
def value = zigbee.parseSmartThingsTemperatureValue(part, "temp: ", temperatureScale)
@@ -360,7 +360,7 @@ private Map getTempResult(part, description) {
]
}
private Map getXyzResult(results, description) {
private getXyzResult(results, description) {
def name = "threeAxis"
def value = "${results.x},${results.y},${results.z}"
def linkText = getLinkText(device)
@@ -379,7 +379,7 @@ private Map getXyzResult(results, description) {
]
}
private Map getBatteryResult(part, description) {
private getBatteryResult(part, description) {
def batteryDivisor = description.split(",").find {it.split(":")[0].trim() == "batteryDivisor"} ? description.split(",").find {it.split(":")[0].trim() == "batteryDivisor"}.split(":")[1].trim() : null
def name = "battery"
def value = zigbee.parseSmartThingsBatteryValue(part, batteryDivisor)
@@ -400,7 +400,7 @@ private Map getBatteryResult(part, description) {
]
}
private Map getRssiResult(part, description, lastHop=false) {
private getRssiResult(part, description, lastHop=false) {
def name = lastHop ? "lastHopRssi" : "rssi"
def valueString = part.split(":")[1].trim()
def value = (Integer.parseInt(valueString) - 128).toString()
@@ -431,7 +431,7 @@ private Map getRssiResult(part, description, lastHop=false) {
* Note: To make the signal strength indicator more accurate, we could combine
* LQI with RSSI.
*/
private Map getLqiResult(part, description, lastHop=false) {
private getLqiResult(part, description, lastHop=false) {
def name = lastHop ? "lastHopLqi" : "lqi"
def valueString = part.split(":")[1].trim()
def percentageOf = 255

View File

@@ -56,17 +56,21 @@ metadata {
def parse(String description) {
log.debug "description is $description"
def event = zigbee.getEvent(description)
if (event) {
log.info event
if (event.name == "power") {
def resultMap = zigbee.getKnownDescription(description)
if (resultMap) {
log.info resultMap
if (resultMap.type == "update") {
log.info "$device updates: ${resultMap.value}"
}
else if (resultMap.type == "power") {
def powerValue
if (device.getDataValue("manufacturer") != "OSRAM") { //OSRAM devices do not reliably update power
event.value = (event.value as Integer) / 10 //TODO: The divisor value needs to be set as part of configuration
sendEvent(event)
powerValue = (resultMap.value as Integer)/10 //TODO: The divisor value needs to be set as part of configuration
sendEvent(name: "power", value: powerValue)
}
}
else {
sendEvent(event)
sendEvent(name: resultMap.type, value: resultMap.value)
}
}
else {

View File

@@ -51,9 +51,15 @@ metadata {
def parse(String description) {
log.debug "description is $description"
def event = zigbee.getEvent(description)
if (event) {
sendEvent(event)
def resultMap = zigbee.getKnownDescription(description)
if (resultMap) {
log.info resultMap
if (resultMap.type == "update") {
log.info "$device updates: ${resultMap.value}"
}
else {
sendEvent(name: resultMap.type, value: resultMap.value)
}
}
else {
log.warn "DID NOT PARSE MESSAGE for description : $description"

View File

@@ -83,19 +83,32 @@ def uninstalled() {
}
def configure() {
/*
def cmds =
zigbee.configureReporting(CLUSTER_DOORLOCK, DOORLOCK_ATTR_LOCKSTATE,
TYPE_ENUM8, 0, 3600, null) +
zigbee.configureReporting(CLUSTER_POWER, POWER_ATTR_BATTERY_PERCENTAGE_REMAINING,
TYPE_U8, 600, 21600, 0x01)
zigbee.configSetup("${CLUSTER_DOORLOCK}", "${DOORLOCK_ATTR_LOCKSTATE}",
"${TYPE_ENUM8}", 0, 3600, "{01}") +
zigbee.configSetup("${CLUSTER_POWER}", "${POWER_ATTR_BATTERY_PERCENTAGE_REMAINING}",
"${TYPE_U8}", 600, 21600, "{01}")
*/
def zigbeeId = device.zigbeeId
def cmds =
[
"zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 1 ${CLUSTER_DOORLOCK} {$zigbeeId} {}", "delay 200",
"zcl global send-me-a-report ${CLUSTER_DOORLOCK} ${DOORLOCK_ATTR_LOCKSTATE} ${TYPE_ENUM8} 0 3600 {01}", "delay 200",
"send 0x${device.deviceNetworkId} 1 0x${device.endpointId}", "delay 200",
"zdo bind 0x${device.deviceNetworkId} 0x${device.endpointId} 1 ${CLUSTER_POWER} {$zigbeeId} {}", "delay 200",
"zcl global send-me-a-report ${CLUSTER_POWER} ${POWER_ATTR_BATTERY_PERCENTAGE_REMAINING} ${TYPE_U8} 600 21600 {01}", "delay 200",
"send 0x${device.deviceNetworkId} 1 0x${device.endpointId}", "delay 200",
]
log.info "configure() --- cmds: $cmds"
return cmds + refresh() // send refresh cmds as part of config
}
def refresh() {
def cmds =
zigbee.readAttribute(CLUSTER_DOORLOCK, DOORLOCK_ATTR_LOCKSTATE) +
zigbee.readAttribute(CLUSTER_POWER, POWER_ATTR_BATTERY_PERCENTAGE_REMAINING)
zigbee.refreshData("${CLUSTER_DOORLOCK}", "${DOORLOCK_ATTR_LOCKSTATE}") +
zigbee.refreshData("${CLUSTER_POWER}", "${POWER_ATTR_BATTERY_PERCENTAGE_REMAINING}")
log.info "refresh() --- cmds: $cmds"
return cmds
}
@@ -108,27 +121,34 @@ def parse(String description) {
map = parseReportAttributeMessage(description)
}
log.debug "parse() --- Parse returned $map"
def result = map ? createEvent(map) : null
log.debug "parse() --- returned: $result"
return result
}
// Lock capability commands
def lock() {
def cmds = zigbee.command(CLUSTER_DOORLOCK, DOORLOCK_CMD_LOCK_DOOR)
log.info "lock() -- cmds: $cmds"
return cmds
//def cmds = zigbee.zigbeeCommand("${CLUSTER_DOORLOCK}", "${DOORLOCK_CMD_LOCK_DOOR}", "{}")
//log.info "lock() -- cmds: $cmds"
//return cmds
"st cmd 0x${device.deviceNetworkId} 0x${device.endpointId} ${CLUSTER_DOORLOCK} ${DOORLOCK_CMD_LOCK_DOOR} {}"
}
def unlock() {
def cmds = zigbee.command(CLUSTER_DOORLOCK, DOORLOCK_CMD_UNLOCK_DOOR)
log.info "unlock() -- cmds: $cmds"
return cmds
//def cmds = zigbee.zigbeeCommand("${CLUSTER_DOORLOCK}", "${DOORLOCK_CMD_UNLOCK_DOOR}", "{}")
//log.info "unlock() -- cmds: $cmds"
//return cmds
"st cmd 0x${device.deviceNetworkId} 0x${device.endpointId} ${CLUSTER_DOORLOCK} ${DOORLOCK_CMD_UNLOCK_DOOR} {}"
}
// Private methods
private Map parseReportAttributeMessage(String description) {
log.trace "parseReportAttributeMessage() --- description: $description"
Map descMap = zigbee.parseDescriptionAsMap(description)
log.debug "parseReportAttributeMessage() --- descMap: $descMap"
Map resultMap = [:]
if (descMap.clusterInt == CLUSTER_POWER && descMap.attrInt == POWER_ATTR_BATTERY_PERCENTAGE_REMAINING) {
resultMap.name = "battery"
@@ -136,24 +156,18 @@ private Map parseReportAttributeMessage(String description) {
if (device.getDataValue("manufacturer") == "Yale") { //Handling issue with Yale locks incorrect battery reporting
resultMap.value = Integer.parseInt(descMap.value, 16)
}
log.info "parseReportAttributeMessage() --- battery: ${resultMap.value}"
}
else if (descMap.clusterInt == CLUSTER_DOORLOCK && descMap.attrInt == DOORLOCK_ATTR_LOCKSTATE) {
def value = Integer.parseInt(descMap.value, 16)
def linkText = getLinkText(device)
resultMap.name = "lock"
if (value == 0) {
resultMap.value = "unknown"
resultMap.descriptionText = "${linkText} is not fully locked"
} else if (value == 1) {
resultMap.value = "locked"
resultMap.descriptionText = "${linkText} is locked"
} else if (value == 2) {
resultMap.value = "unlocked"
resultMap.descriptionText = "${linkText} is unlocked"
} else {
resultMap.value = "unknown"
resultMap.descriptionText = "${linkText} is in unknown lock state"
}
resultMap.putAll([0:["value":"unknown",
"descriptionText":"Not fully locked"],
1:["value":"locked"],
2:["value":"unlocked"]].get(value,
["value":"unknown",
"descriptionText":"Unknown lock state"]))
log.info "parseReportAttributeMessage() --- lock: ${resultMap.value}"
}
else {
log.debug "parseReportAttributeMessage() --- ignoring attribute"

View File

@@ -51,15 +51,22 @@ metadata {
// Parse incoming device messages to generate events
def parse(String description) {
log.debug "description is $description"
def event = zigbee.getEvent(description)
if (event) {
if (event.name == "power") {
def resultMap = zigbee.getKnownDescription(description)
if (resultMap) {
log.info resultMap
if (resultMap.type == "update") {
log.info "$device updates: ${resultMap.value}"
}
else if (resultMap.type == "power") {
def powerValue
powerValue = (event.value as Integer)/10 //TODO: The divisor value needs to be set as part of configuration
sendEvent(name: "power", value: powerValue)
if (device.getDataValue("manufacturer") != "OSRAM") { //OSRAM devices do not reliably update power
powerValue = (resultMap.value as Integer)/10 //TODO: The divisor value needs to be set as part of configuration
sendEvent(name: "power", value: powerValue)
}
}
else {
sendEvent(event)
sendEvent(name: resultMap.type, value: resultMap.value)
}
}
else {

View File

@@ -53,9 +53,16 @@ metadata {
// Parse incoming device messages to generate events
def parse(String description) {
log.debug "description is $description"
def event = zigbee.getEvent(description)
if (event) {
sendEvent(event)
def resultMap = zigbee.getKnownDescription(description)
if (resultMap) {
log.info resultMap
if (resultMap.type == "update") {
log.info "$device updates: ${resultMap.value}"
}
else {
sendEvent(name: resultMap.type, value: resultMap.value)
}
}
else {
log.warn "DID NOT PARSE MESSAGE for description : $description"

View File

@@ -73,9 +73,16 @@ metadata {
// Parse incoming device messages to generate events
def parse(String description) {
log.debug "description is $description"
def event = zigbee.getEvent(description)
if (event) {
sendEvent(event)
def finalResult = zigbee.getKnownDescription(description)
if (finalResult) {
log.info finalResult
if (finalResult.type == "update") {
log.info "$device updates: ${finalResult.value}"
}
else {
sendEvent(name: finalResult.type, value: finalResult.value)
}
}
else {
log.warn "DID NOT PARSE MESSAGE for description : $description"

View File

@@ -245,7 +245,6 @@ def retypeBasedOnMSR() {
break
case "011F-0001-0001": // Schlage motion
case "014A-0001-0001": // Ecolink motion
case "014A-0004-0001": // Ecolink motion +
case "0060-0001-0002": // Everspring SP814
case "0060-0001-0003": // Everspring HSP02
case "011A-0601-0901": // Enerwave ZWN-BPC

View File

@@ -161,7 +161,7 @@ private sendDeveloperReq() {
headers: [
HOST: host
],
body: [devicetype: "$token-0"]], "${selectedHue}"))
body: [devicetype: "$token-0", username: "$token-0"]], "${selectedHue}"))
}
private discoverHueBulbs() {
@@ -289,7 +289,7 @@ def bulbListHandler(hub, data = "") {
def object = new groovy.json.JsonSlurper().parseText(data)
object.each { k,v ->
if (v instanceof Map)
bulbs[k] = [id: k, name: v.name, type: v.type, modelid: v.modelid, hub:hub]
bulbs[k] = [id: k, name: v.name, type: v.type, hub:hub]
}
}
def bridge = null
@@ -300,40 +300,6 @@ def bulbListHandler(hub, data = "") {
return msg
}
private upgradeDeviceType(device, newHueType) {
def deviceType = getDeviceType(newHueType)
// Automatically change users Hue bulbs to correct device types
if (deviceType && !(device?.typeName?.equalsIgnoreCase(deviceType))) {
log.debug "Update device type: \"$device.label\" ${device?.typeName}->$deviceType"
device.setDeviceType(deviceType)
}
}
private getDeviceType(hueType) {
// Determine ST device type based on Hue classification of light
if (hueType?.equalsIgnoreCase("Dimmable light"))
return "Hue Lux Bulb"
else if (hueType?.equalsIgnoreCase("Extended Color Light"))
return "Hue Bulb"
else if (hueType?.equalsIgnoreCase("Color Light"))
return "Hue Bloom"
else
return null
}
private addChildBulb(dni, hueType, name, hub, update=false, device = null) {
def deviceType = getDeviceType(hueType)
if (deviceType) {
return addChildDevice("smartthings", deviceType, dni, hub, ["label": name])
}
else {
log.warn "Device type $hueType not supported"
return null
}
}
def addBulbs() {
def bulbs = getHueBulbs()
selectedBulbs?.each { dni ->
@@ -343,7 +309,11 @@ def addBulbs() {
if (bulbs instanceof java.util.Map) {
newHueBulb = bulbs.find { (app.id + "/" + it.value.id) == dni }
if (newHueBulb != null) {
d = addChildBulb(dni, newHueBulb?.value?.type, newHueBulb?.value?.name, newHueBulb?.value?.hub)
if (newHueBulb?.value?.type?.equalsIgnoreCase("Dimmable light") ) {
d = addChildDevice("smartthings", "Hue Lux Bulb", dni, newHueBulb?.value.hub, ["label":newHueBulb?.value.name])
} else {
d = addChildDevice("smartthings", "Hue Bulb", dni, newHueBulb?.value.hub, ["label":newHueBulb?.value.name])
}
log.debug "created ${d.displayName} with id $dni"
d.refresh()
} else {
@@ -352,15 +322,16 @@ def addBulbs() {
} else {
//backwards compatable
newHueBulb = bulbs.find { (app.id + "/" + it.id) == dni }
d = addChildBulb(dni, "Extended Color Light", newHueBulb?.value?.name, newHueBulb?.value?.hub)
d = addChildDevice("smartthings", "Hue Bulb", dni, newHueBulb?.hub, ["label":newHueBulb?.name])
d.refresh()
}
} else {
log.debug "found ${d.displayName} with id $dni already exists, type: '$d.typeName'"
if (bulbs instanceof java.util.Map) {
// Update device type if incorrect
def newHueBulb = bulbs.find { (app.id + "/" + it.value.id) == dni }
upgradeDeviceType(d, newHueBulb?.value?.type)
if (newHueBulb?.value?.type?.equalsIgnoreCase("Dimmable light") && d.typeName == "Hue Bulb") {
d.setDeviceType("Hue Lux Bulb")
}
}
}
}
@@ -502,7 +473,7 @@ def locationHandler(evt) {
def bulbs = getHueBulbs()
log.debug "Adding bulbs to state!"
body.each { k,v ->
bulbs[k] = [id: k, name: v.name, type: v.type, modelid: v.modelid, hub:parsedEvent.hub]
bulbs[k] = [id: k, name: v.name, type: v.type, hub:parsedEvent.hub]
}
}
}
@@ -686,53 +657,39 @@ def setColorTemperature(childDevice, huesettings) {
}
def setColor(childDevice, huesettings) {
log.debug "Executing 'setColor($huesettings)'"
def value = [:]
log.debug "Executing 'setColor($huesettings)'"
def hue = null
def sat = null
def xy = null
if (huesettings.hex != null) {
value.xy = getHextoXY(huesettings.hex)
} else {
if (huesettings.hue != null)
value.hue = Math.min(Math.round(huesettings.hue * 65535 / 100), 65535)
if (huesettings.saturation != null)
value.sat = Math.min(Math.round(huesettings.saturation * 255 / 100), 255)
if (huesettings.hex) {
xy = getHextoXY(huesettings.hex)
} else if (huesettings.hue && huesettings.saturation) {
hue = Math.min(Math.round(huesettings.hue * 65535 / 100), 65535)
sat = Math.min(Math.round(huesettings.saturation * 255 / 100), 255)
}
// Default behavior is to turn light on
value.on = true
def alert = huesettings.alert ? huesettings.alert : "none"
def transition = huesettings.transition ? huesettings.transition : 4
if (huesettings.level != null) {
if (huesettings.level <= 0)
value.on = false
else if (huesettings.level == 1)
value.bri = 1
else
value.bri = Math.min(Math.round(huesettings.level * 255 / 100), 255)
}
value.alert = huesettings.alert ? huesettings.alert : "none"
value.transition = huesettings.transition ? huesettings.transition : 4
def value = [xy: xy, sat: sat, hue: hue, alert: alert, transitiontime: transition, on: true]
// Make sure to turn off light if requested
if (huesettings.switch == "off")
value.on = false
if (huesettings.level != null) {
if (huesettings.level == 1) value.bri = 1 else value.bri = Math.min(Math.round(huesettings.level * 255 / 100), 255)
value.on = value.bri > 0
}
log.debug "sending command $value"
put("lights/${getId(childDevice)}/state", value)
return "Color set to $value"
log.debug "sending command $value"
put("lights/${getId(childDevice)}/state", value)
}
def nextLevel(childDevice) {
def level = device.latestValue("level") as Integer ?: 0
if (level < 100) {
level = Math.min(25 * (Math.round(level / 25) + 1), 100) as Integer
} else {
level = 25
}
setLevel(childDevice,level)
def level = device.latestValue("level") as Integer ?: 0
if (level < 100) {
level = Math.min(25 * (Math.round(level / 25) + 1), 100) as Integer
}
else {
level = 25
}
setLevel(childDevice,level)
}
private getId(childDevice) {
@@ -831,14 +788,16 @@ private getHextoXY(String colorStr) {
// Make green more vivid
if (normalizedToOne[1] > 0.04045) {
green = (float) Math.pow((normalizedToOne[1] + 0.055) / (1.0 + 0.055), 2.4);
green = (float) Math.pow((normalizedToOne[1] + 0.055)
/ (1.0 + 0.055), 2.4);
} else {
green = (float) (normalizedToOne[1] / 12.92);
}
// Make blue more vivid
if (normalizedToOne[2] > 0.04045) {
blue = (float) Math.pow((normalizedToOne[2] + 0.055) / (1.0 + 0.055), 2.4);
blue = (float) Math.pow((normalizedToOne[2] + 0.055)
/ (1.0 + 0.055), 2.4);
} else {
blue = (float) (normalizedToOne[2] / 12.92);
}
@@ -847,8 +806,8 @@ private getHextoXY(String colorStr) {
float Y = (float) (red * 0.234327 + green * 0.743075 + blue * 0.022598);
float Z = (float) (red * 0.0000000 + green * 0.053077 + blue * 1.035763);
float x = (X != 0 ? X / (X + Y + Z) : 0);
float y = (Y != 0 ? Y / (X + Y + Z) : 0);
float x = X / (X + Y + Z);
float y = Y / (X + Y + Z);
double[] xy = new double[2];
xy[0] = x;
@@ -865,7 +824,7 @@ def convertBulbListToMap() {
if (state.bulbs instanceof java.util.List) {
def map = [:]
state.bulbs.unique {it.id}.each { bulb ->
map << ["${bulb.id}":["id":bulb.id, "name":bulb.name, "type": bulb.type, "modelid": bulb.modelid, "hub":bulb.hub]]
map << ["${bulb.id}":["id":bulb.id, "name":bulb.name, "hub":bulb.hub]]
}
state.bulbs = map
}

View File

@@ -0,0 +1,50 @@
/**
* test 앱
*
* Copyright 2016 이진희
*
* 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: "test 앱",
namespace: "test앱",
author: "이진희",
description: "\uD14C\uC2A4\uD2B8\uC785\uB2C8\uB2E4",
category: "",
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("Title") {
// TODO: put inputs here
}
}
def installed() {
log.debug "Installed with settings: ${settings}"
initialize()
}
def updated() {
log.debug "Updated with settings: ${settings}"
unsubscribe()
initialize()
}
def initialize() {
// TODO: subscribe to attributes, devices, locations, etc.
}
// TODO: implement event handlers

View File

@@ -40,7 +40,6 @@ preferences {
page name:"pageSetup"
page name:"Setup"
page name:"Settings"
page name: "timeIntervalInput"
}
@@ -186,8 +185,7 @@ def Settings() {
}
}
def timeIntervalInput() {
dynamicPage(name: "timeIntervalInput") {
page(name: "timeIntervalInput", title: "Only during a certain time", refreshAfterSelection:true) {
section {
input "startTimeType", "enum", title: "Starting at", options: [["time": "A specific time"], ["sunrise": "Sunrise"], ["sunset": "Sunset"]], defaultValue: "time", submitOnChange: true
if (startTimeType in ["sunrise","sunset"]) {
@@ -206,10 +204,9 @@ def timeIntervalInput() {
input "ending", "time", title: "End time", required: false
}
}
}
}
def installed() {
initialize()
}