Skip to content

Commit c1cc03c

Browse files
authored
Merge pull request #24 from Windham-High-School/main
update from main branch
2 parents c0e2281 + 4290b54 commit c1cc03c

File tree

12 files changed

+146
-56
lines changed

12 files changed

+146
-56
lines changed

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ dist/
1414
downloads/
1515
eggs/
1616
.eggs/
17-
lib/
1817
lib64/
1918
parts/
2019
sdist/

code.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,15 @@
77
connection = Connection()
88
print("Connected!")
99

10-
# Turn off WiFi to save power:
11-
connection.close_wifi()
10+
# Sync time:
11+
connection.sync_time()
1212

13-
# Reconnect WiFi when ready:
14-
connection.connect_wifi()
13+
# If none, post Hello World:
14+
connection.post(Text("Hello World!"))
1515

1616
# Check for code updates from the server:
1717
connection.code_update()
1818

19-
# If none, post Hello World:
20-
connection.post(Text("Hello World!"))
21-
2219
# Get status:
2320
print("Getting status:")
2421
print(connection.get_status())

cubeserver-api-python.zip

-184 KB
Binary file not shown.

lib/servercom

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../servercom

package.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#!/usr/bin/env bash
22

33
./update_version.sh
4-
zip -r cubeserver-api-python.zip . -x .gitignore -x ./.git -x package.sh -x update_version.sh -x version.txt 2>&1 > /dev/null
4+
zip cubeserver-api-python.zip -r lib/ boot.py code.py 2>&1 > /dev/null
55
echo cubeserver-api-python.zip

servercom/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "v0.2.0-beta"
1+
__version__ = "v1.1.0"

servercom/implementations/circuitpy.py

Lines changed: 28 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,10 @@ def basic_auth_str(user: str, pwd: str) -> str:
3030

3131
DataClass = enum(
3232
TEMPERATURE = "temperature",
33-
HUMIDITY = "humidity",
3433
PRESSURE = "pressure",
35-
LIGHT_INTENSITY = "light intensity",
3634
COMMENT = "comment",
37-
BATTERY = "remaining battery"
35+
BATTERY = "remaining battery",
36+
BEACON = "beacon challenge"
3837
)
3938

4039
class DataPoint():
@@ -69,39 +68,30 @@ class Temperature(DataPoint):
6968
def __init__(self, value):
7069
super().__init__(DataClass.TEMPERATURE, value)
7170

72-
class Humidity(DataPoint):
73-
"""A class for DataPoints that store humidity values"""
74-
UNIT = "%"
75-
def __init__(self, value):
76-
super().__init__(DataClass.HUMIDITY, value)
77-
7871
class Pressure(DataPoint):
7972
"""A class for DataPoints that store barometric pressure values"""
8073
UNIT="inHg"
8174
def __init__(self, value):
8275
super().__init__(DataClass.PRESSURE, value)
8376

84-
class Intensity(DataPoint):
85-
"""A class for DataPoints that store light intensity values"""
86-
UNIT="lux"
87-
def __init__(self, value):
88-
super().__init__(DataClass.LIGHT_INTENSITY, value)
89-
9077
class Text(DataPoint):
9178
"""A class reserved for DataPoints that are intended as a text comment"""
9279
UNIT="" # No unit for regular strings of text
9380
def __init__(self, value: str):
9481
super().__init__(DataClass.COMMENT, value)
9582

83+
class BeaconChallenge(DataPoint):
84+
"""A class reserved for DataPoints that are in response to a message from the beacon"""
85+
UNIT="" # No unit for regular strings of text
86+
def __init__(self, value: str):
87+
super().__init__(DataClass.BEACON, value)
88+
9689
class BatteryLevel(DataPoint):
9790
"""A class reserved for DataPoints that are intended as an indication of battery level"""
9891
UNIT="%" # No unit for regular strings of text
9992
def __init__(self, value: int):
10093
super().__init__(DataClass.BATTERY, value)
10194

102-
class ConnectionError(Exception):
103-
"""Indicates an issue with the server connection"""
104-
10595
class AuthorizationError(ConnectionError):
10696
"""Indicates an issue with the team credentials"""
10797

@@ -165,7 +155,7 @@ def __init__(
165155
self.context.load_verify_locations(cadata=server_cert)
166156
self.connect_wifi()
167157

168-
def connect_wifi(self, attempts=10) -> None:
158+
def connect_wifi(self, attempts=50) -> None:
169159
"""Creates the wifi connection to the access point"""
170160
wifi.radio.enabled = True
171161
if self.v:
@@ -179,7 +169,7 @@ def connect_wifi(self, attempts=10) -> None:
179169
if self.v:
180170
print(e.with_traceback)
181171
attempts -= 1
182-
time.sleep(1)
172+
time.sleep(.1)
183173
if self.v:
184174
print("Initializing socket pool...")
185175
self.pool = socketpool.SocketPool(wifi.radio)
@@ -218,6 +208,7 @@ def radio(self):
218208
return wifi.radio
219209

220210
def rx_bytes(self) -> bytes:
211+
total_length: int = None
221212
response = b""
222213
while True:
223214
buf = bytearray(256)
@@ -228,12 +219,21 @@ def rx_bytes(self) -> bytes:
228219
recvd = 0
229220
else:
230221
raise
231-
response += buf
222+
response += bytes(buf).replace(b'\x00', b'')
232223
del buf
233224
collect()
234225
if self.v:
235226
print(f"Received {recvd} bytes")
236-
if recvd == 0:
227+
if response.endswith(b'\r\n\r\n'):
228+
header_chunks = response.split(b'\r\n')
229+
for header in header_chunks:
230+
if header.startswith(b'Content-Length: '):
231+
total_length = len(response) + int(header.split(b' ')[1])
232+
break
233+
if recvd == 0 or (
234+
total_length is not None and \
235+
len(response) >= total_length
236+
):
237237
del recvd
238238
collect()
239239
break
@@ -267,8 +267,8 @@ def _do_request(
267267
f"{method} {path} HTTP/1.1\r\n" +
268268
"Host: api.local\r\n" +
269269
"Connection: close\r\n" +
270-
f"Authorization: Basic {auth_str}" +
271-
'\r\n'.join(headers) + "\r\n"
270+
f"Authorization: Basic {auth_str}\r\n" +
271+
'\r\n'.join(headers)
272272
)
273273
del auth_str
274274
collect()
@@ -283,7 +283,8 @@ def _do_request(
283283
f"Content-Length: {len(body)}\r\n" +
284284
f"\r\n{body}\r\n"
285285
)
286-
req_text += '\r\n'
286+
else:
287+
req_text += '\r\n\r\n'
287288

288289
if self.v:
289290
print("Sending request...")
@@ -302,7 +303,7 @@ def _do_request(
302303
collect()
303304
if self.v:
304305
print("Receiving response...")
305-
self.wrapped_socket.setblocking(False)
306+
self.wrapped_socket.settimeout(self.conf.TIMEOUT)
306307
response = self.rx_bytes()
307308
except Exception as e:
308309
if self.v:
@@ -388,7 +389,7 @@ def get_status(self) -> GameStatus:
388389
resp_json = loads(resp[1])
389390
if self.v:
390391
print(f"It is {resp_json['unix_time']} seconds since the epoch.")
391-
return GameStatus(Time(resp_json['unix_time']), resp_json['status']['score'])
392+
return GameStatus(Time(resp_json['unix_time']), resp_json['status']['score'], resp_json['CubeServer_version'])
392393

393394
def sync_time(self) -> bool:
394395
"""Syncs the current clock against the server"""

servercom/implementations/common.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515

1616
GameStatus = namedtuple("GameStatus",
1717
['time',
18-
'score']
18+
'score',
19+
'CubeServer_version']
1920
)
2021

2122
HTTPResponse = namedtuple("HTTPResponse",

servercom/implementations/cpy.py

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,10 @@
1818
# Helpers:
1919
class DataClass(str, Enum):
2020
TEMPERATURE = "temperature"
21-
HUMIDITY = "humidity"
2221
PRESSURE = "pressure"
23-
LIGHT_INTENSITY = "light intensity"
2422
COMMENT = "comment"
2523
BATTERY = "remaining battery"
24+
BEACON = "beacon challenge"
2625

2726
class DataPoint():
2827
"""A class for storing and handling datapoints"""
@@ -56,30 +55,24 @@ class Temperature(DataPoint):
5655
def __init__(self, value):
5756
super().__init__(DataClass.TEMPERATURE, value)
5857

59-
class Humidity(DataPoint):
60-
"""A class for DataPoints that store humidity values"""
61-
UNIT = "%"
62-
def __init__(self, value):
63-
super().__init__(DataClass.HUMIDITY, value)
64-
6558
class Pressure(DataPoint):
6659
"""A class for DataPoints that store barometric pressure values"""
6760
UNIT="inHg"
6861
def __init__(self, value):
6962
super().__init__(DataClass.PRESSURE, value)
7063

71-
class Intensity(DataPoint):
72-
"""A class for DataPoints that store light intensity values"""
73-
UNIT="lux"
74-
def __init__(self, value):
75-
super().__init__(DataClass.LIGHT_INTENSITY, value)
76-
7764
class Text(DataPoint):
7865
"""A class reserved for DataPoints that are intended as a text comment"""
7966
UNIT="" # No unit for regular strings of text
8067
def __init__(self, value: str):
8168
super().__init__(DataClass.COMMENT, value)
8269

70+
class BeaconChallenge(DataPoint):
71+
"""A class reserved for DataPoints that are in response to a message from the beacon"""
72+
UNIT="" # No unit for regular strings of text
73+
def __init__(self, value: str):
74+
super().__init__(DataClass.BEACON, value)
75+
8376
class BatteryLevel(DataPoint):
8477
"""A class reserved for DataPoints that are intended as an indication of battery level"""
8578
UNIT="%" # No unit for regular strings of text
@@ -344,7 +337,7 @@ def get_status(self) -> GameStatus:
344337
"""
345338
resp = self.request('GET', '/status')
346339
resp_json = loads(resp[1])
347-
return GameStatus(Time(resp_json['unix_time']), resp_json['status']['score'])
340+
return GameStatus(Time(resp_json['unix_time']), resp_json['status']['score'], resp_json['CubeServer_version'])
348341

349342
def sync_time(self) -> bool:
350343
"""Syncs the current clock against the server"""

servercom/irrx.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
from sys import implementation as _implementation
2+
if _implementation.name != 'circuitpython':
3+
raise NotImplementedError("Woah there, Bud! You can only do IR stuff from CircuitPython!")
4+
5+
import pulseio
6+
import board
7+
import digitalio
8+
import adafruit_irremote
9+
#irPin = digitalio.DigitalInOut(board.A0)
10+
#irPin.direction = digitalio.Direction.INPUT
11+
DEFAULT_PULSEIN = pulseio.PulseIn(board.A0, maxlen=30000, idle_state=True)
12+
DEFAULT_DECODER = adafruit_irremote.GenericDecode()
13+
14+
def decode_chunk(decoder, pulsein) -> bytes:
15+
pulses = decoder.read_pulses(pulsein)
16+
try:
17+
buf = bytes(decoder.decode_bits(pulses))
18+
print(".", end="")
19+
return buf
20+
except adafruit_irremote.IRNECRepeatException:
21+
return b''
22+
except adafruit_irremote.IRDecodeException:
23+
return b''
24+
except adafruit_irremote.FailedToDecode:
25+
return b''
26+
27+
def decode_line(decoder, pulsein) -> bytes:
28+
buf = b''
29+
for i in range(10):
30+
buf += decode_chunk(decoder, pulsein)
31+
if buf.endswith(b'\r\n'):
32+
break
33+
return buf
34+
35+
class IRMsg:
36+
def __init__(self, headers, body):
37+
self.headers = headers
38+
self.body = body
39+
40+
@property
41+
def division(self):
42+
return self.headers[b'Division'].decode()
43+
44+
@property
45+
def text(self):
46+
return self.body.decode()
47+
48+
def receive(decoder = DEFAULT_DECODER, pulsein = DEFAULT_PULSEIN):
49+
# Read until BELL
50+
print("*", end="")
51+
while decode_chunk(decoder, pulsein) != b'\x07':
52+
pass
53+
buf = b'\x07'
54+
# Read until no BELL
55+
print("*", end="")
56+
while buf == b'\x07':
57+
buf = decode_chunk(decoder, pulsein)
58+
# Figure out datagram length
59+
print("*", end="")
60+
total_length = int.from_bytes(buf, 'big')
61+
read_length = 0
62+
if total_length < 8:
63+
return b'', b'', b''
64+
# Start receiving the datagram
65+
print("*", end="")
66+
protocol = decode_line(decoder, pulsein)
67+
read_length += len(protocol)
68+
if protocol != b"CSMSG/1.0\r\n":
69+
print("Potentially unsupported protocol version. Try getting the latest servercom library?")
70+
headers = {}
71+
buf = b': '
72+
while buf.find(b': ') >= 0 or buf == b'\r\n':
73+
print("*", end="")
74+
buf = decode_line(decoder, pulsein)
75+
read_length += len(buf)
76+
if buf == b'\r\n':
77+
break
78+
split_buf = buf.strip(b'\r\n').split(b': ')
79+
headers.update({split_buf[0]: split_buf[1]})
80+
body = b''
81+
content_length = int(headers[b'Content-Length'])
82+
while len(body) < content_length:
83+
print("*", end="")
84+
body += decode_chunk(decoder, pulsein)
85+
return IRMsg(headers, body)
86+
87+
#dude = receive()
88+
#print()
89+
#print(dude.division)
90+
#print(dude.text)

0 commit comments

Comments
 (0)