diff --git a/setup.cfg b/setup.cfg index aaefb16..89d5060 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = anedya-dev-sdk -version = 0.1.3 +version = 0.1.4 url = https://github.com/anedyaio/anedya-dev-sdk-pyhton author = Anedya Systems author_email = support@anedya.io diff --git a/src/anedya/anedya.py b/src/anedya/anedya.py index 06ca896..9cd4793 100644 --- a/src/anedya/anedya.py +++ b/src/anedya/anedya.py @@ -46,8 +46,8 @@ def __init__(self, config: AnedyaConfig): self.on_connect = config.on_connect self.on_disconnect = config.on_disconnect self.on_message = config.on_message - self._mqttclient.on_connect = self.onconnect_handler - self._mqttclient.on_disconnect = self.ondisconnect_handler + self._mqttclient.on_connect = self._onconnect_handler + self._mqttclient.on_disconnect = self._ondisconnect_handler # self._mqttclient.on_message = self.onmessage_handler self._mqttclient._connect_timeout = 1.0 self._transactions = Transactions() @@ -103,6 +103,7 @@ def disconnect(self): from .client.bindDevice import bind_device from .client.submitData import submit_data + from .client.submitLogs import submit_logs from .client.timeSync import get_time - from .client.mqttHandlers import onconnect_handler, ondisconnect_handler + from .client.mqttHandlers import _onconnect_handler, _ondisconnect_handler from .client.callbacks import _error_callback, _response_callback diff --git a/src/anedya/client/bindDevice.py b/src/anedya/client/bindDevice.py index cf72ddd..d137036 100644 --- a/src/anedya/client/bindDevice.py +++ b/src/anedya/client/bindDevice.py @@ -3,13 +3,21 @@ from ..errors import AnedyaInvalidConfig, AnedyaTxFailure -def bind_device(self, binding_secret: str, timeout: float | None = None): +def bind_device(self, binding_secret: str, timeout: float | None = None) -> bool: """ - :param binding_secret: Binding secret to be used for binding the device - :raises AnedyaInvalidConfig: If the configuration is not provided - :raises AnedyaTxFailure: If the transaction fails + Call this function to bind a device with the Anedya platform - This function provides a way to bind a device to the Anedya platform. + Args: + binding_secret (str): A one time Binding secret obtained from the platform. This secret is usually passed to device during provisioning process through + mobile app or any other process. + 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. + + Raises: + AnedyaInvalidConfig: Method can raise this method if either configuration is not provided or if the connection mode is invalid. + AnedyaTxFailure: Method can raise this method if the transaction fails. + + Returns: + bool: Returns true if the device is successfully bound with the platform. """ if self._config is None: raise AnedyaInvalidConfig('Configuration not provided') @@ -21,7 +29,7 @@ def bind_device(self, binding_secret: str, timeout: float | None = None): raise AnedyaInvalidConfig('Invalid connection mode') -def _bind_device_http(self, binding_secret: str, timeout: float | None = None): +def _bind_device_http(self, binding_secret: str, timeout: float | None = None) -> bool: if self._config._testmode: url = "https://device.stageapi.anedya.io/v1/submitData" else: @@ -36,7 +44,7 @@ def _bind_device_http(self, binding_secret: str, timeout: float | None = None): raise AnedyaTxFailure(payload['error'], payload['errCode']) except ValueError: raise AnedyaTxFailure(message="Invalid JSON response") - return + return True def _bind_device_mqtt(self, binding_secret: str, timeout: float | None = None): diff --git a/src/anedya/client/mqttHandlers.py b/src/anedya/client/mqttHandlers.py index b8f1788..6fc23fd 100644 --- a/src/anedya/client/mqttHandlers.py +++ b/src/anedya/client/mqttHandlers.py @@ -2,7 +2,7 @@ from ..errors import AnedyaException -def onconnect_handler(self, client, userdata, flags, reason_code, properties): +def _onconnect_handler(self, client, userdata, flags, reason_code, properties): rc = reason_code.value if rc == 135: raise AnedyaInvalidCredentials("Invalid credentials") @@ -36,12 +36,12 @@ def onconnect_handler(self, client, userdata, flags, reason_code, properties): return -def ondisconnect_handler(self, - client, - userdata, - disconnect_flags, - reason_code, - properties): +def _ondisconnect_handler(self, + client, + userdata, + disconnect_flags, + reason_code, + properties): # First call the disconnect handler callback if self.on_disconnect is not None: self.on_disconnect(client, diff --git a/src/anedya/client/submitData.py b/src/anedya/client/submitData.py index dffe7da..8cb921e 100644 --- a/src/anedya/client/submitData.py +++ b/src/anedya/client/submitData.py @@ -6,12 +6,15 @@ def submit_data(self, data: DataPoints, timeout: float | None = None): """ - :param data: Data to send as a :class: DataPoints object - :param timeout: Timeout in seconds, default is None + Submit data to Anedya Platform. - :raises AnedyaTxFailure: If data could not be submitted due to an error + Args: + data (DataPoints): Datapoints object, you can submit multiple types of variable in single request. + 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. - 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). + Raises: + AnedyaInvalidConfig: Method can raise this method if either configuration is not provided or if the connection mode is invalid. + AnedyaTxFailure: Method can raise this method if the transaction fails. """ if self._config is None: diff --git a/src/anedya/client/submitLogs.py b/src/anedya/client/submitLogs.py index 9a2af92..dc448b4 100644 --- a/src/anedya/client/submitLogs.py +++ b/src/anedya/client/submitLogs.py @@ -6,8 +6,15 @@ def submit_logs(self, logs: LogsCache, timeout: float | None = None): """ - :param logs: Logs to be send. :class: LogCache format. - :param timeout: Timeout in seconds, default is None. + Submit logs to Anedya + + Args: + logs (LogsCache): Logs + 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. + + Raises: + AnedyaInvalidConfig: Method can raise this method if either configuration is not provided or if the connection mode is invalid. + AnedyaTxFailure: Method can raise this method if the transaction fails. """ if self._config is None: raise AnedyaInvalidConfig('Configuration not provided') diff --git a/src/anedya/client/timeSync.py b/src/anedya/client/timeSync.py index d8a206a..dcfbab8 100644 --- a/src/anedya/client/timeSync.py +++ b/src/anedya/client/timeSync.py @@ -4,12 +4,19 @@ import time -def get_time(self, timeout: float | None = None): +def get_time(self, timeout: float | None = None) -> int: """ - Get current time from Anedya Time Service using HTTP request - - Gets current time using HTTP requests. - Accuracy is generally within few tens of millisecond. For greater accuracy - consider using NTP time service from Anedya + Fetch the time information from Anedya. + + Args: + 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. + + Raises: + AnedyaInvalidConfig: Method can raise this method if either configuration is not provided or if the connection mode is invalid. + AnedyaTxFailure: Method can raise this method if the transaction fails. + + Returns: + int: The method returns the current time in Unix millisecond epoch in UTC timezone """ if self._config is None: raise AnedyaInvalidConfig('Configuration not provided') @@ -36,9 +43,9 @@ def _time_sync_http(self, timeout: float | None = None): if payload['success'] is not True: raise AnedyaTxFailure(payload['error'], payload['errCode']) deviceRecTime = int(time.time_ns() / 1000000) - ServerReceiveTime = jsonResponse["serverReceiveTime"] - ServerSendTime = jsonResponse["serverSendTime"] - currentTime = (ServerReceiveTime + ServerSendTime + deviceRecTime - deviceSendTime) / 2 + ServerReceiveTime = int(jsonResponse["serverReceiveTime"]) + ServerSendTime = int(jsonResponse["serverSendTime"]) + currentTime = int((ServerReceiveTime + ServerSendTime + deviceRecTime - deviceSendTime) / 2) except ValueError: raise AnedyaTxFailure(message="Invalid JSON response") return currentTime diff --git a/src/anedya/config.py b/src/anedya/config.py index 1434fda..b43ae2b 100644 --- a/src/anedya/config.py +++ b/src/anedya/config.py @@ -1,6 +1,7 @@ from enum import Enum import uuid from .errors import AnedyaInvalidConfig +from typing import Callable class ConnectionMode(Enum): @@ -18,6 +19,10 @@ class Encoding(Enum): CBOR = "CBOR" +class RegionCode(Enum): + AP_IN_1 = "ap-in-1" + + class AnedyaConfig: def __init__(self): """ @@ -52,15 +57,24 @@ def __init__(self): self._security_set = False self._testmode = False - def set_connection_key(self, key): + def set_connection_key(self, key: str): """ - Set a connection key + This method sets the connection key for the client + + Args: + key (str): Connection key for the client """ self.connection_key = key def set_deviceid(self, id: str): """ - Set DeviceID + This method sets the device ID for the client + + Args: + id (str): A Unique physical device ID which should not change for the entire lifetime of the device. Requires a valid UUID + + Raises: + AnedyaInvalidConfig: The provided device ID should be a valid UUID, otherwise this exception will be raised """ try: self._deviceID = uuid.UUID(id) @@ -68,41 +82,33 @@ def set_deviceid(self, id: str): raise AnedyaInvalidConfig("Device ID needs to be valid UUID") self._deviceid_set = True - def set_timeout(self, timeout): - """ - Set timeout for automatic flush of the data - """ - self.timeout = timeout - - def set_maxbuffer_size(self, buffersize): + def set_region(self, region: RegionCode): """ - Set maximum buffer size - """ - self.max_buffer_size = buffersize + This method sets the region for the client - def set_region(self, region): - """ - Set region + Args: + region (RegionCode): Set the regioncode where your Anedya project is created. """ self.region = region - def set_on_connect(self, callback): + def set_on_connect(self, callback: Callable[[],]): """ - Set on connect callback + You can use this function to set a callback function that will be called when the connection is established. + + Args: + callback (Callable[[],]): A function which will be called when the connection is established. Please note that callback functions should not be blocking. """ self.on_connect = callback - def set_on_disconnect(self, callback): + def set_on_disconnect(self, callback: Callable[[],]): """ - Set on disconnect callback - """ - self.on_disconnect = callback + You can use this function to set a callback function that will be called when the connection is disconnected. This callback will be called for both intentional + and unintentional disconnections. - def set_on_message(self, callback): - """ - Set on message callback + Args: + callback (Callable[[],]): A function which will be called when the connection is disconnected. Please note that callback functions should not be blocking. """ - self.on_message = callback + self.on_disconnect = callback def default_config(): diff --git a/src/anedya/models.py b/src/anedya/models.py index cd5c06e..a350c76 100644 --- a/src/anedya/models.py +++ b/src/anedya/models.py @@ -7,6 +7,14 @@ class FloatData: def __init__(self, variable: str, value: float, timestamp_milli: int = int(time.time_ns() / 1000000)): + """ + Create a Float datapoint object which can be sent to Anedya Server + + Args: + variable (str): Name of the variable + value (float): Value of the variable + timestamp_milli (int, optional): Timestamp in millisecond unix epoch. Defaults to current time. Pass 0 to take Anedya Server Time + """ self.variable = variable self.timestamp = timestamp_milli self.value = value @@ -22,6 +30,13 @@ def toJSON(self): class Log: def __init__(self, log: str, timestamp_milli: int = int(time.time_ns() / 1000000)): + """ + Create a Log object which can be sent to Anedya Server + + Args: + log (str): Log string that you want to send to Anedya + timestamp_milli (int, optional): Timestamp in millisecond unix epoch. Defaults to current time. Pass 0 to take Anedya Server Time + """ self.log = log self.timestamp = timestamp_milli @@ -38,6 +53,12 @@ def __init__(self): self.data = [] def append(self, datapoint: FloatData): + """ + Append a datapoint to the datapoints batch which will be sent to Anedya Server in a single request + + Args: + datapoint (FloatData): Datapoint object + """ self.data.append(datapoint) def toJSON(self): @@ -59,6 +80,12 @@ def __init__(self): self.logs = [] def append(self, log: Log): + """ + Append a log to the log batch which will be sent to Anedya Server in a single request + + Args: + log (Log): Log object + """ self.logs.append(log) def toJSON(self):