Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save kurrik/035f7b9718fc509138007ceaaf8342a7 to your computer and use it in GitHub Desktop.
Save kurrik/035f7b9718fc509138007ceaaf8342a7 to your computer and use it in GitHub Desktop.
A SmartThings Device Handler for Elgato Key Light
/**
* Copyright 2020 Malte Ubl
*
* 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.
*
*/
// Reverse engineering reference https://gitlab.com/obviate.io/pyleglight/-/blob/master/leglight/leglight.py
// Modified by Arne Roomann-Kurrik (@kurrik) to add color temperature controls.
metadata {
definition (name: "Elgato Key Light", namespace: "cramforce", author: "Malte Ubl") {
capability "Switch Level"
capability "Color Temperature" // https://docs.smartthings.com/en/latest/capabilities-reference.html#color-temperature
capability "Actuator"
capability "Switch"
capability "Refresh"
capability "Sensor"
capability "Health Check"
capability "Light"
}
preferences {
input name: "hostname", type: "text", title: "IP Address", description: "IP Address of the light. Should be fixed.", required: true
}
tiles(scale: 2) {
multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
attributeState "on", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#00a0dc"
attributeState "off", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff"
}
tileAttribute ("device.level", key: "SLIDER_CONTROL") {
attributeState "level", action:"switch level.setLevel"
}
}
standardTile("refresh", "device.switch", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
state "default", label:'Refresh', action:"refresh.refresh", icon:"st.secondary.refresh"
}
// https://docs.smartthings.com/en/latest/device-type-developers-guide/tiles-metadata.html#slider-control-tile
controlTile("level", "device.level", "slider", height: 2, width: 2, inactiveLabel: false, range:"(0..100)") {
state "level", label:'${currentValue}%', unit:"%%", action:"switch level.setLevel"
}
controlTile("temperature", "device.colorTemperature", "slider", height: 2, width: 2, inactiveLabel: false, range:"(2900..7000)") {
state "colorTemperature", label:'${currentValue}K', unit:"K", action:"color temperature.setColorTemperature"
}
main(["switch"])
details(["switch", "level", "temperature", "refresh"])
}
}
def installed() {
updated()
}
def updated() {
unschedule()
runEvery5Minutes(refresh)
refresh()
}
def on() {
sendElgatoOn(true)
}
def off() {
sendElgatoOn(false)
}
def setLevel(value) {
sendElgatoBrightness(value)
}
def setLevel(value, duration) {
setBrightness(value)
}
def setColorTemperature(temperature) {
sendElgatoTemperature(temperature)
}
/**
* PING is used by Device-Watch in attempt to reach the Device
* */
def ping() {
refresh()
}
def refresh() {
sendHttpRequest("GET", hostname + ":9123", "/elgato/lights")
}
def sendElgatoOn(on) {
sendHttpRequest("PUT", hostname + ":9123", "/elgato/lights", """{"numberOfLights":1,"lights":[{"on":${on ? 1 : 0}}]}""")
}
def sendElgatoBrightness(level) {
sendHttpRequest("PUT", hostname + ":9123", "/elgato/lights", """{"numberOfLights":1,"lights":[{"brightness": ${level}}]}""")
}
def sendElgatoTemperature(temp) {
def adjustedTemp = colorFit(temp)
sendHttpRequest("PUT", hostname + ":9123", "/elgato/lights", """{"numberOfLights":1,"lights":[{"temperature": ${adjustedTemp}}]}""")
}
def sendHttpRequest(method, host, path, body="") {
log.debug "Executing 'sendHttpRequest' method: "+method+" host: "+host+" path: "+path+" body: "+body
def bodyHead = "";
if(!body.equals("")) {
bodyHead = "Content-Type: application/json\r\nContent-Length: ${body.length()}\r\n";
body = body;
}
def httpRequest = """${method} ${path} HTTP/1.1\r\nHOST: $host\r\n${bodyHead}\r\n${body}"""
log.debug httpRequest
sendHubCommand(new physicalgraph.device.HubAction(httpRequest, physicalgraph.device.Protocol.LAN, host, [callback: cb]))
}
// Take a color temp (in K) and convert it to the format the Elgato Light wants
// Elgado API range: [143..344] maps to [7000K..2900K]
def colorFit(value) {
def output = Math.round(987007 * value ** -0.999)
log.debug "colorFit converting ${value} to ${output}"
return output
}
// Take the int that the Elgato Light returns and convert it roughly back to color temp (in K)
def postFit(value) {
def output = Math.round(1000000 * value ** -1)
log.debug "postFit converting ${value} to ${output}"
return output
}
def cb(physicalgraph.device.HubResponse hubResponse) {
log.debug "Response: " + hubResponse.status + " Body: " + hubResponse.body
def json = hubResponse.json
if (json == null) {
log.debug "No JSON"
return;
}
log.debug "json" + json
def on = json.lights[0].on == 1
def brightness = json.lights[0].brightness
def temperature = postFit(json.lights[0].temperature);
sendEvent(name: "switch", value: on ? "on" : "off")
sendEvent(name: "level", value: brightness, unit: "%")
sendEvent(name: "colorTemperature", value: temperature, unit: "K")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment