The files contained in this repository can be downloaded to your computer using a svn client.
On Linux you simply type the command displayed below.

This URL has Read-Only access.

Statistics
| Revision:

root / trunk / DomotiGa / Events.module @ 838

History | View | Annotate | Download (35.3 kB)

1
' Gambas module file
2
3
' Description:
4
' Events.module
5
' This module provides support for events, triggers, actions and conditions.
6
7
' Development Status:
8
' Works 99.99% ok now. Partly rewritten by Geert-Jan van den Hurk.
9
10
' DomotiGa - an open source home automation program.
11
' Copyright(C) 2008-2012 Ron Klinkien
12
13
' Read file called COPYING for license details.
14
15
PRIVATE oDelayTimers AS NEW Object[]
16
PUBLIC tDelayTimer AS CTimerDelay
17
18
' trigger database table
19
' description        | type | param1   | param2  | param3  | param4 | param5  | param6 | param7
20
' time now           |   1  | crontab
21
' globalvar change   |   2  | variable | operand | value
22
' device change      |   3  | id       | fieldno | operand | value
23
' ir remote received |   4  | remote   | button  | repeat
24
' iviewer remote     |   5  | remote   | join    | value
25
' multi-trigger      |   6  |
26
27
' condition database table
28
' description        | type | param1   | param2  | param3  | param4 | param5  | param6 | param7
29
' time now           |   1  | crontab
30
' globalvar value    |   2  | variable | operand | value
31
' device value       |   3  | id       | fieldno | operand | value
32
33
' called every new minute from EventLoop.Run()
34
PUBLIC SUB CheckTimeNowEvents()
35
36
  DIM rResult AS Result
37
  DIM sSql AS String = "SELECT * FROM events, triggers WHERE events.trigger1 = triggers.id AND triggers.type = 1 AND events.enabled"
38
  DIM aCron AS String[]
39
  DIM iCnt AS Integer
40
41
  IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 1a. Running TimeNow query '") & sSql & "'")
42
  TRY rResult = Main.hDB.Exec(sSql)
43
  IF rResult.Available THEN
44
    IF rResult.Count THEN
45
      DoEvent(rResult)
46
    ELSE
47
      IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 1b. No result."))
48
    END IF
49
  ENDIF
50
51
  sSql AS String = "SELECT * FROM events, triggers WHERE events.trigger1 = triggers.id AND triggers.type = 6 AND events.enabled AND triggers.param1 REGEXP '[[:<:]]TimeCron(.*)[[:>:]]'"
52
  IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 1a. Running TimeNow query '") & sSql & "'")
53
  rResult = Main.hDB.Exec(sSql)
54
  IF rResult THEN
55
    IF rResult.Count THEN
56
      FOR iCnt = 1 TO rResult.Count ' search for cron events
57
        aCron = Scan(rResult["triggers.param1"], "*(*)*")
58
        IF aCron.Count = 3 THEN
59
          IF TimeCron(aCron[1]) = TRUE THEN DoTimeEvent(rResult)
60
        ELSE
61
          IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] TimeCron multi-trigger has invalid format '") & rResult["triggers.param1"] & "'")
62
        ENDIF
63
        rResult.MoveNext()
64
      NEXT
65
    ELSE
66
      IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 1b. No result."))
67
    ENDIF
68
  ENDIF
69
70
END
71
72
' called when a value of a device is set, and is different from last one from EventLoop.DeviceChanged()
73
' iID = id of device, sField = the field that has changed, sValue = the new value it's set to
74
PUBLIC SUB CheckDeviceChangeEvents(iId AS Integer, sField AS String, sValue AS String)
75
76
  DIM rResult AS Result
77
  DIM sSql AS String = "SELECT * FROM events, triggers WHERE events.trigger1 = triggers.id AND triggers.type = 3 AND events.enabled AND triggers.param1 = &1 AND triggers.param2 = &2"
78
  DIM aFieldNo AS String[] = ["", "Value", "Value2", "Value3", "Value4", "LastSeen"]
79
80
  IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 1a. Running DeviceChange query '") & Subst(sSql, iId, aFieldNo[sField]) & "'")
81
  rResult = Main.hDB.Exec(sSql, iId, sField)
82
  IF rResult THEN
83
    IF rResult.Count THEN
84
      DoEvent(rResult, sValue)
85
    ELSE
86
      IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 1b. No result."))
87
    END IF
88
  ELSE
89
    Main.WriteDebugLog(("[Events] Error while running CheckDeviceChangeEvents query!"))
90
    RETURN
91
  ENDIF
92
93
  sSql AS String = "SELECT * FROM events, triggers WHERE events.trigger1 = triggers.id AND triggers.type = 6 AND events.enabled AND triggers.param1 REGEXP '[[:<:]]Dev_" & iId & "_" & aFieldNo[sField] & "[[:>:]]'"
94
  IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 1a. Running DeviceChange query '") & sSql & "'")
95
  rResult = Main.hDB.Exec(sSql, iId, sField)
96
  IF rResult THEN
97
    IF rResult.Count THEN
98
      DoEvent(rResult, sValue)
99
    ELSE
100
      IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 1b. No result."))
101
    END IF
102
  ELSE
103
    Main.WriteDebugLog(("[Events] Error while running CheckDeviceChangeEvents query!"))
104
    RETURN
105
  ENDIF
106
107
END
108
109
' called when a IR signal is received from CLIRC.ParseLine(), CIRMan.ParseLine() or CIRTrans.ParseLine()
110
PUBLIC SUB CheckIRRemoteEvents(sRemote AS String, sButton AS String)
111
112
  DIM rResult AS Result
113
  DIM sSql AS String = "SELECT * FROM events, triggers WHERE events.trigger1 = triggers.id AND triggers.type = 4 AND events.enabled AND triggers.param1 = &1 AND triggers.param2 = &2"
114
115
  IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 1a. Running IRRemote query '") & Subst(sSql, sRemote, sButton) & "'")
116
  rResult = Main.hDB.Exec(sSql, sRemote, sButton)
117
  IF rResult THEN
118
    IF rResult.Count THEN
119
      DoEvent(rResult)
120
     ELSE
121
      IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 1b. No result."))
122
     ENDIF
123
  ELSE
124
    Main.WriteDebugLog(("[Events] Error while running CheckIRRemoteEvents query!"))
125
    RETURN
126
  ENDIF
127
128
END
129
130
' called when a iViewer string is received from CIViewer.ParseLine()
131
PUBLIC SUB CheckIViewerRemoteEvents(sRemote AS String, sJoin AS String, sValue AS String)
132
133
  DIM rResult AS Result
134
  DIM sSql AS String = "SELECT * FROM events, triggers WHERE events.trigger1 = triggers.id AND triggers.type = 5 AND events.enabled AND triggers.param2 = &1 AND triggers.param3 = &2"
135
136
  IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 1a. Running IViewerRemote query '") & Subst(sSql, sJoin, sValue) & "'")
137
  rResult = Main.hDB.Exec(sSql, sJoin, sValue)
138
  IF rResult THEN
139
    IF rResult.Count THEN
140
      DoEvent(rResult)
141
     ELSE
142
      IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 1b. No result."))
143
     ENDIF
144
  ELSE
145
    Main.WriteDebugLog(("[Events] Error while running CheckIViewerRemoteEvents query!"))
146
    RETURN
147
  ENDIF
148
149
END
150
151
' called when a globalvar value has changed from Main.SetGlobalVar()
152
PUBLIC SUB CheckGlobalVarEvents(sVar AS String, vValue AS Variant)
153
154
  DIM rResult AS Result
155
  DIM sSql AS String = "SELECT * FROM events, triggers WHERE events.trigger1 = triggers.id AND triggers.type = 2 AND events.enabled AND triggers.param1 = &1"
156
157
  IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 1a. Running GlobalVar query '") & Subst(sSql, sVar) & "'")
158
  rResult = Main.hDB.Exec(sSql, sVar)
159
  IF rResult THEN
160
    IF rResult.Count THEN
161
      DoEvent(rResult, vValue)
162
    ELSE
163
      IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 1b. No result."))
164
    ENDIF
165
  ELSE
166
    Main.WriteDebugLog(("[Events] Error while running CheckGlobalVarEvents query!"))
167
    RETURN
168
  ENDIF
169
170
  sSql AS String = "SELECT * FROM events, triggers WHERE events.trigger1 = triggers.id AND triggers.type = 6 AND events.enabled AND triggers.param1 REGEXP '[[:<:]]Var_" & sVar & "[[:>:]]'"
171
172
  IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 1a. Running GlobalVar query '") & sSql & "'")
173
  rResult = Main.hDB.Exec(sSql, sVar)
174
  IF rResult THEN
175
    IF rResult.Count THEN
176
      DoEvent(rResult, vValue)
177
    ELSE
178
      IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 1b. No result."))
179
    ENDIF
180
  ELSE
181
    Main.WriteDebugLog(("[Events] Error while running CheckGlobalVarEvents query!"))
182
    RETURN
183
  ENDIF
184
185
END
186
187
PRIVATE SUB DoEvent(rResult AS Result, OPTIONAL sValue AS Variant)
188
189
  IF rResult.Count THEN
190
    IF Main.bEventsDebug THEN main.WriteDebugLog(("[Events] 1b. Got ") & rResult.Count & (" result(s)."))
191
    FOR EACH rResult
192
      ' check if event is allowed to get triggered again
193
      IF rResult!rerunenabled = TRUE AND IF IsDate(rResult!lastrun) THEN
194
        IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 2a. ReRun condition set for event id ") & rResult!id & (" named '") & rResult!name & "'")
195
        IF NOT CheckReRunCondition(rResult!lastrun, rResult!reruntype, rResult!rerunvalue) THEN
196
          IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 2b. Event id ") & rResult!id & (" named '") & rResult!name & ("' already ran in the last ") & rResult!rerunvalue & " " & rResult!reruntype & (" so discarding!"))
197
          CONTINUE
198
        ENDIF
199
      ENDIF
200
201
      SELECT rResult["triggers.type"]
202
        CASE 1
203
          ' timenow trigger
204
          IF TimeCron(rResult["triggers.param1"]) = FALSE THEN
205
            IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 2c. TimeNow trigger check TimeCron() for event id ") & rResult!id & (" and trigger condition '") & rResult["triggers.param1"] & ("' returned False"))
206
            CONTINUE
207
          ELSE
208
            IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 2c. TimeNow trigger check TimeCron() for event id ") & rResult!id & (" and trigger condition '") & rResult["triggers.param1"] & ("' returned True"))
209
          ENDIF
210
        CASE 2
211
          ' check globalvar change trigger
212
          IF NOT CheckCondition(sValue, rResult["triggers.param2"], rResult["triggers.param3"]) THEN CONTINUE
213
          IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 2c. GlobalVar Change trigger on event id ") & rResult!id & (" named '") & rResult!name & ("' with trigger condition '") & rResult["triggers.param1"] & " " & rResult["triggers.param2"] & " " & rResult["triggers.param3"] & "'")
214
        CASE 3
215
          ' check device change trigger
216
          IF NOT CheckCondition(sValue, rResult["triggers.param3"], rResult["triggers.param4"]) THEN CONTINUE
217
          IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 2c. Device Change trigger on event id ") & rResult!id & (" named '") & rResult!name & ("' with trigger condition '") & rResult["triggers.param2"] & "' " & sValue & " " & rResult["triggers.param3"] & " " & rResult["triggers.param4"])
218
        CASE 4
219
          ' irremote trigger
220
          IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 2c. IRRemote trigger on event id ") & rResult!id & (" named '") & rResult!name & ("' with Remote name '") & rResult["triggers.param1"] & "' and Button '" & rResult["triggers.param2"] & "'")
221
        CASE 5
222
          ' iviewer trigger
223
          IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 2c. IViewerRemote trigger on event id ") & rResult!id & (" named '") & rResult!name & ("' with Remote name '") & rResult["triggers.param1"] & "' with Join '" & rResult["triggers.param2"] & " and Value " & rResult["triggers.param3"] & "'")
224
        CASE ELSE
225
          IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 2c. Event with id ") & rResult!id & (" named '") & rResult!name & ("' has unknown trigger type ") & rResult["triggers.type"])
226
      END SELECT
227
228
      ' check optional conditions
229
      IF CheckConditions(rResult!condition1, rResult!condition2, rResult!operand) = TRUE THEN
230
        ' run action(s)
231
        IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 2f. Event id " & rResult!id & " named '") & rResult["events.name"] & ("' is validated, running action(s)."))
232
        RunActions(rResult!id)
233
      ELSE
234
        IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 2f. Event id " & rResult!id & " named '") & rResult["events.name"] & ("' has failed CheckConditions, discarding."))
235
      ENDIF
236
    NEXT
237
  ENDIF
238
239
END
240
241
PRIVATE SUB DoTimeEvent(rResult AS Result, OPTIONAL sValue AS Variant)
242
243
  ' check if event is allowed to get triggered again
244
  IF rResult!rerunenabled = TRUE AND IF IsDate(rResult!lastrun) THEN
245
    IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 2a. ReRun condition set for event id ") & rResult!id & (" named '") & rResult!name & "'")
246
    IF NOT CheckReRunCondition(rResult!lastrun, rResult!reruntype, rResult!rerunvalue) THEN
247
      IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 2b. Event id ") & rResult!id & (" named '") & rResult!name & ("' already ran in the last ") & rResult!rerunvalue & " " & rResult!reruntype & (" so discarding!"))
248
      RETURN
249
    ENDIF
250
  ENDIF
251
252
  ' check optional conditions
253
  IF CheckConditions(rResult!condition1, rResult!condition2, rResult!operand) = TRUE THEN
254
    ' run action(s)
255
    IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 2f. Event id " & rResult!id & " named '") & rResult["events.name"] & ("' is validated, running action(s)."))
256
    IF Main.bEventsDebug THEN Main.WriteDebugLog("Running '" & rResult!name & "' event")
257
    RunActions(rResult!id)
258
  ELSE
259
    IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 2f. Event id " & rResult!id & " named '") & rResult["events.name"] & ("' has failed CheckConditions, discarding."))
260
  ENDIF
261
262
END
263
264
PUBLIC SUB CheckCondition(sValue AS Variant, sOperand AS String, sCond AS Variant, OPTIONAL bMute AS Boolean) AS Boolean
265
266
  DIM bReturn AS Boolean
267
268
  IF IsBoolean(sValue) THEN sValue = Main.DisplayBool(sValue)
269
  IF IsBoolean(sCond) THEN sCond = Main.DisplayBool(sCond)
270
271
  SELECT sOperand
272
    CASE "="
273
      IF Comp(sValue, sCond, gb.Text) = 0 THEN bReturn = TRUE
274
    CASE "<>"
275
      IF sValue <> sCond THEN bReturn = TRUE
276
    CASE ">"
277
      IF CFloat(Replace(sValue, ",", ".", gb.String)) > CFloat(Replace(sCond, ",", ".", gb.String)) THEN bReturn = TRUE
278
    CASE "<"
279
      IF CFloat(Replace(sValue, ",", ".", gb.String)) < CFloat(Replace(sCond, ",", ".", gb.String)) THEN bReturn = TRUE
280
    CASE ELSE
281
      IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 2c. Unsupported operand '" & sOperand & "' found!"))
282
  END SELECT
283
  IF Main.bEventsDebug AND IF NOT bMute THEN Main.WriteDebugLog(("[Events] 2c. Check condition '") & sValue & " " & sOperand & " " & sCond & "' = " & Main.DisplayBool(bReturn))
284
  RETURN bReturn
285
286
CATCH
287
  IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 2c. Invalid comparison in CheckCondition() routine!"))
288
  RETURN FALSE
289
290
END
291
292
PRIVATE SUB AddVariablesToContext(cContext AS Collection)
293
294
  DIM vValue AS Variant
295
296
  FOR EACH vValue IN Main.GlobalVar
297
    cContext["Var_" & Main.GlobalVar.Key] = vValue
298
  NEXT
299
300
END
301
302
PRIVATE SUB AddDevicesToContext(cContext AS Collection)
303
304
  DIM iCount AS Integer
305
  DIM rResult AS Result
306
  DIM sSql AS String
307
308
  sSql = "SELECT * FROM devices WHERE enabled IS TRUE ORDER BY name"
309
  rResult = Main.hDB.Exec(sSql)
310
311
  FOR iCount = 0 TO rResult.Max
312
    cContext["Dev_" & rResult!id & "_Value"] = rResult!value
313
    cContext["Dev_" & rResult!id & "_Value2"] = rResult!value2
314
    cContext["Dev_" & rResult!id & "_Value3"] = rResult!value3
315
    cContext["Dev_" & rResult!id & "_Value4"] = rResult!value4
316
    cContext["Dev_" & rResult!id & "_LastChanged"] = rResult!lastchanged
317
    cContext["Dev_" & rResult!id & "_LastSeen"] = rResult!lastseen
318
    cContext["Dev_" & rResult!id & "_BatteryStatus"] = rResult!batterystatus
319
    rResult.MoveNext
320
  NEXT
321
322
END
323
324
PRIVATE SUB AddMacroToContext(cContext AS Collection)
325
326
  DIM iCount AS Integer
327
  DIM rResult AS Result
328
329
  rResult = Main.hDB.Exec("SELECT * FROM macros")
330
  FOR iCount = 0 TO rResult.Max
331
    cContext["Macro_" & rResult!name] = Eval(Replace(rResult!formula, "\n", " "), cContext)
332
    rResult.MoveNext
333
  NEXT
334
335
END
336
337
PUBLIC FUNCTION EvalFormula(sFormula AS String) AS String
338
339
  DIM sToEval, sResult AS String 
340
  DIM cContext AS NEW Collection
341
  DIM vResult AS Variant
342
343
  sToEval = Replace(sFormula, "\n", " ")
344
  ' here we replace variables & devices by their values
345
  AddVariablesToContext(cContext)
346
  AddDevicesToContext(cContext)
347
  AddMacroToContext(cContext)
348
  ' here the formula is evaluated
349
  TRY vResult = Eval(sToEval, cContext)
350
  IF ERROR THEN
351
    sResult = ("Error: ") & Error.Text
352
  ELSE IF IsBoolean(vResult) THEN
353
    sResult = Main.DisplayBool(vResult)
354
  ELSE
355
    sResult = vResult
356
  ENDIF
357
  RETURN sResult
358
359
CATCH
360
  RETURN Error.Text
361
362
END
363
364
PRIVATE SUB CheckSingleCondition(iCondition AS Integer) AS Boolean
365
366
  DIM bResult AS Boolean
367
  DIM rResultCondition AS Result
368
369
  rResultCondition = Main.hDB.Exec("SELECT * FROM conditions WHERE id = " & iCondition)
370
  IF rResultCondition THEN
371
    IF EvalFormula(rResultCondition!formula) = "True" THEN bResult = TRUE
372
  ENDIF
373
  IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 2d. Condition with id ") & iCondition & (" and formula '") & rResultCondition!formula & ("' returned ") & Main.DisplayBool(bResult))
374
  RETURN bResult
375
376
END
377
378
' check the optional conditions 1 and 2
379
PRIVATE SUB CheckConditions(iCondition1 AS Integer, iCondition2 AS Integer, sOperand AS String) AS Boolean
380
381
  DIM bCondition1, bCondition2 AS Boolean
382
383
  IF iCondition1 THEN
384
      bCondition1 = CheckSingleCondition(iCondition1)
385
      IF iCondition2 THEN
386
        bCondition2 = CheckSingleCondition(iCondition2)
387
      ELSE
388
        sOperand = ""
389
      ENDIF
390
391
      ' check the OR and AND clause
392
      SELECT CASE UCase(sOperand)
393
        CASE ""
394
          IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 2e. CheckConditions checking only iCondition1 with id ") & iCondition1)
395
          IF bCondition1 THEN RETURN TRUE
396
        CASE "OR"
397
          IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 2e. CheckConditions checking iCondition1 with id ") & iCondition1 & (" OR iCondition2 with id ") & iCondition2)
398
          IF bCondition1 OR IF bCondition2 THEN RETURN TRUE
399
        CASE "AND"
400
          IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 2e. CheckConditions checking iCondition1 with id ") & iCondition1 & (" AND iCondition2 with id ") & iCondition2)
401
          IF bCondition1 AND IF bCondition2 THEN RETURN TRUE
402
        CASE ELSE
403
      END SELECT
404
405
  ELSE ' no conditions defined so return true
406
    RETURN TRUE
407
  ENDIF
408
  RETURN FALSE
409
410
END
411
412
' run all configured actions for this event
413
PUBLIC SUB RunActions(iEventId AS Integer, OPTIONAL iFrom AS Integer) AS Boolean
414
415
  DIM rActions AS Result
416
417
  UpdateEvent(iEventId)
418
  rActions = Main.hDB.Exec("SELECT * FROM events_actions WHERE event = &1 ORDER BY events_actions.order", iEventId)
419
  IF rActions THEN
420
    FOR EACH rActions
421
      IF rActions!order < iFrom THEN CONTINUE
422
      IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 3a. Running action with id ") & rActions!action & (" for event with id ") & iEventId)
423
      IF RunAction(rActions!action, rActions!order, iEventId) THEN RETURN TRUE
424
    NEXT
425
  ENDIF
426
  RETURN FALSE
427
428
END
429
430
PRIVATE SUB CheckReRunCondition(dDate AS Date, sPeriod AS String, iValue AS Integer) AS Boolean
431
432
  IF DateDiff(dDate, Now(), Eval(sPeriod)) > iValue THEN
433
    RETURN TRUE
434
  ELSE
435
    IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] Event Rerun condition ") & iValue & " " & sPeriod & (" is false."))
436
    RETURN FALSE
437
  ENDIF
438
439
END
440
441
' action database table
442
' description       | rResultAction!type | rResultAction!param1      | rResultAction!param2 | rResultAction!param3 | rResultAction!param4 | rResultAction!param5
443
' set device value  | 1                  | deviceid                  | value fieldname      | value
444
' set globalvar     | 2                  | globalvar name            | value
445
' send e-mail       | 3                  | to address                | subject              | body
446
' speak text        | 4                  | voice or 'female', 'male' | text
447
' execute command   | 5                  | shell command to run
448
' send tweet        | 6                  | tweet message to send
449
' send SMS message  | 7                  | to SMS number             | message
450
' send IRTrans IR   | 8                  | command string
451
' play sound        | 9                  | sound file                | volume
452
' write log entry   | 10                 | log text
453
' display led msg   | 11                 | message to display        | display id           | color                | speed
454
' av control        | 12                 | model                     | command              | value                | address
455
' timer delay       | 13                 | delay in secs or rnd min  | random max seconds   | mode fixed or random
456
' notify-send       | 14                 | title                     | text
457
' script            | 15                 | script
458
459
PUBLIC SUB RunAction(iAction AS Integer, iOrder AS Integer, iEventId AS Integer) AS Boolean
460
461
  DIM rResultAction AS Result
462
  DIM bOk AS Boolean
463
  DIM sDeviceName, sResult AS String
464
465
  rResultAction = Main.hDB.Exec("SELECT * FROM actions WHERE id = &1 ", iAction)
466
  IF rResultAction.Available THEN
467
    SELECT rResultAction!type
468
      CASE 1 ' set device value
469
        ' only changing field Value will trigger interface code, and Value2-Value4 will only set field value
470
        IF rResultAction!param1 AND IF rResultAction!param2 AND IF rResultAction!param3 THEN
471
          sDeviceName = Devices.FindNameForDevice(rResultAction!param1)
472
          IF Len(sDeviceName) THEN
473
            SELECT CASE rResultAction!param2
474
              CASE 1
475
                bOk = Devices.SetDevice(sDeviceName, ParseText(rResultAction!param3))
476
              CASE 2
477
                bOk = Devices.ValueUpdate(rResultAction!param1, "", ParseText(rResultAction!param3), "", "")
478
              CASE 3
479
                bOk = Devices.ValueUpdate(rResultAction!param1, "", "", ParseText(rResultAction!param3), "")
480
              CASE 4
481
                bOk = Devices.ValueUpdate(rResultAction!param1, "", "", "", ParseText(rResultAction!param3))
482
              CASE ELSE
483
                Main.WriteDebugLog(("[Events] 3b. Invalid value field given ") & rResultAction!param2 & "!")
484
                bOk = FALSE
485
            END SELECT
486
          ELSE
487
            Main.WriteDebugLog(("[Events] 3b. Device with id ") & rResultAction!param1 & (" doesn't exists!"))
488
            bOk = FALSE
489
          ENDIF
490
        ENDIF
491
      CASE 2 ' set globalvar value
492
        ' if globalvar doesn't exist it will be created
493
        IF rResultAction!param1 AND IF rResultAction!param2 THEN
494
          TRY Main.SetGlobalVar(rResultAction!param1, ParseText(rResultAction!param2))
495
          IF ERROR THEN
496
            bOk = FALSE
497
          ELSE
498
            bOk = TRUE
499
          ENDIF
500
        ENDIF
501
      CASE 3 ' send e-mail
502
        IF Main.bEmailEnabled THEN
503
          TRY Mail.SendMail(ParseText(rResultAction!param2), ParseText(rResultAction!param3), rResultAction!param1)
504
          IF ERROR THEN
505
            bOk = FALSE
506
          ELSE
507
            bOk = TRUE
508
          ENDIF
509
        ELSE
510
          Main.WriteDebugLog(("[Events] 3b. e-mail support is disabled!"))
511
        ENDIF
512
      CASE 4 ' speak text
513
        IF Main.bVoiceTextEnabled THEN
514
          TRY VoiceText.Speak(ParseText(rResultAction!param2), rResultAction!param1)
515
          IF ERROR THEN
516
            bOk = FALSE
517
          ELSE
518
            bOk = TRUE
519
          ENDIF
520
        ELSE
521
          Main.WriteDebugLog(("[Events] 3b. VoiceText support is disabled!"))
522
        ENDIF
523
      CASE 5 ' execute command
524
        TRY SHELL ParseText(rResultAction!param1)
525
        IF ERROR THEN
526
          bOk = FALSE
527
        ELSE
528
          bOk = TRUE
529
        ENDIF
530
      CASE 6 ' send tweet
531
        IF Main.bTwitterEnabled THEN
532
          TRY Twitter.PostTweet(ParseText(rResultAction!param1))
533
          IF ERROR THEN
534
            bOk = FALSE
535
          ELSE
536
            bOk = TRUE
537
          ENDIF
538
        ELSE
539
          Main.WriteDebugLog(("[Events] 3b. Twitter support is disabled!"))
540
        ENDIF
541
      CASE 7 ' send SMS message
542
        IF Main.bSMSEnabled THEN
543
          TRY Main.hSMS.SendSMS(ParseText(rResultAction!param2), rResultAction!param1)
544
          IF ERROR THEN
545
            bOk = FALSE
546
          ELSE
547
            bOk = TRUE
548
          ENDIF
549
        ELSE
550
          Main.WriteDebugLog(("[Events] 3b. SMS support is disabled!"))
551
        ENDIF
552
      CASE 8 ' send IRTrans IR
553
        IF Main.bIRTransEnabled THEN
554
          TRY Main.hIRTrans.SendIRCommand(rResultAction!param1)
555
          IF ERROR THEN
556
            bOk = FALSE
557
          ELSE
558
            bOk = TRUE
559
          ENDIF
560
        ELSE
561
          Main.WriteDebugLog(("[Events] 3b. IRTrans support is disabled!"))
562
        ENDIF
563
      CASE 9 ' play sound
564
        IF Main.bSoundEnabled THEN
565
          TRY Sounds.PlaySnd(rResultAction!param1, rResultAction!param2)
566
          IF ERROR THEN
567
            bOk = FALSE
568
          ELSE
569
            bOk = TRUE
570
          ENDIF
571
        ELSE
572
          Main.WriteDebugLog(("[Events] 3b. Sound support is disabled!"))
573
        ENDIF
574
      CASE 10 ' write log entry
575
        TRY Main.WriteLog(ParseText(rResultAction!param1))
576
        IF ERROR THEN
577
          bOk = FALSE
578
        ELSE
579
          bOk = TRUE
580
        ENDIF
581
      CASE 11 ' display message on led matrix display
582
        IF Main.bLEDMatrixEnabled THEN
583
          TRY Main.hLEDMatrix.DisplayMessage(rResultAction!param2, ParseText(rResultAction!param1), rResultAction!param3, rResultAction!param4)
584
          IF ERROR THEN
585
            bOk = FALSE
586
          ELSE
587
            bOk = TRUE
588
          ENDIF
589
        ELSE
590
          Main.WriteDebugLog(("[Events] 3b. LED Matrix support is disabled!"))
591
        ENDIF
592
      CASE 12 ' av control command
593
        TRY AVControl.Set(rResultAction!param1, rResultAction!param2, rResultAction!param3, rResultAction!param4)
594
        IF ERROR THEN
595
          bOk = FALSE
596
        ELSE
597
          bOk = TRUE
598
        ENDIF
599
      CASE 13 ' timer delay
600
        IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 3b. Action with id ") & rResultAction!id & (" named '") & rResultAction!name & ("' is a delay timer!"))
601
        StartDelayTimer(iEventId, iOrder + 1, rResultAction!param1, rResultAction!param2, rResultAction!param3)
602
        RETURN TRUE
603
      CASE 14 ' notify-send
604
        Main.NotifySend(ParseText(rResultAction!param1), ParseText(rResultAction!param2))
605
        bOk = TRUE
606
      CASE 15 ' script
607
        sResult = EvalFormula(rResultAction!param1)
608
        IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 3b. Action with id ") & rResultAction!id & (" named '") & rResultAction!name & ("' returned '") & sResult & "'")
609
        bOk = TRUE
610
      CASE ELSE
611
        bOk = FALSE
612
    END SELECT
613
614
    IF bOk THEN
615
      IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 3b. Action with id ") & rResultAction!id & (" named '") & rResultAction!name & ("' executed!"))
616
      RETURN FALSE
617
    ELSE
618
      IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 3b. Action with id ") & rResultAction!id & (" named '") & rResultAction!name & ("' failed to execute!"))
619
      RETURN FALSE
620
    ENDIF
621
  ELSE
622
    IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 3b. Action with id ") & iAction & (" not found!"))
623
  ENDIF
624
625
END
626
627
' update event timestamps
628
PRIVATE SUB UpdateEvent(iId AS Integer)
629
630
  DIM rResult AS Result
631
632
  rResult = Main.hDB.Exec("SELECT * FROM events where id = &1", iId)
633
  IF rResult.Available THEN
634
    IF rResult.Count = 1 THEN
635
      IF rResult!firstrun = "00:00:00" OR rResult!firstrun = "" THEN
636
        rResult = Main.hDB.Exec("UPDATE events SET firstrun = &1 WHERE id = &2", Format(Now(), "yyyy-mm-dd hh:nn:ss"), iId)
637
      ENDIF
638
    ENDIF
639
    rResult = Main.hDB.Exec("UPDATE events SET lastrun = &1 WHERE id = &2", Format(Now(), "yyyy-mm-dd hh:nn:ss"), iId)
640
  ENDIF
641
642
END
643
644
' replace templates with their values in text
645
' <%global var%> - insert globalvar value
646
' <#device name|field#> - insert value field number 'field' from device with name 'device name'
647
' <$device id|field$> - insert value field number 'field' from device with 'device id'
648
PRIVATE SUB ParseText(sText AS String) AS String
649
650
  DIM iPos1, iPos2, iPos3 AS Integer
651
  DIM sTag, sResult AS String
652
653
  IF InStr(stext, "<") AND IF InStr(stext, ">") THEN
654
    iPos1 = InStr(sText, "<")
655
    DO WHILE iPos1 > 0
656
      iPos2 = InStr(sText, ">", iPos1 + 1)
657
      IF iPos2 > 0 THEN ' tags found
658
        sTag = Mid(sText, iPos1 + 1, iPos2 - iPos1 - 1)
659
        IF Left(sTag, 1) = "%" AND IF Right(sTag, 1) = "%" THEN ' globalvar tag
660
          sText = Replace(sText, "<" & sTag & ">", InsertGlobalVar(sTag))
661
        ENDIF
662
        IF Left(sTag, 1) = "#" AND IF Right(sTag, 1) = "#" THEN ' devicename tag
663
          sText = Replace(sText, "<" & sTag & ">", InsertDeviceValue(sTag))
664
        ENDIF
665
        IF Left(sTag, 1) = "$" AND IF Right(sTag, 1) = "$" THEN ' deviceid tag
666
          sText = Replace(sText, "<" & sTag & ">", InsertDeviceValue(sTag, TRUE))
667
        ENDIF
668
      ENDIF
669
      iPos3 = InStr(sText, "<")
670
      IF iPos3 <= iPos1 THEN
671
        BREAK
672
      ELSE
673
        iPos1 = iPos3
674
      ENDIF
675
    LOOP
676
  ENDIF
677
  RETURN sText
678
679
END
680
681
' return value of %global var%
682
PRIVATE SUB InsertGlobalVar(sTag AS String) AS String
683
684
  TRY RETURN Main.GlobalVar[Mid(sTag, 2, Len(sTag) - 2)]
685
  IF ERROR THEN RETURN ("ERROR!")
686
687
END
688
689
' return value of <#device name|field#> or <$device id|field$>
690
PRIVATE SUB InsertDeviceValue(sTag AS String, OPTIONAL bUseId AS Boolean) AS String
691
692
  DIM aScan AS String[]
693
  DIM sValue AS String
694
695
  IF InStr(sTag, "|") THEN
696
    IF bUseId THEN
697
      aScan = Scan(sTag, "$*|*$")
698
    ELSE
699
      aScan = Scan(sTag, "#*|*#")
700
    ENDIF
701
    IF aScan.Count = 2 THEN
702
      IF bUseId THEN aScan[0] = Devices.FindNameForDevice(aScan[0])
703
      SELECT CASE LCase(aScan[1])
704
        CASE "value", "value2", "value3", "value4", "1", "2", "3", "4", "lastseen"
705
          sValue = Devices.GetValueForDevice(aScan[0], aScan[1])
706
        CASE ELSE
707
          sValue = Devices.GetValueForDevice(aScan[0])
708
      END SELECT
709
    ENDIF
710
  ELSE
711
    sValue = Devices.GetValueForDevice(Mid(sTag, 2, Len(sTag) - 2))
712
  ENDIF
713
  RETURN sValue
714
715
CATCH
716
  RETURN ("ERROR: Tag '" & sTag & "' is invalid!")
717
718
END
719
720
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
721
' parse cron function
722
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
723
PUBLIC FUNCTION TimeCron(sTimeDate AS String) AS Boolean
724
725
  DIM aCron, aMore, aCron_Now, aLowHigh, aDivide AS String[]
726
  DIM sField AS String
727
  DIM iField AS Integer
728
  DIM sTag AS Boolean
729
730
  ' * * * * *
731
  ' - - - - -
732
  ' | | | | |
733
  ' | | | | +----- day of week (0 - 6) (Sunday=0)
734
  ' | | | +------- month (1 - 12)
735
  ' | | +--------- day of month (1 - 31)
736
  ' | +----------- hour (0 - 23)
737
  ' +------------- min(0 - 59)
738
739
  aCron = Split(sTimeDate, " ")
740
741
  IF aCron.Count <> 5 THEN
742
    Main.WriteDebugLog(("[Cron] Bad TimeCron() format (needs 5 fields): ") & sTimeDate)
743
    RETURN FALSE
744
  END IF
745
746
  aCron_Now = [Str(Main.GlobalVar["Minute"]), Str(Main.GlobalVar["Hour"]), Str(Main.GlobalVar["Day"]), Str(Main.GlobalVar["Month"]), Str(Main.GlobalVar["Weekday"])]
747
  FOR iField = 0 TO 4
748
    ' wildcard used
749
    sTag = FALSE
750
    IF aCron[iField] = "*" THEN
751
      IF Main.bEventsDebug THEN Main.WriteDebugLog("[Cron] '" & sTimeDate & ("' | * so Continue"))
752
      CONTINUE
753
    ' normal value
754
    ELSE IF aCron[iField] = aCron_Now[iField] THEN
755
      IF Main.bEventsDebug THEN Main.WriteDebugLog("[Cron] '" & sTimeDate & "' | aCron[" & iField & "] " & aCron[iField] & " = aCron_Now[" & iField & "] " & aCron_Now[iField] & " so Continue")
756
      CONTINUE
757
    ' more values given
758
    ELSE IF InStr(aCron[iField], ",") THEN
759
      aMore = Split(aCron[iField], ",")
760
      FOR EACH sField IN aMore
761
          IF sField = aCron_Now[iField] THEN
762
            IF Main.bEventsDebug THEN Main.WriteDebugLog("[Cron] '" & sTimeDate & "' | sField " & sField & " = aCron_Now[" & iField & "] " & aCron_Now[iField] & (" so tag=True"))
763
            sTag = TRUE
764
          ELSE
765
            IF Main.bEventsDebug THEN Main.WriteDebugLog("[Cron] '" & sTimeDate & "' | sField " & sField & " <> aCron_Now[" & iField & "] " & aCron_Now[iField])
766
          END IF
767
      NEXT
768
      IF sTag THEN
769
        CONTINUE
770
      ELSE
771
        IF Main.bEventsDebug THEN Main.WriteDebugLog("[Cron] '" & sTimeDate & ("' | none of , sFields match so return False"))
772
        RETURN FALSE
773
      END IF
774
    ' / value given
775
    ELSE IF InStr(aCron[iField], "/") THEN
776
      aDivide = Scan(aCron[iField], "*/*")
777
      IF aDivide.Count = 2 THEN
778
        ' 5-30/5
779
        IF InStr(aDivide[0], "-") THEN
780
          aLowHigh = Scan(aDivide[0], "*-*")
781
          IF Val(aCron_Now[iField]) >= Val(aLowHigh[0]) AND IF Val(aCron_Now[iField]) <= Val(aLowHigh[1]) THEN
782
            IF Main.bEventsDebug THEN Main.WriteDebugLog("[Cron] '" & sTimeDate & "' | *-*/* within range so Continue")
783
          ELSE
784
            IF Main.bEventsDebug THEN Main.WriteDebugLog("[Cron] '" & sTimeDate & "' | *-*/* not within range so return False")
785
            RETURN FALSE
786
          END IF
787
        END IF
788
        ' */10
789
        IF Val(aCron_Now[iField]) MOD Val(aDivide[1]) = 0 THEN
790
          IF Main.bEventsDebug THEN Main.WriteDebugLog("[Cron] '" & sTimeDate & "' | /* has no remainder so Continue")
791
          CONTINUE
792
        ELSE
793
          IF Main.bEventsDebug THEN Main.WriteDebugLog("[Cron] '" & sTimeDate & "' | /* has remainder so return False")
794
          RETURN FALSE
795
        ENDIF
796
      ELSE
797
        IF Main.bEventsDebug THEN Main.WriteDebugLog("[Cron] '" & sTimeDate & "' | invalid / entry so return False")
798
        RETURN FALSE
799
      ENDIF
800
    ' range given
801
    ELSE IF InStr(aCron[iField], "-") THEN
802
      aLowHigh = Scan(aCron[iField], "*-*")
803
      IF Val(aCron_Now[iField]) >= Val(aLowHigh[0]) AND IF Val(aCron_Now[iField]) <= Val(aLowHigh[1]) THEN
804
        IF Main.bEventsDebug THEN Main.WriteDebugLog("[Cron] '" & sTimeDate & "' | within range so Continue")
805
        CONTINUE
806
      ELSE
807
        IF Main.bEventsDebug THEN Main.WriteDebugLog("[Cron] '" & sTimeDate & "' | not within range so return False")
808
        RETURN FALSE
809
      END IF
810
    END IF
811
    IF Main.bEventsDebug THEN Main.WriteDebugLog("[Cron] '" & sTimeDate & "' | aCron[" & iField & "] " & aCron[iField] & " <> aCron_Now[" & iField & "] " & aCron_Now[iField] & (" so return False"))
812
    RETURN FALSE ' not all of the entries qualified
813
    sTag = FALSE
814
  NEXT
815
  IF Main.bEventsDebug THEN Main.WriteDebugLog("[Cron] '" & sTimeDate & "' | return True")
816
  RETURN TRUE
817
818
END
819
820
' start delay timer for iEventId and iOrder with param1-3
821
PRIVATE SUB StartDelayTimer(iEventId AS Integer, iOrder AS Integer, sParam1 AS String, sParam2 AS String, sParam3 AS String)
822
823
  DIM iDelay AS Integer
824
825
  ' sParam3 contains timer mode
826
  ' "fixed" sParam1 is timer delay in seconds
827
  ' "random" sParam1 is min value, sParam2 is max value for rnd
828
  IF LCase(sParam3) = "random" THEN
829
    iDelay = Rnd(Val(sParam1), Val(sParam2))
830
  ELSE
831
    iDelay = Val(sParam1)
832
  ENDIF
833
834
  IF FindTimer(iEventId, iOrder, iDelay) = FALSE THEN
835
    tDelayTimer = NEW CTimerDelay AS "tDelayTimer"
836
    oDelayTimers.Add(tDelayTimer)
837
    tDelayTimer.Delay = iDelay * 1000
838
    tDelayTimer.Dur = iDelay
839
    tDelayTimer.EventId = iEventId
840
    tDelayTimer.Order = iOrder
841
    tDelayTimer.Started = Now()
842
    tDelayTimer.Start
843
    IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 3c. Started delay timer for event id ") & iEventId & (", delay for ") & iDelay & (" second(s)"))
844
  ENDIF
845
846
END
847
848
' looks if there is a timer running for iEventId and iOrder
849
PRIVATE SUB FindTimer(iEventId AS Integer, iOrder AS Integer, iDelay AS Integer) AS Boolean
850
851
  DIM oObject AS Object
852
853
  FOR EACH oObject IN oDelayTimers
854
    IF oObject.EventId = iEventId AND IF oObject.Order = iOrder AND IF oObject.Dur = iDelay THEN
855
      IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 3c. Delay timer for event id ") & iEventId & (" and order# ") & iOrder & (" with delay of ") & iDelay & (" second(s) already runs, restarted it."))
856
      oObject.Delay = iDelay * 1000
857
      RETURN TRUE
858
    ENDIF
859
  NEXT
860
  RETURN FALSE
861
862
END
863
864
' timer is finished, run remaining action(s)
865
PUBLIC SUB tDelayTimer_Timer()
866
867
  DIM iEventId, iOrder AS Integer
868
869
  iEventId = LAST.EventId
870
  iOrder = LAST.Order
871
  LAST.Stop
872
  oDelayTimers.Remove(oDelayTimers.Find(LAST))
873
  IF Main.bEventsDebug THEN Main.WriteDebugLog(("[Events] 3d. Delay timer finished for event id ") & tDelayTimer.EventId & (", continue running it's actions from order# ") & tDelayTimer.Order)
874
  RunActions(iEventId, iOrder)
875
876
END
877
878
' disable an event
879
PUBLIC SUB ChangeEventState(iId AS Integer, bStatus AS Boolean) AS Boolean
880
881
  DIM rResult AS Result
882
883
  rResult = Main.hDB.Exec("SELECT * FROM events where id = &1", iId)
884
  IF rResult.Available THEN
885
    IF rResult.Count = 1 THEN
886
      IF bStatus = TRUE THEN
887
        rResult = Main.hDB.Exec("UPDATE events SET enabled = &1 WHERE id = &2", -1, iId)
888
      ELSE
889
        rResult = Main.hDB.Exec("UPDATE events SET enabled = &1 WHERE id = &2", 0, iId)
890
      ENDIF
891
    ENDIF
892
  ENDIF
893
  RETURN TRUE
894
895
END