Skip to content

Instantly share code, notes, and snippets.

@andrewkroh
Created March 23, 2017 16:02
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save andrewkroh/74bd896a11570799a9163f532f654d37 to your computer and use it in GitHub Desktop.
Save andrewkroh/74bd896a11570799a9163f532f654d37 to your computer and use it in GitHub Desktop.
Elasticsearch Output for SmartThings Events
/**
* Elasticsearch Event Publisher
*
* Copyright 2017 Andrew Kroh
*/
import java.text.DateFormat;
import java.text.SimpleDateFormat;
definition(
name: "Elasticsearch Event Publisher",
namespace: "com.andrewkroh",
author: "Andrew Kroh",
description: "Publishes event to Elasticsearch.",
category: "My Apps",
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"
) {
appSetting "ES_USER"
appSetting "ES_PASS"
appSetting "ES_URL"
}
def capabilities() {
return [
accelerationSensor: [
name: 'Acceleration Sensor',
attributes: ['acceleration'],
],
actuator: [
name: 'Actuator',
attributes: [],
],
alarm: [
name: 'Alarm',
attributes: ['alarm'],
],
battery: [
name: 'Battery',
attributes: ['battery'],
],
beacon: [
name: 'Beacon',
attributes: ['presence'],
],
button: [
name: 'Button',
attributes: ['button'],
],
carbonDioxideMeasurement: [
name: 'Carbon Dioxide Measurement',
attributes: ['carbonDioxide'],
],
carbonMonoxideDetector: [
name: 'Carbon Monoxide Detector',
attributes: ['carbonMonoxide'],
],
colorControl: [
name: 'Color Control',
attributes: ['hue', 'saturation', 'color'],
],
colorTemperature: [
name: 'Color Temperature',
attributes: ['colorTemperature'],
],
configuration: [
name: 'Configuration',
attributes: [],
],
consumable: [
name: 'Consumable',
attributes: ['consumable'],
],
contactSensor: [
name: 'Contact Sensor',
attributes: ['contact'],
],
doorControl: [
name: 'Door Control',
attributes: ['door'],
],
energyMeter: [
name: 'Energy Meter',
attributes: ['energy'],
],
garageDoorControl: [
name: 'Garage Door Control',
attributes: ['door'],
],
illuminanceMeasurement: [
name: 'Illuminance Measurement',
attributes: ['illuminance'],
],
imageCapture: [
name: 'Image Capture',
attributes: ['image'],
],
lock: [
name: 'Lock',
attributes: ['lock'],
],
mediaController: [
name: 'Media Controller',
attributes: ['activities', 'currentActivity'],
],
momentary: [
name: 'Momentary',
attributes: [],
],
motionSensor: [
name: 'Motion Sensor',
attributes: ['motion'],
],
musicPlayer: [
name: 'Music Player',
attributes: ['status', 'level', 'trackDescription', 'trackData', 'mute'],
],
notification: [
name: 'Notification',
attributes: [],
],
pHMeasurement: [
name: 'pH Measurement',
attributes: ['pH'],
],
polling: [
name: 'Polling',
attributes: [],
],
powerMeter: [
name: 'Power Meter',
attributes: ['power'],
],
presenceSensor: [
name: 'Presence Sensor',
attributes: ['presence'],
],
refresh: [
name: 'Refresh',
attributes: [],
],
relativeHumidityMeasurement: [
name: 'Relative Humidity Measurement',
attributes: ['humidity'],
],
relaySwitch: [
name: 'Relay Switch',
attributes: ['switch'],
],
sensor: [
name: 'Sensor',
attributes: [],
],
shockSensor: [
name: 'Shock Sensor',
attributes: ['shock'],
],
signalStrength: [
name: 'Signal Strength',
attributes: ['lqi', 'rssi'],
],
sleepSensor: [
name: 'Sleep Sensor',
attributes: ['sleeping'],
],
smokeDetector: [
name: 'Smoke Detector',
attributes: ['smoke'],
],
soundSensor: [
name: 'Sound Sensor',
attributes: ['sound'],
],
speechSynthesis: [
name: 'Speech Synthesis',
attributes: [],
],
stepSensor: [
name: 'Step Sensor',
attributes: ['steps', 'goal'],
],
switch: [
name: 'Switch',
attributes: ['switch'],
],
switchLevel: [
name: 'Switch Level',
attributes: ['level'],
],
soundPressureLevel: [
name: 'Sound Pressure Level',
attributes: ['soundPressureLevel'],
],
tamperAlert: [
name: 'Tamper Alert',
attributes: ['tamper'],
],
temperatureMeasurement: [
name: 'Temperature Measurement',
attributes: ['temperature'],
],
thermostat: [
name: 'Thermostat',
attributes: ['temperature', 'heatingSetpoint', 'coolingSetpoint', 'thermostatSetpoint', 'thermostatMode', 'thermostatFanMode', 'thermostatOperatingState'],
],
thermostatCoolingSetpoint: [
name: 'Thermostat Cooling Setpoint',
attributes: ['coolingSetpoint'],
],
thermostatFanMode: [
name: 'Thermostat Fan Mode',
attributes: ['thermostatFanMode'],
],
thermostatHeatingSetpoint: [
name: 'Thermostat Heating Setpoint',
attributes: ['heatingSetpoint'],
],
thermostatMode: [
name: 'Thermostat Mode',
attributes: ['thermostatMode'],
],
thermostatOperatingState: [
name: 'Thermostat Operating State',
attributes: ['thermostatOperatingState'],
],
thermostatSetpoint: [
name: 'Thermostat Setpoint',
attributes: ['thermostatSetpoint'],
],
threeAxis: [
name: 'Three Axis',
attributes: ['threeAxis'],
],
timedSession: [
name: 'Timed Session',
attributes: ['sessionStatus', 'timeRemaining'],
],
tone: [
name: 'Tone',
attributes: [],
],
touchSensor: [
name: 'Touch Sensor',
attributes: ['touch'],
],
valve: [
name: 'Valve',
attributes: ['contact'],
],
voltageMeasurement: [
name: 'Voltage Measurement',
attributes: ['voltage'],
],
waterSensor: [
name: 'Water Sensor',
attributes: ['water'],
],
windowShade: [
name: 'Window Shade',
attributes: ['windowShade'],
],
]
}
preferences {
section("Choose one or more, when..."){
capabilities().each{ capability, data ->
input capability, "capability.${capability}", title: data['name'], required: false, multiple:true
}
}
}
def installed() {
log.debug "Installed with settings: ${settings}"
subscribeToEvents()
}
def updated() {
log.debug "Updated with settings: ${settings}"
unsubscribe()
subscribeToEvents()
}
def subscribeToEvents() {
capabilities().each{ capability, data ->
def attributes = data['attributes']
attributes.each{ attribute ->
log.trace "Subscribing to capability=${capability} attribute: ${attribute}"
subscribe(settings[capability], attribute, eventHandler)
}
}
}
def toJson(data) {
return new groovy.json.JsonBuilder(data)
}
def eventToMap(evt) {
// These fields are not safe to call unconditionally. They can cause
// exceptions.
//doubleValue: evt.doubleValue
//floatValue: evt.floatValue
//integerValue: evt.integerValue
//isoDate: evt.isoDate
//jsonValue: evt.jsonValue
//location: evt.location
//dateValue: evt.dateValue
//longValue: evt.longValue
//numberValue: evt.numberValue
//numericValue: evt.numericValue
//stringValue: evt.stringValue
//xyzValue: evt.xyzValue
// This field is usually null.
//installedSmartAppId: evt.installedSmartAppId,
// This field causes a StackOverflow because it of circular
// references.
//device: evt.device
def out = [
id: evt.id.toString(),
data: evt.data,
description: evt.description,
descriptionText: evt.descriptionText,
displayName: evt.displayName,
deviceId: evt.deviceId,
hubId: evt.hubId,
isDigital: evt.isDigital(),
isPhysical: evt.isPhysical(),
isStateChange: evt.isStateChange(),
linkText: evt.linkText,
locationId: evt.locationId,
name: evt.name,
source: evt.source,
unit: evt.unit,
"@timestamp": timestamp(evt.date),
value: evt.value,
]
if (out['floatValue'] == null) {
try {
out['floatValue'] = evt.doubleValue
} catch (e) {
// Ignore
}
}
if (out['floatValue'] == null) {
try {
out['floatValue'] = evt.floatValue
} catch (e) {
// Ignore
}
}
if (out['floatValue'] == null) {
try {
out['floatValue'] = evt.numberValue
} catch (e) {
// Ignore
log.trace "numberValue " + e
}
}
if (out['floatValue'] == null) {
try {
out['floatValue'] = evt.numericValue
} catch (e) {
// Ignore
}
}
if (out['longValue'] == null) {
try {
out['longValue'] = evt.integerValue
} catch (e) {
// Ignore
}
}
if (out['longValue'] == null) {
try {
out['longValue'] = evt.longValue
} catch (e) {
// Ignore
}
}
return out
}
def eventHandler(evt) {
try {
def eventMap = eventToMap(evt)
def credsBase64 = "${appSettings.ES_USER}:${appSettings.ES_PASS}".bytes.encodeBase64()
def basicAuthHeader = "Basic ${credsBase64}"
def params = [
uri: "${appSettings.ES_URL}/smartthings/event",
headers: ['Authorization' : basicAuthHeader],
body: eventMap,
]
log.trace "Request parameters: ${params}"
httpPostJson(params) { resp ->
log.debug "Response status:${resp.status} contentType:${resp.contentType} data:${resp.data}"
}
} catch (Throwable t) {
log.error "Exception while processing event ${evt.value}: ${t}"
}
}
String timestamp(Date d) {
Calendar cal = Calendar.getInstance();
DateFormat dfm = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
dfm.setTimeZone(TimeZone.getTimeZone("GMT"));
String timestamp = dfm.format(d);
return timestamp;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment