Created
March 23, 2017 16:02
-
-
Save andrewkroh/74bd896a11570799a9163f532f654d37 to your computer and use it in GitHub Desktop.
Elasticsearch Output for SmartThings Events
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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/[email protected]", | |
iconX3Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/[email protected]" | |
) { | |
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