Skip to content

Commit 8ab8c45

Browse files
Merge pull request #19 from anedyaio/development
RC v0.1.3
2 parents 7b823de + 9c8e6a2 commit 8ab8c45

7 files changed

Lines changed: 293 additions & 86 deletions

File tree

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[metadata]
22
name = anedya-dev-sdk
3-
version = 0.1.2
3+
version = 0.1.3
44
url = https://github.com/anedyaio/anedya-dev-sdk-pyhton
55
author = Anedya Systems
66
author_email = support@anedya.io

src/anedya/client/bindDevice.py

Lines changed: 57 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,71 @@
1-
import requests
21
import json
32
from ..config import ConnectionMode
3+
from ..errors import AnedyaInvalidConfig, AnedyaTxFailure
44

55

6-
def bind_device(self, binding_secret: str):
6+
def bind_device(self, binding_secret: str, timeout: float | None = None):
77
"""
8-
Bind device to Anedya Cloud
8+
:param binding_secret: Binding secret to be used for binding the device
9+
:raises AnedyaInvalidConfig: If the configuration is not provided
10+
:raises AnedyaTxFailure: If the transaction fails
11+
12+
This function provides a way to bind a device to the Anedya platform.
913
"""
14+
if self._config is None:
15+
raise AnedyaInvalidConfig('Configuration not provided')
1016
if self._config.connection_mode == ConnectionMode.MQTT:
11-
result = _bind_device_mqtt(self, binding_secret)
17+
return _bind_device_mqtt(self, binding_secret=binding_secret, timeout=timeout)
1218
elif self._config.connection_mode == ConnectionMode.HTTP:
13-
result = _bind_device_http(self, binding_secret)
14-
return result
15-
16-
17-
def _bind_device_mqtt(self, binding_secret: str):
18-
return
19+
return _bind_device_http(self, binding_secret=binding_secret, timeout=timeout)
20+
else:
21+
raise AnedyaInvalidConfig('Invalid connection mode')
1922

2023

21-
def _bind_device_http(self, binding_secret: str):
22-
headers = {'Content-type': 'application/json',
23-
'Auth-mode': self._config.authmode,
24-
'Authorization': self._config.connection_key}
24+
def _bind_device_http(self, binding_secret: str, timeout: float | None = None):
2525
if self._config._testmode:
26-
url = "https://device.stageapi.anedya.io/v1/bindDevice"
26+
url = "https://device.stageapi.anedya.io/v1/submitData"
2727
else:
28-
url = self._baseurl + "/v1/bindDevice/json"
28+
url = self._baseurl + "v1/bindDevice"
2929
requestPayload = {"bindingsecret": binding_secret,
3030
"deviceid": str(self._config._deviceID)}
31-
r = requests.post(url, data=json.dumps(requestPayload), headers=headers)
32-
jsonResponse = r.json()
33-
if r.status_code != 200:
34-
raise RuntimeError(jsonResponse)
35-
# Check whether the call was successful or not
36-
if jsonResponse["success"] is not True:
37-
raise Exception(jsonResponse)
38-
# Check whether the call was successful or not
39-
if jsonResponse["success"] is not True:
40-
raise Exception(jsonResponse)
41-
return True
31+
r = self._httpsession.post(url, data=json.dumps(requestPayload), timeout=timeout)
32+
try:
33+
jsonResponse = r.json()
34+
payload = json.loads(jsonResponse)
35+
if payload['success'] is not True:
36+
raise AnedyaTxFailure(payload['error'], payload['errCode'])
37+
except ValueError:
38+
raise AnedyaTxFailure(message="Invalid JSON response")
39+
return
40+
41+
42+
def _bind_device_mqtt(self, binding_secret: str, timeout: float | None = None):
43+
# Create and register a transaction
44+
tr = self._transactions.create_transaction()
45+
# Encode the payload
46+
requestPayload = {
47+
"reqId": tr.get_id(),
48+
"bindingsecret": binding_secret,
49+
"deviceid": str(self._config._deviceID)}
50+
payload = json.dumps(requestPayload)
51+
# Publish the message
52+
topic_prefix = "$anedya/device/" + str(self._config._deviceID)
53+
msginfo = self._mqttclient.publish(topic=topic_prefix + "/bindDevice/json",
54+
payload=payload, qos=1)
55+
try:
56+
msginfo.wait_for_publish(timeout=timeout)
57+
except ValueError:
58+
raise AnedyaTxFailure(message="Publish queue full")
59+
except RuntimeError as err:
60+
raise AnedyaTxFailure(message=str(err))
61+
# Wait for transaction to complete
62+
tr.wait_to_complete()
63+
# Transaction completed
64+
# Get the data from the transaction
65+
data = tr.get_data()
66+
# Clear transaction
67+
self._transactions.clear_transaction(tr)
68+
# Check if transaction is successful or not
69+
if data['success'] is not True:
70+
raise AnedyaTxFailure(data['error'], data['errCode'])
71+
return

src/anedya/client/submitData.py

Lines changed: 49 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,64 @@
11
from ..errors import AnedyaInvalidConfig, AnedyaTxFailure
22
from ..config import ConnectionMode
3-
from ..models import DataPoints, SubmitDataMQTTReq
3+
from ..models import DataPoints, AnedyaEncoder
4+
import json
45

56

6-
def submit_data(self, data: DataPoints, timeout: int = 1000):
7+
def submit_data(self, data: DataPoints, timeout: float | None = None):
78
"""
8-
Send data to Anedya Cloud
9+
:param data: Data to send as a :class: DataPoints object
10+
:param timeout: Timeout in seconds, default is None
11+
12+
:raises AnedyaTxFailure: If data could not be submitted due to an error
13+
14+
This function sends data to the Anedya Cloud platform. It determines the connection mode from the SDK configuration and calls the appropriate submit data method (_submit_data_http or _submit_data_mqtt).
915
"""
16+
1017
if self._config is None:
1118
raise AnedyaInvalidConfig('Configuration not provided')
1219
if self._config.connection_mode == ConnectionMode.HTTP:
13-
return _submit_data_http(self, data)
20+
return _submit_data_http(self, data=data, timeout=timeout)
1421
elif self._config.connection_mode == ConnectionMode.MQTT:
15-
return _submit_data_mqtt(self, data)
22+
return _submit_data_mqtt(self, data=data, timeout=timeout)
1623
else:
1724
raise AnedyaInvalidConfig('Invalid connection mode')
1825

1926

20-
def _submit_data_http(self, data: DataPoints):
21-
"""
22-
Send data to Anedya Cloud
23-
"""
27+
def _submit_data_http(self, data: DataPoints, timeout: float | None = None):
2428
if self._config._testmode:
2529
url = "https://device.stageapi.anedya.io/v1/submitData"
2630
else:
2731
url = self._baseurl + "/v1/submitData"
28-
r = self._httpsession.post(url, data=data.encodeJSON())
32+
r = self._httpsession.post(url, data=data.encodeJSON(), timeout=timeout)
2933
# print(r.json())
30-
if r.status_code != 200:
34+
try:
3135
jsonResponse = r.json()
32-
print(jsonResponse)
33-
return False
34-
return True
36+
payload = json.loads(jsonResponse)
37+
if payload['success'] is not True:
38+
raise AnedyaTxFailure(payload['error'], payload['errCode'])
39+
except ValueError:
40+
raise AnedyaTxFailure(message="Invalid JSON response")
41+
return
3542

3643

37-
def _submit_data_mqtt(self, data: DataPoints):
38-
"""
39-
Send data to Anedya Cloud through MQTT
40-
"""
44+
def _submit_data_mqtt(self, data: DataPoints, timeout: float | None = None):
4145
# Create and register a transaction
4246
tr = self._transactions.create_transaction()
4347
# Encode the payload
4448
d = SubmitDataMQTTReq(tr.get_id(), data)
4549
payload = d.encodeJSON()
4650
# Publish the message
47-
print(payload)
51+
# print(payload)
4852
topic_prefix = "$anedya/device/" + str(self._config._deviceID)
49-
print(topic_prefix + "/submitData/json")
50-
self._mqttclient.publish(topic=topic_prefix + "/submitdata/json",
51-
payload=payload, qos=1)
53+
# print(topic_prefix + "/submitData/json")
54+
msginfo = self._mqttclient.publish(topic=topic_prefix + "/submitdata/json",
55+
payload=payload, qos=1)
56+
try:
57+
msginfo.wait_for_publish(timeout=timeout)
58+
except ValueError:
59+
raise AnedyaTxFailure(message="Publish queue full")
60+
except RuntimeError as err:
61+
raise AnedyaTxFailure(message=str(err))
5262
# Wait for transaction to complete
5363
tr.wait_to_complete()
5464
# Transaction completed
@@ -60,3 +70,20 @@ def _submit_data_mqtt(self, data: DataPoints):
6070
if data['success'] is not True:
6171
raise AnedyaTxFailure(data['error'], data['errCode'])
6272
return
73+
74+
75+
class SubmitDataMQTTReq:
76+
def __init__(self, reqID: str, data: DataPoints):
77+
self.data = data
78+
self.reqID = reqID
79+
80+
def toJSON(self):
81+
dict = {
82+
"reqId": self.reqID,
83+
"data": self.data.data
84+
}
85+
return dict
86+
87+
def encodeJSON(self):
88+
data = json.dumps(self, cls=AnedyaEncoder)
89+
return data

src/anedya/client/submitLogs.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
from ..models import LogsCache, AnedyaEncoder
2+
from ..errors import AnedyaInvalidConfig, AnedyaTxFailure
3+
from ..config import ConnectionMode
4+
import json
5+
6+
7+
def submit_logs(self, logs: LogsCache, timeout: float | None = None):
8+
"""
9+
:param logs: Logs to be send. :class: LogCache format.
10+
:param timeout: Timeout in seconds, default is None.
11+
"""
12+
if self._config is None:
13+
raise AnedyaInvalidConfig('Configuration not provided')
14+
if self._config.connection_mode == ConnectionMode.HTTP:
15+
return _submit_log_http(self, logs=logs, timeout=timeout)
16+
elif self._config.connection_mode == ConnectionMode.MQTT:
17+
return _submit_data_mqtt(self, logs=logs, timeout=timeout)
18+
else:
19+
raise AnedyaInvalidConfig('Invalid connection mode')
20+
21+
22+
def _submit_log_http(self, logs: LogsCache, timeout: float | None = None):
23+
if self._config._testmode:
24+
url = "https://device.stageapi.anedya.io/v1/logs/submitLogs"
25+
else:
26+
url = self._baseurl + "/v1/submitLogs"
27+
r = self._httpsession.post(url, data=logs.encodeJSON(), timeout=timeout)
28+
# print(r.json())
29+
try:
30+
jsonResponse = r.json()
31+
payload = json.loads(jsonResponse)
32+
if payload['success'] is not True:
33+
raise AnedyaTxFailure(payload['error'], payload['errCode'])
34+
except ValueError:
35+
raise AnedyaTxFailure(message="Invalid JSON response")
36+
37+
38+
def _submit_data_mqtt(self, data: LogsCache, timeout: float | None = None):
39+
# Create and register a transaction
40+
tr = self._transactions.create_transaction()
41+
# Encode the payload
42+
d = SubmitLogsMQTTReq(tr.get_id(), data)
43+
payload = d.encodeJSON()
44+
# Publish the message
45+
self._mqttclient.publish(topic="$anedya/device/" + str(self._config._deviceID) + "/submitLogs/json",
46+
payload=payload, qos=1)
47+
# Wait for transaction to complete
48+
tr.wait_to_complete()
49+
# Transaction completed
50+
# Get the data from the transaction
51+
data = tr.get_data()
52+
# Clear transaction
53+
self._transactions.clear_transaction(tr)
54+
# Check if transaction is successful or not
55+
if data['success'] is not True:
56+
raise AnedyaTxFailure(data['error'], data['errCode'])
57+
return
58+
59+
60+
class SubmitLogsMQTTReq:
61+
def __init__(self, reqID: str, logs: LogsCache):
62+
self.logs = logs
63+
self.reqID = reqID
64+
65+
def toJSON(self):
66+
dict = {
67+
"reqId": self.reqID,
68+
"data": self.logs.logs
69+
}
70+
return dict
71+
72+
def encodeJSON(self):
73+
data = json.dumps(self, cls=AnedyaEncoder)
74+
return data

src/anedya/client/timeSync.py

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,67 @@
1-
import time
1+
from ..errors import AnedyaInvalidConfig, AnedyaTxFailure
2+
from ..config import ConnectionMode
23
import json
3-
import requests
4+
import time
45

56

6-
def get_time(self):
7+
def get_time(self, timeout: float | None = None):
78
"""
89
Get current time from Anedya Time Service using HTTP request -
910
Gets current time using HTTP requests.
1011
Accuracy is generally within few tens of millisecond. For greater accuracy
1112
consider using NTP time service from Anedya
1213
"""
14+
if self._config is None:
15+
raise AnedyaInvalidConfig('Configuration not provided')
16+
if self._config.connection_mode == ConnectionMode.HTTP:
17+
return _time_sync_http(self, timeout=timeout)
18+
elif self._config.connection_mode == ConnectionMode.MQTT:
19+
return _time_sync_mqtt(self, timeout=timeout)
20+
else:
21+
raise AnedyaInvalidConfig('Invalid connection mode')
22+
23+
24+
def _time_sync_http(self, timeout: float | None = None):
1325
# print("called time API")
1426
if self._config._testmode:
1527
url = "https://device.stageapi.anedya.io/v1/time"
1628
else:
1729
url = "https://device." + self._config.region + ".anedya.io/v1/time"
1830
deviceSendTime = int(time.time_ns() / 1000000)
1931
requestPayload = {"deviceSendTime": deviceSendTime}
20-
# print(json.dumps(requestPayload))
21-
headers = {'Content-type': 'application/json'}
22-
r = requests.post(url, data=json.dumps(requestPayload), headers=headers)
32+
r = self._httpsession.post(url, data=json.dumps(requestPayload), timeout=timeout)
33+
try:
34+
jsonResponse = r.json()
35+
payload = json.loads(jsonResponse)
36+
if payload['success'] is not True:
37+
raise AnedyaTxFailure(payload['error'], payload['errCode'])
38+
deviceRecTime = int(time.time_ns() / 1000000)
39+
ServerReceiveTime = jsonResponse["serverReceiveTime"]
40+
ServerSendTime = jsonResponse["serverSendTime"]
41+
currentTime = (ServerReceiveTime + ServerSendTime + deviceRecTime - deviceSendTime) / 2
42+
except ValueError:
43+
raise AnedyaTxFailure(message="Invalid JSON response")
44+
return currentTime
45+
46+
47+
def _time_sync_mqtt(self, timeout: float | None = None):
48+
# Create and register a transaction
49+
tr = self._transactions.create_transaction()
50+
# Create the payload
51+
deviceSendTime = int(time.time_ns() / 1000000)
52+
requestPayload = {"deviceSendTime": deviceSendTime}
53+
payload = json.dumps(requestPayload)
54+
# Publish the message
55+
self._mqttclient.publish(topic="$anedya/device/" + str(self._config._deviceID) + "/getTime/json",
56+
payload=payload, qos=1)
57+
# Wait for transaction to complete
58+
tr.wait_to_complete()
2359
deviceRecTime = int(time.time_ns() / 1000000)
24-
jsonResponse = r.json()
25-
# print(jsonResponse)
26-
if r.status_code != 200:
27-
raise RuntimeError(jsonResponse)
28-
# Now compute the time from response
29-
ServerReceiveTime = jsonResponse["serverReceiveTime"]
30-
ServerSendTime = jsonResponse["serverSendTime"]
60+
# Transaction completed
61+
# Get the data from the transaction
62+
data = tr.get_data()
63+
ServerReceiveTime = data["serverReceiveTime"]
64+
ServerSendTime = data["serverSendTime"]
3165
currentTime = (ServerReceiveTime + ServerSendTime + deviceRecTime - deviceSendTime) / 2
66+
# Decode the paylo
3267
return currentTime

src/anedya/config.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from enum import Enum
22
import uuid
3+
from .errors import AnedyaInvalidConfig
34

45

56
class ConnectionMode(Enum):
@@ -61,7 +62,10 @@ def set_deviceid(self, id: str):
6162
"""
6263
Set DeviceID
6364
"""
64-
self._deviceID = uuid.UUID(id)
65+
try:
66+
self._deviceID = uuid.UUID(id)
67+
except ValueError:
68+
raise AnedyaInvalidConfig("Device ID needs to be valid UUID")
6569
self._deviceid_set = True
6670

6771
def set_timeout(self, timeout):

0 commit comments

Comments
 (0)