@@ -4,6 +4,7 @@ module.exports = (env) ->
4
4
Promise = env .require ' bluebird'
5
5
net = require ' net'
6
6
cap = require ' cap'
7
+ bootp = require ' ./bootp'
7
8
commons = require (' pimatic-plugin-commons' )(env)
8
9
9
10
@@ -41,32 +42,32 @@ module.exports = (env) ->
41
42
@candidatesSeen = []
42
43
@lastId = null
43
44
44
- @ arpPacketHandler = (arp ) =>
45
- candidateArpAddress = arp . info .sendermac
46
- if candidateArpAddress not in @candidatesSeen
47
- @base .debug ' Amazon device detected: ' + candidateArpAddress
48
- @candidatesSeen .push candidateArpAddress
49
- @ _probeChromeCastPort (arp . info .senderip ).then (probeSucceeded) =>
45
+ @ candidateInfoHandler = (info ) =>
46
+ candidateAddress = info .mac
47
+ if candidateAddress not in @candidatesSeen
48
+ @base .debug ' Amazon device detected: ' + candidateAddress
49
+ @candidatesSeen .push candidateAddress
50
+ @ _probeChromeCastPort (info .ip ).then (probeSucceeded) =>
50
51
if probeSucceeded
51
- @base .debug ' Amazon device appears to be a Chromecast server: ' + candidateArpAddress
52
+ @base .debug ' Amazon device appears to be a Chromecast server: ' + candidateAddress
52
53
else
53
54
@lastId = @base .generateDeviceId @framework , " dash" , @lastId
54
55
55
56
deviceConfig =
56
57
id : @lastId
57
58
name : @lastId
58
59
class : ' AmazingDashButton'
59
- macAddress : candidateArpAddress
60
+ macAddress : candidateAddress
60
61
61
62
@framework .deviceManager .discoveredDevice (
62
63
' pimatic-amazing-dash-button' ,
63
- " #{ deviceConfig .name } (#{ deviceConfig .macAddress } , #{ arp . info .senderip } )" ,
64
+ " #{ deviceConfig .name } (#{ deviceConfig .macAddress } , #{ info .ip } )" ,
64
65
deviceConfig
65
66
)
66
67
67
- @ on ' arpPacket ' , @arpPacketHandler
68
+ @ on ' candidateInfo ' , @candidateInfoHandler
68
69
@timer = setTimeout ( =>
69
- @ removeListener ' arpPacket ' , @arpPacketHandler
70
+ @ removeListener ' candidateInfo ' , @candidateInfoHandler
70
71
, eventData .time
71
72
)
72
73
)
@@ -98,7 +99,8 @@ module.exports = (env) ->
98
99
left + " or " + right
99
100
)
100
101
101
- linkType = @capture .open device, " arp and (#{ filter} )" , 10 * 65536 , @buffer
102
+ pcapFilter = " (arp or (udp and src port 68 and dst port 67 and udp[247:4] == 0x63350103)) and (#{ filter} )"
103
+ linkType = @capture .open device, pcapFilter, 10 * 65536 , @buffer
102
104
try
103
105
@capture .setMinBytes 0
104
106
catch e
@@ -112,27 +114,42 @@ module.exports = (env) ->
112
114
113
115
_rawPacketHandler : () =>
114
116
ret = cap .decoders .Ethernet @buffer
115
- if ret .info .type is cap .decoders .PROTOCOL .ETHERNET .ARP
116
- @ emit ' arpPacket' , cap .decoders .ARP @buffer , ret .offset
117
+ candidateInfo = {}
118
+ if ret .info .type is cap .decoders .PROTOCOL .ETHERNET .IPV4
119
+ r = cap .decoders .IPV4 @buffer , ret .offset
120
+ dhcp = bootp .BOOTP @buffer , r .offset + 8
121
+ candidateInfo .mac = dhcp .info .clientmac
122
+ candidateInfo .ip = dhcp .info .requestedip
123
+ @base .debug " DHCP" , candidateInfo
124
+ @ emit ' candidateInfo' , candidateInfo
125
+ else if ret .info .type is cap .decoders .PROTOCOL .ETHERNET .ARP
126
+ arp = cap .decoders .ARP @buffer , ret .offset
127
+ candidateInfo .mac = arp .info .sendermac
128
+ candidateInfo .ip = arp .info .senderip
129
+ @base .debug " ARP" , candidateInfo
130
+ @ emit ' candidateInfo' , candidateInfo
117
131
118
132
_probeChromeCastPort : (host , port = 8008 ) ->
119
- client = new net.Socket
120
- return new Promise ( (resolve ) =>
121
- client .setTimeout 3000 , =>
122
- @base .debug " Timeout"
123
- resolve false
124
- client .on " error" , (error ) =>
125
- @base .debug error
133
+ if host ? .length
134
+ client = new net.Socket
135
+ new Promise ( (resolve ) =>
136
+ client .setTimeout 3000 , =>
137
+ @base .debug " Timeout"
138
+ resolve false
139
+ client .on " error" , (error ) =>
140
+ @base .debug error
141
+ resolve false
142
+ client .connect port, host, =>
143
+ @base .debug " Connected to device #{ host} :#{ port} "
144
+ resolve true
145
+ )
146
+ .catch =>
147
+ @base .debug " Exception"
126
148
resolve false
127
- client .connect port, host, =>
128
- @base .debug " Connected to device #{ host} :#{ port} "
129
- resolve true
130
- )
131
- .catch =>
132
- @base .debug " Exception"
133
- resove false
134
- .finally =>
135
- client .destroy ()
149
+ .finally =>
150
+ client .destroy ()
151
+ else
152
+ Promise .resolve false
136
153
137
154
138
155
class AmazingDashButton extends env.devices.ContactSensor
@@ -153,8 +170,9 @@ module.exports = (env) ->
153
170
@_contact = @_invert
154
171
@debug = @plugin .debug || false
155
172
@base = commons .base @ , @config .class
156
- @ arpPacketHandler = (arp ) =>
157
- if arp .info .sendermac is @macAddress
173
+ @ candidateInfoHandler = (info ) =>
174
+ if not @timer ? and info .mac is @macAddress
175
+ @base .debug " Amazon dash button triggered (#{ info .mac } )"
158
176
@ _setContact not @_invert
159
177
clearTimeout @timer if @timer ?
160
178
@timer = setTimeout ( =>
@@ -163,11 +181,11 @@ module.exports = (env) ->
163
181
, @config .holdTime
164
182
)
165
183
super ()
166
- @plugin .on ' arpPacket ' , @arpPacketHandler
184
+ @plugin .on ' candidateInfo ' , @candidateInfoHandler
167
185
168
186
destroy : () ->
169
187
clearTimeout @timer if @timer ?
170
- @plugin .removeListener ' arpPacket ' , @arpPacketHandler
188
+ @plugin .removeListener ' candidateInfo ' , @candidateInfoHandler
171
189
super ()
172
190
173
191
getContact : () -> Promise .resolve @_contact
0 commit comments