Update groveStreams.groovy
[smartapps.git] / third-party / AutomaticReport.groovy
1 /**
2  *  AutomaticReport
3  *
4  *  Copyright 2015 Yves Racine
5  *  LinkedIn profile: ca.linkedin.com/pub/yves-racine-m-sc-a/0/406/4b/ 
6  *
7  *  Developer retains all right, title, copyright, and interest, including all copyright, patent rights, trade secret 
8  *  in the Background technology. May be subject to consulting fees under the Agreement between the Developer and the Customer. 
9  *  Developer grants a non exclusive perpetual license to use the Background technology in the Software developed for and delivered 
10  *  to Customer under this Agreement. However, the Customer shall make no commercial use of the Background technology without
11  *  Developer's written consent.
12  *
13  *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
14  *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
15  *
16  *  Software Distribution is restricted and shall be done only with Developer's written approval.
17  *
18  *  N.B. Requires MyAutomatic device available at 
19  *          http://www.ecomatiqhomes.com/#!store/tc3yr 
20  **/
21  
22 import java.text.SimpleDateFormat
23 import groovy.json.JsonSlurper
24
25 definition(
26     name: "AutomaticReport",
27     namespace: "yracine",
28     author: "Yves Racine",
29     description: "This smartapp allows a ST user to manually generate daily Reports on their Automatic Connected vehicle",
30     category: "My Apps",
31         iconUrl: "https://www.automatic.com/_assets/images/favicons/favicon-32x32-3df4de42.png",
32         iconX2Url: "https://www.automatic.com/_assets/images/favicons/favicon-96x96-06fd8c85.png",
33         iconX3Url: "https://www.automatic.com/_assets/images/favicons/favicon-96x96-06fd8c85.png")
34
35 preferences {
36         section("About") {
37                 paragraph "automaticReport, the smartapp that generates daily runtime reports about your Automatic connected vehicle"
38                 paragraph "You can only run the smartapp manually by pressing the arrow sign on the app's icon" 
39                 paragraph "Version 1.6.8" 
40                 paragraph "If you like this smartapp, please support the developer via PayPal and click on the Paypal link below " 
41                         href url: "https://www.paypal.me/ecomatiqhomes",
42                                         title:"Paypal donation..."
43                 paragraph "Copyright©2015 Yves Racine" 
44                         href url:"http://github.com/yracine/device-type.myautomatic", style:"embedded", required:false, title:"More information..."  
45                                 description: "http://github.com/yracine"
46         }                
47
48         section("Generate daily report for this Automatic Connected Vehicle") {
49                 input "automatic", "capability.presenceSensor", title: "Automatic?"
50
51         }
52         section("Start date for the report, format = YYYY-MM-DD") {
53                 input "givenStartDate", "text", title: "Beginning Date [default=yesterday]", required:false
54         }        
55         section("Start time for report HH:MM (24HR)") {
56                 input "givenStartTime", "text", title: "Beginning time [default=00:00]",  required:false        
57         }        
58         section("End date for the report = YYYY-MM-DD") {
59                 input "givenEndDate", "text", title: "End Date [default=today]", required:false
60         }        
61         section("End time for the report (24HR)" ) {
62                 input "givenEndTime", "text", title: "End time [default=00:00]", required:false
63         }        
64         section( "Notifications" ) {
65                 input "sendPushMessage", "enum", title: "Send a push notification?", metadata:[values:["Yes", "No"]], required: false
66                 input "phoneNumber", "phone", title: "Send a text message?", required: false
67     }
68     
69 }
70
71 def installed() {
72         log.debug "automaticReport>installed with settings: ${settings}"
73         initialize()
74 }
75
76 def updated() {
77         log.debug "Updated with settings: ${settings}"
78
79         unsubscribe()
80         unschedule()    
81         initialize()
82 }
83
84 def initialize() {
85         subscribe(app, appTouch)
86 }
87
88 def appTouch(evt) {
89         generateReport()
90 }
91
92
93 private def generateReport() {
94         def AUTOMATIC_SPEEDING_EVENT='speeding'    
95         def AUTOMATIC_HARD_BRAKE_EVENT='hard_brake'    
96         def AUTOMATIC_HARD_BRAKE_ACCEL='hard_accel'    
97         def msg
98    
99         String dateInLocalTime = new Date().format("yyyy-MM-dd", location.timeZone) 
100         String timezone = new Date().format("zzz", location.timeZone)
101         String dateAtMidnight = dateInLocalTime + " 00:00 " + timezone    
102         log.debug("generateReport>date at Midnight= ${dateAtMidnight}")
103         Date endDate = formatDate(dateAtMidnight) 
104         Date startDate = endDate -1
105         
106         def givenStartDate = (settings.givenStartDate) ?: startDate.format("yyyy-MM-dd", location.timeZone) 
107         def givenStartTime=(settings.givenStartTime) ?:"00:00"    
108         def dateTime = givenStartDate + " " + givenStartTime + " " + timezone
109         startDate = formatDate(dateTime)
110         log.debug("generateReport>start dateTime = ${dateTime}, startDate in UTC = ${startDate.format("yyyy-MM-dd HH:mm:ss", TimeZone.getTimeZone("UTC"))}")
111     
112         def givenEndDate = (settings.givenEndDate) ?: endDate.format("yyyy-MM-dd", location.timeZone) 
113         def givenEndTime=(settings.givenEndTime) ?:"00:00"    
114         dateTime = givenEndDate + " " + givenEndTime + " " + timezone
115         endDate = formatDate(dateTime)
116         log.debug("generateReport>end dateTime = ${dateTime}, endDate in UTC =${endDate.format("yyyy-MM-dd HH:mm:ss", TimeZone.getTimeZone("UTC"))}")
117
118         automatic.getTrips("","", null,null, null, true)
119
120         def currentTripList = automatic.currentTripsList
121         def tripFields =null   
122         if (!currentTripList) {
123                 log.debug "generateReport> empty tripList exiting"
124                 return          
125         } 
126         String tripData = automatic.currentTripsData
127         if (!tripData) {
128                 log.debug("generateReport>no trip data found, exiting")            
129                 return        
130         }
131         try {
132                 tripFields = new JsonSlurper().parseText(tripData)   
133 //              log.debug("generateReport> vehicle_events=$tripFields.vehicle_events[0]")            
134         } catch (e) {
135                 log.error("generateReport>tripData not formatted correctly or empty (exception $e), exiting")
136                 return
137         } 
138         def nbTripsValue = automatic.currentTotalNbTripsInPeriod
139         int nbTrips = (nbTripsValue)? nbTripsValue.toInteger():0
140         for (i in 0..nbTrips-1) {
141                 def tripId=tripFields[i].id    
142                 String startAddress=tripFields[i].start_address.name
143                 String endAddress=tripFields[i].end_address.name        
144                 def vehicleEvents=tripFields[i]?.vehicle_events 
145                 log.debug ("generateReport> found ${tripId} trip from start Address=${startAddress} to endAddress=${endAddress}, \nvehicleEvents=${vehicleEvents}")
146                 String eventCreatedAt
147                 vehicleEvents.each {
148                         def type = it.type        
149                         if (type == AUTOMATIC_SPEEDING_EVENT) {
150                                 eventCreatedAt=formatDateInLocalTime(it.started_at)
151                                 def startDistance = it.start_distance_m
152                                 def endDistance= it.end_distance_m
153                                 float startPos= getDistance(startDistance)
154                                 float endPos= getDistance(endDistance)                
155                                 log.debug ("generateReport> found ${type} event startedAt: ${it.started_at}, eventCreatedInLocalTime=$eventCreatedAt, timezone=${tripFields[i].end_timezone}")
156                                 def speed =it.velocity_kph
157                                 float speedValue=getSpeed(speed)
158                                 msg = "automaticReport>${automatic} was speeding (speed> ${speedValue.round()}${getSpeedScale()}) at ${eventCreatedAt} on trip ${tripId} from ${startAddress} to ${endAddress};" +
159                                         "Start Trip Distance=${startPos.round()}${getDistanceScale()},End Trip Distance=${endPos.round()}${getDistanceScale()}"
160                                 send(msg)
161                         }            
162                 
163                         if (type ==AUTOMATIC_HARD_BRAKE_EVENT) {
164                                 eventCreatedAt=formatDateInLocalTime(it.created_at)
165                                 def gforce=it.g_force   
166                                 def lon =it.lon
167                                 def lat = it.lat                
168                                 log.debug ("generateReport> found ${type} event startedAt: ${it.created_at}, eventCreatedInLocalTime=$eventCreatedAt, timezone=${tripFields[i].end_timezone}")
169                                 msg = "automaticReport>${automatic} triggered the ${type} event (gforce=${gforce}, lat=${lat},lon=${lon}) at ${eventCreatedAt} on trip ${tripId} from ${startAddress} to ${endAddress} "
170                                 send(msg)
171                         }                
172                         if (type==AUTOMATIC_HARD_ACCEL_EVENT) {
173                                 eventCreatedAt=formatDateInLocalTime(it.created_at)   
174                                 def gforce=it.g_force                
175                                 def lon =it.lon
176                                 def lat = it.lat                
177                                 log.debug ("generateReport> found ${type} event startedAt: ${it.created_at}, eventCreatedInLocalTime=$eventCreatedAt, timezone=${tripFields[i].end_timezone}")
178                                 msg = "automaticReport>${automatic} triggered the ${type} event (gforce=${gforce}, lat=${lat},lon=${lon}) at ${eventCreatedAt} on trip ${tripId} from ${startAddress} to ${endAddress} "
179                                 send(msg)
180                         }
181                 } /* end each vehicle Event */       
182         } /* end for each Trip  */
183     
184     
185 }
186
187 private def report_states_between_dates(eventType, startDate, endDate) {
188         def eventStates = automatic.statesBetween("eventType",  startDate as Date, endDate as Date)
189     
190         def countEventType = eventStates.count {it.value && it.value == eventType} 
191         
192         def msg = "automaticReport>${automatic} triggered ${countEventType} ${eventType} event(s) between  " +
193                 "${startDate.format("yyyy-MM-dd HH:mm:ss")} and ${endDate.format("yyyy-MM-dd HH:mm:ss")}"    
194         send(msg)
195 }
196
197
198 private def getSpeed(value) {
199         if (!value) {
200                 return 0    
201         }    
202         if(getTemperatureScale() == "C"){
203                 return value
204         } else {
205                 return milesToKm(value)
206         }
207 }
208
209 private def getSpeedScale() {
210         def scale= getTemperatureScale()
211         if (scale == 'C') {
212                 return "kmh"
213         }
214         return "mph"
215 }
216
217 private def milesToKm(distance) {
218         if (!distance) {
219                 return 0    
220         }    
221         return (distance * 1.609344) 
222 }
223
224 private def kmToMiles(distance) {
225         if (!distance) {
226                 return 0    
227         }    
228         return (distance * 0.62137) 
229 }
230 private def getDistance(value) {
231         if (!value) {
232                 return 0    
233         }    
234         def km = value/1000
235         if (getTemperatureScale() == "C"){
236                 return km
237         } else {
238                 return kmToMiles(km)
239         }
240 }
241
242 private def getDistanceScale() {
243         def scale= getTemperatureScale()
244         if (scale == 'C') {
245                 return "km"
246         }
247         return "mi"
248 }
249
250 private String formatDateInLocalTime(dateInString, timezone='') {
251         def myTimezone=(timezone)?TimeZone.getTimeZone(timezone):location.timeZone 
252         if ((dateInString==null) || (dateInString.trim()=="")) {
253                 return (new Date().format("yyyy-MM-dd HH:mm:ss", myTimezone))
254         }    
255         SimpleDateFormat ISODateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")
256         Date ISODate = ISODateFormat.parse(dateInString.substring(0,19)+ 'Z')
257         String dateInLocalTime =new Date(ISODate.getTime()).format("yyyy-MM-dd HH:mm:ss zzz", myTimezone)
258         return dateInLocalTime
259 }
260
261
262 private def formatDate(dateString) {
263         SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm zzz")
264         Date aDate = sdf.parse(dateString)
265         return aDate
266 }
267
268 private def sendMsgWithDelay() {
269         
270         if (state.msg) {
271                 send(state.msg)
272         }
273         state.msg=""    
274 }
275
276 private send(msg) {
277         if ( sendPushMessage != "No" ) {
278                 log.debug( "sending push message" )
279                 sendPush( msg )
280        
281         }
282         if ( phoneNumber ) {
283                 log.debug( "sending text message" )
284                 sendSms( phoneNumber, msg )
285         }
286
287         log.debug msg
288 }