-
Notifications
You must be signed in to change notification settings - Fork 1
/
modInsteon.vb
2172 lines (2069 loc) · 106 KB
/
modInsteon.vb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
Module modInsteon
' IMMENSE amount of credit goes to Jonathan Dale at http://www.madreporite.com for the Insteon code
' When asked about licensing, he stated the code was free for anyone to use
Structure InsteonDevice
Dim Address As String
Dim Name As String
Dim Device_On As Boolean
Dim Level As Short ' Note: I store this as 0-100, not 0-255 as it is truly represented
Dim Checking As Boolean ' Issued a Status Request to the device, waiting for response
Dim LastCommand As Byte ' Command1 most recently received (0 if none)
Dim LastFlags As Byte ' Just the top three bits (ACK/NAK, group/direct/cleanup/broadcast)
Dim LastTime As Date ' Time last command was received
Dim LastGroup As Byte ' Group for last group command
End Structure
Public Insteon(200) As InsteonDevice
Public NumInsteon As Short ' Number of assigned Insteon devices
Public SerialPLM As System.IO.Ports.SerialPort
Public AlarmMuted As Boolean = False
Public PLM_Address As String
Public PLM_LastX10Device As Byte
Public PLM_X10_House() As Byte = New Byte(16) {0, 96, 224, 32, 160, 16, 144, 80, 208, 112, 240, 48, 176, 0, 128, 64, 192} 'House Codes A-P
Public PLM_X10_Device() As Byte = New Byte(16) {0, 6, 14, 2, 10, 1, 9, 5, 13, 7, 15, 3, 11, 0, 8, 4, 12} 'Device Codes 1-16
Public x(1030) As Byte ' Serial data as it gets brought in
Public x_LastWrite As Short ' Index of last byte in array updated with new data
Public x_Start As Short ' Index of next byte of data to process in array
Private serialLock As New Object
Dim tmrIThermCheckTimer As System.Timers.Timer
Function AddInsteonDeviceDb(ByVal strAddress As String, ByVal DevCat As Short, ByVal SubCat As Short, ByVal Firmware As Short) As String
If CheckDbForInsteon(strAddress) = 0 Then
Dim strModel As String = InsteonDeviceLookup(DevCat, SubCat)
modDatabase.Execute("INSERT INTO INSTEON_DEVICES (Address, DevCat, SubCat, Firmware) VALUES('" & strAddress & "', '" & CStr(DevCat) & "', '" & CStr(SubCat) & "', '" & CStr(Firmware) & "')")
modDatabase.Execute("INSERT INTO DEVICES (Name, Type, Model, Address) VALUES('Insteon " & strAddress & "', 'Insteon', '" & strModel & "', '" & strAddress & "')")
Return "Device added"
Else
Return "Device already exists"
End If
End Function
Function AddX10DeviceDb(ByVal strAddress As String) As String
If CheckDbForX10(strAddress) = 0 Then
modDatabase.Execute("INSERT INTO DEVICES (Name, Type, Address) VALUES('X10 Device', 'X10', '" & strAddress & "')")
Return "Device added"
Else
Return "Device already exists"
End If
End Function
Private Sub CheckInsteonThermostat()
My.Application.Log.WriteEntry("Executing scheduled thermostat check")
Dim response As String = ""
InsteonThermostatControl(My.Settings.Insteon_ThermostatAddr, response, "read")
End Sub
Sub CreateInsteonDb()
' Mirroring the InsteonDevice structure for now
modDatabase.Execute("CREATE TABLE IF NOT EXISTS INSTEON_DEVICES(Id INTEGER PRIMARY KEY, Address TEXT UNIQUE, DeviceOn INTEGER, Level INTEGER, Checking INTEGER, LastCommand INTEGER, LastFlags INTEGER, LastTime STRING, LastGroup INTEGER, DevCat INTEGER, SubCat INTEGER, Firmware INTEGER)")
End Sub
Function Disable() As String
Unload()
My.Settings.Insteon_Enable = False
My.Application.Log.WriteEntry("Insteon module is disabled")
Return "Insteon module disabled"
End Function
Function Enable() As String
My.Settings.Insteon_Enable = True
My.Application.Log.WriteEntry("Insteon module is enabled")
Load()
Return "Insteon module enabled"
End Function
Sub InsteonConnect(ByVal PortName As String, ByRef ResponseMsg As String)
If My.Settings.Insteon_Enable = True Then
If SerialPLM.IsOpen = True Then
My.Application.Log.WriteEntry("Closing serial connection")
SerialPLM.Close()
End If
SerialPLM.PortName = PortName
SerialPLM.BaudRate = 19200
SerialPLM.DataBits = 8
SerialPLM.Handshake = IO.Ports.Handshake.None
SerialPLM.Parity = IO.Ports.Parity.None
SerialPLM.StopBits = 1
Try
My.Application.Log.WriteEntry("Trying to connect on port " + PortName)
SerialPLM.Open()
Catch IOExcep As System.IO.IOException
My.Application.Log.WriteException(IOExcep)
ResponseMsg = "ERROR: " + IOExcep.Message
If My.Settings.Global_SmartCOM = True And My.Settings.Insteon_COMPortDeviceName <> "" Then
PortName = modComputer.GetCOMPortFromFriendlyName(My.Settings.Insteon_COMPortDeviceName)
If PortName <> "" Then
If My.Settings.Global_CarMode = True Then
modSpeech.Say("Smart " & PortName, False)
End If
InsteonConnect(PortName, ResponseMsg)
Exit Sub
End If
End If
Catch UnauthExcep As System.UnauthorizedAccessException
My.Application.Log.WriteException(UnauthExcep)
ResponseMsg = "ERROR: " + UnauthExcep.Message
End Try
If SerialPLM.IsOpen = True Then
My.Application.Log.WriteEntry("Serial connection opened on port " + PortName)
My.Settings.Insteon_LastGoodCOMPort = PortName
My.Settings.Insteon_COMPortDeviceName = modComputer.GetCOMPortFriendlyName(PortName)
ResponseMsg = "Connected"
End If
If My.Settings.Insteon_ThermostatAddr <> "" Then
My.Application.Log.WriteEntry("Scheduling automatic thermostat temperature checks")
tmrIThermCheckTimer = New System.Timers.Timer
AddHandler tmrIThermCheckTimer.Elapsed, AddressOf CheckInsteonThermostat
tmrIThermCheckTimer.Interval = 1200000 ' 20min
tmrIThermCheckTimer.Enabled = True
End If
End If
End Sub
Sub InsteonAlarmControl(ByVal strAddress As String, ByRef ResponseMsg As String, ByVal Command1 As String, Optional ByVal intSeconds As Integer = 0)
Dim comm1 As Short
Dim comm2 As Short
Select Case Command1
Case "Off", "off"
comm1 = 19
comm2 = 0
Case "On", "on"
If AlarmMuted = False Then
comm1 = 17
comm2 = 255
Else
My.Application.Log.WriteEntry("Alarm is muted, will not sound", TraceEventType.Warning)
Exit Sub 'TODO: Less janky logic here
End If
Case Else
My.Application.Log.WriteEntry("InsteonAlarmControl received invalid request", TraceEventType.Warning)
Exit Sub
End Select
InsteonSendStdCommand(strAddress, comm1, comm2)
' Shut it back off
If (Command1 = "On" OrElse Command1 = "on") AndAlso intSeconds > 0 Then
My.Application.Log.WriteEntry("Scheduling automatic shut off of " & strAddress & " in " & intSeconds.ToString & " seconds")
Threading.Thread.Sleep(intSeconds * 1000)
Dim response As String = ""
InsteonAlarmControl(strAddress, response, "Off", 0)
End If
End Sub
Sub InsteonGetEngineVersion(ByVal strAddress As String, ByRef ResponseMsg As String)
InsteonSendStdCommand(strAddress, 13, 0)
End Sub
Sub InsteonLightControl(ByVal strAddress As String, ByRef ResponseMsg As String, ByVal Command1 As String, Optional ByVal intBrightness As Integer = 255)
Dim comm1 As Short
Dim comm2 As Short
Dim needExtended As Boolean = False
Select Case Command1
Case "Beep", "beep"
comm1 = 48
comm2 = 0
Case "Off", "off"
comm1 = 19
comm2 = 0
Case "On", "on"
comm1 = 17
comm2 = intBrightness
Case "Soft", "soft"
comm1 = 17
comm2 = 190
Case "Dim", "dim"
comm1 = 17
comm2 = 136
Case "Nite", "nite"
comm1 = 17
comm2 = 68
'Below sets operating flags, which behave differently on different devices
'Currently intended only for bulbs, which tells them what state to enter when power resumes
'On a 2477S switch, these "resume dim" on (4) and off (5) respectively.
Case "powerstatelast"
comm1 = 32
comm2 = 4
needExtended = True
Case "powerstateon"
comm1 = 32
comm2 = 5
needExtended = True
Case Else
My.Application.Log.WriteEntry("InsteonLightControl received invalid request", TraceEventType.Warning)
Exit Sub
End Select
If needExtended = True Then
InsteonSendExtCommand(strAddress, comm1, comm2)
Else
InsteonSendStdCommand(strAddress, comm1, comm2)
End If
End Sub
Sub InsteonLinkI2CSDevice(ByVal strAddress As String, ByRef ResponseMsg As String, ByVal intLinkType As Integer)
SyncLock serialLock
Dim data(4) As Byte
data(0) = 2
data(1) = 100 '0x64
data(2) = intLinkType
data(3) = 1
Try
SerialPLM.Write(data, 0, 4)
Catch Excep As System.InvalidOperationException
My.Application.Log.WriteException(Excep)
End Try
End SyncLock
Threading.Thread.Sleep(500)
InsteonSendExtCommand(strAddress, 9, 1)
End Sub
Sub InsteonProductDataRequest(ByVal strAddress As String, ByRef ResponseMsg As String, ByVal EngineVersion As Short)
Select Case EngineVersion
Case 0, 1
InsteonSendStdCommand(strAddress, 3, 0)
Case 2
InsteonSendStdCommand(strAddress, 16, 0)
End Select
End Sub
Sub InsteonSendExtCommand(ByVal strAddress As String, ByVal comm1 As Short, ByVal comm2 As Short)
SyncLock serialLock
If My.Settings.Insteon_Enable = True Then
If SerialPLM.IsOpen = True Then
Dim data(21) As Byte
strAddress = Replace(strAddress, ":", ".") 'Handle 00:00:00 format
Dim arrAddress() As String = strAddress.Split(".")
data(0) = 2 'all commands start with 2
data(1) = 98 '0x62 = the PLM command to send an Insteon standard or extended message
data(2) = Convert.ToInt32(arrAddress(0), 16) 'three byte address of device
data(3) = Convert.ToInt32(arrAddress(1), 16)
data(4) = Convert.ToInt32(arrAddress(2), 16)
data(5) = 31 'flags
data(6) = comm1
data(7) = comm2
data(21) = CByte(((Not (CInt(data(6)) + CInt(data(7)))) + 1) And 255)
Try
SerialPLM.Write(data, 0, 22)
Catch Excep As System.InvalidOperationException
My.Application.Log.WriteException(Excep)
End Try
Threading.Thread.Sleep(100)
Else
My.Application.Log.WriteEntry("Command not sent, PLM is not connected", TraceEventType.Warning)
End If
Else
My.Application.Log.WriteEntry("Insteon module is disabled, command not sent", TraceEventType.Warning)
End If
End SyncLock
End Sub
Sub InsteonSendStdCommand(ByVal strAddress As String, ByVal comm1 As Short, ByVal comm2 As Short)
SyncLock serialLock
If My.Settings.Insteon_Enable = True Then
If SerialPLM.IsOpen = True Then
Dim data(7) As Byte
strAddress = Replace(strAddress, ":", ".") 'Handle 00:00:00 format
Dim arrAddress() As String = strAddress.Split(".")
data(0) = 2 'all commands start with 2
data(1) = 98 '0x62 = the PLM command to send an Insteon standard or extended message
data(2) = Convert.ToInt32(arrAddress(0), 16) 'three byte address of device
data(3) = Convert.ToInt32(arrAddress(1), 16)
data(4) = Convert.ToInt32(arrAddress(2), 16)
data(5) = 15 'flags
data(6) = comm1
data(7) = comm2
Try
SerialPLM.Write(data, 0, 8)
Catch Excep As System.InvalidOperationException
My.Application.Log.WriteException(Excep)
End Try
Threading.Thread.Sleep(100)
Else
My.Application.Log.WriteEntry("Command not sent, PLM is not connected", TraceEventType.Warning)
End If
Else
My.Application.Log.WriteEntry("Insteon module is disabled, command not sent", TraceEventType.Warning)
End If
End SyncLock
End Sub
Sub InsteonThermostatControl(ByVal strAddress As String, ByRef ResponseMsg As String, ByVal Command1 As String, Optional ByVal intTemperature As Integer = 72)
Dim comm1 As Short
Dim comm2 As Short
If strAddress = "" And My.Settings.Insteon_ThermostatAddr = "" Then
My.Application.Log.WriteEntry("No thermostat set, asking for it")
My.Settings.Insteon_ThermostatAddr = InputBox("Enter Thermostat Address", "Thermostat")
End If
If strAddress = "" And My.Settings.Insteon_ThermostatAddr <> "" Then
strAddress = My.Settings.Insteon_ThermostatAddr
End If
Select Case Command1
Case "Auto", "auto"
comm1 = 107
comm2 = 6
Case "Cool", "cool"
comm1 = 107
comm2 = 5
Case "CoolSet", "coolset"
comm1 = 108
comm2 = intTemperature * 2
Case "Down", "down"
comm1 = 105
comm2 = 2
Case "FanOff", "fanoff"
comm1 = 107
comm2 = 8
Case "FanOn", "fanon"
comm1 = 107
comm2 = 7
Case "Heat", "heat"
comm1 = 107
comm2 = 4
Case "HeatSet", "heatset"
comm1 = 109
comm2 = intTemperature * 2
Case "Off", "off"
comm1 = 107
comm2 = 9
Case "Read", "read"
comm1 = 106
comm2 = 0
Case "Up", "up"
comm1 = 104
comm2 = 2
Case Else
My.Application.Log.WriteEntry("InsteonThermostatControl received invalid request", TraceEventType.Warning)
Exit Sub
End Select
InsteonSendExtCommand(strAddress, comm1, comm2)
End Sub
Function Load() As String
If My.Settings.Insteon_Enable = True Then
My.Application.Log.WriteEntry("Loading Insteon module")
SerialPLM = New System.IO.Ports.SerialPort
AddHandler SerialPLM.DataReceived, AddressOf SerialPLM_DataReceived
CreateInsteonDb()
Return "Insteon module loaded"
Else
My.Application.Log.WriteEntry("Insteon module is disabled, module not loaded")
Return "Insteon module is disabled, module not loaded"
End If
End Function
Function NicknameInsteonDeviceDb(ByVal strAddress As String, ByVal strNickname As String) As String
If CheckDbForInsteon(strAddress) = 0 Then
My.Application.Log.WriteEntry("Cannot set nickname, Insteon address is unknown")
Return "Cannot set nickname, Insteon address is unknown"
Else
modDatabase.Execute("UPDATE DEVICES SET Name = """ & strNickname & """ WHERE Type = ""Insteon"" AND Address = """ & strAddress.ToUpper & """")
Return "I have saved this information"
End If
End Function
Function NicknameX10DeviceDb(ByVal strAddress As String, ByVal strNickname As String) As String
If CheckDbForX10(strAddress) = 0 Then
My.Application.Log.WriteEntry("Cannot set nickname, X10 address is unknown")
Return "Cannot set nickname, X10 address is unknown"
Else
modDatabase.Execute("UPDATE DEVICES SET Name = """ & strNickname & """ WHERE Type = ""X10"" AND Address = """ & strAddress.ToUpper & """")
Return "I have saved this information"
End If
End Function
Private Sub SerialPLM_DataReceived(sender As Object, e As IO.Ports.SerialDataReceivedEventArgs)
' this is the serial port data received event on a secondary thread
Dim PLMThread As New Threading.Thread(AddressOf PLM)
Do Until SerialPLM.BytesToRead = 0
x(x_LastWrite + 1) = SerialPLM.ReadByte
x(x_LastWrite + 10) = 0
If x_LastWrite < 30 Then x(x_LastWrite + 1001) = x(x_LastWrite + 1)
' the ends overlap so no message breaks over the limit of the array
x_LastWrite = x_LastWrite + 1
' increment only after the data is written, in case PLM() is running at same time
If x_LastWrite > 1000 Then x_LastWrite = 1
Loop
PLMThread.Start()
End Sub
Public Sub PLM()
' This routine handles the serial data on the primary thread
Dim i As Short
Dim X10House As Byte
Dim X10Code As Byte
Dim X10Address As String
Dim FromAddress As String
Dim ToAddress As String
Dim IAddress As Short ' Insteon index number
Dim Flags As Byte
Dim Command1 As Byte
Dim Command2 As Byte
Dim ms As Short ' Position of start of message ( = x_Start + 1)
Dim MessageEnd As Short ' Position of expected end of message (start + length, - 1000 if it's looping)
Dim DataAvailable As Short ' how many bytes of data available between x_Start and X_LastWrite?
Dim data(2) As Byte
Dim Group As Byte
Dim FromName As String
Dim DataString As String
Dim strTemp As String
Dim response As String = ""
If x_Start = x_LastWrite Then Exit Sub ' reached end of data, get out of sub
' x_Start = the last byte that was read and processed here
' Each time this sub is executed, one command will be processed (if enough data has arrived)
' This sub may be called while it is still running, under some circumstances (e.g. if it calls a slow macro)
' Find the beginning of the next command (always starts with 0x02)
Do Until (x(x_Start + 1) = 2) Or (x_Start = x_LastWrite) Or (x_Start + 1 = x_LastWrite)
x_Start = x_Start + 1
If x_Start > 1000 Then x_Start = 1 ' Loop according to the same looping rule as x_LastWrite
Loop
ms = x_Start + 1 ' ms = the actual 1st byte of data (which must = 0x02), whereas x_Start is the byte before it
If x(ms) <> 2 Then Exit Sub ' reached the end of usable data, reset starting position, get out of sub
' How many bytes of data are available to process? (not counting the leading 0x02 of each message)
If x_Start <= x_LastWrite Then
DataAvailable = x_LastWrite - ms
Else
DataAvailable = 1000 + x_LastWrite - ms
End If
If DataAvailable < 1 Then Exit Sub ' not enough for a full message of any type
' Interpret the message and handle it
Select Case x(ms + 1)
Case 96 ' 0x060 response to Get IM Info
MessageEnd = ms + 8
If MessageEnd > 1000 Then MessageEnd = MessageEnd - 1000
If DataAvailable >= 8 Then
x_Start = MessageEnd
' Display message
PLM_Address = Hex(x(ms + 2)) & "." & Hex(x(ms + 3)) & "." & Hex(x(ms + 4))
My.Application.Log.WriteEntry("PLM response to Get IM Info: PLM ID: " & PLM_Address & ", Device Category: " & Hex(x(ms + 5)) & ", Subcategory: " & Hex(x(ms + 6)) & ", Firmware: " & Hex(x(ms + 7)) & ", ACK/NAK: " & Hex(x(ms + 8)), TraceEventType.Verbose)
' Set the PLM as the controller
' --> I use this to verify the PLM is connected, disable some menu options, enable others, etc
End If
Case 80 ' 0x050 Insteon Standard message received
' next three bytes = address of device sending message
' next three bytes = address of PLM
' next byte = flags
' next byte = command1 if status was requested, this = link delta (increments each time link database changes)
' if device level was changed, this = command that was sent
' next byte = command2 if status was requested, this = on level (00-FF)
' if device level was changed, this also = on level (00-FF)
MessageEnd = ms + 10
If MessageEnd > 1000 Then MessageEnd = MessageEnd - 1000
If DataAvailable >= 10 Then
x_Start = MessageEnd
FromAddress = Hex(x(ms + 2)) & "." & Hex(x(ms + 3)) & "." & Hex(x(ms + 4))
ToAddress = Hex(x(ms + 5)) & "." & Hex(x(ms + 6)) & "." & Hex(x(ms + 7))
Flags = x(ms + 8)
Command1 = x(ms + 9)
Command2 = x(ms + 10)
' Check if FromAddress is in device database, if not request info (ToAddress will generally = PLM)
'If CheckDbForInsteon(FromAddress) = 0 And Command1 <> 3 And Command1 <> 13 And Command1 <> 16 Then
' Threading.Thread.Sleep(1000)
' InsteonGetEngineVersion(FromAddress, response)
'End If
'Always print the whole command
strTemp = "PLM Raw In: "
For i = 0 To DataAvailable
strTemp = strTemp & Hex(x(ms + i)) & " "
Next
My.Application.Log.WriteEntry(strTemp, TraceEventType.Verbose)
strTemp = "PLM: Insteon Received: From: " & FromAddress & " To: " & ToAddress
If ToAddress = PLM_Address Then
strTemp = strTemp & " (PLM)"
Else
strTemp = strTemp & " (" & ToAddress & ")"
' TODO: Fix this redundancy later
End If
strTemp = strTemp & " Flags: " & Hex(Flags)
Select Case Flags And 224
Case 0 ' 000 Direct message
strTemp = strTemp & " (direct) "
Case 32 ' 001 ACK direct message
strTemp = strTemp & " (ACK direct) "
Case 64 ' 010 Group cleanup direct message
strTemp = strTemp & " (Group cleanup direct) "
Case 96 ' 011 ACK group cleanup direct message
strTemp = strTemp & " (ACK Group cleanup direct) "
Case 128 ' 100 Broadcast message
strTemp = strTemp & " (Broadcast) "
Case 160 ' 101 NAK direct message
strTemp = strTemp & " (NAK direct) "
Case 192 ' 110 Group broadcast message
strTemp = strTemp & " (Group broadcast) "
Case 224 ' 111 NAK group cleanup direct message
strTemp = strTemp & " (NAK Group cleanup direct) "
End Select
If (FromAddress = My.Settings.Insteon_ThermostatAddr OrElse FromAddress = My.Settings.Insteon_ThermostatSlaveAddr) AndAlso Command1 > 109 Then ' TODO: Detect this by device model
strTemp = strTemp & InsteonThermostatResponse(Command1, Command2, FromAddress)
ElseIf (FromAddress = My.Settings.Insteon_ThermostatAddr OrElse FromAddress = My.Settings.Insteon_ThermostatSlaveAddr) AndAlso Command1 = 106 Then ' TODO: Detect this by device model
strTemp = strTemp & InsteonThermostatResponse(Command1, Command2, FromAddress)
ElseIf (FromAddress = My.Settings.Insteon_DoorSensorAddr OrElse FromAddress = My.Settings.Insteon_BackDoorSensorAddr) AndAlso ToAddress = "0.0.1" Then ' TODO: Detect this by device model
strTemp = strTemp & InsteonDoorSensorResponse(Command1, Command2)
ElseIf FromAddress = My.Settings.Insteon_SmokeBridgeAddr AndAlso Flags = 203 AndAlso x(ms + 5) = 0 AndAlso x(ms + 6) = 0 Then ' TODO: Detect this by device model
strTemp = strTemp & InsteonSmokeBridgeResponse(x(ms + 7))
ElseIf FromAddress = My.Settings.Insteon_SumpAlarmAddr AndAlso Flags = 203 AndAlso x(ms + 5) = 0 AndAlso x(ms + 6) = 0 Then
strTemp = strTemp & InsteonSumpAlarmResponse(Command1)
Else
strTemp = strTemp & " Command1: " & Hex(Command1) & " (" & modInsteon.InsteonCommandLookup(Command1) & ")" & " Command2: " & Hex(Command2)
End If
My.Application.Log.WriteEntry(strTemp, TraceEventType.Verbose)
If Flags = 139 AndAlso (Command1 = 1 OrElse Command1 = 2) Then
' This is a 0x01 or 0x02 Product Request Data response. The 'To' field is actually the DevCat, SubCat, and Firmware Rev.
My.Application.Log.WriteEntry("ADD CASE 4", TraceEventType.Verbose)
AddInsteonDeviceDb(FromAddress, x(ms + 5), x(ms + 6), x(ms + 7))
End If
' Update the status of the sending device
IAddress = InsteonNum(FromAddress) ' already checked to make sure it was in list
FromName = Insteon(IAddress).Name
If FromName = "" Then FromName = Insteon(IAddress).Address
If (Flags And 160) <> 160 Then
' Not a NAK response, could be an ACK or a new message coming in
' Either way, update the sending device
Select Case Command1
Case 13
My.Application.Log.WriteEntry("Device " + FromAddress + " has engine version of " + Hex(Command2))
Threading.Thread.Sleep(1000)
InsteonProductDataRequest(FromAddress, response, Command2)
Case 17, 18 ' On, Fast On
Insteon(IAddress).Device_On = True
If (Flags And 64) = 64 Then
' Group message (broadcast or cleanup)
Insteon(IAddress).Level = 100 ' the real level is the preset for the link, but...
Else
' Direct message
Insteon(IAddress).Level = Command2 / 2.55 ' change to scale of 0-100
End If
Case 19, 20 ' Off, Fast Off
Insteon(IAddress).Device_On = False
Insteon(IAddress).Level = 0
Case 21 ' Bright
Insteon(IAddress).Device_On = True
If Insteon(IAddress).Level > 100 Then Insteon(IAddress).Level = 100
Insteon(IAddress).Level = Insteon(IAddress).Level + 3
Case 22 ' Dim
Insteon(IAddress).Level = Insteon(IAddress).Level - 3
If Insteon(IAddress).Level < 0 Then Insteon(IAddress).Level = 0
If Insteon(IAddress).Level = 0 Then Insteon(IAddress).Device_On = False
End Select
' Check whether this was a response to a Status Request
If (Flags And 224) = 32 Then
' ACK Direct message
If Insteon(IAddress).Checking = True Then
Insteon(IAddress).Level = Command2 / 2.55
If Insteon(IAddress).Level > 0 Then
Insteon(IAddress).Device_On = True
Else
Insteon(IAddress).Device_On = False
End If
End If
End If
' --> At this point I update a grid display, but you can do whatever you want...
End If
' Now actually respond to events...
If Insteon(IAddress).Checking = True Then
' It was a Status Request response, don't treat it as an event
Insteon(IAddress).Checking = False
Insteon(IAddress).LastCommand = 0
Insteon(IAddress).LastFlags = Flags And 224
Insteon(IAddress).LastTime = Now
Insteon(IAddress).LastGroup = 0
Else
' It wasn't a Status Request, process it
Select Case Flags And 224
Case 128 ' 100 Broadcast message
' Button-press linking, etc. Just display a message.
' Message format: FromAddress, DevCat, SubCat, Firmware, Flags, Cmd1, Cmd2 (=Device Attributes)
strTemp = Format(Now) & " "
If Command1 = 1 Then
strTemp = strTemp & FromName & " broadcast 'Set Button Pressed'"
Else
strTemp = strTemp & FromName & " broadcast command " & Hex(Command1)
End If
My.Application.Log.WriteEntry(strTemp, TraceEventType.Verbose)
Insteon(IAddress).LastCommand = Command1
Insteon(IAddress).LastFlags = Flags And 224
Insteon(IAddress).LastTime = Now
Insteon(IAddress).LastGroup = 0
Case 0, 64, 192 ' 000 Direct, 010 Group cleanup direct, 110 Group broadcast message
' Message sent to PLM by another device - trigger sounds, macro, etc
If Flags And 64 Then
' Group message
If Flags And 128 Then
' Group broadcast - group number is third byte of ToAddress
Group = x(ms + 7)
If Command1 = Insteon(IAddress).LastCommand And (Group = Insteon(IAddress).LastGroup) Then
' This is the same command we last received from this device
' Is this a repeat? (Some devices, like the RemoteLinc, seem to double their group broadcasts)
If (Flags And 224) = (Insteon(IAddress).LastFlags) Then
If DateDiff(DateInterval.Second, Insteon(IAddress).LastTime, Now) < 1 Then
' Same command, same flags, within the last second....
Exit Select ' So skip out of here without doing anything else
End If
End If
End If
Else
' Group cleanup direct - group number is Command2
Group = Command2
If Command1 = Insteon(IAddress).LastCommand And (Group = Insteon(IAddress).LastGroup) Then
' This is the same command we last received from this device
' Is this a Group Cleanup Direct message we already got a Group Broadcast for?
If (Insteon(IAddress).LastFlags And 224) = 192 Then
' Last message was, in fact, a Group Broadcast
If DateDiff(DateInterval.Second, Insteon(IAddress).LastTime, Now) < 3 Then
' Within the last 3 seconds....
Exit Select ' So skip out of here without doing anything else
End If
End If
End If
End If
Else
' Direct message
Group = 0
End If
Insteon(IAddress).LastCommand = Command1
Insteon(IAddress).LastFlags = Flags And 224
Insteon(IAddress).LastTime = Now
Insteon(IAddress).LastGroup = Group
strTemp = Format(Now) & " "
' Write command to event log
If Group > 0 Then
strTemp = strTemp & FromName & " " & modInsteon.InsteonCommandLookup(Command1) & " (Group " & Format(Group) & ")"
Else
strTemp = strTemp & FromName & " " & modInsteon.InsteonCommandLookup(Command1)
End If
If FromAddress = My.Settings.Insteon_SmokeBridgeAddr Then ' TODO: Detect this by device model
strTemp = strTemp & " Smoke Bridge: " & InsteonSmokeBridgeResponse(Group)
End If
My.Application.Log.WriteEntry(strTemp, TraceEventType.Verbose)
' Handle incoming event and play sounds
' --> at this point I play a WAV file and run any macro associated with the device
Case 32, 96 ' 001 ACK direct message, 011 ACK group cleanup direct message
' Command received and acknowledged by another device - update device status is already done (above).
' Nothing left to do here.
Insteon(IAddress).LastCommand = Command1
Insteon(IAddress).LastFlags = Flags And 224
Insteon(IAddress).LastTime = Now
Insteon(IAddress).LastGroup = 0
Case 160, 224 ' 101 NAK direct message, 111 NAK group cleanup direct message
' Command received by another device but failed - display message in log
strTemp = Format(Now) & " " & FromAddress & " NAK to command " & Hex(Command1) & " (" & modInsteon.InsteonCommandLookup(Command1) & ")"
My.Application.Log.WriteEntry(strTemp, TraceEventType.Verbose)
Insteon(IAddress).LastCommand = Command1
Insteon(IAddress).LastFlags = Flags And 224
Insteon(IAddress).LastTime = Now
Insteon(IAddress).LastGroup = 0
End Select
End If
End If
Case 81 ' 0x051 Insteon Extended message received - 23 byte message
MessageEnd = ms + 24
If MessageEnd > 1000 Then MessageEnd = MessageEnd - 1000
If DataAvailable >= 24 Then
x_Start = MessageEnd
FromAddress = Hex(x(ms + 2)) & "." & Hex(x(ms + 3)) & "." & Hex(x(ms + 4))
ToAddress = Hex(x(ms + 5)) & "." & Hex(x(ms + 6)) & "." & Hex(x(ms + 7))
Flags = x(ms + 8)
Command1 = x(ms + 9)
Command2 = x(ms + 10)
strTemp = "PLM: Insteon Extended Received: From: " & FromAddress & " To: " & ToAddress
If ToAddress = PLM_Address Then
strTemp = strTemp & " (PLM)"
Else
strTemp = strTemp & " (" & ToAddress & ")"
' TODO: Fix this redundancy later
End If
strTemp = strTemp & " Flags: " & Hex(Flags)
Select Case Flags And 224
Case 0 ' 000 Direct message
strTemp = strTemp & " (direct) "
Case 32 ' 001 ACK direct message
strTemp = strTemp & " (ACK direct) "
Case 64 ' 010 Group cleanup direct message
strTemp = strTemp & " (Group cleanup direct) "
Case 96 ' 011 ACK group cleanup direct message
strTemp = strTemp & " (ACK Group cleanup direct) "
Case 128 ' 100 Broadcast message
strTemp = strTemp & " (Broadcast) "
Case 160 ' 101 NAK direct message
strTemp = strTemp & " (NAK direct) "
Case 192 ' 110 Group broadcast message
strTemp = strTemp & " (Group broadcast) "
Case 224 ' 111 NAK group cleanup direct message
strTemp = strTemp & " (NAK Group cleanup direct) "
End Select
strTemp = strTemp & " Command1: " & Hex(Command1) & " (" & Command1 & ")" & " Command2: " & Hex(Command2)
If Command1 = 3 Then
' Product Data Response
Select Case Command2
Case 0 ' Product Data Response
strTemp = strTemp & " Product Data Response:" & " Data: "
For i = 11 To 24
strTemp = strTemp & Hex(x(ms + i)) & " "
Next
strTemp = strTemp & "--> Product Key " & Hex(x(ms + 12)) & Hex(x(ms + 13)) & Hex(x(ms + 14)) & " DevCat: " & Hex(x(ms + 15)) & " SubCat: " & Hex(x(ms + 16)) & " Firmware: " & Hex(x(ms + 17))
My.Application.Log.WriteEntry("ADD CASE 1", TraceEventType.Verbose)
AddInsteonDeviceDb(FromAddress, x(ms + 15), x(ms + 16), x(ms + 17))
Case 1 ' FX Username Response
strTemp = strTemp & " FX Username Response:" & " D1-D8 FX Command Username: "
For i = 11 To 18
strTemp = strTemp & Hex(x(ms + i)) & " "
Next
strTemp = strTemp & " D9-D14: "
For i = 19 To 24
strTemp = strTemp & Hex(x(ms + i)) & " "
Next
Case 2 ' Device Text String
strTemp = strTemp & " Device Text String Response:" & " D1-D8 FX Command Username: "
DataString = ""
For i = 11 To 24
strTemp = strTemp & Hex(x(ms + i)) & " "
Next
For i = 11 To 24
If x(ms + i) = 0 Then Exit For
DataString = DataString + Chr(x(ms + i))
Next
strTemp = strTemp & """" & DataString & """"
Case 3 ' Set Device Text String
strTemp = strTemp & " Set Device Text String:" & " D1-D8 FX Command Username: "
DataString = ""
For i = 11 To 24
strTemp = strTemp & Hex(x(ms + i)) & " "
Next
For i = 11 To 24
If x(ms + i) = 0 Then Exit For
DataString = DataString + Chr(x(ms + i))
Next
strTemp = strTemp & """" & DataString & """"
Case 4 ' Set ALL-Link Command Alias
strTemp = strTemp & " Set ALL-Link Command Alias:" & " Data: "
For i = 11 To 24
strTemp = strTemp & Hex(x(ms + i)) & " "
Next
Case 5 ' Set ALL-Link Command Alias Extended Data
strTemp = strTemp & " Set ALL-Link Command Alias Extended Data:" & " Data: "
For i = 11 To 24
strTemp = strTemp & Hex(x(ms + i)) & " "
Next
Case Else
strTemp = strTemp & " (unrecognized product data response)" & " Data: "
For i = 11 To 24
strTemp = strTemp & Hex(x(ms + i)) & " "
Next
End Select
Else
' Anything other than a product data response
strTemp = strTemp & " Data: "
For i = 11 To 24
strTemp = strTemp & Hex(x(ms + i)) & " "
Next
End If
My.Application.Log.WriteEntry(strTemp, TraceEventType.Verbose)
End If
' I’m not planning on actually doing anything with this data, just displayed
Case 82 ' 0x052 X10 Received
' next byte: raw X10 x(MsStart + 2)
' next byte: x10 flag x(MsStart + 3)
MessageEnd = ms + 3
If MessageEnd > 1000 Then MessageEnd = MessageEnd - 1000
If DataAvailable >= 3 Then
x_Start = MessageEnd
strTemp = "PLM: X10 Received: "
X10House = X10House_from_PLM(x(ms + 2) And 240)
Select Case x(ms + 3)
Case 0 ' House + Device
X10Code = X10Device_from_PLM(x(ms + 2) And 15)
strTemp = strTemp & Chr(65 + X10House) & (X10Code + 1)
PLM_LastX10Device = X10Code ' Device Code 0-15
Case 63, 128 ' 0x80 House + Command 63 = 0x3F - should be 0x80 but for some reason I keep getting 0x3F instead
X10Code = (x(ms + 2) And 15) + 1
X10Address = Chr(65 + X10House) & (PLM_LastX10Device + 1)
strTemp = strTemp & Chr(65 + X10House) & " " & X10Code
' Now actually process the event
' Does it have a name?
'If DeviceName(X10Address) = X10Address Then HasName = False Else HasName = True
My.Application.Log.WriteEntry(Format(Now) & " " & X10Address & " " & X10Code, TraceEventType.Verbose)
'If LoggedIn And HasName Then frmHack.WriteWebtrix(Blue, VB6.Format(TimeOfDay) & " ")
' Write command to event log
' Handle incoming event
Select Case X10Code
Case 3 ' On
' --> at this point I play a WAV file and run any macro associated with the device
Case 4 ' Off
' --> at this point I play a WAV file and run any macro associated with the device
Case 5 ' Dim
' --> at this point I play a WAV file and run any macro associated with the device
Case 6 ' Bright
' --> at this point I play a WAV file and run any macro associated with the device
End Select
Case Else ' invalid data
strTemp = strTemp & "Unrecognized X10: " & Hex(x(ms + 2)) & " " & Hex(x(ms + 3))
End Select
My.Application.Log.WriteEntry(strTemp, TraceEventType.Verbose)
End If
Case 98 ' 0x062 Send Insteon standard OR extended message
' PLM is just echoing the last command sent, discard this: 7 or 21 bytes
MessageEnd = ms + 8
If MessageEnd > 1000 Then MessageEnd = MessageEnd - 1000
If DataAvailable >= 8 Then
If (x(ms + 5) And 16) = 16 Then
' Extended message
MessageEnd = x_Start + 1 + 22
If MessageEnd > 1000 Then MessageEnd = MessageEnd - 1000
If DataAvailable >= 22 Then
x_Start = MessageEnd
strTemp = "PLM: Sent Insteon message (extended): "
For i = 0 To 22
strTemp = strTemp & Hex(x(ms + i)) & " "
Next
My.Application.Log.WriteEntry(strTemp, TraceEventType.Verbose)
End If
Else
' Standard message
x_Start = MessageEnd
strTemp = "PLM: Sent Insteon message (standard): "
For i = 0 To 8
strTemp = strTemp & Hex(x(ms + i)) & " "
Next
My.Application.Log.WriteEntry(strTemp, TraceEventType.Verbose)
End If
End If
Case 99 ' 0x063 Sent X10
' PLM is just echoing the command we last sent, discard: 3 bytes --- although could error check this for NAKs...
MessageEnd = ms + 4
If MessageEnd > 1000 Then MessageEnd = MessageEnd - 1000
If DataAvailable >= 4 Then
x_Start = MessageEnd
strTemp = "PLM: X10 Sent: "
X10House = X10House_from_PLM(x(ms + 2) And 240)
Select Case x(ms + 3)
Case 0 ' House + Device
X10Code = X10Device_from_PLM(x(ms + 2) And 15)
strTemp = strTemp & Chr(65 + X10House) & (X10Code + 1)
Case 63, 128 ' 0x80 House + Command 63 = 0x3F - should be 0x80 but for some reason I keep getting 0x3F instead
X10Code = (x(ms + 2) And 15) + 1
If X10Code > -1 And X10Code < 17 Then
strTemp = strTemp & Chr(65 + X10House) & " " & X10Code
Else
strTemp = strTemp & Chr(65 + X10House) & " unrecognized command " & Hex(x(ms + 2) And 15)
End If
Case Else ' invalid data
strTemp = strTemp & "Unrecognized X10: " & Hex(x(ms + 2)) & " " & Hex(x(ms + 3))
End Select
strTemp = strTemp & " ACK/NAK: "
Select Case x(ms + 4)
Case 6
strTemp = strTemp & "06 (sent)"
Case 21
strTemp = strTemp & "15 (failed)"
Case Else
strTemp = strTemp & Hex(x(ms + 4)) & " (?)"
End Select
My.Application.Log.WriteEntry(strTemp, TraceEventType.Verbose)
End If
Case 83 ' 0x053 ALL-Linking complete - 8 bytes of data
MessageEnd = ms + 9
If MessageEnd > 1000 Then MessageEnd = MessageEnd - 1000
If DataAvailable >= 9 Then
x_Start = MessageEnd
strTemp = "PLM: ALL-Linking Complete: 0x53 Link Code: " & Hex(x(ms + 2))
Select Case x(ms + 2)
Case 0
strTemp = strTemp & " (responder)"
Case 1
strTemp = strTemp & " (controller)"
Case 244
strTemp = strTemp & " (deleted)"
End Select
FromAddress = Hex(x(ms + 4)) & "." & Hex(x(ms + 5)) & "." & Hex(x(ms + 6))
strTemp = strTemp & " Group: " & Hex(x(ms + 3)) & " ID: " & FromAddress & " DevCat: " & Hex(x(ms + 7)) & " SubCat: " & Hex(x(ms + 8)) & " Firmware: " & Hex(x(ms + 9))
If x(ms + 9) = 255 Then strTemp = strTemp & " (all newer devices = FF)"
My.Application.Log.WriteEntry(strTemp, TraceEventType.Verbose)
My.Application.Log.WriteEntry("ADD CASE 2", TraceEventType.Verbose)
AddInsteonDeviceDb(FromAddress, x(ms + 7), x(ms + 8), x(ms + 9))
End If
Case 87 ' 0x057 ALL-Link record response - 8 bytes of data
MessageEnd = ms + 9
If MessageEnd > 1000 Then MessageEnd = MessageEnd - 1000
If DataAvailable >= 9 Then
x_Start = MessageEnd
FromAddress = Hex(x(ms + 4)) & "." & Hex(x(ms + 5)) & "." & Hex(x(ms + 6))
' Check if FromAddress is in device database, if not add it
If InsteonNum(FromAddress) = 0 Then
My.Application.Log.WriteEntry("ADD CASE 3", TraceEventType.Verbose)
' TODO: Make this: AddInsteonDevice(FromAddress)
' TODO: Make this: SortInsteon()
End If
My.Application.Log.WriteEntry("PLM: ALL-Link Record response: 0x57 Flags: " & Hex(x(ms + 2)) & " Group: " & Hex(x(ms + 3)) & " Address: " & FromAddress & " Data: " & Hex(x(ms + 7)) & " " & Hex(x(ms + 8)) & " " & Hex(x(ms + 9)), TraceEventType.Verbose)
' --> I assume this happened because I requested the data, and want the rest of it. So now
' Send 02 6A to get next record (e.g. continue reading link database from PLM)
data(0) = 2 ' all commands start with 2
data(1) = 106 ' 0x06A = Get Next ALL-Link Record
SerialPLM.Write(data, 0, 2)
End If
Case 85 ' 0x055 User reset the PLM - 0 bytes of data, not possible for this to be a partial message
MessageEnd = ms + 1
If MessageEnd > 1000 Then MessageEnd = MessageEnd - 1000
x_Start = MessageEnd
My.Application.Log.WriteEntry("PLM: User Reset 0x55")
Case 86 ' 0x056 ALL-Link cleanup failure report - 5 bytes of data
MessageEnd = ms + 6
If MessageEnd > 1000 Then MessageEnd = MessageEnd - 1000
If DataAvailable >= 6 Then
x_Start = MessageEnd
ToAddress = Hex(x(ms + 4)) & "." & Hex(x(ms + 5)) & "." & Hex(x(ms + 6))
My.Application.Log.WriteEntry("PLM: ALL-Link (Group Broadcast) Cleanup Failure Report 0x56 Data: " & Hex(x(ms + 2)) & " Group: " & Hex(x(ms + 3)) & " Address: " & ToAddress, TraceEventType.Verbose)
End If
Case 97 ' 0x061 Sent ALL-Link (Group Broadcast) command - 4 bytes
MessageEnd = ms + 5
If MessageEnd > 1000 Then MessageEnd = MessageEnd - 1000
If DataAvailable >= 5 Then
x_Start = MessageEnd
strTemp = "PLM: Sent Group Broadcast: 0x61 Group: " & Hex(x(ms + 2)) & " Command1: " & Hex(x(ms + 3)) & " Command2 (Group): " & Hex(x(ms + 4)) & " ACK/NAK: "
Select Case x(ms + 5)
Case 6
strTemp = strTemp & "06 (sent)"
Case 21
strTemp = strTemp & "15 (failed)"
Case Else
strTemp = strTemp & Hex(x(ms + 5)) & " (?)"
End Select
My.Application.Log.WriteEntry(strTemp, TraceEventType.Verbose)
End If
Case 102 ' 0x066 Set Host Device Category - 4 bytes
MessageEnd = ms + 5
If MessageEnd > 1000 Then MessageEnd = MessageEnd - 1000
If DataAvailable >= 5 Then
x_Start = MessageEnd
strTemp = "PLM: Set Host Device Category: 0x66 DevCat: " & Hex(x(ms + 2)) & " SubCat: " & Hex(x(ms + 3)) & " Firmware: " & Hex(x(ms + 4))
If x(ms + 4) = 255 Then strTemp = strTemp & " (all newer devices = FF)"
strTemp = strTemp & " ACK/NAK: "
Select Case x(ms + 5)
Case 6
strTemp = strTemp & "06 (executed correctly)"
Case 21
strTemp = strTemp & "15 (failed)"
Case Else
strTemp = strTemp & Hex(x(ms + 5)) & " (?)"
End Select
My.Application.Log.WriteEntry(strTemp, TraceEventType.Verbose)
End If
Case 115 ' 0x073 Get IM Configuration - 4 bytes
MessageEnd = ms + 5
If MessageEnd > 1000 Then MessageEnd = MessageEnd - 1000
If DataAvailable >= 5 Then
x_Start = MessageEnd
strTemp = "PLM: Get IM Configuration: 0x73 Flags: " & Hex(x(ms + 2))
If x(ms + 2) And 128 Then strTemp = strTemp & " (no button linking)"
If x(ms + 2) And 64 Then strTemp = strTemp & " (monitor mode)"
If x(ms + 2) And 32 Then strTemp = strTemp & " (manual LED control)"
If x(ms + 2) And 16 Then strTemp = strTemp & " (disable deadman comm feature)"
If x(ms + 2) And (128 + 64 + 32 + 16) Then strTemp = strTemp & " (default)"
strTemp = strTemp & " Data: " & Hex(x(ms + 3)) & " " & Hex(x(ms + 4)) & " ACK: " & Hex(x(ms + 5))
My.Application.Log.WriteEntry(strTemp, TraceEventType.Verbose)
End If
Case 100 ' 0x064 Start ALL-Linking, echoed - 3 bytes
MessageEnd = ms + 4
If MessageEnd > 1000 Then MessageEnd = MessageEnd - 1000
If DataAvailable >= 4 Then
x_Start = MessageEnd
strTemp = "PLM: Start ALL-Linking 0x64 Code: " & Hex(x(ms + 2))
Select Case x(ms + 2)
Case 0