From 388114be2e8bab0bf376e5dda93bc5fc546298ea Mon Sep 17 00:00:00 2001 From: hrottenberg Date: Sat, 7 Sep 2019 17:25:46 -0400 Subject: [PATCH 1/6] - updated metadata, including new icon,category - properly formatted w/indentation, removed some unused commented code - removed HOST HTTP header so that Splunk will set that properly - added new "http:smartthings" sourcetype field for Splunk - added some comments here and there --- .../splunk-http-event-logger.groovy | 459 +++++++++--------- 1 file changed, 219 insertions(+), 240 deletions(-) diff --git a/smartapps/thefuzz4/splunk-http-event-logger.src/splunk-http-event-logger.groovy b/smartapps/thefuzz4/splunk-http-event-logger.src/splunk-http-event-logger.groovy index 52452b6..3b7bdc1 100644 --- a/smartapps/thefuzz4/splunk-http-event-logger.src/splunk-http-event-logger.groovy +++ b/smartapps/thefuzz4/splunk-http-event-logger.src/splunk-http-event-logger.groovy @@ -1,8 +1,5 @@ /** * Event Logger For Splunk -* This was originally created by Brian Keifer I modified it to work with the Splunk HTTP Event Collector -* -* Copyright 2015 Brian Keifer * * 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: @@ -18,328 +15,310 @@ * 02-16-2016 Added the ability for non-ssl/SSL * 05-18-2016 Added the ability to log to splunk over the lan and * 10-24-2017 Added the code from Uto to log humidity readings and spelling fixes -* used adrabkin code fix for the length with local logging +* used adrabkin code fix for the length with local logging +* 08-07-2019 Reformatting, removed HOST header from HTTP payload. See full changelog at https://github.com/halr9000/SmartThingsSplunkLogger */ definition( -name: "Splunk HTTP Event Logger", -namespace: "thefuzz4", -author: "Brian Keifer and Jason Hamilton", -description: "Log SmartThings events to a Splunk HTTP Event Collector server", -category: "Convenience", -iconUrl: "http://apmblog.dynatrace.com/wp-content/uploads/2014/07/Splunk_thumbnail.png", -iconX2Url: "http://apmblog.dynatrace.com/wp-content/uploads/2014/07/Splunk_thumbnail.png", -iconX3Url: "http://apmblog.dynatrace.com/wp-content/uploads/2014/07/Splunk_thumbnail.png") + name: "Splunk HTTP Event Logger", + namespace: "halr9000", + author: "Brian Keifer, Jason Hamilton, Hal Rottenberg", + description: "Log SmartThings events to a Splunk HTTP Event Collector server", + category: "Analytics", + iconUrl: "https://cdn.apps.splunk.com/media/public/icons/cdb1e1dc-5a0e-11e4-84b5-0af1e3fac1ba.png", + iconX2Url: "https://cdn.apps.splunk.com/media/public/icons/cdb1e1dc-5a0e-11e4-84b5-0af1e3fac1ba.png", + iconX3Url: "https://cdn.apps.splunk.com/media/public/icons/cdb1e1dc-5a0e-11e4-84b5-0af1e3fac1ba.png") preferences { -section("Log these presence sensors:") { -input "presences", "capability.presenceSensor", multiple: true, required: false -} -section("Log these switches:") { -input "switches", "capability.switch", multiple: true, required: false -} -section("Log these switch levels:") { -input "levels", "capability.switchLevel", multiple: true, required: false -} -section("Log these motion sensors:") { -input "motions", "capability.motionSensor", multiple: true, required: false -} -section("Log these temperature sensors:") { -input "temperatures", "capability.temperatureMeasurement", multiple: true, required: false -} -section("Log these humidity sensors:") { -input "humidities", "capability.relativeHumidityMeasurement", multiple: true, required: false -} -section("Log these contact sensors:") { -input "contacts", "capability.contactSensor", multiple: true, required: false -} -section("Log these alarms:") { -input "alarms", "capability.alarm", multiple: true, required: false -} -section("Log these indicators:") { -input "indicators", "capability.indicator", multiple: true, required: false -} -section("Log these CO detectors:") { -input "codetectors", "capability.carbonMonoxideDetector", multiple: true, required: false -} -section("Log these smoke detectors:") { -input "smokedetectors", "capability.smokeDetector", multiple: true, required: false -} -section("Log these water detectors:") { -input "waterdetectors", "capability.waterSensor", multiple: true, required: false -} -section("Log these acceleration sensors:") { -input "accelerations", "capability.accelerationSensor", multiple: true, required: false -} -section("Log these energy meters:") { -input "energymeters", "capability.energyMeter", multiple: true, required: false -} -section("Log these music players:") { -input "musicplayer", "capability.musicPlayer", multiple: true, required: false -} -section("Log these power meters:") { -input "powermeters", "capability.powerMeter", multiple: true, required: false -} -section("Log these illuminance sensors:") { -input "illuminances", "capability.illuminanceMeasurement", multiple: true, required: false -} -section("Log these batteries:") { -input "batteries", "capability.battery", multiple: true, required: false -} -section("Log these buttons:") { -input "button", "capability.button", multiple: true, required: false -} -section("Log these voltages:") { -input "voltage", "capability.voltageMeasurement", multiple: true, required: false -} -section("Log these locks:") { -input "lockDevice", "capability.lock", multiple: true, required: false -} - -section ("Splunk Server") { - input "use_local", "boolean", title: "Local Server?", required: true - input "splunk_host", "text", title: "Splunk Hostname/IP", required: true - input "use_ssl", "boolean", title: "Use SSL?", required: true - input "splunk_port", "number", title: "Splunk Port", required: true - input "splunk_token", "text", title: "Splunk Authentication Token", required: true -} + section("Log these presence sensors:") { + input "presences", "capability.presenceSensor", multiple: true, required: false + } + section("Log these switches:") { + input "switches", "capability.switch", multiple: true, required: false + } + section("Log these switch levels:") { + input "levels", "capability.switchLevel", multiple: true, required: false + } + section("Log these motion sensors:") { + input "motions", "capability.motionSensor", multiple: true, required: false + } + section("Log these temperature sensors:") { + input "temperatures", "capability.temperatureMeasurement", multiple: true, required: false + } + section("Log these humidity sensors:") { + input "humidities", "capability.relativeHumidityMeasurement", multiple: true, required: false + } + section("Log these contact sensors:") { + input "contacts", "capability.contactSensor", multiple: true, required: false + } + section("Log these alarms:") { + input "alarms", "capability.alarm", multiple: true, required: false + } + section("Log these indicators:") { + input "indicators", "capability.indicator", multiple: true, required: false + } + section("Log these CO detectors:") { + input "codetectors", "capability.carbonMonoxideDetector", multiple: true, required: false + } + section("Log these smoke detectors:") { + input "smokedetectors", "capability.smokeDetector", multiple: true, required: false + } + section("Log these water detectors:") { + input "waterdetectors", "capability.waterSensor", multiple: true, required: false + } + section("Log these acceleration sensors:") { + input "accelerations", "capability.accelerationSensor", multiple: true, required: false + } + section("Log these energy meters:") { + input "energymeters", "capability.energyMeter", multiple: true, required: false + } + section("Log these music players:") { + input "musicplayer", "capability.musicPlayer", multiple: true, required: false + } + section("Log these power meters:") { + input "powermeters", "capability.powerMeter", multiple: true, required: false + } + section("Log these illuminance sensors:") { + input "illuminances", "capability.illuminanceMeasurement", multiple: true, required: false + } + section("Log these batteries:") { + input "batteries", "capability.battery", multiple: true, required: false + } + section("Log these buttons:") { + input "button", "capability.button", multiple: true, required: false + } + section("Log these voltages:") { + input "voltage", "capability.voltageMeasurement", multiple: true, required: false + } + section("Log these locks:") { + input "lockDevice", "capability.lock", multiple: true, required: false + } + section ("Splunk Server") { + input "use_local", "boolean", title: "Local Server?", required: true + input "splunk_host", "text", title: "Splunk Hostname/IP", required: true + input "use_ssl", "boolean", title: "Use SSL?", required: true + input "splunk_port", "number", title: "Splunk Port", required: true + input "splunk_token", "text", title: "Splunk Authentication Token", required: true + } } def installed() { -log.debug "Installed with settings: ${settings}" - -initialize() + log.debug "Installed with settings: ${settings}" + initialize() } def updated() { -log.debug "Updated with settings: ${settings}" - -unsubscribe() -initialize() + log.debug "Updated with settings: ${settings}" + unsubscribe() + initialize() } def initialize() { -// TODO: subscribe to attributes, devices, locations, etc. -doSubscriptions() + doSubscriptions() } +// Subscribes to the various Events for a device or Location. The specified handlerMethod will be called when the Event is fired. +// subscribe specification: https://docs.smartthings.com/en/latest/ref-docs/smartapp-ref.html#subscribe def doSubscriptions() { -subscribe(alarms, "alarm", alarmHandler) -subscribe(codetectors, "carbonMonoxideDetector", coHandler) -subscribe(contacts, "contact", contactHandler) -subscribe(indicators, "indicator", indicatorHandler) -subscribe(modes, "locationMode", modeHandler) -subscribe(motions, "motion", motionHandler) -subscribe(presences, "presence", presenceHandler) -subscribe(relays, "relaySwitch", relayHandler) -subscribe(smokedetectors, "smokeDetector", smokeHandler) -subscribe(switches, "switch", switchHandler) -subscribe(levels, "level", levelHandler) -subscribe(temperatures, "temperature", temperatureHandler) -subscribe(waterdetectors, "water", waterHandler) -subscribe(location, "location", locationHandler) -subscribe(accelerations, "acceleration", accelerationHandler) -subscribe(energymeters, "energy", energyHandler) -subscribe(musicplayers, "music", musicHandler) -subscribe(lightSensor, "illuminance", illuminanceHandler) -subscribe(powermeters, "power", powerHandler) -subscribe(batteries, "battery", batteryHandler) -subscribe(button, "button", buttonHandler) -subscribe(voltageMeasurement, "voltage", voltageHandler) -subscribe(lockDevice, "lock", lockHandler) -subscribe(humidities, "humidity", humidityHandler) -} - + subscribe(alarms,"alarm",alarmHandler) + subscribe(codetectors,"carbonMonoxideDetector",coHandler) + subscribe(contacts,"contact", contactHandler) + subscribe(indicators,"indicator", indicatorHandler) + subscribe(modes,"locationMode", modeHandler) + subscribe(motions,"motion", motionHandler) + subscribe(presences,"presence", presenceHandler) + subscribe(relays,"relaySwitch", relayHandler) + subscribe(smokedetectors,"smokeDetector",smokeHandler) + subscribe(switches,"switch", switchHandler) + subscribe(levels,"level",levelHandler) + subscribe(temperatures,"temperature", temperatureHandler) + subscribe(waterdetectors,"water",waterHandler) + subscribe(location,"location",locationHandler) + subscribe(accelerations, "acceleration", accelerationHandler) + subscribe(energymeters, "energy", energyHandler) + subscribe(musicplayers, "music", musicHandler) + subscribe(lightSensor,"illuminance",illuminanceHandler) + subscribe(powermeters,"power",powerHandler) + subscribe(batteries,"battery", batteryHandler) + subscribe(button, "button", buttonHandler) + subscribe(voltageMeasurement, "voltage", voltageHandler) + subscribe(lockDevice, "lock", lockHandler) + subscribe(humidities, "humidity", humidityHandler) +} + +// Build JSON object and write it to Splunk HEC +// event specification: https://docs.smartthings.com/en/latest/ref-docs/event-ref.html def genericHandler(evt) { -/* -log.debug("------------------------------") -log.debug("date: ${evt.date}") -log.debug("name: ${evt.name}") -log.debug("displayName: ${evt.displayName}") -log.debug("device: ${evt.device}") -log.debug("deviceId: ${evt.deviceId}") -log.debug("value: ${evt.value}") -log.debug("isStateChange: ${evt.isStateChange()}") -log.debug("id: ${evt.id}") -log.debug("description: ${evt.description}") -log.debug("descriptionText: ${evt.descriptionText}") -log.debug("installedSmartAppId: ${evt.installedSmartAppId}") -log.debug("isoDate: ${evt.isoDate}") -log.debug("isDigital: ${evt.isDigital()}") -log.debug("isPhysical: ${evt.isPhysical()}") -log.debug("location: ${evt.location}") -log.debug("locationId: ${evt.locationId}") -log.debug("source: ${evt.source}") -log.debug("unit: ${evt.unit}") -*/ - -def json = "" -json += "{\"event\":" -json += "{\"date\":\"${evt.date}\"," -json += "\"name\":\"${evt.name}\"," -json += "\"displayName\":\"${evt.displayName}\"," -json += "\"device\":\"${evt.device}\"," -json += "\"deviceId\":\"${evt.deviceId}\"," -json += "\"value\":\"${evt.value}\"," -json += "\"isStateChange\":\"${evt.isStateChange()}\"," -json += "\"id\":\"${evt.id}\"," -json += "\"description\":\"${evt.description}\"," -json += "\"descriptionText\":\"${evt.descriptionText}\"," -json += "\"installedSmartAppId\":\"${evt.installedSmartAppId}\"," -json += "\"isoDate\":\"${evt.isoDate}\"," -json += "\"isDigital\":\"${evt.isDigital()}\"," -json += "\"isPhysical\":\"${evt.isPhysical()}\"," -json += "\"location\":\"${evt.location}\"," -json += "\"locationId\":\"${evt.locationId}\"," -json += "\"unit\":\"${evt.unit}\"," -json += "\"source\":\"${evt.source}\",}" -json += "}" -//log.debug("JSON: ${json}") -def ssl = use_ssl.toBoolean() -def local = use_local.toBoolean() -def http_protocol -def splunk_server = "${splunk_host}:${splunk_port}" -def length = json.getBytes().size().toString() -def msg = parseLanMessage(description) -def body = msg.body -def status = msg.status - -if (local == true) { -//sendHubCommand(new physicalgraph.device.HubAction([ -def result = (new physicalgraph.device.HubAction([ -method: "POST", -path: "/services/collector/event", -headers: [ -'Authorization': "Splunk ${splunk_token}", -"Content-Length":"${length}", -HOST: "${splunk_server}", -"Content-Type":"application/json", -"Accept-Encoding":"gzip,deflate" -], -body:json -])) -log.debug result -sendHubCommand(result); -return result -} -else { -//log.debug "Use Remote" -//log.debug "Current SSL Value ${use_ssl}" -if (ssl == true) { -//log.debug "Using SSL" -http_protocol = "https" -} -else { -//log.debug "Not Using SSL" -http_protocol = "http" -} - -def params = [ -uri: "${http_protocol}://${splunk_host}:${splunk_port}/services/collector/event", -headers: [ -'Authorization': "Splunk ${splunk_token}" -], -body: json -] -log.debug params -try { -httpPostJson(params) -} catch ( groovyx.net.http.HttpResponseException ex ) { -log.debug "Unexpected response error: ${ex.statusCode}" -} -} -} + def json = "" + json += "{\"event\":" + json += "{\"date\":\"${evt.date}\"," + json += "\"name\":\"${evt.name}\"," + json += "\"displayName\":\"${evt.displayName}\"," + json += "\"device\":\"${evt.device}\"," + json += "\"deviceId\":\"${evt.deviceId}\"," + json += "\"value\":\"${evt.value}\"," + json += "\"isStateChange\":\"${evt.isStateChange()}\"," + json += "\"id\":\"${evt.id}\"," + json += "\"description\":\"${evt.description}\"," + json += "\"descriptionText\":\"${evt.descriptionText}\"," + json += "\"installedSmartAppId\":\"${evt.installedSmartAppId}\"," + json += "\"isoDate\":\"${evt.isoDate}\"," + json += "\"isDigital\":\"${evt.isDigital()}\"," + json += "\"isPhysical\":\"${evt.isPhysical()}\"," + json += "\"location\":\"${evt.location}\"," + json += "\"locationId\":\"${evt.locationId}\"," + json += "\"unit\":\"${evt.unit}\"," + json += "\"source\":\"${evt.source}\",}" + json += "\"sourcetype\":\"http:smartthings\",}" + json += "}" + //log.debug("JSON: ${json}") + def ssl = use_ssl.toBoolean() + def local = use_local.toBoolean() + def http_protocol + def splunk_server = "${splunk_host}:${splunk_port}" + def length = json.getBytes().size().toString() + def msg = parseLanMessage(description) + def body = msg.body + def status = msg.status + + // Write data locally using Hub Action (internal LAN IP ok) + if (local == true) { + def result = (new physicalgraph.device.HubAction([ + method: "POST", + path: "/services/collector/event", + headers: [ + 'Authorization': "Splunk ${splunk_token}", + "Content-Length":"${length}", + "Content-Type":"application/json", + "Accept-Encoding":"gzip,deflate" + ], + body:json + ])) + log.debug result + sendHubCommand(result); // do it! + return result + } + // Write data via ST cloud (must ensure Splunk HEC IP and port are publicly accessible) + else { + //log.debug "Use Remote" + //log.debug "Current SSL Value ${use_ssl}" + if (ssl == true) { + //log.debug "Using SSL" + http_protocol = "https" + } + else { + //log.debug "Not Using SSL" + http_protocol = "http" + } + + def params = [ + uri: "${http_protocol}://${splunk_host}:${splunk_port}/services/collector/event", + headers: [ + 'Authorization': "Splunk ${splunk_token}" + ], + body: json + ] + log.debug params + try { + httpPostJson(params) // do it! + } catch ( groovyx.net.http.HttpResponseException ex ) { + log.debug "Unexpected response error: ${ex.statusCode}" + } + } +} + +// Today all of the subscriptions use the generic handler, but could be customized if needed def alarmHandler(evt) { -genericHandler(evt) + genericHandler(evt) } def coHandler(evt) { -genericHandler(evt) + genericHandler(evt) } def indicatorHandler(evt) { -genericHandler(evt) + genericHandler(evt) } def presenceHandler(evt) { -genericHandler(evt) + genericHandler(evt) } def switchHandler(evt) { -genericHandler(evt) + genericHandler(evt) } def smokeHandler(evt) { -genericHandler(evt) + genericHandler(evt) } def levelHandler(evt) { -genericHandler(evt) + genericHandler(evt) } def contactHandler(evt) { -genericHandler(evt) + genericHandler(evt) } def temperatureHandler(evt) { -genericHandler(evt) + genericHandler(evt) } def motionHandler(evt) { -genericHandler(evt) + genericHandler(evt) } def modeHandler(evt) { -genericHandler(evt) + genericHandler(evt) } def relayHandler(evt) { -genericHandler(evt) + genericHandler(evt) } def waterHandler(evt) { -genericHandler(evt) + genericHandler(evt) } def locationHandler(evt) { -genericHandler(evt) + genericHandler(evt) } def accelerationHandler(evt) { -genericHandler(evt) + genericHandler(evt) } def energyHandler(evt) { -genericHandler(evt) + genericHandler(evt) } def musicHandler(evt) { -genericHandler(evt) + genericHandler(evt) } def illuminanceHandler(evt) { -genericHandler(evt) + genericHandler(evt) } def powerHandler(evt) { -genericHandler(evt) + genericHandler(evt) } def humidityHandler(evt) { -genericHandler(evt) + genericHandler(evt) } def batteryHandler(evt) { -genericHandler(evt) + genericHandler(evt) } def buttonHandler(evt) { -genericHandler(evt) + genericHandler(evt) } def voltageHandler(evt) { -genericHandler(evt) + genericHandler(evt) } def lockHandler(evt) { -genericHandler(evt) + genericHandler(evt) } From b91538ff7096b7e9b82753d5a1efbe5b258efcb5 Mon Sep 17 00:00:00 2001 From: hrottenberg Date: Sat, 7 Sep 2019 22:48:09 -0400 Subject: [PATCH 2/6] - Fixes #2 - JSON typo --- .../splunk-http-event-logger.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/smartapps/thefuzz4/splunk-http-event-logger.src/splunk-http-event-logger.groovy b/smartapps/thefuzz4/splunk-http-event-logger.src/splunk-http-event-logger.groovy index 3b7bdc1..cd3d72a 100644 --- a/smartapps/thefuzz4/splunk-http-event-logger.src/splunk-http-event-logger.groovy +++ b/smartapps/thefuzz4/splunk-http-event-logger.src/splunk-http-event-logger.groovy @@ -168,8 +168,8 @@ def genericHandler(evt) { json += "\"location\":\"${evt.location}\"," json += "\"locationId\":\"${evt.locationId}\"," json += "\"unit\":\"${evt.unit}\"," - json += "\"source\":\"${evt.source}\",}" - json += "\"sourcetype\":\"http:smartthings\",}" + json += "\"stSource\":\"${evt.source}\"," + json += "\"sourcetype\":\"smartthings\"}" json += "}" //log.debug("JSON: ${json}") def ssl = use_ssl.toBoolean() From 0dc34092dd0acafb9898f29ddaf07b8dc793c27e Mon Sep 17 00:00:00 2001 From: hrottenberg Date: Sat, 7 Sep 2019 23:00:34 -0400 Subject: [PATCH 3/6] - Fixes #5. Turns out category is not free text --- .../splunk-http-event-logger.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smartapps/thefuzz4/splunk-http-event-logger.src/splunk-http-event-logger.groovy b/smartapps/thefuzz4/splunk-http-event-logger.src/splunk-http-event-logger.groovy index cd3d72a..29b6d8a 100644 --- a/smartapps/thefuzz4/splunk-http-event-logger.src/splunk-http-event-logger.groovy +++ b/smartapps/thefuzz4/splunk-http-event-logger.src/splunk-http-event-logger.groovy @@ -23,7 +23,7 @@ definition( namespace: "halr9000", author: "Brian Keifer, Jason Hamilton, Hal Rottenberg", description: "Log SmartThings events to a Splunk HTTP Event Collector server", - category: "Analytics", + category: "Convenience", iconUrl: "https://cdn.apps.splunk.com/media/public/icons/cdb1e1dc-5a0e-11e4-84b5-0af1e3fac1ba.png", iconX2Url: "https://cdn.apps.splunk.com/media/public/icons/cdb1e1dc-5a0e-11e4-84b5-0af1e3fac1ba.png", iconX3Url: "https://cdn.apps.splunk.com/media/public/icons/cdb1e1dc-5a0e-11e4-84b5-0af1e3fac1ba.png") From 04f69a5e88eb1ee32c41a45f05db9c120f216639 Mon Sep 17 00:00:00 2001 From: hrottenberg Date: Sat, 7 Sep 2019 23:38:22 -0400 Subject: [PATCH 4/6] - fixed local messages which do need HOST header --- .../splunk-http-event-logger.src/splunk-http-event-logger.groovy | 1 + 1 file changed, 1 insertion(+) diff --git a/smartapps/thefuzz4/splunk-http-event-logger.src/splunk-http-event-logger.groovy b/smartapps/thefuzz4/splunk-http-event-logger.src/splunk-http-event-logger.groovy index 29b6d8a..9a211b0 100644 --- a/smartapps/thefuzz4/splunk-http-event-logger.src/splunk-http-event-logger.groovy +++ b/smartapps/thefuzz4/splunk-http-event-logger.src/splunk-http-event-logger.groovy @@ -189,6 +189,7 @@ def genericHandler(evt) { headers: [ 'Authorization': "Splunk ${splunk_token}", "Content-Length":"${length}", + HOST: "${splunk_server}", "Content-Type":"application/json", "Accept-Encoding":"gzip,deflate" ], From fd3fb5d2d560b19e5c9f188f058b58ae44b90493 Mon Sep 17 00:00:00 2001 From: Nick Zambo Date: Mon, 9 Sep 2019 08:58:07 -0700 Subject: [PATCH 5/6] refactored some variable names to match the naming structure. added event/device to JSON functions. added ability to enable device polling and implemented 5 minutes polling for all devices if enabled. --- .../splunk-http-event-logger.groovy | 102 ++++++++++++++++-- 1 file changed, 94 insertions(+), 8 deletions(-) diff --git a/smartapps/thefuzz4/splunk-http-event-logger.src/splunk-http-event-logger.groovy b/smartapps/thefuzz4/splunk-http-event-logger.src/splunk-http-event-logger.groovy index 9a211b0..105ea50 100644 --- a/smartapps/thefuzz4/splunk-http-event-logger.src/splunk-http-event-logger.groovy +++ b/smartapps/thefuzz4/splunk-http-event-logger.src/splunk-http-event-logger.groovy @@ -92,6 +92,10 @@ preferences { section("Log these locks:") { input "lockDevice", "capability.lock", multiple: true, required: false } + section("Scheduled Device Polling") { + input "do_device_poll", "boolean", title: "Poll devices every 5 mins?", required: true + } + section ("Splunk Server") { input "use_local", "boolean", title: "Local Server?", required: true input "splunk_host", "text", title: "Splunk Hostname/IP", required: true @@ -114,8 +118,45 @@ def updated() { def initialize() { doSubscriptions() + if(do_device_poll) { + runEvery5Minutes(reportStates) + } +} + +// enumerates all devices, converts to JSON output and adds to an array to send to splunk +def reportStates { + def detailList = [] + alarms.each { detailList.add(deviceToJSON(it)) } + codetectors.each { detailList.add(deviceToJSON(it)) } + contacts.each { detailList.add(deviceToJSON(it)) } + indicators.each { detailList.add(deviceToJSON(it)) } + modes.each { detailList.add(deviceToJSON(it)) } + motions.each { detailList.add(deviceToJSON(it)) } + presences.each { detailList.add(deviceToJSON(it)) } + relays.each { detailList.add(deviceToJSON(it)) } + smokedetectors.each { detailList.add(deviceToJSON(it)) } + switches.each { detailList.add(deviceToJSON(it)) } + levels.each { detailList.add(deviceToJSON(it)) } + temperatures.each { detailList.add(deviceToJSON(it)) } + waterdetectors.each { detailList.add(deviceToJSON(it)) } + locations.each { detailList.add(deviceToJSON(it)) } + accelerations.each { detailList.add(deviceToJSON(it)) } + energymeters.each { detailList.add(deviceToJSON(it)) } + musicplayers.each { detailList.add(deviceToJSON(it)) } + lightsensors.each { detailList.add(deviceToJSON(it)) } + powermeters.each { detailList.add(deviceToJSON(it)) } + batteries.each { detailList.add(deviceToJSON(it)) } + buttons.each { detailList.add(deviceToJSON(it)) } + voltagemeasurements.each { detailList.add(deviceToJSON(it)) } + lockdevices.each { detailList.add(deviceToJSON(it)) } + humidities.each { detailList.add(deviceToJSON(it)) } + + detailList.unique() + detailList.each { logToSplunkHEC it } + } + // Subscribes to the various Events for a device or Location. The specified handlerMethod will be called when the Event is fired. // subscribe specification: https://docs.smartthings.com/en/latest/ref-docs/smartapp-ref.html#subscribe def doSubscriptions() { @@ -132,23 +173,53 @@ def doSubscriptions() { subscribe(levels,"level",levelHandler) subscribe(temperatures,"temperature", temperatureHandler) subscribe(waterdetectors,"water",waterHandler) - subscribe(location,"location",locationHandler) + subscribe(locations,"location",locationHandler) subscribe(accelerations, "acceleration", accelerationHandler) subscribe(energymeters, "energy", energyHandler) subscribe(musicplayers, "music", musicHandler) - subscribe(lightSensor,"illuminance",illuminanceHandler) + subscribe(lightsensors,"illuminance",illuminanceHandler) subscribe(powermeters,"power",powerHandler) subscribe(batteries,"battery", batteryHandler) - subscribe(button, "button", buttonHandler) - subscribe(voltageMeasurement, "voltage", voltageHandler) - subscribe(lockDevice, "lock", lockHandler) + subscribe(buttons, "button", buttonHandler) + subscribe(voltagemeasurements, "voltage", voltageHandler) + subscribe(lockdevices, "lock", lockHandler) subscribe(humidities, "humidity", humidityHandler) } -// Build JSON object and write it to Splunk HEC -// event specification: https://docs.smartthings.com/en/latest/ref-docs/event-ref.html -def genericHandler(evt) { + +// convert any device into a json output +def deviceToJSON(device) { + def theDevice = device + def deviceCapabilities = theDevice.capabilities + def eventString = "" + eventString += "{\"event\":{\"eventType\":\"scheduledPoll\",\"sourcetype\":\"smartthings\", \"deviceName\":\"${theDevice.label}\", \"deviceId\":\"${theDevice.id}\",\"capabilities\":{" + deviceCapabilities.each { cap -> + eventString = eventString + "\"${cap.name}\":{" + cap.attributes.each { attr -> + def currentValue = theDevice.currentValue(attr.name) + eventString = eventString + "\"${attr.name}\":\"${currentValue}\"," + } + if(eventString[-1] == ",") { + eventString = eventString.substring(0,eventString.length()-1) + "}," + } else { + eventString = eventString + "}," + } + } + if(eventString[-1] == ",") { + eventString = eventString.substring(0,eventString.length()-1) + "}" + } else { + eventString = eventString + "}" + } + + eventString = eventString + "}}" + return eventString + +} + + +// converts smartthings events to json +def eventToJSON(evt) { def json = "" json += "{\"event\":" json += "{\"date\":\"${evt.date}\"," @@ -171,6 +242,13 @@ def genericHandler(evt) { json += "\"stSource\":\"${evt.source}\"," json += "\"sourcetype\":\"smartthings\"}" json += "}" + + return json +} + + +// assumes well-formed HEC json, sends to HEC endpoint item by item +def logToSplunkHEC(json) { //log.debug("JSON: ${json}") def ssl = use_ssl.toBoolean() def local = use_local.toBoolean() @@ -226,6 +304,14 @@ def genericHandler(evt) { log.debug "Unexpected response error: ${ex.statusCode}" } } + +} + +// Build JSON object and write it to Splunk HEC +// event specification: https://docs.smartthings.com/en/latest/ref-docs/event-ref.html +// logToSplunkHEC is used for both events and scheduled device polling +def genericHandler(evt) { + logToSplunkHEC(eventToJSON(evt)) } // Today all of the subscriptions use the generic handler, but could be customized if needed From dcb73977c68ec7ff95a137f9c35af4ea991edd12 Mon Sep 17 00:00:00 2001 From: Nick Zambo Date: Mon, 9 Sep 2019 09:00:00 -0700 Subject: [PATCH 6/6] refactored some variable names to match the naming structure. added event/device to JSON functions. added ability to enable device polling and implemented 5 minutes polling for all devices if enabled. --- .../splunk-http-event-logger.groovy | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/smartapps/thefuzz4/splunk-http-event-logger.src/splunk-http-event-logger.groovy b/smartapps/thefuzz4/splunk-http-event-logger.src/splunk-http-event-logger.groovy index 105ea50..7884231 100644 --- a/smartapps/thefuzz4/splunk-http-event-logger.src/splunk-http-event-logger.groovy +++ b/smartapps/thefuzz4/splunk-http-event-logger.src/splunk-http-event-logger.groovy @@ -17,6 +17,7 @@ * 10-24-2017 Added the code from Uto to log humidity readings and spelling fixes * used adrabkin code fix for the length with local logging * 08-07-2019 Reformatting, removed HOST header from HTTP payload. See full changelog at https://github.com/halr9000/SmartThingsSplunkLogger +* 09-09-2019 Refactored variables for consistency. Moved JSON conversion out of the logging subroutine. Added device polling option. */ definition( name: "Splunk HTTP Event Logger", @@ -28,6 +29,8 @@ definition( iconX2Url: "https://cdn.apps.splunk.com/media/public/icons/cdb1e1dc-5a0e-11e4-84b5-0af1e3fac1ba.png", iconX3Url: "https://cdn.apps.splunk.com/media/public/icons/cdb1e1dc-5a0e-11e4-84b5-0af1e3fac1ba.png") + +// TODO - more granular ability to poll only specific device or devicetypes preferences { section("Log these presence sensors:") { input "presences", "capability.presenceSensor", multiple: true, required: false @@ -247,7 +250,7 @@ def eventToJSON(evt) { } -// assumes well-formed HEC json, sends to HEC endpoint item by item +// assumes well-formed HEC json, sends to HEC endpoint via POST each call. def logToSplunkHEC(json) { //log.debug("JSON: ${json}") def ssl = use_ssl.toBoolean()