Infrastructure that works for all the locks' group!
[smartthings-infrastructure.git] / Extractor / App1 / App1.groovy
index 299b494920f770d218e896874d8a6f458f5e7864..49a97c3e9e3d332f78ebf126fc3b6d534f54e1f3 100644 (file)
 /**
- *  Lock it at a specific time
+ *  groveStreams
  *
- *  Copyright 2014 Erik Thayer
+ *  Copyright 2014 Yves Racine
  *
- *  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:
+ *  LinkedIn profile: ca.linkedin.com/pub/yves-racine-m-sc-a/0/406/4b/
  *
- *      http://www.apache.org/licenses/LICENSE-2.0
+ *  Developer retains all right, title, copyright, and interest, including all copyright, patent rights, trade secret 
+ *  in the Background technology. May be subject to consulting fees under the Agreement between the Developer and the Customer. 
+ *  Developer grants a non exclusive perpetual license to use the Background technology in the Software developed for and delivered 
+ *  to Customer under this Agreement. However, the Customer shall make no commercial use of the Background technology without
+ *  Developer's written consent.
  *
  *  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.
+ *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+ *  Software Distribution is restricted and shall be done only with Developer's written approval.
  *
+ *  Based on code from Jason Steele & Minollo
+ *  Adapted to be compatible with MyEcobee and My Automatic devices which are available at my store:
+ *          http://www.ecomatiqhomes.com/#!store/tc3yr 
+ * 
  */
 definition(
-    name: "Lock it at a specific time",
-    namespace: "user8798",
-    author: "Erik Thayer",
-    description: "Make sure a door is locked at a specific time.  Option to add door contact sensor to only lock if closed.",
-    category: "Safety & Security",
-    iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
-    iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png"
+       name: "groveStreams",
+       namespace: "yracine",
+       author: "Yves Racine",
+       description: "Log to groveStreams and send data streams based on devices selection",
+       category: "My Apps",
+       iconUrl: "https://s3.amazonaws.com/smartapp-icons/Partner/ecobee.png",
+       iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Partner/ecobee@2x.png"
 )
 
 
+
 preferences {
-  section("At this time every day") {
-    input "time", "time", title: "Time of Day"
-  }
-  section("Make sure this is locked") {
-    input "lock","capability.lock"
-  }
-  section("Make sure it's closed first..."){
-    input "contact", "capability.contactSensor", title: "Which contact sensor?", required: false
-  }
-  section( "Notifications" ) {
-    input "sendPushMessage", "enum", title: "Send a push notification?", metadata:[values:["Yes", "No"]], required: false
-    input "phone", "phone", title: "Send a text message?", required: false
-  } 
+       section("About") {
+               paragraph "groveStreams, the smartapp that sends your device states to groveStreams for data correlation"
+               paragraph "Version 2.2.2" 
+               paragraph "If you like this smartapp, please support the developer via PayPal and click on the Paypal link below " 
+                       href url: "https://www.paypal.me/ecomatiqhomes",
+                               title:"Paypal donation..."
+               paragraph "Copyright©2014 Yves Racine"
+                       href url:"http://github.com/yracine/device-type.myecobee", style:"embedded", required:false, title:"More information..."  
+                               description: "http://github.com/yracine"
+       }
+       section("Log devices...") {
+               input "temperatures", "capability.temperatureMeasurement", title: "Temperatures", required: false, multiple: true
+               input "thermostats", "capability.thermostat", title: "Thermostats", required: false, multiple: true
+               //input "ecobees", "device.myEcobeeDevice", title: "Ecobees", required: false, multiple: true
+               input "automatic", "capability.presenceSensor", title: "Automatic Connected Device(s)", required: false, multiple: true
+               input "detectors", "capability.smokeDetector", title: "Smoke/CarbonMonoxide Detectors", required: false, multiple: true
+               input "humidities", "capability.relativeHumidityMeasurement", title: "Humidity sensors", required: false, multiple: true
+               input "waters", "capability.waterSensor", title: "Water sensors", required: false, multiple: true
+               input "illuminances", "capability.illuminanceMeasurement", title: "Illuminance sensor", required: false, multiple: true
+               input "locks", "capability.lock", title: "Locks", required: false, multiple: true
+               input "contacts", "capability.contactSensor", title: "Doors open/close", required: false, multiple: true
+               input "accelerations", "capability.accelerationSensor", title: "Accelerations", required: false, multiple: true
+               input "motions", "capability.motionSensor", title: "Motions", required: false, multiple: true
+               input "presence", "capability.presenceSensor", title: "Presence", required: false, multiple: true
+               input "switches", "capability.switch", title: "Switches", required: false, multiple: true
+               input "dimmerSwitches", "capability.switchLevel", title: "Dimmer Switches", required: false, multiple: true
+               input "batteries", "capability.battery", title: "Battery-powered devices", required: false, multiple: true
+               input "powers", "capability.powerMeter", title: "Power Meters", required: false, multiple: true
+               input "energys", "capability.energyMeter", title: "Energy Meters", required: false, multiple: true
+
+       }
+
+       section("GroveStreams Feed PUT API key...") {
+               input "channelKey", "text", title: "API key"
+       }
+       section("Sending data at which interval in minutes (default=5)?") {
+               input "givenInterval", "number", title: 'Send Data Interval', required: false
+       }
 }
+
 def installed() {
-  schedule(time, "setTimeCallback")
-
-}
-
-def updated(settings) {
-  unschedule()
-  schedule(time, "setTimeCallback")
-}
-
-def setTimeCallback() {
-  if (contact) {
-    doorOpenCheck()
-  } else {
-    lockMessage()
-    lock.lock()
-  }
-}
-def doorOpenCheck() {
-  def currentState = contact.contactState
-  if (currentState?.value == "open") {
-    def msg = "${contact.displayName} is open.  Scheduled lock failed."
-    log.info msg
-    if (sendPushMessage) {
-      sendPush msg
-    }
-    if (phone) {
-      sendSms phone, msg
-    }
-  } else {
-    lockMessage()
-    lock.lock()
-  }
-}
-
-def lockMessage() {
-  def msg = "Locking ${lock.displayName} due to scheduled lock."
-  log.info msg
-  if (sendPushMessage) {
-    sendPush msg
-  }
-  if (phone) {
-    sendSms phone, msg
-  }
+       initialize()
+}
+
+def updated() {
+       unsubscribe()
+       unschedule()
+       initialize()
+}
+
+def initialize() {
+       subscribe(temperatures, "temperature", handleTemperatureEvent)
+       subscribe(humidities, "humidity", handleHumidityEvent)
+       subscribe(waters, "water", handleWaterEvent)
+       subscribe(waters, "water", handleWaterEvent)
+       subscribe(detectors, "smoke", handleSmokeEvent)
+       subscribe(detectors, "carbonMonoxide", handleCarbonMonoxideEvent)
+       subscribe(illuminances, "illuminance", handleIlluminanceEvent)
+       subscribe(contacts, "contact", handleContactEvent)
+       subscribe(locks, "lock", handleLockEvent)
+       subscribe(accelerations, "acceleration", handleAccelerationEvent)
+       subscribe(motions, "motion", handleMotionEvent)
+       subscribe(presence, "presence", handlePresenceEvent)
+       subscribe(switches, "switch", handleSwitchEvent)
+       subscribe(dimmerSwitches, "switch", handleSwitchEvent)
+       subscribe(dimmerSwitches, "level", handleSetLevelEvent)
+       subscribe(batteries, "battery", handleBatteryEvent)
+       subscribe(powers, "power", handlePowerEvent)
+       subscribe(energys, "energy", handleEnergyEvent)
+       subscribe(energys, "cost", handleCostEvent)
+       subscribe(thermostats, "heatingSetpoint", handleHeatingSetpointEvent)
+       subscribe(thermostats, "coolingSetpoint", handleCoolingSetpointEvent)
+       subscribe(thermostats, "thermostatMode", handleThermostatModeEvent)
+       subscribe(thermostats, "fanMode", handleFanModeEvent)
+       subscribe(thermostats, "thermostatOperatingState", handleThermostatOperatingStateEvent)
+       /*subscribe(ecobees, "dehumidifierMode", handleDehumidifierModeEvent)
+       subscribe(ecobees, "equipmentStatus", handleEquipmentStatusEvent)
+       subscribe(ecobees, "dehumidifierLevel", handleDehumidifierLevelEvent)
+       subscribe(ecobees, "humidifierMode", handleHumidifierModeEvent)
+       subscribe(ecobees, "humidifierLevel", handleHumidifierLevelEvent)
+       subscribe(ecobees, "fanMinOnTime", handleFanMinOnTimeEvent)
+       subscribe(ecobees, "ventilatorMode", handleVentilatorModeEvent)
+       subscribe(ecobees, "ventilatorMinOnTime", handleVentilatorMinOnTimeEvent)
+       subscribe(ecobees, "programScheduleName", handleProgramNameEvent)
+       subscribe(ecobees, "auxHeat1RuntimeDaily", handleDailyStats)
+       subscribe(ecobees, "auxHeat2RuntimeDaily", handleDailyStats)
+       subscribe(ecobees, "auxHeat3RuntimeDaily", handleDailyStats)
+       subscribe(ecobees, "compCool1RuntimeDaily", handleDailyStats)
+       subscribe(ecobees, "compCool2RuntimeDaily", handleDailyStats)
+       subscribe(ecobees, "fanRuntimeDaily", handleDailyStats)
+       subscribe(ecobees, "humidifierRuntimeDaily", handleDailyStats)
+       subscribe(ecobees, "dehumidifierRuntimeDaily", handleDailyStats)
+       subscribe(ecobees, "ventilatorRuntimeDaily", handleDailyStats)
+       subscribe(ecobees, "presence", handlePresenceEvent)
+       subscribe(ecobees, "compCool2RuntimeDaily", handleDailyStats)*/
+       subscribe(automatic, "yesterdayTripsAvgAverageKmpl",handleDailyStats)
+       subscribe(automatic, "yesterdayTripsAvgDistanceM",handleDailyStats)
+       subscribe(automatic, "yesterdayTripsAvgDurationS",handleDailyStats)
+       subscribe(automatic, "yesterdayTotalDistanceM",handleDailyStats)
+       subscribe(automatic, "yesterdayTripsAvgFuelVolumeL",handleDailyStats)
+       subscribe(automatic, "yesterdayTotalFuelVolumeL",handleDailyStats)
+       subscribe(automatic, "yesterdayTotalDurationS:",handleDailyStats)
+       subscribe(automatic, "yesterdayTotalNbTrips",handleDailyStats)
+       subscribe(automatic, "yesterdayTotalHardAccels",handleDailyStats)
+       subscribe(automatic, "yesterdayTotalHardBrakes:",handleDailyStats)
+       subscribe(automatic, "yesterdayTripsAvgScoreSpeeding",handleDailyStats)
+       subscribe(automatic, "yesterdayTripsAvgScoreEvents",handleDailyStats)
+       def queue = []
+       atomicState.queue=queue
+    
+       if (atomicState.queue==null) {
+               atomicState.queue = []
+       }    
+       atomicState?.poll = [ last: 0, rescheduled: now() ]
+
+       Integer delay  = givenInterval ?: 5 // By default, schedule processQueue every 5 min.
+       log.debug "initialize>scheduling processQueue every ${delay} minutes"
+
+       //Subscribe to different events (ex. sunrise and sunset events) to trigger rescheduling if needed
+       subscribe(location, "sunrise", rescheduleIfNeeded)
+       subscribe(location, "sunset", rescheduleIfNeeded)
+       subscribe(location, "mode", rescheduleIfNeeded)
+       subscribe(location, "sunriseTime", rescheduleIfNeeded)
+       subscribe(location, "sunsetTime", rescheduleIfNeeded)
+       subscribe(app, appTouch)
+
+       //rescheduleIfNeeded()   
+}
+
+def appTouch(evt) {
+       rescheduleIfNeeded(evt)
+       processQueue()
+       def queue = []
+       atomicState.queue=queue
+}
+
+
+def rescheduleIfNeeded(evt) {
+       if (evt) log.debug("rescheduleIfNeeded>$evt.name=$evt.value")
+       Integer delay  = givenInterval ?: 5 // By default, schedule processQueue every 5 min.
+       BigDecimal currentTime = now()    
+       BigDecimal lastPollTime = (currentTime - (atomicState?.poll["last"]?:0))  
+       if (lastPollTime != currentTime) {    
+               Double lastPollTimeInMinutes = (lastPollTime/60000).toDouble().round(1)      
+               log.info "rescheduleIfNeeded>last poll was  ${lastPollTimeInMinutes.toString()} minutes ago"
+       }
+       if (((atomicState?.poll["last"]?:0) + (delay * 60000) < currentTime) && canSchedule()) {
+               log.info "rescheduleIfNeeded>scheduling processQueue in ${delay} minutes.."
+               unschedule()  
+               schedule("14:00", processQueue)
+       }
+       // Update rescheduled state
+    
+       if (!evt) {
+               atomicState.poll["rescheduled"] = now()    
+       }        
+}    
+
+def handleTemperatureEvent(evt) {
+       queueValue(evt) {
+               it.toString()
+       }
 }
+
+def handleHumidityEvent(evt) {
+       queueValue(evt) {
+               it.toString()
+       }
+}
+
+def handleHeatingSetpointEvent(evt) {
+       queueValue(evt) {
+               it.toString()
+       }
+}
+def handleCoolingSetpointEvent(evt) {
+       queueValue(evt) {
+               it.toString()
+       }
+}
+
+def handleThermostatModeEvent(evt) {
+       queueValue(evt) {
+               it.toString()
+       }
+}
+def handleFanModeEvent(evt) {
+       queueValue(evt) {
+               it.toString()
+       }
+}
+def handleHumidifierModeEvent(evt) {
+       queueValue(evt) {
+               it.toString()
+       }
+}
+def handleHumidifierLevelEvent(evt) {
+       queueValue(evt) {
+               it.toString()
+       }
+}
+def handleDehumidifierModeEvent(evt) {
+       queueValue(evt) {
+               it.toString()
+       }
+}
+def handleDehumidifierLevelEvent(evt) {
+       queueValue(evt) {
+               it.toString()
+       }
+}
+def handleVentilatorModeEvent(evt) {
+       queueValue(evt) {
+               it.toString()
+       }
+}
+def handleFanMinOnTimeEvent(evt) {
+       queueValue(evt) {
+               it.toString()
+       }
+}
+def handleVentilatorMinOnTimeEvent(evt) {
+       queueValue(evt) {
+               it.toString()
+       }
+}
+
+def handleThermostatOperatingStateEvent(evt) {
+       queueValue(evt) {
+               it == "idle" ? 0 : (it == 'fan only') ? 1 : (it == 'heating') ? 2 : 3
+       }
+
+}
+def handleDailyStats(evt) {
+       queueValue(evt) {
+               it.toString()
+       }
+
+}
+def handleEquipmentStatusEvent(evt) {
+       queueValue(evt) {
+               it.toString()
+       }
+}
+
+def handleProgramNameEvent(evt) {
+       queueValue(evt) {
+               it.toString()
+       }
+}
+
+def handleWaterEvent(evt) {
+       queueValue(evt) {
+               it.toString()
+       }
+}
+def handleSmokeEvent(evt) {
+       queueValue(evt) {
+               it.toString()
+       }
+}
+def handleCarbonMonoxideEvent(evt) {
+       queueValue(evt) {
+               it.toString()
+       }
+}
+
+def handleIlluminanceEvent(evt) {
+       log.debug ("handleIlluminanceEvent> $evt.name= $evt.value")
+       queueValue(evt) {
+               it.toString()
+       }
+}
+
+def handleLockEvent(evt) {
+       queueValue(evt) {
+               it == "locked" ? 1 : 0
+       }
+}
+
+def handleBatteryEvent(evt) {
+       queueValue(evt) {
+               it.toString()
+       }
+}
+
+def handleContactEvent(evt) {
+       queueValue(evt) {
+               it == "open" ? 1 : 0
+       }
+}
+
+def handleAccelerationEvent(evt) {
+       queueValue(evt) {
+               it == "active" ? 1 : 0
+       }
+}
+
+def handleMotionEvent(evt) {
+       queueValue(evt) {
+               it == "active" ? 1 : 0
+       }
+}
+
+def handlePresenceEvent(evt) {
+       queueValue(evt) {
+               it == "present" ? 1 : 0
+       }
+}
+
+def handleSwitchEvent(evt) {
+       queueValue(evt) {
+               it == "on" ? 1 : 0
+       }
+}
+
+def handleSetLevelEvent(evt) {
+       queueValue(evt) {
+               it.toString()
+       }
+}
+
+def handlePowerEvent(evt) {
+       if (evt.value) {
+               queueValue(evt) {
+                       it.toString()
+               }
+       }
+}
+
+def handleEnergyEvent(evt) {
+       if (evt.value) {
+               queueValue(evt) {
+                       it.toString()
+               }
+       }
+}
+def handleCostEvent(evt) {
+       if (evt.value) {
+               queueValue(evt) {
+                       it.toString()
+               }
+       }
+}
+
+private queueValue(evt, Closure convert) {
+       def MAX_QUEUE_SIZE=95000
+       def jsonPayload = [compId: evt.displayName, streamId: evt.name, data: convert(evt.value), time: now()]
+       def queue
+
+       queue = atomicState.queue
+       queue << jsonPayload
+       atomicState.queue = queue    
+       def queue_size = queue.toString().length()
+       def last_item_in_queue = queue[queue.size() -1]    
+       log.debug "queueValue>queue size in chars=${queue_size}, appending ${jsonPayload} to queue, last item in queue= $last_item_in_queue"
+       if (queue_size >  MAX_QUEUE_SIZE) {
+               processQueue()
+       }
+}
+
+def processQueue() {
+       Integer delay  = givenInterval ?: 5 // By default, schedule processQueue every 5 min.
+       atomicState?.poll["last"] = now()
+
+       if (((atomicState?.poll["rescheduled"]?:0) + (delay * 60000)) < now()) {
+               log.info "processQueue>scheduling rescheduleIfNeeded() in ${delay} minutes.."
+               schedule("0 0/${delay} * * * ?", rescheduleIfNeeded)
+               // Update rescheduled state
+               atomicState?.poll["rescheduled"] = now()
+       }
+
+       def queue = atomicState.queue
+    
+   
+       def url = "https://grovestreams.com/api/feed?api_key=${channelKey}"
+       log.debug "processQueue"
+       if (queue != []) {
+               log.debug "Events to be sent to groveStreams: ${queue}"
+
+               /*try {
+                       httpPutJson([uri: url, body: queue]) {response ->
+                               if (response.status != 200) {
+                                       log.debug "GroveStreams logging failed, status = ${response.status}"
+                               } else {
+                                       log.debug "GroveStreams accepted event(s)"
+                                       // reset the queue 
+                                       queue =[]                         
+                                       atomicState.queue = queue                     
+                               }
+                       }
+               } catch (groovyx.net.http.ResponseParseException e) {
+                       // ignore error 200, bogus exception
+                       if (e.statusCode != 200) {
+                               log.error "Grovestreams: ${e}"
+                       } else {
+                               log.debug "GroveStreams accepted event(s)"
+                       }
+                       // reset the queue 
+                       queue =[]                         
+                       atomicState.queue = queue                      
+            
+               } catch (e) {
+                       def errorInfo = "Error sending value: ${e}"
+                       log.error errorInfo
+                       // reset the queue 
+                       queue =[]                         
+                       atomicState.queue = queue                        
+               }*/
+       }
+
+}
+