refactor to iotc
This commit is contained in:
Родитель
e5ee8f0f67
Коммит
505ea11f97
|
@ -11,6 +11,6 @@ exp.py
|
|||
.vscode/
|
||||
|
||||
# publish
|
||||
azure_iotcentral_device_client.egg-info
|
||||
*.egg-info
|
||||
build
|
||||
dist
|
71
README.md
71
README.md
|
@ -23,13 +23,20 @@ These clients are available with an asynchronous API, as well as a blocking sync
|
|||
## Samples
|
||||
Check out the [sample repository](samples) for example code showing how the SDK can be used in the various scenarios:
|
||||
|
||||
* Sending telemetry and receiving properties and commands with device connected through symmetric key (Python 2.7+)
|
||||
* [py3](samples/py3.py) - Sending telemetry and receiving properties and commands with device connected through **symmetric key** (Python 3.7+)
|
||||
* [py3_x509](samples/py3_x509.py) - Sending telemetry and receiving properties and commands with device connected through **x509 certificates** (Python 3.7+)
|
||||
* [py3_file_logger](samples/py3_file_logger.py) - Print logs on file with rotation (Python 3.7+)
|
||||
* [py3_eventhub_logger](samples/py3_eventhub_logger.py) - Redirect logs to Azure Event Hub (Python 3.7+)
|
||||
|
||||
* Sending telemetry and receiving properties and commands with device connected through symmetric key (Python 3.7+)
|
||||
* Sending telemetry and receiving properties and commands with device connected through x509 certificates (Python 2.7+)
|
||||
* Sending telemetry and receiving properties and commands with device connected through x509 certificates (Python 3.7+)
|
||||
|
||||
Samples by default parse a configuration file including required credentials. Just create a file called **samples.ini** inside the _samples_ folder with this content:
|
||||
**The following samples are legacy samples**, they use the sycnhronous API intended for use with Python 2.7, or in compatibility scenarios with later versions. We recommend you use the asynchronous API and Python3 samples above instead.
|
||||
|
||||
|
||||
* [py2](samples/py2.py) - Sending telemetry and receiving properties and commands with device connected through **symmetric key** (Python 2.7+)
|
||||
* [py2_x509](samples/py2_x509.py) - Sending telemetry and receiving properties and commands with device connected through **x509 certificates** (Python 2.7+)
|
||||
|
||||
|
||||
Samples by default parse a configuration file including required credentials. Just create a file called **samples.ini** inside the _samples_ folder with a content like this:
|
||||
|
||||
```ini
|
||||
[SymmetricKey]
|
||||
|
@ -46,6 +53,14 @@ CertPassphrase = optional password
|
|||
```
|
||||
The configuration file can include one of the sections or both. Section names must match references in the sample file.
|
||||
|
||||
### Run samples with local changes
|
||||
It is possible to run samples against the local copy of the device client. This is particularly useful when testing patches not yet published upstream.
|
||||
Just add an entry to **samples.ini** in the _DEFAULT_ section:
|
||||
```ini
|
||||
[DEFAULT]
|
||||
Local = yes
|
||||
```
|
||||
|
||||
## Importing the module
|
||||
Sync client (Python 2.7+ and 3.7+) can be imported in this way:
|
||||
|
||||
|
@ -98,12 +113,33 @@ await iotc.connect()
|
|||
```
|
||||
After successfull connection, IOTC context is available for further commands.
|
||||
|
||||
### Reconnection
|
||||
The device client automatically handle reconnection in case of network failures or disconnections. However if process runs for long time (e.g. unmonitored devices) a reconnection might fail because of credentials expiration.
|
||||
|
||||
To control reconnection and reset credentials the function _is_connected()_ is available and can be used to test connection status inside a loop or before running operations.
|
||||
|
||||
e.g.
|
||||
```py
|
||||
retry = 0 # stop reconnection attempts
|
||||
while true:
|
||||
if iotc.is_connected():
|
||||
# do something
|
||||
else
|
||||
if retry == 10:
|
||||
sys.exit("error")
|
||||
retry += 1
|
||||
iotc.connect()
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Operations
|
||||
|
||||
### Send telemetry
|
||||
|
||||
e.g. Send telemetry every 3 seconds
|
||||
```py
|
||||
while iotc.isConnected():
|
||||
while iotc.is_connected():
|
||||
await iotc.send_telemetry({
|
||||
'accelerometerX': str(randint(20, 45)),
|
||||
'accelerometerY': str(randint(20, 45)),
|
||||
|
@ -116,11 +152,11 @@ Properties can be custom or part of the reserved ones (see list [here](https://g
|
|||
|
||||
### Send property update
|
||||
```py
|
||||
iotc.sendProperty({'fieldName':'fieldValue'});
|
||||
iotc.sendProperty({'fieldName':'fieldValue'})
|
||||
```
|
||||
### Listen to properties update
|
||||
```py
|
||||
iotc.on(IOTCEvents.IOTC_PROPERTIES, callback);
|
||||
iotc.on(IOTCEvents.IOTC_PROPERTIES, callback)
|
||||
```
|
||||
To provide setting sync aknowledgement, the callback must reply **True** if the new value has been applied or **False** otherwise
|
||||
```py
|
||||
|
@ -128,7 +164,7 @@ async def onProps(propName, propValue):
|
|||
print(propValue)
|
||||
return True
|
||||
|
||||
iotc.on(IOTCEvents.IOTC_PROPERTIES, onProps);
|
||||
iotc.on(IOTCEvents.IOTC_PROPERTIES, onProps)
|
||||
```
|
||||
|
||||
### Listen to commands
|
||||
|
@ -144,6 +180,23 @@ async def onCommands(command, ack):
|
|||
await ack(command.name, 'Command received', command.request_id)
|
||||
```
|
||||
|
||||
## Logging
|
||||
|
||||
The default log prints to console operations status and errors.
|
||||
This is the _IOTC_LOGGING_API_ONLY_ logging level.
|
||||
The function set_log_level() can be used to change options or disable logs. It accepts a _IOTCLogLevel_ value among the following:
|
||||
|
||||
- IOTC_LOGGING_DISABLED (log disabled)
|
||||
- IOTC_LOGGING_API_ONLY (information and errors, default)
|
||||
- IOTC_LOGGING_ALL (all messages, debug and underlying errors)
|
||||
|
||||
The device client also accepts an optional Logger instance to redirect logs to other targets than console.
|
||||
The custom class must implement three methods:
|
||||
|
||||
- info(message)
|
||||
- debug(message)
|
||||
- set_log_level(message);
|
||||
|
||||
## One-touch device provisioning and approval
|
||||
A device can send custom data during provision process: if a device is aware of its IoT Central template Id, then it can be automatically provisioned.
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
shopt -s expand_aliases
|
||||
source ~/.bashrc
|
||||
|
||||
rm -rf build/ dist/ src/azure/iotcentral/device/client/iotc_device.egg-info src/azure/iotcentral/device/client/_pycache_ src/azure/iotcentral/device/client/_init_.pyc
|
||||
rm -rf build/ dist/ src/iotc/iotc_device.egg-info src/iotc/_pycache_ src/iotc/_init_.pyc
|
||||
|
||||
TEST=""
|
||||
if [[ $1 == 'test' ]]; then
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import sys
|
||||
import os
|
||||
import configparser
|
||||
import time
|
||||
from random import randint
|
||||
from azure.iotcentral.device.client import IoTCClient, IOTCConnectType, IOTCLogLevel, IOTCEvents
|
||||
|
||||
|
||||
|
||||
config = configparser.ConfigParser()
|
||||
|
@ -13,6 +12,12 @@ device_id = config['SymmetricKey']['DeviceId']
|
|||
scope_id = config['SymmetricKey']['ScopeId']
|
||||
key = config['SymmetricKey']['Key']
|
||||
|
||||
|
||||
if config['DEFAULT'].getboolean('Local'):
|
||||
sys.path.insert(0, 'src')
|
||||
|
||||
from iotc import IoTCClient, IOTCConnectType, IOTCLogLevel, IOTCEvents
|
||||
|
||||
# optional model Id for auto-provisioning
|
||||
try:
|
||||
model_id = config['SymmetricKey']['ModelId']
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import sys
|
||||
import os
|
||||
import configparser
|
||||
import time
|
||||
from random import randint
|
||||
from azure.iotcentral.device.client import IoTCClient, IOTCConnectType, IOTCLogLevel, IOTCEvents
|
||||
|
||||
|
||||
config = configparser.ConfigParser()
|
||||
config.read(os.path.join(os.path.dirname(__file__),'samples.ini'))
|
||||
|
@ -11,6 +12,11 @@ device_id = config['x509']['DeviceId']
|
|||
scope_id = config['x509']['ScopeId']
|
||||
key = {'certFile': config['x509']['CertFilePath'],'keyFile':config['x509']['KeyFilePath'],'certPhrase':config['x509']['CertPassphrase']}
|
||||
|
||||
if config['DEFAULT'].getboolean('Local'):
|
||||
sys.path.insert(0, 'src')
|
||||
|
||||
from iotc import IoTCClient, IOTCConnectType, IOTCLogLevel, IOTCEvents
|
||||
|
||||
# optional model Id for auto-provisioning
|
||||
try:
|
||||
model_id = config['x509']['ModelId']
|
||||
|
|
|
@ -1,12 +1,19 @@
|
|||
import os
|
||||
import asyncio
|
||||
import configparser
|
||||
from azure.iotcentral.device.client.aio import IoTCClient, IOTCConnectType, IOTCLogLevel, IOTCEvents
|
||||
import sys
|
||||
|
||||
from random import randint
|
||||
|
||||
config = configparser.ConfigParser()
|
||||
config.read(os.path.join(os.path.dirname(__file__),'samples.ini'))
|
||||
|
||||
if config['DEFAULT'].getboolean('Local'):
|
||||
sys.path.insert(0, 'src')
|
||||
|
||||
from iotc import IOTCConnectType, IOTCLogLevel, IOTCEvents
|
||||
from iotc.aio import IoTCClient
|
||||
|
||||
device_id = config['SymmetricKey']['DeviceId']
|
||||
scope_id = config['SymmetricKey']['ScopeId']
|
||||
key = config['SymmetricKey']['Key']
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
import sys
|
||||
import os
|
||||
import asyncio
|
||||
import configparser
|
||||
from random import randint
|
||||
from azure.eventhub.aio import EventHubProducerClient
|
||||
from azure.eventhub import EventData
|
||||
|
||||
config = configparser.ConfigParser()
|
||||
config.read(os.path.join(os.path.dirname(__file__), 'samples.ini'))
|
||||
|
||||
if config['DEFAULT'].getboolean('Local'):
|
||||
sys.path.insert(0, 'src')
|
||||
|
||||
from iotc import IOTCConnectType, IOTCLogLevel, IOTCEvents
|
||||
from iotc.aio import IoTCClient
|
||||
|
||||
device_id = config['SymmetricKey']['DeviceId']
|
||||
scope_id = config['SymmetricKey']['ScopeId']
|
||||
key = config['SymmetricKey']['Key']
|
||||
|
||||
event_hub_conn_str = config['EventHub']['ConnectionString']
|
||||
event_hub_name = config['EventHub']['EventHubName']
|
||||
|
||||
# optional model Id for auto-provisioning
|
||||
try:
|
||||
model_id = config['SymmetricKey']['ModelId']
|
||||
except:
|
||||
model_id = None
|
||||
|
||||
|
||||
class EventHubLogger:
|
||||
def __init__(self, conn_str, eventhub_name):
|
||||
self._producer = EventHubProducerClient.from_connection_string(conn_str, eventhub_name=eventhub_name)
|
||||
|
||||
async def _create_batch(self):
|
||||
self._event_data_batch = await self._producer.create_batch()
|
||||
|
||||
async def _log(self, message):
|
||||
event_data_batch = await self._producer.create_batch()
|
||||
event_data_batch.add(EventData(message))
|
||||
await self._producer.send_batch(event_data_batch)
|
||||
|
||||
async def info(self, message):
|
||||
if self._log_level != IOTCLogLevel.IOTC_LOGGING_DISABLED:
|
||||
await self._log(message)
|
||||
|
||||
async def debug(self, message):
|
||||
if self._log_level == IOTCLogLevel.IOTC_LOGGING_ALL:
|
||||
await self._log(message)
|
||||
|
||||
def set_log_level(self, log_level):
|
||||
self._log_level = log_level
|
||||
|
||||
|
||||
async def on_props(propName, propValue):
|
||||
print(propValue)
|
||||
return True
|
||||
|
||||
|
||||
async def on_commands(command, ack):
|
||||
print(command.name)
|
||||
await ack(command.name, 'Command received', command.request_id)
|
||||
|
||||
|
||||
# change connect type to reflect the used key (device or group)
|
||||
iotc = IoTCClient(device_id, scope_id,
|
||||
IOTCConnectType.IOTC_CONNECT_DEVICE_KEY, key, EventHubLogger(event_hub_conn_str, event_hub_name))
|
||||
if model_id != None:
|
||||
iotc.set_model_id(model_id)
|
||||
|
||||
iotc.set_log_level(IOTCLogLevel.IOTC_LOGGING_ALL)
|
||||
iotc.on(IOTCEvents.IOTC_PROPERTIES, on_props)
|
||||
iotc.on(IOTCEvents.IOTC_COMMAND, on_commands)
|
||||
|
||||
# iotc.setQosLevel(IOTQosLevel.IOTC_QOS_AT_MOST_ONCE)
|
||||
|
||||
|
||||
async def main():
|
||||
await iotc.connect()
|
||||
while iotc.is_connected():
|
||||
await iotc.send_telemetry({
|
||||
'accelerometerX': str(randint(20, 45)),
|
||||
'accelerometerY': str(randint(20, 45)),
|
||||
"accelerometerZ": str(randint(20, 45))
|
||||
})
|
||||
await asyncio.sleep(3)
|
||||
|
||||
|
||||
asyncio.run(main())
|
|
@ -0,0 +1,89 @@
|
|||
import sys
|
||||
import os
|
||||
import asyncio
|
||||
import configparser
|
||||
import logging
|
||||
import logging.handlers
|
||||
from random import randint
|
||||
|
||||
config = configparser.ConfigParser()
|
||||
config.read(os.path.join(os.path.dirname(__file__), 'samples.ini'))
|
||||
|
||||
if config['DEFAULT'].getboolean('Local'):
|
||||
sys.path.insert(0, 'src')
|
||||
|
||||
from iotc import IOTCConnectType, IOTCLogLevel, IOTCEvents
|
||||
from iotc.aio import IoTCClient
|
||||
|
||||
device_id = config['SymmetricKey']['DeviceId']
|
||||
scope_id = config['SymmetricKey']['ScopeId']
|
||||
key = config['SymmetricKey']['Key']
|
||||
|
||||
logpath = config['FileLog']['LogsPath']
|
||||
|
||||
# optional model Id for auto-provisioning
|
||||
try:
|
||||
model_id = config['SymmetricKey']['ModelId']
|
||||
except:
|
||||
model_id = None
|
||||
|
||||
|
||||
class FileLogger:
|
||||
def __init__(self,logpath,logname="iotc_py_log"):
|
||||
self._logger=logging.getLogger(logname)
|
||||
self._logger.setLevel(logging.DEBUG)
|
||||
handler= logging.handlers.RotatingFileHandler(
|
||||
os.path.join(logpath,logname), maxBytes=20, backupCount=5)
|
||||
self._logger.addHandler(handler)
|
||||
|
||||
async def _log(self, message):
|
||||
print(message)
|
||||
self._logger.debug(message)
|
||||
|
||||
async def info(self, message):
|
||||
if self._log_level != IOTCLogLevel.IOTC_LOGGING_DISABLED:
|
||||
await self._log(message)
|
||||
|
||||
async def debug(self, message):
|
||||
if self._log_level == IOTCLogLevel.IOTC_LOGGING_ALL:
|
||||
await self._log(message)
|
||||
|
||||
def set_log_level(self, log_level):
|
||||
self._log_level = log_level
|
||||
|
||||
|
||||
async def on_props(propName, propValue):
|
||||
print(propValue)
|
||||
return True
|
||||
|
||||
|
||||
async def on_commands(command, ack):
|
||||
print(command.name)
|
||||
await ack(command.name, 'Command received', command.request_id)
|
||||
|
||||
|
||||
# change connect type to reflect the used key (device or group)
|
||||
iotc = IoTCClient(device_id, scope_id,
|
||||
IOTCConnectType.IOTC_CONNECT_DEVICE_KEY, key, FileLogger(logpath))
|
||||
if model_id != None:
|
||||
iotc.set_model_id(model_id)
|
||||
|
||||
iotc.set_log_level(IOTCLogLevel.IOTC_LOGGING_ALL)
|
||||
iotc.on(IOTCEvents.IOTC_PROPERTIES, on_props)
|
||||
iotc.on(IOTCEvents.IOTC_COMMAND, on_commands)
|
||||
|
||||
# iotc.setQosLevel(IOTQosLevel.IOTC_QOS_AT_MOST_ONCE)
|
||||
|
||||
|
||||
async def main():
|
||||
await iotc.connect()
|
||||
while iotc.is_connected():
|
||||
await iotc.send_telemetry({
|
||||
'accelerometerX': str(randint(20, 45)),
|
||||
'accelerometerY': str(randint(20, 45)),
|
||||
"accelerometerZ": str(randint(20, 45))
|
||||
})
|
||||
await asyncio.sleep(3)
|
||||
|
||||
|
||||
asyncio.run(main())
|
|
@ -1,7 +1,7 @@
|
|||
import os
|
||||
import asyncio
|
||||
import configparser
|
||||
from azure.iotcentral.device.client.aio import IoTCClient, IOTCConnectType, IOTCLogLevel, IOTCEvents
|
||||
import sys
|
||||
from random import randint
|
||||
|
||||
config = configparser.ConfigParser()
|
||||
|
@ -11,6 +11,13 @@ device_id = config['x509']['DeviceId']
|
|||
scope_id = config['x509']['ScopeId']
|
||||
key = {'certFile': config['x509']['CertFilePath'],'keyFile':config['x509']['KeyFilePath'],'certPhrase':config['x509']['CertPassphrase']}
|
||||
|
||||
|
||||
if config['DEFAULT'].getboolean('Local'):
|
||||
sys.path.insert(0, 'src')
|
||||
|
||||
from iotc import IOTCConnectType, IOTCLogLevel, IOTCEvents
|
||||
from iotc.aio import IoTCClient
|
||||
|
||||
# optional model Id for auto-provisioning
|
||||
try:
|
||||
model_id = config['x509']['ModelId']
|
||||
|
|
4
setup.py
4
setup.py
|
@ -2,7 +2,7 @@ import setuptools
|
|||
import sys
|
||||
|
||||
sys.path.insert(0, 'src')
|
||||
from azure.iotcentral.device.client import __version__, __name__
|
||||
from iotc import __version__, __name__
|
||||
|
||||
with open("README.md", "r") as fh:
|
||||
long_description = fh.read()
|
||||
|
@ -30,5 +30,5 @@ setuptools.setup(
|
|||
'Programming Language :: Python :: 3.8',
|
||||
],
|
||||
include_package_data=True,
|
||||
install_requires=["azure-iot-device"]
|
||||
install_requires=["azure-iot-device","uuid","hmac","hashlib","base64","json"]
|
||||
)
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
__path__ = __import__("pkgutil").extend_path(__path__, __name__)
|
|
@ -1 +0,0 @@
|
|||
__path__ = __import__("pkgutil").extend_path(__path__, __name__)
|
|
@ -1 +0,0 @@
|
|||
__path__ = __import__("pkgutil").extend_path(__path__, __name__)
|
|
@ -1,21 +0,0 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ
|
||||
RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD
|
||||
VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX
|
||||
DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y
|
||||
ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy
|
||||
VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr
|
||||
mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr
|
||||
IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK
|
||||
mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu
|
||||
XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy
|
||||
dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye
|
||||
jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1
|
||||
BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3
|
||||
DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92
|
||||
9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx
|
||||
jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0
|
||||
Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz
|
||||
ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS
|
||||
R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp
|
||||
-----END CERTIFICATE-----
|
|
@ -1,5 +1,3 @@
|
|||
__path__ = __import__("pkgutil").extend_path(__path__, __name__)
|
||||
|
||||
|
||||
import sys
|
||||
import threading
|
||||
|
@ -10,55 +8,51 @@ from azure.iot.device import ProvisioningDeviceClient
|
|||
from azure.iot.device import Message, MethodResponse
|
||||
from datetime import datetime
|
||||
|
||||
__version__ = "0.2.0-beta.12"
|
||||
__name__ = "azure-iotcentral-device-client"
|
||||
try:
|
||||
import hmac
|
||||
except ImportError:
|
||||
print("ERROR: missing dependency `hmac`")
|
||||
sys.exit()
|
||||
|
||||
try:
|
||||
import hashlib
|
||||
except ImportError:
|
||||
print("ERROR: missing dependency `hashlib`")
|
||||
sys.exit()
|
||||
|
||||
try:
|
||||
import base64
|
||||
except ImportError:
|
||||
print("ERROR: missing dependency `base64`")
|
||||
sys.exit()
|
||||
|
||||
try:
|
||||
import json
|
||||
except ImportError:
|
||||
print("ERROR: missing dependency `json`")
|
||||
sys.exit()
|
||||
|
||||
try:
|
||||
import uuid
|
||||
except ImportError:
|
||||
print("ERROR: missing dependency `uuid`")
|
||||
sys.exit()
|
||||
|
||||
|
||||
__version__ = "0.2.1-beta.2"
|
||||
__name__ = "iotc"
|
||||
|
||||
|
||||
def version():
|
||||
print(__version__)
|
||||
|
||||
|
||||
try:
|
||||
import hmac
|
||||
except ImportError:
|
||||
print("ERROR: missing dependency `micropython-hmac`")
|
||||
sys.exit()
|
||||
|
||||
try:
|
||||
import hashlib
|
||||
except ImportError:
|
||||
print("ERROR: missing dependency `micropython-hashlib`")
|
||||
sys.exit()
|
||||
|
||||
try:
|
||||
import base64
|
||||
except ImportError:
|
||||
print("ERROR: missing dependency `micropython-base64`")
|
||||
sys.exit()
|
||||
|
||||
try:
|
||||
import json
|
||||
except ImportError:
|
||||
print("ERROR: missing dependency `micropython-json`")
|
||||
sys.exit()
|
||||
|
||||
try:
|
||||
import uuid
|
||||
except ImportError:
|
||||
print("ERROR: missing dependency `micropython-uuid`")
|
||||
sys.exit()
|
||||
|
||||
g_is_micro_python = ('implementation' in dir(sys)) and ('name' in dir(
|
||||
sys.implementation)) and (sys.implementation.name == 'micropython')
|
||||
|
||||
|
||||
class IOTCConnectType:
|
||||
IOTC_CONNECT_SYMM_KEY = 1
|
||||
IOTC_CONNECT_X509_CERT = 2
|
||||
IOTC_CONNECT_DEVICE_KEY = 3
|
||||
|
||||
|
||||
|
||||
class IOTCLogLevel:
|
||||
IOTC_LOGGING_DISABLED = 1
|
||||
IOTC_LOGGING_API_ONLY = 2
|
||||
|
@ -112,7 +106,6 @@ class IoTCClient:
|
|||
self._cred_type = cred_type
|
||||
self._key_or_cert = key_or_cert
|
||||
self._model_id = None
|
||||
self._connected = False
|
||||
self._events = {}
|
||||
self._prop_thread = None
|
||||
self._cmd_thread = None
|
||||
|
@ -120,7 +113,12 @@ class IoTCClient:
|
|||
if logger is None:
|
||||
self._logger = ConsoleLogger(IOTCLogLevel.IOTC_LOGGING_API_ONLY)
|
||||
else:
|
||||
self._logger = logger
|
||||
if hasattr(logger, "info") & hasattr(logger, "debug") & hasattr(logger, "set_log_level"):
|
||||
self._logger = logger
|
||||
else:
|
||||
print("ERROR: Logger object has unsupported format. It must implement the following functions\n\
|
||||
info(message);\ndebug(message);\nset_log_level(message);")
|
||||
sys.exit()
|
||||
|
||||
def is_connected(self):
|
||||
"""
|
||||
|
@ -128,10 +126,10 @@ class IoTCClient:
|
|||
:returns: Connection state
|
||||
:rtype: bool
|
||||
"""
|
||||
if self._connected:
|
||||
return True
|
||||
if not self._device_client:
|
||||
print("ERROR: A connection was never attempted. You need to first call connect() before querying the connection state")
|
||||
else:
|
||||
return False
|
||||
return self._device_client.connected
|
||||
|
||||
def set_global_endpoint(self, endpoint):
|
||||
"""
|
||||
|
@ -300,7 +298,6 @@ class IoTCClient:
|
|||
# Connect to iothub
|
||||
try:
|
||||
self._device_client.connect()
|
||||
self._connected = True
|
||||
self._logger.debug('Device connected')
|
||||
except:
|
||||
t, v, tb = sys.exc_info()
|
||||
|
@ -319,7 +316,6 @@ class IoTCClient:
|
|||
|
||||
def _compute_derived_symmetric_key(self, secret, reg_id):
|
||||
# pylint: disable=no-member
|
||||
global g_is_micro_python
|
||||
try:
|
||||
secret = base64.b64decode(secret)
|
||||
except:
|
||||
|
@ -327,7 +323,4 @@ class IoTCClient:
|
|||
"ERROR: broken base64 secret => `" + secret + "`")
|
||||
sys.exit()
|
||||
|
||||
if g_is_micro_python == False:
|
||||
return base64.b64encode(hmac.new(secret, msg=reg_id.encode('utf8'), digestmod=hashlib.sha256).digest()).decode('utf-8')
|
||||
else:
|
||||
return base64.b64encode(hmac.new(secret, msg=reg_id.encode('utf8'), digestmod=hashlib._sha256.sha256).digest())
|
||||
return base64.b64encode(hmac.new(secret, msg=reg_id.encode('utf8'), digestmod=hashlib.sha256).digest()).decode('utf-8')
|
|
@ -1,99 +1,55 @@
|
|||
import sys
|
||||
import asyncio
|
||||
from azure.iot.device import X509
|
||||
from azure.iot.device.aio import IoTHubDeviceClient
|
||||
from azure.iot.device.aio import ProvisioningDeviceClient
|
||||
from azure.iot.device import Message, MethodResponse
|
||||
from datetime import datetime
|
||||
|
||||
__version__ = "0.2.0-beta.12"
|
||||
__name__ = "azure-iotcentral-device-client"
|
||||
|
||||
|
||||
def version():
|
||||
print(__version__)
|
||||
|
||||
from .. import IOTCLogLevel, IOTCEvents, IOTCConnectType
|
||||
from azure.iot.device import X509, MethodResponse, Message
|
||||
from azure.iot.device.aio import IoTHubDeviceClient, ProvisioningDeviceClient
|
||||
|
||||
try:
|
||||
import hmac
|
||||
except ImportError:
|
||||
print("ERROR: missing dependency `micropython-hmac`")
|
||||
print("ERROR: missing dependency `hmac`")
|
||||
sys.exit()
|
||||
|
||||
try:
|
||||
import hashlib
|
||||
except ImportError:
|
||||
print("ERROR: missing dependency `micropython-hashlib`")
|
||||
print("ERROR: missing dependency `hashlib`")
|
||||
sys.exit()
|
||||
|
||||
try:
|
||||
import base64
|
||||
except ImportError:
|
||||
print("ERROR: missing dependency `micropython-base64`")
|
||||
print("ERROR: missing dependency `base64`")
|
||||
sys.exit()
|
||||
|
||||
try:
|
||||
import json
|
||||
except ImportError:
|
||||
print("ERROR: missing dependency `micropython-json`")
|
||||
print("ERROR: missing dependency `json`")
|
||||
sys.exit()
|
||||
|
||||
try:
|
||||
import uuid
|
||||
except ImportError:
|
||||
print("ERROR: missing dependency `micropython-uuid`")
|
||||
print("ERROR: missing dependency `uuid`")
|
||||
sys.exit()
|
||||
|
||||
g_is_micro_python = ('implementation' in dir(sys)) and ('name' in dir(
|
||||
sys.implementation)) and (sys.implementation.name == 'micropython')
|
||||
|
||||
|
||||
class IOTCConnectType:
|
||||
IOTC_CONNECT_SYMM_KEY = 1
|
||||
IOTC_CONNECT_X509_CERT = 2
|
||||
IOTC_CONNECT_DEVICE_KEY = 3
|
||||
|
||||
|
||||
|
||||
class IOTCLogLevel:
|
||||
IOTC_LOGGING_DISABLED = 1
|
||||
IOTC_LOGGING_API_ONLY = 2
|
||||
IOTC_LOGGING_ALL = 16
|
||||
|
||||
|
||||
class IOTCConnectionState:
|
||||
IOTC_CONNECTION_EXPIRED_SAS_TOKEN = 1
|
||||
IOTC_CONNECTION_DEVICE_DISABLED = 2
|
||||
IOTC_CONNECTION_BAD_CREDENTIAL = 4
|
||||
IOTC_CONNECTION_RETRY_EXPIRED = 8
|
||||
IOTC_CONNECTION_NO_NETWORK = 16
|
||||
IOTC_CONNECTION_COMMUNICATION_ERROR = 32
|
||||
IOTC_CONNECTION_OK = 64
|
||||
|
||||
|
||||
class IOTCMessageStatus:
|
||||
IOTC_MESSAGE_ACCEPTED = 1
|
||||
IOTC_MESSAGE_REJECTED = 2
|
||||
IOTC_MESSAGE_ABANDONED = 4
|
||||
|
||||
|
||||
class IOTCEvents:
|
||||
IOTC_COMMAND = 2,
|
||||
IOTC_PROPERTIES = 4,
|
||||
|
||||
__version__ = "0.2.1-beta.2"
|
||||
__name__ = "iotc"
|
||||
|
||||
class ConsoleLogger:
|
||||
def __init__(self, log_level):
|
||||
self._log_level = log_level
|
||||
|
||||
def _log(self, message):
|
||||
async def _log(self, message):
|
||||
print(message)
|
||||
|
||||
def info(self, message):
|
||||
async def info(self, message):
|
||||
if self._log_level != IOTCLogLevel.IOTC_LOGGING_DISABLED:
|
||||
self._log(message)
|
||||
|
||||
def debug(self, message):
|
||||
async def debug(self, message):
|
||||
if self._log_level == IOTCLogLevel.IOTC_LOGGING_ALL:
|
||||
self._log(message)
|
||||
|
||||
|
@ -117,7 +73,12 @@ class IoTCClient:
|
|||
if logger is None:
|
||||
self._logger = ConsoleLogger(IOTCLogLevel.IOTC_LOGGING_API_ONLY)
|
||||
else:
|
||||
self._logger = logger
|
||||
if hasattr(logger,"info") & hasattr(logger,"debug") & hasattr(logger,"set_log_level"):
|
||||
self._logger = logger
|
||||
else:
|
||||
print("ERROR: Logger object has unsupported format. It must implement the following functions\n\
|
||||
info(message);\ndebug(message);\nset_log_level(message);")
|
||||
sys.exit()
|
||||
|
||||
def is_connected(self):
|
||||
"""
|
||||
|
@ -130,7 +91,6 @@ class IoTCClient:
|
|||
else:
|
||||
return False
|
||||
|
||||
|
||||
def set_global_endpoint(self, endpoint):
|
||||
"""
|
||||
Set the device provisioning endpoint.
|
||||
|
@ -162,16 +122,16 @@ class IoTCClient:
|
|||
return 0
|
||||
|
||||
async def _on_properties(self):
|
||||
self._logger.debug('Setup properties listener')
|
||||
await self._logger.debug('Setup properties listener')
|
||||
while True:
|
||||
try:
|
||||
prop_cb = self._events[IOTCEvents.IOTC_PROPERTIES]
|
||||
except KeyError:
|
||||
self._logger.debug('Properties callback not found')
|
||||
await self._logger.debug('Properties callback not found')
|
||||
await asyncio.sleep(10)
|
||||
continue
|
||||
patch = await self._device_client.receive_twin_desired_properties_patch()
|
||||
self._logger.debug('Received desired properties. {}'.format(patch))
|
||||
await self._logger.debug('Received desired properties. {}'.format(patch))
|
||||
|
||||
for prop in patch:
|
||||
if prop == '$version':
|
||||
|
@ -179,7 +139,7 @@ class IoTCClient:
|
|||
|
||||
ret = await prop_cb(prop, patch[prop]['value'])
|
||||
if ret:
|
||||
self._logger.debug('Acknowledging {}'.format(prop))
|
||||
await self._logger.debug('Acknowledging {}'.format(prop))
|
||||
await self.send_property({
|
||||
'{}'.format(prop): {
|
||||
"value": patch[prop]["value"],
|
||||
|
@ -188,10 +148,10 @@ class IoTCClient:
|
|||
'message': 'Property received'}
|
||||
})
|
||||
else:
|
||||
self._logger.debug(
|
||||
await self._logger.debug(
|
||||
'Property "{}" unsuccessfully processed'.format(prop))
|
||||
|
||||
async def _cmd_ack(self,name, value, requestId):
|
||||
async def _cmd_ack(self, name, value, requestId):
|
||||
await self.send_property({
|
||||
'{}'.format(name): {
|
||||
'value': value,
|
||||
|
@ -200,18 +160,18 @@ class IoTCClient:
|
|||
})
|
||||
|
||||
async def _on_commands(self):
|
||||
self._logger.debug('Setup commands listener')
|
||||
await self._logger.debug('Setup commands listener')
|
||||
|
||||
while True:
|
||||
try:
|
||||
cmd_cb = self._events[IOTCEvents.IOTC_COMMAND]
|
||||
except KeyError:
|
||||
self._logger.debug('Commands callback not found')
|
||||
await self._logger.debug('Commands callback not found')
|
||||
await asyncio.sleep(10)
|
||||
continue
|
||||
# Wait for unknown method calls
|
||||
method_request = await self._device_client.receive_method_request()
|
||||
self._logger.debug(
|
||||
await self._logger.debug(
|
||||
'Received command {}'.format(method_request.name))
|
||||
await self._device_client.send_method_response(MethodResponse.create_from_method_request(
|
||||
method_request, 200, {
|
||||
|
@ -219,7 +179,6 @@ class IoTCClient:
|
|||
))
|
||||
await cmd_cb(method_request, self._cmd_ack)
|
||||
|
||||
|
||||
async def _send_message(self, payload, properties):
|
||||
msg = Message(payload)
|
||||
msg.message_id = uuid.uuid4()
|
||||
|
@ -233,7 +192,7 @@ class IoTCClient:
|
|||
Send a property message
|
||||
:param dict payload: The properties payload. Can contain multiple properties in the form {'<propName>':{'value':'<propValue>'}}
|
||||
"""
|
||||
self._logger.debug('Sending property {}'.format(json.dumps(payload)))
|
||||
await self._logger.debug('Sending property {}'.format(json.dumps(payload)))
|
||||
await self._device_client.patch_twin_reported_properties(payload)
|
||||
|
||||
async def send_telemetry(self, payload, properties=None):
|
||||
|
@ -242,7 +201,7 @@ class IoTCClient:
|
|||
:param dict payload: The telemetry payload. Can contain multiple telemetry fields in the form {'<fieldName1>':<fieldValue1>,...,'<fieldNameN>':<fieldValueN>}
|
||||
:param dict optional properties: An object with custom properties to add to the message.
|
||||
"""
|
||||
self._logger.info('Sending telemetry message: {}'.format(payload))
|
||||
await self._logger.info('Sending telemetry message: {}'.format(payload))
|
||||
await self._send_message(json.dumps(payload), properties)
|
||||
|
||||
async def connect(self):
|
||||
|
@ -252,24 +211,26 @@ class IoTCClient:
|
|||
"""
|
||||
if self._cred_type in (IOTCConnectType.IOTC_CONNECT_DEVICE_KEY, IOTCConnectType.IOTC_CONNECT_SYMM_KEY):
|
||||
if self._cred_type == IOTCConnectType.IOTC_CONNECT_SYMM_KEY:
|
||||
self._key_or_cert = self._compute_derived_symmetric_key(
|
||||
self._key_or_cert = await self._compute_derived_symmetric_key(
|
||||
self._key_or_cert, self._device_id)
|
||||
|
||||
self._logger.debug('Device key: {}'.format(self._key_or_cert))
|
||||
|
||||
await self._logger.debug('Device key: {}'.format(self._key_or_cert))
|
||||
|
||||
self._provisioning_client = ProvisioningDeviceClient.create_from_symmetric_key(
|
||||
self._global_endpoint, self._device_id, self._scope_id, self._key_or_cert)
|
||||
self._global_endpoint, self._device_id, self._scope_id, self._key_or_cert)
|
||||
else:
|
||||
self._key_file = self._key_or_cert['key_file']
|
||||
self._cert_file = self._key_or_cert['cert_file']
|
||||
try:
|
||||
self._cert_phrase=self._key_or_cert['cert_phrase']
|
||||
x509=X509(self._cert_file,self._key_file,self._cert_phrase)
|
||||
self._cert_phrase = self._key_or_cert['cert_phrase']
|
||||
x509 = X509(self._cert_file, self._key_file, self._cert_phrase)
|
||||
except:
|
||||
self._logger.debug('No passphrase available for certificate. Trying without it')
|
||||
x509=X509(self._cert_file,self._key_file)
|
||||
await self._logger.debug(
|
||||
'No passphrase available for certificate. Trying without it')
|
||||
x509 = X509(self._cert_file, self._key_file)
|
||||
# Certificate provisioning
|
||||
self._provisioning_client=ProvisioningDeviceClient.create_from_x509_certificate(provisioning_host=self._global_endpoint,registration_id=self._device_id,id_scope=self._scope_id,x509=x509)
|
||||
self._provisioning_client = ProvisioningDeviceClient.create_from_x509_certificate(
|
||||
provisioning_host=self._global_endpoint, registration_id=self._device_id, id_scope=self._scope_id, x509=x509)
|
||||
|
||||
if self._model_id:
|
||||
self._provisioning_client.provisioning_payload = {
|
||||
|
@ -277,27 +238,29 @@ class IoTCClient:
|
|||
try:
|
||||
registration_result = await self._provisioning_client.register()
|
||||
assigned_hub = registration_result.registration_state.assigned_hub
|
||||
self._logger.debug(assigned_hub)
|
||||
await self._logger.debug(assigned_hub)
|
||||
self._hub_conn_string = 'HostName={};DeviceId={};SharedAccessKey={}'.format(
|
||||
assigned_hub, self._device_id, self._key_or_cert)
|
||||
self._logger.debug(
|
||||
await self._logger.debug(
|
||||
'IoTHub Connection string: {}'.format(self._hub_conn_string))
|
||||
|
||||
|
||||
if self._cred_type in (IOTCConnectType.IOTC_CONNECT_DEVICE_KEY, IOTCConnectType.IOTC_CONNECT_SYMM_KEY):
|
||||
self._device_client = IoTHubDeviceClient.create_from_connection_string(self._hub_conn_string)
|
||||
self._device_client = IoTHubDeviceClient.create_from_connection_string(
|
||||
self._hub_conn_string)
|
||||
else:
|
||||
self._device_client = IoTHubDeviceClient.create_from_x509_certificate(x509=x509,hostname=assigned_hub,device_id=registration_result.registration_state.device_id)
|
||||
self._device_client = IoTHubDeviceClient.create_from_x509_certificate(
|
||||
x509=x509, hostname=assigned_hub, device_id=registration_result.registration_state.device_id)
|
||||
except:
|
||||
self._logger.info(
|
||||
await self._logger.info(
|
||||
'ERROR: Failed to get device provisioning information')
|
||||
sys.exit()
|
||||
# Connect to iothub
|
||||
try:
|
||||
await self._device_client.connect()
|
||||
self._connected = True
|
||||
self._logger.debug('Device connected')
|
||||
await self._logger.debug('Device connected')
|
||||
except:
|
||||
self._logger.info('ERROR: Failed to connect to Hub')
|
||||
await self._logger.info('ERROR: Failed to connect to Hub')
|
||||
sys.exit()
|
||||
|
||||
# setup listeners
|
||||
|
@ -308,17 +271,13 @@ class IoTCClient:
|
|||
# self._on_commands()
|
||||
# )
|
||||
|
||||
def _compute_derived_symmetric_key(self, secret, reg_id):
|
||||
async def _compute_derived_symmetric_key(self, secret, reg_id):
|
||||
# pylint: disable=no-member
|
||||
global g_is_micro_python
|
||||
try:
|
||||
secret = base64.b64decode(secret)
|
||||
except:
|
||||
self._logger.debug(
|
||||
await self._logger.debug(
|
||||
"ERROR: broken base64 secret => `" + secret + "`")
|
||||
sys.exit()
|
||||
|
||||
if g_is_micro_python == False:
|
||||
return base64.b64encode(hmac.new(secret, msg=reg_id.encode('utf8'), digestmod=hashlib.sha256).digest()).decode('utf-8')
|
||||
else:
|
||||
return base64.b64encode(hmac.new(secret, msg=reg_id.encode('utf8'), digestmod=hashlib._sha256.sha256).digest())
|
||||
return base64.b64encode(hmac.new(secret, msg=reg_id.encode('utf8'), digestmod=hashlib.sha256).digest()).decode('utf-8')
|
|
@ -27,4 +27,24 @@ python3 -m pytest test/v3
|
|||
Python 2.7+
|
||||
```
|
||||
python2 -m pytest test/v2
|
||||
```
|
||||
```
|
||||
|
||||
Tests by default parse a configuration file including required credentials. Just create a file called **tests.ini** inside the _test_ folder with a content like this:
|
||||
|
||||
```ini
|
||||
[TESTS]
|
||||
GroupKey = ....
|
||||
DeviceKey = ....
|
||||
DeviceId = ....
|
||||
ScopeId = ....
|
||||
AssignedHub = ....
|
||||
ExpectedHub = ....
|
||||
```
|
||||
|
||||
### Run locally
|
||||
It is possible to run tests against the local copy of the device client. This is particularly useful when testing patches not yet published upstream.
|
||||
Just add an entry to the configuration file under the _TESTS_ section
|
||||
```ini
|
||||
[TESTS]
|
||||
Local = yes
|
||||
```
|
||||
|
|
|
@ -3,14 +3,19 @@ import mock
|
|||
import time
|
||||
import configparser
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
from azure.iot.device.provisioning.models import RegistrationResult
|
||||
from azure.iot.device.iothub.models import MethodRequest
|
||||
|
||||
from azure.iotcentral.device.client import IoTCClient, IOTCConnectType, IOTCLogLevel, IOTCEvents
|
||||
|
||||
config = configparser.ConfigParser()
|
||||
config.read(os.path.join(os.path.dirname(__file__),'../tests.ini'))
|
||||
config.read(os.path.join(os.path.dirname(__file__), '../tests.ini'))
|
||||
|
||||
if config['TESTS'].getboolean('Local'):
|
||||
sys.path.insert(0, 'src')
|
||||
|
||||
from iotc import IoTCClient, IOTCConnectType, IOTCLogLevel, IOTCEvents
|
||||
|
||||
groupKey = config['TESTS']['GroupKey']
|
||||
deviceKey = config['TESTS']['DeviceKey']
|
||||
|
@ -68,7 +73,6 @@ def init(mocker):
|
|||
return iotc
|
||||
|
||||
|
||||
|
||||
def test_computeKey(mocker):
|
||||
iotc = init(mocker)
|
||||
assert iotc._compute_derived_symmetric_key(
|
||||
|
|
|
@ -4,62 +4,70 @@ import time
|
|||
import asyncio
|
||||
import configparser
|
||||
import os
|
||||
import sys
|
||||
|
||||
from contextlib import suppress
|
||||
|
||||
from azure.iot.device.provisioning.models import RegistrationResult
|
||||
from azure.iot.device.iothub.models import MethodRequest
|
||||
|
||||
from azure.iotcentral.device.client.aio import IoTCClient, IOTCConnectType, IOTCLogLevel, IOTCEvents
|
||||
|
||||
config = configparser.ConfigParser()
|
||||
config.read(os.path.join(os.path.dirname(__file__),'../tests.ini'))
|
||||
config.read(os.path.join(os.path.dirname(__file__), '../tests.ini'))
|
||||
|
||||
groupKey=config['TESTS']['GroupKey']
|
||||
deviceKey=config['TESTS']['DeviceKey']
|
||||
device_id=config['TESTS']['DeviceId']
|
||||
scopeId=config['TESTS']['ScopeId']
|
||||
assignedHub=config['TESTS']['AssignedHub']
|
||||
expectedHub=config['TESTS']['ExpectedHub']
|
||||
if config['TESTS'].getboolean('Local'):
|
||||
sys.path.insert(0, 'src')
|
||||
|
||||
propPayload={'prop1':{'value':40},'$version':5}
|
||||
from iotc import IOTCConnectType, IOTCLogLevel, IOTCEvents
|
||||
from iotc.aio import IoTCClient
|
||||
|
||||
cmdRequestId='abcdef'
|
||||
cmdName='command1'
|
||||
cmdPayload='payload'
|
||||
methodRequest=MethodRequest(cmdRequestId,cmdName,cmdPayload)
|
||||
groupKey = config['TESTS']['GroupKey']
|
||||
deviceKey = config['TESTS']['DeviceKey']
|
||||
device_id = config['TESTS']['DeviceId']
|
||||
scopeId = config['TESTS']['ScopeId']
|
||||
assignedHub = config['TESTS']['AssignedHub']
|
||||
expectedHub = config['TESTS']['ExpectedHub']
|
||||
|
||||
propPayload = {'prop1': {'value': 40}, '$version': 5}
|
||||
|
||||
cmdRequestId = 'abcdef'
|
||||
cmdName = 'command1'
|
||||
cmdPayload = 'payload'
|
||||
methodRequest = MethodRequest(cmdRequestId, cmdName, cmdPayload)
|
||||
|
||||
|
||||
class NewRegState():
|
||||
def __init__(self):
|
||||
self.assigned_hub=assignedHub
|
||||
|
||||
def assigned_hub(self):
|
||||
return self.assigned_hub
|
||||
def __init__(self):
|
||||
self.assigned_hub = assignedHub
|
||||
|
||||
def assigned_hub(self):
|
||||
return self.assigned_hub
|
||||
|
||||
|
||||
class NewProvClient():
|
||||
async def register(self):
|
||||
reg=RegistrationResult('3o375i827i852','assigned',NewRegState())
|
||||
return reg
|
||||
async def register(self):
|
||||
reg = RegistrationResult('3o375i827i852', 'assigned', NewRegState())
|
||||
return reg
|
||||
|
||||
|
||||
class NewDeviceClient():
|
||||
async def connect(self):
|
||||
return True
|
||||
async def connect(self):
|
||||
return True
|
||||
|
||||
async def receive_twin_desired_properties_patch(self):
|
||||
await asyncio.sleep(3)
|
||||
return propPayload
|
||||
|
||||
async def receive_method_request(self):
|
||||
await asyncio.sleep(3)
|
||||
return methodRequest
|
||||
async def receive_twin_desired_properties_patch(self):
|
||||
await asyncio.sleep(3)
|
||||
return propPayload
|
||||
|
||||
async def receive_method_request(self):
|
||||
await asyncio.sleep(3)
|
||||
return methodRequest
|
||||
|
||||
async def send_method_response(self, payload):
|
||||
return True
|
||||
|
||||
async def patch_twin_reported_properties(self, payload):
|
||||
return True
|
||||
|
||||
async def send_method_response(self,payload):
|
||||
return True
|
||||
|
||||
async def patch_twin_reported_properties(self,payload):
|
||||
return True
|
||||
|
||||
def async_return(result):
|
||||
f = asyncio.Future()
|
||||
|
@ -68,128 +76,128 @@ def async_return(result):
|
|||
|
||||
|
||||
async def stop_threads(iotc):
|
||||
iotc._prop_thread.cancel()
|
||||
iotc._cmd_thread.cancel()
|
||||
|
||||
|
||||
iotc._prop_thread.cancel()
|
||||
iotc._cmd_thread.cancel()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
def init(mocker):
|
||||
iotc=IoTCClient(device_id, scopeId,
|
||||
IOTCConnectType.IOTC_CONNECT_SYMM_KEY, groupKey)
|
||||
mocker.patch('azure.iotcentral.device.client.aio.ProvisioningDeviceClient.create_from_symmetric_key',return_value=NewProvClient())
|
||||
mocker.patch('azure.iotcentral.device.client.aio.IoTHubDeviceClient.create_from_connection_string',return_value=NewDeviceClient())
|
||||
return iotc
|
||||
|
||||
iotc = IoTCClient(device_id, scopeId,
|
||||
IOTCConnectType.IOTC_CONNECT_SYMM_KEY, groupKey)
|
||||
mocker.patch('azure.iotcentral.device.client.aio.ProvisioningDeviceClient.create_from_symmetric_key',
|
||||
return_value=NewProvClient())
|
||||
mocker.patch('azure.iotcentral.device.client.aio.IoTHubDeviceClient.create_from_connection_string',
|
||||
return_value=NewDeviceClient())
|
||||
return iotc
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_computeKey(mocker):
|
||||
iotc=init(mocker)
|
||||
assert iotc._compute_derived_symmetric_key(groupKey,device_id) == deviceKey
|
||||
iotc = init(mocker)
|
||||
key = await iotc._compute_derived_symmetric_key(
|
||||
groupKey, device_id)
|
||||
assert key == deviceKey
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_deviceKeyGeneration(mocker):
|
||||
iotc = init(mocker)
|
||||
await iotc.connect()
|
||||
assert iotc._key_or_cert == deviceKey
|
||||
await stop_threads(iotc)
|
||||
iotc = init(mocker)
|
||||
await iotc.connect()
|
||||
assert iotc._key_or_cert == deviceKey
|
||||
await stop_threads(iotc)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_hubConnectionString(mocker):
|
||||
iotc = init(mocker)
|
||||
await iotc.connect()
|
||||
assert iotc._hub_conn_string == expectedHub
|
||||
await stop_threads(iotc)
|
||||
iotc = init(mocker)
|
||||
await iotc.connect()
|
||||
assert iotc._hub_conn_string == expectedHub
|
||||
await stop_threads(iotc)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_onproperties_before(mocker):
|
||||
iotc = init(mocker)
|
||||
iotc = init(mocker)
|
||||
|
||||
async def onProps(propname,propvalue):
|
||||
assert propname == 'prop1'
|
||||
assert propvalue == 40
|
||||
await stop_threads(iotc)
|
||||
async def onProps(propname, propvalue):
|
||||
assert propname == 'prop1'
|
||||
assert propvalue == 40
|
||||
await stop_threads(iotc)
|
||||
|
||||
mocker.patch.object(iotc,'send_property',return_value=True)
|
||||
iotc.on(IOTCEvents.IOTC_PROPERTIES,onProps)
|
||||
await iotc.connect()
|
||||
try:
|
||||
await iotc._prop_thread
|
||||
await iotc._cmd_thread
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
|
||||
mocker.patch.object(iotc, 'send_property', return_value=True)
|
||||
iotc.on(IOTCEvents.IOTC_PROPERTIES, onProps)
|
||||
await iotc.connect()
|
||||
try:
|
||||
await iotc._prop_thread
|
||||
await iotc._cmd_thread
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_onproperties_after(mocker):
|
||||
iotc = init(mocker)
|
||||
|
||||
async def onProps(propname,propvalue):
|
||||
assert propname == 'prop1'
|
||||
assert propvalue == 40
|
||||
await stop_threads(iotc)
|
||||
return True
|
||||
iotc = init(mocker)
|
||||
|
||||
mocker.patch.object(iotc,'send_property',return_value=True)
|
||||
await iotc.connect()
|
||||
iotc.on(IOTCEvents.IOTC_PROPERTIES,onProps)
|
||||
async def onProps(propname, propvalue):
|
||||
assert propname == 'prop1'
|
||||
assert propvalue == 40
|
||||
await stop_threads(iotc)
|
||||
return True
|
||||
|
||||
try:
|
||||
await iotc._prop_thread
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
mocker.patch.object(iotc, 'send_property', return_value=True)
|
||||
await iotc.connect()
|
||||
iotc.on(IOTCEvents.IOTC_PROPERTIES, onProps)
|
||||
|
||||
try:
|
||||
await iotc._prop_thread
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_on_commands_before(mocker):
|
||||
|
||||
iotc = init(mocker)
|
||||
iotc = init(mocker)
|
||||
|
||||
async def onCmds(command,ack):
|
||||
ret=ack()
|
||||
assert ret=='mocked'
|
||||
await stop_threads(iotc)
|
||||
return True
|
||||
async def onCmds(command, ack):
|
||||
ret = ack()
|
||||
assert ret == 'mocked'
|
||||
await stop_threads(iotc)
|
||||
return True
|
||||
|
||||
def mockedAck():
|
||||
return 'mocked'
|
||||
|
||||
def mockedAck():
|
||||
return 'mocked'
|
||||
mocker.patch.object(iotc, '_cmd_ack', mockedAck)
|
||||
|
||||
mocker.patch.object(iotc,'_cmd_ack',mockedAck)
|
||||
iotc.on(IOTCEvents.IOTC_COMMAND, onCmds)
|
||||
await iotc.connect()
|
||||
try:
|
||||
await iotc._cmd_thread
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
|
||||
iotc.on(IOTCEvents.IOTC_COMMAND,onCmds)
|
||||
await iotc.connect()
|
||||
try:
|
||||
await iotc._cmd_thread
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_onCommands_after(mocker):
|
||||
|
||||
iotc = init(mocker)
|
||||
iotc = init(mocker)
|
||||
|
||||
async def onCmds(command,ack):
|
||||
ret=ack()
|
||||
assert ret=='mocked'
|
||||
await stop_threads(iotc)
|
||||
return True
|
||||
async def onCmds(command, ack):
|
||||
ret = ack()
|
||||
assert ret == 'mocked'
|
||||
await stop_threads(iotc)
|
||||
return True
|
||||
|
||||
def mockedAck():
|
||||
return 'mocked'
|
||||
|
||||
def mockedAck():
|
||||
return 'mocked'
|
||||
mocker.patch.object(iotc, '_cmd_ack', mockedAck)
|
||||
|
||||
mocker.patch.object(iotc,'_cmd_ack',mockedAck)
|
||||
|
||||
await iotc.connect()
|
||||
iotc.on(IOTCEvents.IOTC_COMMAND,onCmds)
|
||||
|
||||
try:
|
||||
await iotc._cmd_thread
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
await iotc.connect()
|
||||
iotc.on(IOTCEvents.IOTC_COMMAND, onCmds)
|
||||
|
||||
try:
|
||||
await iotc._cmd_thread
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
|
|
Загрузка…
Ссылка в новой задаче