]>
vault307.fbx.one Git - Blynk.git/blob - BlynkLib.py
1 # Copyright (c) 2015-2019 Volodymyr Shymanskyy. See the file LICENSE for copying permission.
12 gettime
= lambda: time
.ticks_ms()
16 gettime
= lambda: int(time
.time() * 1000)
27 MSG_NOTIFY
= const(14)
28 MSG_BRIDGE
= const(15)
29 MSG_HW_SYNC
= const(16)
30 MSG_INTERNAL
= const(17)
31 MSG_PROPERTY
= const(19)
33 MSG_HW_LOGIN
= const(29)
34 MSG_EVENT_LOG
= const(64)
36 MSG_REDIRECT
= const(41) # TODO: not implemented
37 MSG_DBG_PRINT
= const(55) # TODO: not implemented
39 STA_SUCCESS
= const(200)
40 STA_INVALID_TOKEN
= const(9)
42 DISCONNECTED
= const(0)
49 / _ / / // / _ \\/ '_/
50 /____/_/\\_, /_//_/_/\\_\\
51 /___/ for Python v""" + __version__
+ " (" + sys
.platform
+ ")\n")
57 def on(self
, evt
, f
=None):
66 def emit(self
, evt
, *a
, **kv
):
68 self
._cbks
[evt
](*a
, **kv
)
71 class BlynkProtocol(EventEmitter
):
72 def __init__(self
, auth
, tmpl_id
=None, fw_ver
=None, heartbeat
=50, buffin
=1024, log
=None):
73 EventEmitter
.__init
__(self
)
74 self
.heartbeat
= heartbeat
*1000
76 self
.log
= log
or dummy
78 self
.tmpl_id
= tmpl_id
80 self
.state
= DISCONNECTED
83 def virtual_write(self
, pin
, *val
):
84 self
._send
(MSG_HW
, 'vw', pin
, *val
)
86 def send_internal(self
, pin
, *val
):
87 self
._send
(MSG_INTERNAL
, pin
, *val
)
89 def set_property(self
, pin
, prop
, *val
):
90 self
._send
(MSG_PROPERTY
, pin
, prop
, *val
)
92 def sync_virtual(self
, *pins
):
93 self
._send
(MSG_HW_SYNC
, 'vr', *pins
)
95 def log_event(self
, *val
):
96 self
._send
(MSG_EVENT_LOG
, *val
)
98 def _send(self
, cmd
, *args
, **kwargs
):
100 id = kwargs
.get('id')
104 if self
.msg_id
> 0xFFFF:
111 data
= ('\0'.join(map(str, args
))).encode('utf8')
114 self
.log('<', cmd
, id, '|', *args
)
115 msg
= struct
.pack("!BHH", cmd
, id, dlen
) + data
116 self
.lastSend
= gettime()
120 if self
.state
!= DISCONNECTED
: return
122 (self
.lastRecv
, self
.lastSend
, self
.lastPing
) = (gettime(), 0, 0)
124 self
.state
= CONNECTING
125 self
._send
(MSG_HW_LOGIN
, self
.auth
)
126 self
.emit('connected')
128 def disconnect(self
):
129 if self
.state
== DISCONNECTED
: return
131 self
.state
= DISCONNECTED
132 self
.emit('disconnected')
134 def process(self
, data
=None):
135 if not (self
.state
== CONNECTING
or self
.state
== CONNECTED
): return
137 if now
- self
.lastRecv
> self
.heartbeat
+(self
.heartbeat
//2):
138 return self
.disconnect()
139 if (now
- self
.lastPing
> self
.heartbeat
//10 and
140 (now
- self
.lastSend
> self
.heartbeat
or
141 now
- self
.lastRecv
> self
.heartbeat
)):
145 if data
!= None and len(data
):
149 if len(self
.bin
) < 5:
152 cmd
, i
, dlen
= struct
.unpack("!BHH", self
.bin
[:5])
153 if i
== 0: return self
.disconnect()
157 self
.bin
= self
.bin
[5:]
159 self
.log('>', cmd
, i
, '|', dlen
)
160 if self
.state
== CONNECTING
and i
== 1:
161 if dlen
== STA_SUCCESS
:
162 self
.state
= CONNECTED
163 dt
= now
- self
.lastSend
164 info
= ['ver', __version__
, 'h-beat', self
.heartbeat
//1000, 'buff-in', self
.buffin
, 'dev', sys
.platform
+'-py']
166 info
.extend(['tmpl', self
.tmpl_id
])
167 info
.extend(['fw-type', self
.tmpl_id
])
169 info
.extend(['fw', self
.fw_ver
])
170 self
._send
(MSG_INTERNAL
, *info
)
172 self
.emit('connected', ping
=dt
)
174 self
.emit('connected')
176 if dlen
== STA_INVALID_TOKEN
:
177 self
.emit("invalid_auth")
178 print("Invalid auth token")
179 return self
.disconnect()
181 if dlen
>= self
.buffin
:
182 print("Cmd too big: ", dlen
)
183 return self
.disconnect()
185 if len(self
.bin
) < 5+dlen
:
188 data
= self
.bin
[5:5+dlen
]
189 self
.bin
= self
.bin
[5+dlen
:]
191 args
= list(map(lambda x
: x
.decode('utf8'), data
.split(b
'\0')))
193 self
.log('>', cmd
, i
, '|', ','.join(args
))
195 self
._send
(MSG_RSP
, STA_SUCCESS
, id=i
)
196 elif cmd
== MSG_HW
or cmd
== MSG_BRIDGE
:
198 self
.emit("V"+args
[1], args
[2:])
199 self
.emit("V*", args
[1], args
[2:])
200 elif cmd
== MSG_INTERNAL
:
201 self
.emit("internal:"+args
[0], args
[1:])
202 elif cmd
== MSG_REDIRECT
:
203 self
.emit("redirect", args
[0], int(args
[1]))
205 print("Unexpected command: ", cmd
)
206 return self
.disconnect()
210 class Blynk(BlynkProtocol
):
211 def __init__(self
, auth
, **kwargs
):
212 self
.insecure
= kwargs
.pop('insecure', False)
213 self
.server
= kwargs
.pop('server', 'blynk.cloud')
214 self
.port
= kwargs
.pop('port', 80 if self
.insecure
else 443)
215 BlynkProtocol
.__init
__(self
, auth
, **kwargs
)
216 self
.on('redirect', self
.redirect
)
218 def redirect(self
, server
, port
):
225 print('Connecting to %s:%d...' % (self
.server
, self
.port
))
227 s
.connect(socket
.getaddrinfo(self
.server
, self
.port
)[0][-1])
230 s
.setsockopt(socket
.IPPROTO_TCP
, socket
.TCP_NODELAY
, 1)
241 ssl_context
= ssl
.create_default_context()
242 self
.conn
= ssl_context
.wrap_socket(s
, server_hostname
=self
.server
)
244 self
.conn
.settimeout(SOCK_TIMEOUT
)
246 s
.settimeout(SOCK_TIMEOUT
)
247 BlynkProtocol
.connect(self
)
249 def _write(self
, data
):
251 self
.conn
.write(data
)
252 # TODO: handle disconnect
257 data
= self
.conn
.read(self
.buffin
)
259 except KeyboardInterrupt:
261 except socket
.timeout
:
262 # No data received, call process to send ping messages when needed
264 except: # TODO: handle disconnect