-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathlifx_webservice.py
executable file
·133 lines (99 loc) · 3.3 KB
/
lifx_webservice.py
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
#!/usr/bin/env python
"""
Web-service wrapper that includes retries for non-ACKed packets.
Author: Petr Klus
"""
import bottle as btl
import threading
import socket
import time
import tools
import receive
try:
from queue import Queue
except ImportError: # python 2
from Queue import Queue
# seqnum generator
def get_seq_num_fun():
# workaround for nested function scoping for python 2.7
state = {
"seq_lock": threading.Lock(),
"seq_num": 0
}
def _get_seq_num():
with state["seq_lock"]:
state["seq_num"] = (state["seq_num"] + 1) % 256
return state["seq_num"]
return _get_seq_num
@btl.route('/<bulb_ip>/<hue>/<sat>/<bri>/<kel>')
def set_colour(**kwargs):
print(kwargs["bulb_ip"])
for key in ["hue", "sat", "bri", "kel"]:
kwargs[key] = float(kwargs[key])
print(key, kwargs[key])
command_q.put(kwargs)
return "ENQUEUED"
@btl.route('/hello')
def hello():
return "HELLO"
RETRIES = 10
DELAY = 0.05
UDP_PORT = 56700
UDP_IP = "0.0.0.0" # where to listen
NO_OF_SENDERS = 3
MAX_ACK_AGE = 1 # maximum ACK age
ACKS = [0.0 for _ in range(256)]
def packet_listener():
while True:
data, addr = sock.recvfrom(1024) # buffer size is 1024 bytes
try:
response = receive.process_incoming_data(data, addr)
print(response)
if response["packet_id"] == 45:
ACKS[response["seqnum"]] = time.time()
except Exception as e:
print("Unable to process packet from: {}, (error {})".format(
addr, e))
def command_sender():
while True:
try:
kwargs = command_q.get()
seq_num = get_seq_num()
print("seq num", seq_num)
ack = False
for i in range(RETRIES):
with send_lock:
sock.sendto(tools.gen_packet(kwargs["hue"], kwargs["sat"],
kwargs["bri"], kwargs["kel"],
seq_num), (kwargs["bulb_ip"],
UDP_PORT))
# wait...
time.sleep(DELAY*(i*1.5+1))
# do we have ACK?
if time.time() - ACKS[seq_num] < MAX_ACK_AGE:
ack = True
print("ACK confirmed, no more retries needed")
break
if not ack:
print("Unable to ACK packet with seq num", seq_num)
except Exception as e:
print("Unable to process command: {}".format(e))
if __name__ == "__main__":
# testing http://192.168.2.212:8888/192.168.2.222/120/100/100/3500
# UDP functionality
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP
get_seq_num = get_seq_num_fun() # initialise sequence number
# listen for lifx packets
sock.bind((UDP_IP, UDP_PORT))
# coordination objects - lock and command queue
send_lock = threading.Lock()
command_q = Queue(maxsize=5)
t = threading.Thread(target=packet_listener)
t.daemon = True
t.start()
# spawn command sending thread
for _ in range(NO_OF_SENDERS):
t = threading.Thread(target=command_sender)
t.daemon = True
t.start()
btl.run(host='0.0.0.0', port=8888)