Skip to content

Commit 6e9dac2

Browse files
Merge pull request #22 from anedyaio/development
RC 0.1.6
2 parents e6f4d1f + 6df251a commit 6e9dac2

8 files changed

Lines changed: 151 additions & 13 deletions

File tree

setup.cfg

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[metadata]
22
name = anedya-dev-sdk
3-
version = 0.1.5
4-
url = https://github.com/anedyaio/anedya-dev-sdk-pyhton
3+
version = 0.1.6
4+
url = https://github.com/anedyaio/anedya-dev-sdk-python
55
author = Anedya Systems
66
author_email = support@anedya.io
77
description = Anedya python based SDK for IoT devices. This SDK streamlines connectivity with Anedya platform. As this SDK is in Beta release, future versions may have breaking changes.
@@ -21,8 +21,8 @@ classifiers =
2121
Topic :: Software Development :: Libraries :: Python Modules
2222
project_urls =
2323
Documentation = https://docs.anedya.io
24-
Release notes = https://github.com/anedyaio/anedya-dev-sdk-pyhton
25-
Source = https://github.com/anedyaio/anedya-dev-sdk-pyhton
24+
Release notes = https://github.com/anedyaio/anedya-dev-sdk-python
25+
Source = https://github.com/anedyaio/anedya-dev-sdk-python
2626

2727
[bdist_wheel]
2828
universal = 1

src/anedya/anedya.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ def disconnect(self):
104104
from .client.bindDevice import bind_device
105105
from .client.submitData import submit_data
106106
from .client.submitLogs import submit_logs
107+
from .client.commandsUpdate import update_command_status
107108
from .client.timeSync import get_time
108109
from .client.mqttHandlers import _onconnect_handler, _ondisconnect_handler
109110
from .client.callbacks import _error_callback, _response_callback, _command_callback

src/anedya/client/callbacks.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import json
2-
from ..models import CommandMessage
2+
from ..models import CommandDetails
33

44

55
def _response_callback(self, client, userdata, message):
@@ -25,9 +25,9 @@ def _command_callback(self, client, userdata, message):
2525
payload_data = json.loads(message.payload)
2626
# This callback is called when a command is received.
2727
# Convert the received command to python object and call client callback
28-
cmd = CommandMessage(payload_data)
2928
# Call the client callback
3029
try:
30+
cmd = CommandDetails(payload_data)
3131
self.on_command(cmd)
3232
except Exception:
3333
pass
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import json
2+
import string
3+
import random
4+
import base64
5+
from ..models import CommandDetails, AnedyaEncoder
6+
from enum import Enum
7+
from ..errors import AnedyaInvalidConfig, AnedyaInvalidType, AnedyaTxFailure
8+
from ..config import ConnectionMode
9+
10+
11+
class CommandStatus(Enum):
12+
PENDING = "pending"
13+
RECEIVED = "received"
14+
PROCESSING = "processing"
15+
SUCCESS = "success"
16+
FAILURE = "failure"
17+
INVALIDATED = "invalidated"
18+
19+
20+
def update_command_status(self, command: CommandDetails, status: CommandStatus, ackdata: str | bytes | None = None, acktype: str = "string", timeout: float | None = None) -> None:
21+
"""
22+
Update status of a command
23+
24+
Args:
25+
command (CommandDetails): Command object of which status needs to be updated
26+
status (CommandStatus): New status of the command
27+
ackdata (str | bytes | None, optional): Data to be submitted along with acknowledgement. Maximum 1 kB of data is allowed. Defaults to None.
28+
acktype (str, optional): Specify the type of data submitted. Defaults to "string".
29+
timeout (float | None, optional): Time out in seconds for the request. In production setup it is advisable to use a timeout or else your program can get stuck indefinitely. Defaults to None.
30+
31+
Raises:
32+
AnedyaInvalidConfig: Invalid configuration
33+
AnedyaInvalidType: Invalid datatype is specified
34+
AnedyaTxFailure: Transaction failure
35+
"""
36+
if self._config is None:
37+
raise AnedyaInvalidConfig('Configuration not provided')
38+
if self._config.connection_mode == ConnectionMode.HTTP:
39+
return _update_command_status_http(self, command=command, status=status, timeout=timeout)
40+
elif self._config.connection_mode == ConnectionMode.MQTT:
41+
return _update_command_status_mqtt(self, command=command, status=status, timeout=timeout)
42+
else:
43+
raise AnedyaInvalidConfig('Invalid connection mode')
44+
45+
46+
def _update_command_status_http(self, command: CommandDetails, status: CommandStatus, ackdata: str | tuple | None = None, acktype: str = "string", timeout: float | None = None) -> None:
47+
if self._config._testmode:
48+
url = "https://device.stageapi.anedya.io/v1/submitData"
49+
else:
50+
url = self._baseurl + "/v1/submitData"
51+
d = _UpdateCommandStatusReq("req_" + ''.join(random.choices(string.ascii_letters + string.digits, k=16)), command=command, status=status, ackdata=ackdata, acktype=acktype)
52+
r = self._httpsession.post(url, data=d.encodeJSON(), timeout=timeout)
53+
# print(r.json())
54+
try:
55+
jsonResponse = r.json()
56+
payload = json.loads(jsonResponse)
57+
if payload['success'] is not True:
58+
raise AnedyaTxFailure(payload['error'], payload['errCode'])
59+
except ValueError:
60+
raise AnedyaTxFailure(message="Invalid JSON response")
61+
return
62+
63+
64+
def _update_command_status_mqtt(self, command: CommandDetails, status: CommandStatus, ackdata: str | tuple | None = None, acktype: str = "string", timeout: float | None = None) -> None:
65+
# Create and register a transaction
66+
tr = self._transactions.create_transaction()
67+
# Encode the payload
68+
d = _UpdateCommandStatusReq(tr.get_id(), command=command, status=status, ackdata=ackdata, acktype=acktype)
69+
payload = d.encodeJSON()
70+
# Publish the message
71+
# print(payload)
72+
topic_prefix = "$anedya/device/" + str(self._config._deviceID)
73+
# print(topic_prefix + "/submitData/json")
74+
msginfo = self._mqttclient.publish(topic=topic_prefix + "/commands/updateStatus/json",
75+
payload=payload, qos=1)
76+
try:
77+
msginfo.wait_for_publish(timeout=timeout)
78+
except ValueError:
79+
raise AnedyaTxFailure(message="Publish queue full")
80+
except RuntimeError as err:
81+
raise AnedyaTxFailure(message=str(err))
82+
# Wait for transaction to complete
83+
tr.wait_to_complete()
84+
# Transaction completed
85+
# Get the data from the transaction
86+
data = tr.get_data()
87+
# Clear transaction
88+
self._transactions.clear_transaction(tr)
89+
# Check if transaction is successful or not
90+
if data['success'] is not True:
91+
raise AnedyaTxFailure(data['error'], data['errCode'])
92+
return
93+
94+
95+
class _UpdateCommandStatusReq:
96+
def __init__(self, reqId: str, command: CommandDetails, status: CommandStatus, ackdata: str | bytes | None = None, acktype: str = "string"):
97+
self.command_id = command.id
98+
self.reqID = reqId
99+
self.status = status
100+
if acktype == "string":
101+
if isinstance(ackdata, str):
102+
raise AnedyaInvalidType('ackdata is not a valid str')
103+
self.ackdata = ackdata
104+
self.acktype = "string"
105+
elif acktype == "binary":
106+
if isinstance(ackdata, bytes):
107+
raise AnedyaInvalidType('ackdata is not a valid list')
108+
self.ackdata_binary = ackdata
109+
self.ackdata = base64.b64encode(self.ackdata_binary).decode('ascii')
110+
self.acktype = "binary"
111+
else:
112+
raise AnedyaInvalidType('Invalid acktype')
113+
114+
def toJSON(self):
115+
dict = {
116+
"reqId": self.reqID,
117+
"commandId": str(self.command_id),
118+
"status": self.status,
119+
"ackdata": self.ackdata,
120+
"ackdatatype": self.acktype
121+
}
122+
return dict
123+
124+
def encodeJSON(self):
125+
data = json.dumps(self, cls=AnedyaEncoder)
126+
return data

src/anedya/client/submitLogs.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def submit_logs(self, logs: LogsCache, timeout: float | None = None):
2121
if self._config.connection_mode == ConnectionMode.HTTP:
2222
return _submit_log_http(self, logs=logs, timeout=timeout)
2323
elif self._config.connection_mode == ConnectionMode.MQTT:
24-
return _submit_data_mqtt(self, logs=logs, timeout=timeout)
24+
return _submit_log_mqtt(self, logs=logs, timeout=timeout)
2525
else:
2626
raise AnedyaInvalidConfig('Invalid connection mode')
2727

@@ -42,7 +42,7 @@ def _submit_log_http(self, logs: LogsCache, timeout: float | None = None):
4242
raise AnedyaTxFailure(message="Invalid JSON response")
4343

4444

45-
def _submit_data_mqtt(self, data: LogsCache, timeout: float | None = None):
45+
def _submit_log_mqtt(self, data: LogsCache, timeout: float | None = None):
4646
# Create and register a transaction
4747
tr = self._transactions.create_transaction()
4848
# Encode the payload

src/anedya/config.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from enum import Enum
22
import uuid
33
from .errors import AnedyaInvalidConfig
4-
from .models import CommandMessage
4+
from .models import CommandDetails
55
from typing import Callable
66

77

@@ -122,7 +122,7 @@ def set_on_disconnect(self, callback):
122122
"Callback function needs to be a valid function")
123123
self.on_disconnect = callback
124124

125-
def set_on_command(self, callback: Callable[[CommandMessage], None]):
125+
def set_on_command(self, callback: Callable[[CommandDetails], None]):
126126
"""
127127
Set a callback function that will be called when a command is received.
128128

src/anedya/errors.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,12 @@ def __init__(self, message, code):
6262
self.message = message
6363
self.code = code
6464
super().__init__(self.message)
65+
66+
67+
class AnedyaInvalidType(Exception):
68+
"""
69+
Raised when an invalid type is passed
70+
"""
71+
def __init__(self, message):
72+
self.message = message
73+
super().__init__(self.message)

src/anedya/models.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,10 @@ def encodeJSON(self):
102102
return data
103103

104104

105-
class CommandMessage:
105+
class CommandDetails:
106106
def __init__(self, commandMsg: dict):
107107
self.command = commandMsg["command"]
108-
self.id = uuid.UUID(commandMsg["id"])
108+
self.id = uuid.UUID(commandMsg["commandId"])
109109
if type == "string":
110110
self.data = commandMsg["data"]
111111
elif type == "binary":
@@ -114,8 +114,10 @@ def __init__(self, commandMsg: dict):
114114
self.data = data_bytes
115115
else:
116116
raise Exception("Invalid datatype")
117-
self.type = commandMsg["type"]
117+
self.type = commandMsg["datatype"]
118118
self.exp = datetime.datetime.fromtimestamp(commandMsg["exp"] / 1000)
119+
self.status = None
120+
self.updated = None
119121

120122

121123
class AnedyaEncoder(json.JSONEncoder):

0 commit comments

Comments
 (0)