Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
@@ -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
Expand Down
7 changes: 4 additions & 3 deletions src/anedya/anedya.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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
22 changes: 15 additions & 7 deletions src/anedya/client/bindDevice.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand All @@ -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:
Expand All @@ -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):
Expand Down
14 changes: 7 additions & 7 deletions src/anedya/client/mqttHandlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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,
Expand Down
11 changes: 7 additions & 4 deletions src/anedya/client/submitData.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
11 changes: 9 additions & 2 deletions src/anedya/client/submitLogs.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
23 changes: 15 additions & 8 deletions src/anedya/client/timeSync.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand All @@ -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
Expand Down
58 changes: 32 additions & 26 deletions src/anedya/config.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from enum import Enum
import uuid
from .errors import AnedyaInvalidConfig
from typing import Callable


class ConnectionMode(Enum):
Expand All @@ -18,6 +19,10 @@ class Encoding(Enum):
CBOR = "CBOR"


class RegionCode(Enum):
AP_IN_1 = "ap-in-1"


class AnedyaConfig:
def __init__(self):
"""
Expand Down Expand Up @@ -52,57 +57,58 @@ 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)
except ValueError:
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():
Expand Down
27 changes: 27 additions & 0 deletions src/anedya/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand All @@ -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):
Expand All @@ -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):
Expand Down