diff --git a/.coveragerc b/.coveragerc deleted file mode 100644 index f0ec8c83..00000000 --- a/.coveragerc +++ /dev/null @@ -1,3 +0,0 @@ -[run] -branch = True -source = src/wiotp/sdk \ No newline at end of file diff --git a/.coveralls.yml b/.coveralls.yml deleted file mode 100644 index ea06f7a6..00000000 --- a/.coveralls.yml +++ /dev/null @@ -1,2 +0,0 @@ -service_name: travis-ci -parallel: true diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml new file mode 100644 index 00000000..139bbc3e --- /dev/null +++ b/.github/workflows/python-package.yml @@ -0,0 +1,53 @@ +# This workflow will install Python dependencies, run tests and lint with a variety of Python versions +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python + +name: Python package + +on: + push: + branches: [ "master" ] + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.9", "3.10", "3.11"] + primary-config: ["true", "false"] + include: + - python-version: "3.9" + primary-config: "false" + - python-version: "3.10" + primary-config: "false" + - python-version: "3.11" + primary-config: "true" + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + - name: Install + run: | + python -m pip install --upgrade pip + python -m pip install .[dev] flake8 + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 src --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 src --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Test with pytest + env: + ONE_JOB_ONLY_TESTS: ${{secrets[matrix.primary-config]}} + run: | + pytest + # If needed we can gate this step to only run on master + # BRANCH=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" + # if [[ "$BRANCH" == 'refs/heads/master' ]]; then + # pytest + # else + # echo "Tests are only ran for pushes to master" + # fi diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml new file mode 100644 index 00000000..8a1e99d7 --- /dev/null +++ b/.github/workflows/python-publish.yml @@ -0,0 +1,39 @@ +# This workflow will upload a Python Package using Twine when a release is created +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries + +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +name: Upload Python Package + +on: + release: + types: [published] + +permissions: + contents: read + +jobs: + deploy: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v3 + with: + python-version: '3.x' + - name: Install + run: | + python -m pip install --upgrade pip + pip install .[dev] + - name: Build package + run: python -m build + - name: Publish package + uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29 + with: + user: __token__ + password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.gitignore b/.gitignore index c7b97e4a..adff9ae8 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,6 @@ samples/deviceFactory/bin samples/*.exe samples/rbac-config.yaml test/.DS_Store +/venv +pandoc-*-amd64.deb +README.rst diff --git a/.tox.ini.swp b/.tox.ini.swp deleted file mode 100644 index 64a554f0..00000000 Binary files a/.tox.ini.swp and /dev/null differ diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 89ede8f7..00000000 --- a/.travis.yml +++ /dev/null @@ -1,40 +0,0 @@ -group: travis_latest - -language: python - -services: - - docker - -cache: pip - -matrix: - include: - - python: "3.6" - env: - - ONE_JOB_ONLY_TESTS=false - - python: "3.7" - env: - - ONE_JOB_ONLY_TESTS=false - - python: "3.8" - env: - - ONE_JOB_ONLY_TESTS=true - - BUILD_DOCKER_IMAGES=true - -install: - - pip install tox-travis coveralls pyyaml - - echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin - -script: - - tox - - ./buildDockerImages.sh - - -after_success: - # Only upload coverage report from pythonn 3.8 test run if BUILD_DOCKER_IMAGES is not null and not empty - - | - if [ -n "${BUILD_DOCKER_IMAGES}" ]; then - coveralls - fi - -notifications: - webhooks: https://coveralls.io/webhook diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..9a178351 --- /dev/null +++ b/Makefile @@ -0,0 +1,20 @@ +.PHONY: docs install test build clean + +venv: + python3 -m venv venv + +clean: + rm -rf venv + +install: venv + . venv/bin/activate && python -m pip install --editable .[dev] + +test: venv + . venv/bin/activate && pytest + +build: venv + rm README.rst + . venv/bin/activate && python -m build + +docs: + cd docs && make html diff --git a/README.md b/README.md index 3566bfe2..2a554a52 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,36 @@ -# Python for IBM Watson IoT Platform +Python for IBM Watson IoT Platform +=============================================================================== -[![Build Status](https://travis-ci.org/ibm-watson-iot/iot-python.svg?branch=master)](https://travis-ci.org/ibm-watson-iot/iot-python) -[![Coverage Status](https://coveralls.io/repos/github/ibm-watson-iot/iot-python/badge.svg?branch=master)](https://coveralls.io/github/ibm-watson-iot/iot-python?branch=master) [![GitHub issues](https://img.shields.io/github/issues/ibm-watson-iot/iot-python.svg)](https://github.com/ibm-watson-iot/iot-python/issues) [![GitHub](https://img.shields.io/github/license/ibm-watson-iot/iot-python.svg)](https://github.com/ibm-watson-iot/iot-python/blob/master/LICENSE) [![PyPI](https://img.shields.io/pypi/v/wiotp-sdk.svg)](https://pypi.org/project/wiotp-sdk/) +![Project Status](https://img.shields.io/badge/python-3.9%20%7C%203.10%20%7C%203.11-blue) [![Downloads](https://pepy.tech/badge/ibmiotf)](https://pepy.tech/project/ibmiotf) [![Downloads](https://pepy.tech/badge/wiotp-sdk)](https://pepy.tech/project/wiotp-sdk) [![Code Style: Black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black) -Python module for interacting with the [IBM Watson IoT Platform](https://internetofthings.ibmcloud.com). +Python module for interacting with **Maximo IoT** and **[IBM Watson IoT Platform](https://internetofthings.ibmcloud.com)** -- [Python 3.8](https://www.python.org/downloads/release/python-382/) (recommended) -- Python 3.7 -- Python 3.6 +- Python 3.11 +- Python 3.10 +- Python 3.9 -Note: As of version 0.12, versions of Python less than 3.6 are not officially supported. Compatability with older versions of Python is not guaranteed. -## Product Withdrawal Notice +Product Withdrawal Notice +------------------------------------------------------------------------------- Per the September 8, 2020 [announcement](https://www-01.ibm.com/common/ssi/cgi-bin/ssialias?subtype=ca&infotype=an&appname=iSource&supplier=897&letternum=ENUS920-136#rprodnx) IBM Watson IoT Platform (5900-A0N) has been withdrawn from marketing effective **December 9, 2020**. As a result, updates to this project will be limited. -## Dependencies - +Dependencies +------------------------------------------------------------------------------- - [paho-mqtt](https://pypi.python.org/pypi/paho-mqtt) - [iso8601](https://pypi.python.org/pypi/iso8601) - [pytz](https://pypi.python.org/pypi/pytz) - [requests](https://pypi.python.org/pypi/requests) -## Installation - +Installation +------------------------------------------------------------------------------- Install the [latest version](https://pypi.org/project/wiotp-sdk/) of the library with pip ``` @@ -38,26 +38,22 @@ Install the [latest version](https://pypi.org/project/wiotp-sdk/) of the library ``` -## Uninstall - +Uninstall +------------------------------------------------------------------------------- Uninstalling the module is simple. ``` # pip uninstall wiotp-sdk ``` -## Legacy ibmiotf Module - -Version `0.4.0` of the old [ibmiotf](https://pypi.python.org/pypi/ibmiotf) pre-release is still available, if you do not wish to upgrade to the new version, we have no plans to remove this from pypi at this time, however it will not be getting any updates. - - -## Documentation +Documentation +------------------------------------------------------------------------------- https://ibm-watson-iot.github.io/iot-python/ -## Supported Features - +Supported Features +------------------------------------------------------------------------------- - **Device Connectivity**: Connect your device(s) to Watson IoT Platform with ease using this library - **Gateway Connectivity**: Connect your gateway(s) to Watson IoT Platform with ease using this library - **Application connectivity**: Connect your application(s) to Watson IoT Platform with ease using this library @@ -69,7 +65,3 @@ https://ibm-watson-iot.github.io/iot-python/ - **Scalable Applications**: Supports load balancing of MQTT subscriptions over multiple application instances. - **Auto Reconnect**: All clients support automatic reconnect to the Platform in the event of a network interruption. - **Websockets**: Support device/gateway/application connectivity to Watson IoT Platform using WebSocket - - -## Unsupported Features -- **Client side Certificate based authentication**: [Client side Certificate based authentication](https://console.ng.bluemix.net/docs/services/IoT/reference/security/RM_security.html)n diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..4a69b01c --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,6 @@ +[build-system] +requires = [ + "setuptools", + "pypandoc" +] +build-backend = "setuptools.build_meta" diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 00000000..997492c5 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,2 @@ +[pytest] +pythonpath="src" "test" \ No newline at end of file diff --git a/samples/psutil/README.md b/samples/psutil/README.md index 96799668..c7a3ded1 100644 --- a/samples/psutil/README.md +++ b/samples/psutil/README.md @@ -26,30 +26,23 @@ A tutorial guiding you through the process of setting up this sample on a Raspbe ## Before you Begin -Register a device with IBM Watson IoT Platform. +Register a device with IBM Watson IoT Platform. -For information on how to register devices, see the [Connecting Devices](https://www.ibm.com/support/knowledgecenter/SSQP8H/iot/platform/iotplatform_task.html) topic in the IBM Watson IoT Platform documentation. +For information on how to register devices, see the [Connecting Devices](https://www.ibm.com/support/knowledgecenter/SSQP8H/iot/platform/iotplatform_task.html) topic in the IBM Watson IoT Platform documentation. -At the end of the registration process, make a note of the following parameters: +At the end of the registration process, make a note of the following parameters: - Organization ID - Type ID - Device ID - - Authentication Token + - Authentication Token ## Docker -The easiest way to test out the sample is via the [wiotp/psutil](https://cloud.docker.com/u/wiotp/repository/docker/wiotp/psutil) Docker image provided and the `--quickstart` command line option. +The easiest way to test out the sample is via the [wiotp/psutil](https://cloud.docker.com/u/wiotp/repository/docker/wiotp/psutil) Docker image provided. -The resource requirements for this container are tiny, if you use the accompanying helm chart it is by default confiugured with a request of 2m CPU + 18Mi memory, and limits set to 4m cpu + 24Mi memory. +The resource requirements for this container are tiny, if you use the accompanying helm chart it is by default configured with a request of 2m CPU + 18Mi memory, and limits set to 4m cpu + 24Mi memory. -``` -$ docker run -d --name psutil wiotp/psutil --quickstart -psutil -$ docker logs -tf psutil -2019-05-07T11:09:19.672513500Z 2019-05-07 11:09:19,671 wiotp.sdk.device.client.DeviceClient INFO Connected successfully: d:quickstart:sample-iotpsutil:242ac110002 -``` - -To connect as a registered device in your organization you must set the following environment variables in the container's environment. These variables correspond to the device parameters for your registered device: +To connect as a registered device in your organization you must set the following environment variables in the container's environment. These variables correspond to the device parameters for your registered device: - `WIOTP_IDENTITY_ORGID` - `WIOTP_IDENTITY_TYPEID` - `WIOTP_IDENTITY_DEVICEID` @@ -77,13 +70,6 @@ $ helm repo add wiotp https://ibm-watson-iot.github.io/helm/charts/ $ helm install psutil-mydevice wiotp/psutil --values path/to/mydevice.yaml ``` -If you provide no additional values the chart will deploy in a configuration supporting Quickstart by default: - -``` -$ helm repo add wiotp https://ibm-watson-iot.github.io/helm/charts/ -$ helm install psutil-quickstart wiotp/psutil -``` - The pod consumes very little resource during operation, you can easily max out the default 110 pod/node limit with a cheap 2cpu/4gb worker if you are looking to deploy this chart at scale. @@ -104,19 +90,20 @@ pi@raspberrypi ~ $ sudo pip install wiotp-sdk psutil pi@raspberrypi ~ $ wget https://github.com/ibm-watson-iot/iot-python/archive/master.zip pi@raspberrypi ~ $ unzip master.zip pi@raspberrypi ~ $ cd iot-python-master/samples/psutil/src -pi@raspberrypi ~ $ python iotpsutil.py --quickstart +pi@raspberrypi ~ $ export WIOTP_IDENTITY_ORGID=myorgid +pi@raspberrypi ~ $ export WIOTP_IDENTITY_TYPEID=mytypeid +pi@raspberrypi ~ $ export WIOTP_IDENTITY_DEVICEID=mydeviceid +pi@raspberrypi ~ $ export WIOTP_AUTH_TOKEN=myauthtoken +pi@raspberrypi ~ $ python iotpsutil.py (Press Ctrl+C to disconnect) - ``` -Note: Set the same environment variables detailed in the Docker section of this README (above) and ommit the `--quickstart` argument to connect your Raspberry Pi to IBM Watson IoT Platform as a registered device. - ## Support Application A sample application is provided that allows commands to be sent to the device when used in registered mode only. The application provides two functions: - * Adjust the publish rate of the psutil device sample + * Adjust the publish rate of the psutil device sample * Print a debug message to the console on the device ``` diff --git a/samples/psutil/helm/psutil/templates/deployment.yaml b/samples/psutil/helm/psutil/templates/deployment.yaml index 3a242e3b..81e40723 100644 --- a/samples/psutil/helm/psutil/templates/deployment.yaml +++ b/samples/psutil/helm/psutil/templates/deployment.yaml @@ -23,10 +23,6 @@ spec: - name: {{ .Chart.Name }} image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" imagePullPolicy: {{ .Values.image.pullPolicy }} - {{ if eq .Values.identity.orgId "quickstart" }} - args: - - "--quickstart" - {{ else }} env: - name: WIOTP_IDENTITY_ORGID value: {{ .Values.identity.orgId }} @@ -37,7 +33,6 @@ spec: # Note: this can contain special characters, so we wrap the value in quotes - name: WIOTP_AUTH_TOKEN value: "{{ .Values.auth.token }}" - {{ end }} resources: {{ toYaml .Values.resources | indent 12 }} diff --git a/samples/psutil/helm/psutil/values.yaml b/samples/psutil/helm/psutil/values.yaml index 789b6975..a6ab5073 100644 --- a/samples/psutil/helm/psutil/values.yaml +++ b/samples/psutil/helm/psutil/values.yaml @@ -17,9 +17,8 @@ resources: # WIoTP device configuration properties -- override these to connect -# Without any configuration, the defaults will result in a connection to Quickstart identity: - orgId: quickstart + orgId: typeId: deviceId: auth: diff --git a/samples/psutil/src/iotpsutil.py b/samples/psutil/src/iotpsutil.py index 273684bd..90b88d0d 100644 --- a/samples/psutil/src/iotpsutil.py +++ b/samples/psutil/src/iotpsutil.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # ***************************************************************************** -# Copyright (c) 2014, 2019 IBM Corporation and other Contributors. +# Copyright (c) 2014, 2024 IBM Corporation and other Contributors. # # All rights reserved. This program and the accompanying materials # are made available under the terms of the Eclipse Public License v1.0 @@ -69,33 +69,24 @@ def commandProcessor(cmd): # Initialize the properties we need parser = argparse.ArgumentParser( description="IBM Watson IoT Platform PSUtil device client. For more information see https://github.com/ibm-watson-iot/iot-python/samples/psutil", - epilog="If neither the quickstart or cfg parameter is provided the device will attempt to parse the configuration from environment variables.", + epilog="If the cfg parameter is not provided the device will attempt to parse the configuration from environment variables.", ) parser.add_argument( "-n", "--name", required=False, default=platform.node(), help="Defaults to platform.node() if not set" ) - parser.add_argument("-q", "--quickstart", required=False, action="store_true", help="Connect device to quickstart?") parser.add_argument( "-c", "--cfg", required=False, default=None, - help="Location of device configuration file (ignored if quickstart mode is enabled)", + help="Location of device configuration file", ) parser.add_argument("-v", "--verbose", required=False, action="store_true", help="Enable verbose log messages?") args, unknown = parser.parse_known_args() client = None try: - if args.quickstart: - options = { - "identity": { - "orgId": "quickstart", - "typeId": "sample-iotpsutil", - "deviceId": str(hex(int(get_mac())))[2:], - } - } - elif args.cfg is not None: + if args.cfg is not None: options = wiotp.sdk.device.parseConfigFile(args.cfg) else: options = wiotp.sdk.device.parseEnvVars() @@ -107,14 +98,6 @@ def commandProcessor(cmd): print(str(e)) sys.exit(1) - if args.quickstart: - print( - "Welcome to IBM Watson IoT Platform Quickstart, view a vizualization of live data from this device at the URL below:" - ) - print( - "https://quickstart.internetofthings.ibmcloud.com/#/device/%s/sensor/" % (options["identity"]["deviceId"]) - ) - print("(Press Ctrl+C to disconnect)") # Take initial reading diff --git a/samples/simpleApp/README.md b/samples/simpleApp/README.md index 611e9956..d6bd87b9 100644 --- a/samples/simpleApp/README.md +++ b/samples/simpleApp/README.md @@ -1,37 +1,8 @@ # Python Simple Application - Sample code for a very basic appliation which subscribes to both events and connectivity status from one or more devices connected to the IBM Internet of Things service. -## QuickStart Usage -Ssimply provide the device ID of your QuickStart connected device: -``` -[me@localhost ~]$ python simpleApp.py -I 112233445566 -(Press Ctrl+C to disconnect) -============================================================================= -Timestamp Device Event -============================================================================= -2014-07-07T12:04:32.296000+00:00 sample-iotpsutil:112233445566 psutil: {"mem": 73.2, "network_up": 0.75, "cpu": 0.2, "name": "W520", "network_down": 0.44} -2014-07-07T07:03:55.202000-04:00 sample-iotpsutil:112233445566 Connect 1.2.3.4 -2014-07-07T12:04:33.300000+00:00 sample-iotpsutil:112233445566 psutil: {"mem": 73.2, "network_up": 0.42, "cpu": 5.4, "name": "W520", "network_down": 1.08} -2014-07-07T12:04:34.304000+00:00 sample-iotpsutil:112233445566 psutil: {"mem": 73.2, "network_up": 0.27, "cpu": 0.0, "name": "W520", "network_down": 0.29} -2014-07-07T12:04:35.308000+00:00 sample-iotpsutil:112233445566 psutil: {"mem": 73.2, "network_up": 0.27, "cpu": 1.9, "name": "W520", "network_down": 0.29} -2014-07-07T12:04:36.312000+00:00 sample-iotpsutil:112233445566 psutil: {"mem": 73.2, "network_up": 0.27, "cpu": 0.4, "name": "W520", "network_down": 0.35} -2014-07-07T12:04:37.317000+00:00 sample-iotpsutil:112233445566 psutil: {"mem": 73.2, "network_up": 0.27, "cpu": 0.4, "name": "W520", "network_down": 0.29} -2014-07-07T12:04:38.321000+00:00 sample-iotpsutil:112233445566 psutil: {"mem": 73.2, "network_up": 0.27, "cpu": 0.2, "name": "W520", "network_down": 0.59} -2014-07-07T07:04:37.550000-04:00 sample-iotpsutil:112233445566 Disconnect 195.212.29.68 (The connection has completed normally.) -2014-07-07T07:11:47.779000-04:00 sample-iotpsutil:112233445566 Connect 1.2.3.4 -2014-07-07T12:11:49.732000+00:00 sample-iotpsutil:112233445566 psutil: {"mem": 75.4, "network_up": 0.12, "cpu": 0.2, "name": "W520", "network_down": 0.0} -2014-07-07T12:11:50.736000+00:00 sample-iotpsutil:112233445566 psutil: {"mem": 75.4, "network_up": 0.38, "cpu": 1.5, "name": "W520", "network_down": 0.58} -2014-07-07T12:11:51.740000+00:00 sample-iotpsutil:112233445566 psutil: {"mem": 75.4, "network_up": 0.38, "cpu": 5.4, "name": "W520", "network_down": 0.63} -2014-07-07T12:11:52.745000+00:00 sample-iotpsutil:112233445566 psutil: {"mem": 75.4, "network_up": 0.27, "cpu": 3.9, "name": "W520", "network_down": 0.29} -2014-07-07T07:11:52.186000-04:00 sample-iotpsutil:112233445566 Disconnect 1.2.3.4 (The connection has completed normally.) -``` - -## Registered Organization Usage -Once you have access to an API Key for an organization in the Internet of Things Cloud additional the application can be used to display events from multiple devices: - -### Using an application configuration file -Create a file named application.cfg in the simpleApp directory and insert the credentials for your API key as well as an ID that is unique to your application instance. +## Using an application configuration file +Create a file named application.cfg in the simpleApp directory and insert the credentials for your API key as well as an ID that is unique to your application instance. ``` [application] org=$orgId @@ -53,33 +24,33 @@ Timestamp Device Event 2014-07-07T12:16:55.821000+00:00 psutil:001 psutil: {"mem": 75.0, "network_up": 0.27, "cpu": 1.8, "name": "W520", "network_down": 0.29} ``` -### Using command line options +## Using command line options ``` [me@localhost ~] python simpleApp.py -o $orgId -i myApplication -k $key -t $token ``` -### Additional command line options +## Additional command line options By default the application will attempt to subscribe to all events from all devices in the organization. Three options exist to control the scope of the application: * Device Type * Device Id * Event -#### Example 1: Subscribe to all events from all connected devices +### Example 1: Subscribe to all events from all connected devices ``` [me@localhost ~] python simpleApp.py -o $orgId -i myApplication -k $key -t $token ``` -#### Example 2: Subscribe to all events from all connected devices of a specific type +### Example 2: Subscribe to all events from all connected devices of a specific type ``` [me@localhost ~] python simpleApp.py -o $orgId -i myApplication -k $key -t $token -T $deviceType ``` -#### Example 3: Subscribe to all events from a specific device +### Example 3: Subscribe to all events from a specific device ``` [me@localhost ~] python simpleApp.py -o $orgId -i myApplication -k $key -t $token -T $deviceType -I deviceId ``` -#### Example 4: Subscribe a specific event sent from any connected device +### Example 4: Subscribe a specific event sent from any connected device ``` [me@localhost ~] python simpleApp.py -o $orgId -i myApplication -k $key -t $token -E $event ``` diff --git a/samples/simpleDevice/README.md b/samples/simpleDevice/README.md index 9fd97f4e..76c33fdd 100644 --- a/samples/simpleDevice/README.md +++ b/samples/simpleDevice/README.md @@ -1,26 +1,11 @@ -#Python Simple Device +# Python Simple Device Sample code for a very basic appliation which publishes events and subscribes to commands on the IBM Watson Internet of Things platform. -## QuickStart Usage -Simply run to send an event to the platform: -``` -[me@localhost simpleDevice]$ python3 simpleDevice.py -2019-11-20 11:22:24,386 wiotp.sdk.device.client.DeviceClient INFO Connected successfully: d:quickstart:simpleDev:af77f515-68d3-4c7e-932f-ee21c3be60b9 -Confirmed event 0 received by WIoTP - -2019-11-20 11:22:25,392 wiotp.sdk.device.client.DeviceClient INFO Disconnected from IBM Watson IoT Platform -2019-11-20 11:22:25,392 wiotp.sdk.device.client.DeviceClient INFO Closed connection to the IBM Watson IoT Platform +## Using an device configuration file +Create a file named device.cfg in the simpleDevice directory and insert the org, device type, device id and the authentication token:. ``` - -## Registered Organization Usage -Once you have access to an auth token for a device in an organization in the Internet of Things Cloud additional the device can be used to send events to that organization: - -###Using an device configuration file -Create a file named device.cfg in the simpleDevice directory and insert the org, device type, device id and the authentication token:. -``` - identity: orgId: $InsertOrgID typeId: $InsertDeviceType @@ -30,7 +15,7 @@ Create a file named device.cfg in the simpleDevice directory and insert the org, ``` ``` -[jon@localhost simpleDevice]$ python3 simpleDevice.py -c device.cfg +[jon@localhost simpleDevice]$ python3 simpleDevice.py -c device.cfg wiotp.sdk.device.client.DeviceClient INFO Connected successfully: d::: Confirmed event 0 received by WIoTP @@ -48,7 +33,7 @@ Or to send 100 messages, 1 every two seconds: [me@localhost ~] python3 simpleDevice.py -o $orgId -T $deviceType -I $deviceid -t $token ``` -### Additional command line options +## Additional command line options By default the device will send one message and then disconnect. Two options can be used to send more messages (and stay connected longer) * -N : total number of messages (default 1) * -D : Delay in seconds between message (default 1) diff --git a/samples/simpleDevice/simpleDevice.py b/samples/simpleDevice/simpleDevice.py index f66ac29c..2779405b 100644 --- a/samples/simpleDevice/simpleDevice.py +++ b/samples/simpleDevice/simpleDevice.py @@ -40,7 +40,7 @@ def commandProcessor(cmd): parser = argparse.ArgumentParser() # Primary Options -parser.add_argument("-o", "--organization", required=False, default="quickstart") +parser.add_argument("-o", "--organization", required=False) parser.add_argument("-T", "--typeId", required=False, default="simpleDev") parser.add_argument("-I", "--deviceId", required=False, default=str(uuid.uuid4())) parser.add_argument("-t", "--token", required=False, default=None, help="authentication token") @@ -60,8 +60,6 @@ def commandProcessor(cmd): try: if args.cfg is not None: deviceOptions = wiotp.sdk.device.parseConfigFile(args.cfg) - elif args.organization == "quickstart": - deviceOptions = {"identity": {"orgId": args.organization, "typeId": args.typeId, "deviceId": args.deviceId}} else: deviceOptions = { "identity": {"orgId": args.organization, "typeId": args.typeId, "deviceId": args.deviceId}, diff --git a/setup.py b/setup.py index 492caf82..7d8634b3 100644 --- a/setup.py +++ b/setup.py @@ -1,5 +1,5 @@ # ***************************************************************************** -# Copyright (c) 2014, 2018 IBM Corporation and other Contributors. +# Copyright (c) 2014, 2024 IBM Corporation and other Contributors. # # All rights reserved. This program and the accompanying materials # are made available under the terms of the Eclipse Public License v1.0 @@ -8,6 +8,7 @@ # ***************************************************************************** import sys +import os sys.path.insert(0, 'src') try: @@ -15,35 +16,26 @@ except ImportError: from distutils.core import setup -# ============================================================================= -# Convert README.md to README.rst for pypi -# Need to install both pypandoc and pandoc -# - pip insall pypandoc -# - https://pandoc.org/installing.html -# ============================================================================= -try: - from pypandoc import convert - - def read_md(f): - return convert(f, 'rst') -except: - print('Warning: pypandoc module not found, unable to convert README.md to RST') - print('Unless you are packaging this module for distribution you can ignore this error') +if not os.path.exists('README.rst'): + import pypandoc + pypandoc.download_pandoc(targetfolder='~/bin/') + pypandoc.convert_file('README.md', 'rst', outputfile='README.rst') - def read_md(f): - return "Python SDK for IBM Watson IoT Platform" +here = os.path.abspath(os.path.dirname(__file__)) +with open(os.path.join(here, 'README.rst'), encoding='utf-8') as f: + long_description = f.read() setup( name='wiotp-sdk', - version="0.12.0", + version="1.0.0", author='David Parker', author_email='parkerda@uk.ibm.com', package_dir={'': 'src'}, packages=[ - 'wiotp.sdk', - 'wiotp.sdk.device', - 'wiotp.sdk.gateway', - 'wiotp.sdk.application', + 'wiotp.sdk', + 'wiotp.sdk.device', + 'wiotp.sdk.gateway', + 'wiotp.sdk.application', 'wiotp.sdk.api', 'wiotp.sdk.api.dsc', 'wiotp.sdk.api.registry', @@ -61,26 +53,32 @@ def read_md(f): 'bin/wiotp-cli' ], url='https://github.com/ibm-watson-iot/iot-python', - license=open('LICENSE').read(), - description='Python SDK for IBM Watson IoT Platform', - long_description=read_md('README.md'), + license="Eclipse Public License - v1.0", + description='Python SDK for Maximo IoT and IBM Watson IoT Platform', + long_description=long_description, install_requires=[ "iso8601 >= 0.1.12", "pytz >= 2020.1", "pyyaml >= 5.3.1", - "paho-mqtt >= 1.5.0", + "paho-mqtt >= 1.5.0, < 2.0.0", "requests >= 2.23.0", "requests_toolbelt >= 0.9.1", ], + extras_require={ + 'dev': [ + 'build', + 'pytest' + ] + }, classifiers=[ 'Development Status :: 4 - Beta', 'Intended Audience :: Developers', 'Operating System :: Microsoft :: Windows', 'Operating System :: POSIX :: Linux', 'Programming Language :: Python', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', 'Topic :: Communications', 'Topic :: Internet', 'Topic :: Software Development :: Libraries :: Python Modules' diff --git a/src/wiotp/sdk/application/client.py b/src/wiotp/sdk/application/client.py index 1807655f..cc011ebc 100644 --- a/src/wiotp/sdk/application/client.py +++ b/src/wiotp/sdk/application/client.py @@ -1,5 +1,5 @@ # ***************************************************************************** -# Copyright (c) 2014, 2018 IBM Corporation and other Contributors. +# Copyright (c) 2014, 2024 IBM Corporation and other Contributors. # # All rights reserved. This program and the accompanying materials # are made available under the terms of the Eclipse Public License v1.0 @@ -26,17 +26,17 @@ class ApplicationClient(AbstractClient): """ - Extends #wiotp.AbstractClient to implement an application client supporting + Extends #wiotp.AbstractClient to implement an application client supporting messaging over MQTT - + # Parameters options (dict): Configuration options for the client - logHandlers (list): Log handlers to configure. Defaults to `None`, + logHandlers (list): Log handlers to configure. Defaults to `None`, which will result in a default log handler being created. - + # Configuration Options The options parameter expects a Python dictionary containing the following keys: - + - `auth-key` The API key to to securely connect your application to Watson IoT Platform. - `auth-token` An authentication token to securely connect your application to Watson IoT Platform. - `clean-session` A boolean value indicating whether to use MQTT clean session. @@ -71,9 +71,8 @@ def __init__(self, config, logHandlers=None): self.client.message_callback_add("iot-2/type/+/id/+/err/data", self._onErrorTopic) self.client.message_callback_add("iot-2/thing/type/+/id/+/err/data", self._onThingError) - # Add handler for commands if not connected to QuickStart - if not self._config.isQuickstart(): - self.client.message_callback_add("iot-2/type/+/id/+/cmd/+/fmt/+", self._onDeviceCommand) + # Add handler for commands + self.client.message_callback_add("iot-2/type/+/id/+/cmd/+/fmt/+", self._onDeviceCommand) # Attach fallback handler self.client.on_message = self._onUnsupportedMessage @@ -87,26 +86,22 @@ def __init__(self, config, logHandlers=None): self.errorTopicCallback = None self.appStatusCallback = None - # Create an api client if not connected in QuickStart mode - if not self._config.isQuickstart(): - apiClient = ApiClient(self._config, self.logger) - self.registry = Registry(apiClient) - self.usage = Usage(apiClient) - self.dsc = DSC(apiClient) - self.lec = LEC(apiClient) - self.mgmt = Mgmt(apiClient) - self.serviceBindings = ServiceBindings(apiClient) - self.actions = Actions(apiClient) - self.state = StateMgr(apiClient) - - # We directly expose the get() method via self.serviceStatus() - self._serviceStatus = ServiceStatus(apiClient) + # Create an api client + apiClient = ApiClient(self._config, self.logger) + self.registry = Registry(apiClient) + self.usage = Usage(apiClient) + self.dsc = DSC(apiClient) + self.lec = LEC(apiClient) + self.mgmt = Mgmt(apiClient) + self.serviceBindings = ServiceBindings(apiClient) + self.actions = Actions(apiClient) + self.state = StateMgr(apiClient) + + # We directly expose the get() method via self.serviceStatus() + self._serviceStatus = ServiceStatus(apiClient) def serviceStatus(self): - if not self._config.isQuickstart(): - return self._serviceStatus.get() - else: - return None + return self._serviceStatus.get() def subscribeToDeviceEvents(self, typeId="+", deviceId="+", eventId="+", msgFormat="+", qos=0): """ @@ -125,12 +120,6 @@ def subscribeToDeviceEvents(self, typeId="+", deviceId="+", eventId="+", msgForm the mid argument if you register a subscriptionCallback method. If the subscription fails then the return value will be `0` """ - if self._config.isQuickstart() and deviceId == "+": - self.logger.warning( - "QuickStart applications do not support wildcard subscription to events from all devices" - ) - return 0 - topic = "iot-2/type/%s/id/%s/evt/%s/fmt/%s" % (typeId, deviceId, eventId, msgFormat) return self._subscribe(topic, qos) @@ -148,10 +137,6 @@ def subscribeToDeviceStatus(self, typeId="+", deviceId="+"): the mid argument if you register a subscriptionCallback method. If the subscription fails then the return value will be `0` """ - if self._config.isQuickstart() and deviceId == "+": - self.logger.warning("QuickStart applications do not support wildcard subscription to device status") - return 0 - topic = "iot-2/type/%s/id/%s/mon" % (typeId, deviceId) return self._subscribe(topic, 0) @@ -169,10 +154,6 @@ def subscribeToErrorTopic(self, typeId="+", Id="+"): the mid argument if you register a subscriptionCallback method. If the subscription fails then the return value will be `0` """ - if self._config.isQuickstart() and Id == "+": - self.logger.warning("QuickStart applications do not support wildcard subscription to error topics") - return 0 - topic = "iot-2/type/%s/id/%s/err/data" % (typeId, Id) return self._subscribe(topic, 0) @@ -190,10 +171,6 @@ def subscribeToThingErrors(self, typeId="+", Id="+"): the mid argument if you register a subscriptionCallback method. If the subscription fails then the return value will be `0` """ - if self._config.isQuickstart() and Id == "+": - self.logger.warning("QuickStart applications do not support wildcard subscription to error topics") - return 0 - topic = "iot-2/thing/type/%s/id/%s/err/data" % (typeId, Id) return self._subscribe(topic, 0) @@ -212,10 +189,6 @@ def subscribeToThingState(self, typeId="+", thingId="+", logicalInterfaceId="+") the mid argument if you register a subscriptionCallback method. If the subscription fails then the return value will be `0` """ - if self._config.isQuickstart(): - self.logger.warning("QuickStart applications do not support thing state") - return 0 - topic = "iot-2/thing/type/%s/id/%s/intf/%s/evt/state" % (typeId, thingId, logicalInterfaceId) return self._subscribe(topic, 0) @@ -234,10 +207,6 @@ def subscribeToDeviceState(self, typeId="+", deviceId="+", logicalInterfaceId="+ the mid argument if you register a subscriptionCallback method. If the subscription fails then the return value will be `0` """ - if self._config.isQuickstart(): - self.logger.warning("QuickStart applications do not support device state") - return 0 - topic = "iot-2/type/%s/id/%s/intf/%s/evt/state" % (typeId, deviceId, logicalInterfaceId) return self._subscribe(topic, 0) @@ -258,10 +227,6 @@ def subscribeToDeviceCommands(self, typeId="+", deviceId="+", commandId="+", msg the mid argument if you register a subscriptionCallback method. If the subscription fails then the return value will be `0` """ - if self._config.isQuickstart(): - self.logger.warning("QuickStart applications do not support commands") - return 0 - topic = "iot-2/type/%s/id/%s/cmd/%s/fmt/%s" % (typeId, deviceId, commandId, msgFormat) return self._subscribe(topic, 0) @@ -285,9 +250,6 @@ def publishCommand(self, typeId, deviceId, commandId, msgFormat, data=None, qos= - qos 0 : the client has asynchronously begun to send the event - qos 1 and 2 : the client has confirmation of delivery from WIoTP """ - if self._config.isQuickstart(): - self.logger.warning("QuickStart applications do not support sending commands") - return False if not self.connectEvent.wait(timeout=10): return False else: diff --git a/src/wiotp/sdk/application/config.py b/src/wiotp/sdk/application/config.py index 47effe15..aef86950 100644 --- a/src/wiotp/sdk/application/config.py +++ b/src/wiotp/sdk/application/config.py @@ -1,5 +1,5 @@ # ***************************************************************************** -# Copyright (c) 2014, 2019 IBM Corporation and other Contributors. +# Copyright (c) 2014, 2024 IBM Corporation and other Contributors. # # All rights reserved. This program and the accompanying materials # are made available under the terms of the Eclipse Public License v1.0 @@ -18,7 +18,6 @@ class ApplicationClientConfig(defaultdict): def __init__(self, **kwargs): - # Note: Authentication is not supported for quickstart if "auth" in kwargs: if "key" not in kwargs["auth"] or kwargs["auth"]["key"] is None: raise ConfigurationException("Missing auth.key from configuration") @@ -82,13 +81,10 @@ def __init__(self, **kwargs): dict.__init__(self, **kwargs) - def isQuickstart(self): - return self.orgId == "quickstart" - @property def orgId(self): # Get the orgId from the apikey (format: a-orgid-randomness) - return self.apiKey.split("-")[1] if (self.apiKey is not None) else "quickstart" + return self.apiKey.split("-")[1] @property def appId(self): @@ -168,7 +164,7 @@ def verify(self): def parseEnvVars(): """ - Parse environment variables into a Python dictionary suitable for passing to the + Parse environment variables into a Python dictionary suitable for passing to the application client constructor as the `options` parameter - `WIOTP_IDENTITY_APPID` @@ -247,22 +243,19 @@ def parseEnvVars(): }, "http": {"verify": verifyCert in ["True", "true", "1"]}, }, + "auth": {"key": authKey, "token": authToken} } - # Quickstart doesn't support auth, so ensure we only add this if it's defined - if authToken is not None: - cfg["auth"] = {"key": authKey, "token": authToken} - return ApplicationClientConfig(**cfg) def parseConfigFile(configFilePath): """ - Parse a yaml configuration file into a Python dictionary suitable for passing to the + Parse a yaml configuration file into a Python dictionary suitable for passing to the device client constructor as the `options` parameter - + # Example Configuration File - + identity: appId: myApp auth: @@ -280,7 +273,7 @@ def parseConfigFile(configFilePath): keepAlive: 60 caFile: /path/to/certificateAuthorityFile.pem http: - verify: true + verify: true """ try: diff --git a/src/wiotp/sdk/client.py b/src/wiotp/sdk/client.py index 02872f66..b01fb2fc 100644 --- a/src/wiotp/sdk/client.py +++ b/src/wiotp/sdk/client.py @@ -1,5 +1,5 @@ # ***************************************************************************** -# Copyright (c) 2014, 2019 IBM Corporation and other Contributors. +# Copyright (c) 2014, 2024 IBM Corporation and other Contributors. # # All rights reserved. This program and the accompanying materials # are made available under the terms of the Eclipse Public License v1.0 @@ -31,9 +31,9 @@ class AbstractClient(object): """ - The underlying client object utilised for Platform connectivity over MQTT + The underlying client object utilised for Platform connectivity over MQTT in devices, gateways, and applications. - + # Parameters domain (string): Domain denoting the instance of IBM Watson IoT Platform to connect to organization (string): IBM Watson IoT Platform organization ID to connect to @@ -41,13 +41,13 @@ class AbstractClient(object): username (string): MQTT username for the underlying Paho client password (string): MQTT password for the underlying Paho client port (int): MQTT port for the underlying Paho client to connect using. Defaults to `8883` - logHandlers (list): Log handlers to configure. Defaults to `None`, + logHandlers (list): Log handlers to configure. Defaults to `None`, which will result in a default log handler being created. cleanStart (string): Defaults to `false`. Although this is a true|false parameter sessionExpiry (string): Defaults to 3600 seconds. Does nothing today (pending MQTT v5) transport (string): Defaults to `tcp` caFile (string): Defaults to None - + # Attributes client (paho.mqtt.client.Client): Built-in Paho MQTT client handling connectivity for the client. logger (logging.logger): Client logger. @@ -151,19 +151,15 @@ def __init__( # TLS 1.2 is unavailable because the configuration explicitly requested # to use encrypted connection elif self.port is None: - if self.organization == "quickstart": + try: + self.tlsVersion = ssl.PROTOCOL_TLSv1_2 + self.port = 8883 + except: self.tlsVersion = None self.port = 1883 - else: - try: - self.tlsVersion = ssl.PROTOCOL_TLSv1_2 - self.port = 8883 - except: - self.tlsVersion = None - self.port = 1883 - self.logger.warning( - "Unable to encrypt messages because TLSv1.2 is unavailable (MQTT over SSL requires at least Python v2.7.9 or 3.4 and openssl v1.0.1)" - ) + self.logger.warning( + "Unable to encrypt messages because TLSv1.2 is unavailable (MQTT over SSL requires at least Python v2.7.9 or 3.4 and openssl v1.0.1)" + ) else: raise Exception("Unsupported value for port override: %s. Supported values are 1883 & 8883." % self.port) @@ -204,10 +200,10 @@ def __init__( def getMessageCodec(self, messageFormat): """ Get the Python class that is currently defined as the encoder/decoder for a specified message format. - + # Arguments messageFormat (string): The message format to retrieve the encoder for - + # Returns code (class): The python class, or `None` if there is no codec defined for the `messageFormat` """ @@ -218,7 +214,7 @@ def getMessageCodec(self, messageFormat): def setMessageCodec(self, messageFormat, codec): """ Set a Python class as the encoder/decoder for a specified message format. - + # Arguments messageFormat (string): The message format to retreive the encoder for codec (class): The Python class (subclass of `wiotp.common.MessageCodec` to set as the encoder/decoder for `messageFormat` @@ -228,7 +224,7 @@ def setMessageCodec(self, messageFormat, codec): def _logAndRaiseException(self, e): """ Logs an exception at log level `critical` before raising it. - + # Arguments e (Exception): The exception to log/raise """ @@ -238,7 +234,7 @@ def _logAndRaiseException(self, e): def connect(self): """ Connect the client to IBM Watson IoT Platform using the underlying Paho MQTT client - + # Raises ConnectionException: If there is a problem establishing the connection. """ @@ -285,17 +281,17 @@ def isConnected(self): def _onLog(self, mqttc, obj, level, string): """ - Called when the client has log information. - + Called when the client has log information. + See [paho.mqtt.python#on_log](https://github.com/eclipse/paho.mqtt.python#on_log) for more information - + # Parameters mqttc (paho.mqtt.client.Client): The client instance for this callback obj (object): The private user data as set in Client() or user_data_set() - level (int): The severity of the message, will be one of `MQTT_LOG_INFO`, + level (int): The severity of the message, will be one of `MQTT_LOG_INFO`, `MQTT_LOG_NOTICE`, `MQTT_LOG_WARNING`, `MQTT_LOG_ERR`, and `MQTT_LOG_DEBUG`. string (string): The log message itself - + """ self.logger.debug("%d %s" % (level, string)) @@ -347,16 +343,16 @@ def _onConnect(self, mqttc, userdata, flags, rc): def _onDisconnect(self, mqttc, obj, rc): """ Called when the client disconnects from IBM Watson IoT Platform. - + See [paho.mqtt.python#on_disconnect](https://github.com/eclipse/paho.mqtt.python#on_disconnect) for more information - + # Parameters mqttc (paho.mqtt.client.Client): The client instance for this callback obj (object): The private user data as set in Client() or user_data_set() - rc (int): indicates the disconnection state. If `MQTT_ERR_SUCCESS` (0), the callback was - called in response to a `disconnect()` call. If any other value the disconnection was + rc (int): indicates the disconnection state. If `MQTT_ERR_SUCCESS` (0), the callback was + called in response to a `disconnect()` call. If any other value the disconnection was unexpected, such as might be caused by a network error. - + """ # Clear the event to indicate we're no longer connected self.connectEvent.clear() @@ -369,9 +365,9 @@ def _onDisconnect(self, mqttc, obj, rc): def _onPublish(self, mqttc, obj, mid): """ Called when a message from the client has been successfully sent to IBM Watson IoT Platform. - + See [paho.mqtt.python#on_publish](https://github.com/eclipse/paho.mqtt.python#on_publish) for more information - + # Parameters mqttc (paho.mqtt.client.Client): The client instance for this callback obj (object): The private user data as set in Client() or user_data_set() diff --git a/src/wiotp/sdk/device/client.py b/src/wiotp/sdk/device/client.py index d30c43f4..8c738248 100644 --- a/src/wiotp/sdk/device/client.py +++ b/src/wiotp/sdk/device/client.py @@ -1,5 +1,5 @@ # ***************************************************************************** -# Copyright (c) 2014, 2018 IBM Corporation and other Contributors. +# Copyright (c) 2014, 2024 IBM Corporation and other Contributors. # # All rights reserved. This program and the accompanying materials # are made available under the terms of the Eclipse Public License v1.0 @@ -26,12 +26,12 @@ class DeviceClient(AbstractClient): """ - Extends #wiotp.common.AbstractClient to implement a device client supporting + Extends #wiotp.common.AbstractClient to implement a device client supporting messaging over MQTT - + # Parameters options (dict): Configuration options for the client - logHandlers (list): Log handlers to configure. Defaults to `None`, + logHandlers (list): Log handlers to configure. Defaults to `None`, which will result in a default log handler being created. """ @@ -57,16 +57,14 @@ def __init__(self, config, logHandlers=None): logHandlers=logHandlers, ) - # Add handler for commands if not connected to QuickStart - if not self._config.isQuickstart(): - self.client.message_callback_add("iot-2/cmd/+/fmt/+", self._onCommand) + # Add handler for commands + self.client.message_callback_add("iot-2/cmd/+/fmt/+", self._onCommand) # Initialize user supplied callback self.commandCallback = None - # Register startup subscription list (only for non-Quickstart) - if not self._config.isQuickstart(): - self._subscriptions[self._COMMAND_TOPIC] = 1 + # Register startup subscription list + self._subscriptions[self._COMMAND_TOPIC] = 1 def publishEvent(self, eventId, msgFormat, data, qos=0, onPublish=None): """ @@ -77,13 +75,13 @@ def publishEvent(self, eventId, msgFormat, data, qos=0, onPublish=None): msgFormat (string): Format of the data for this event data (dict): Data for this event qos (int): MQTT quality of service level to use (`0`, `1`, or `2`) - onPublish(function): A function that will be called when receipt - of the publication is confirmed. - + onPublish(function): A function that will be called when receipt + of the publication is confirmed. + # Callback and QoS - The use of the optional #onPublish function has different implications depending - on the level of qos used to publish the event: - + The use of the optional #onPublish function has different implications depending + on the level of qos used to publish the event: + - qos 0: the client has asynchronously begun to send the event - qos 1 and 2: the client has confirmation of delivery from the platform """ diff --git a/src/wiotp/sdk/device/config.py b/src/wiotp/sdk/device/config.py index 4c6d1991..f66a34a0 100644 --- a/src/wiotp/sdk/device/config.py +++ b/src/wiotp/sdk/device/config.py @@ -1,5 +1,5 @@ # ***************************************************************************** -# Copyright (c) 2014, 2019 IBM Corporation and other Contributors. +# Copyright (c) 2014, 2024 IBM Corporation and other Contributors. # # All rights reserved. This program and the accompanying materials # are made available under the terms of the Eclipse Public License v1.0 @@ -27,15 +27,10 @@ def __init__(self, **kwargs): if "deviceId" not in kwargs["identity"] or kwargs["identity"]["deviceId"] is None: raise ConfigurationException("Missing identity.deviceId from configuration") - # Authentication is not supported for quickstart - if kwargs["identity"]["orgId"] == "quickstart": - if "auth" in kwargs: - raise ConfigurationException("Quickstart service does not support device authentication") - else: - if "auth" not in kwargs: - raise ConfigurationException("Missing auth from configuration") - if "token" not in kwargs["auth"] or kwargs["auth"]["token"] is None: - raise ConfigurationException("Missing auth.token from configuration") + if "auth" not in kwargs: + raise ConfigurationException("Missing auth from configuration") + if "token" not in kwargs["auth"] or kwargs["auth"]["token"] is None: + raise ConfigurationException("Missing auth.token from configuration") if "options" in kwargs and "mqtt" in kwargs["options"]: # validate port @@ -81,9 +76,6 @@ def __init__(self, **kwargs): dict.__init__(self, **kwargs) - def isQuickstart(self): - return self["identity"]["orgId"] == "quickstart" - @property def orgId(self): return self["identity"]["orgId"] @@ -183,7 +175,7 @@ def parseEnvVars(): raise ConfigurationException("Missing WIOTP_IDENTITY_TYPEID environment variable") if deviceId is None: raise ConfigurationException("Missing WIOTP_IDENTITY_DEVICEID environment variable") - if orgId != "quickstart" and authToken is None: + if authToken is None: raise ConfigurationException("Missing WIOTP_AUTH_TOKEN environment variable") if port is not None: try: @@ -221,12 +213,9 @@ def parseEnvVars(): "keepAlive": keepAlive, }, }, + "auth": {"token": authToken} } - # Quickstart doesn't support auth, so ensure we only add this if it's defined - if authToken is not None: - cfg["auth"] = {"token": authToken} - return DeviceClientConfig(**cfg) diff --git a/src/wiotp/sdk/device/managedClient.py b/src/wiotp/sdk/device/managedClient.py index 8ea9e909..9d4031af 100644 --- a/src/wiotp/sdk/device/managedClient.py +++ b/src/wiotp/sdk/device/managedClient.py @@ -1,5 +1,5 @@ # ***************************************************************************** -# Copyright (c) 2014, 2018 IBM Corporation and other Contributors. +# Copyright (c) 2014, 2024 IBM Corporation and other Contributors. # # All rights reserved. This program and the accompanying materials # are made available under the terms of the Eclipse Public License v1.0 @@ -62,9 +62,6 @@ class ManagedDeviceClient(DeviceClient): UPDATESTATE_INVALID_URI = 6 def __init__(self, config, logHandlers=None, deviceInfo=None): - if config["identity"]["orgId"] == "quickstart": - raise ConfigurationException("QuickStart does not support device management") - DeviceClient.__init__(self, config, logHandlers) # Initialize user supplied callback diff --git a/src/wiotp/sdk/gateway/managedClient.py b/src/wiotp/sdk/gateway/managedClient.py index cc40509b..2452cbed 100644 --- a/src/wiotp/sdk/gateway/managedClient.py +++ b/src/wiotp/sdk/gateway/managedClient.py @@ -1,5 +1,5 @@ # ***************************************************************************** -# Copyright (c) 2016, 2018 IBM Corporation and other Contributors. +# Copyright (c) 2016, 2024 IBM Corporation and other Contributors. # # All rights reserved. This program and the accompanying materials # are made available under the terms of the Eclipse Public License v1.0 @@ -50,9 +50,6 @@ def __init__(self, config, logHandlers=None, deviceInfo=None): """ Override the constructor """ - if config["identity"]["orgId"] == "quickstart": - raise ConfigurationException("QuickStart does not support device management") - self._config = GatewayClientConfig(**config) AbstractClient.__init__( diff --git a/test/test_api_actions.py b/test/test_api_actions.py index 7ef207cb..68f33af9 100644 --- a/test/test_api_actions.py +++ b/test/test_api_actions.py @@ -1,5 +1,5 @@ # ***************************************************************************** -# Copyright (c) 2019 IBM Corporation and other Contributors. +# Copyright (c) 2019, 2024 IBM Corporation and other Contributors. # # All rights reserved. This program and the accompanying materials # are made available under the terms of the Eclipse Public License v1.0 diff --git a/test/test_device_cfg.py b/test/test_device_cfg.py index 25c37c20..c27a3c99 100644 --- a/test/test_device_cfg.py +++ b/test/test_device_cfg.py @@ -1,5 +1,5 @@ # ***************************************************************************** -# Copyright (c) 2016-2019 IBM Corporation and other Contributors. +# Copyright (c) 2016, 2024 IBM Corporation and other Contributors. # # All rights reserved. This program and the accompanying materials # are made available under the terms of the Eclipse Public License v1.0 @@ -41,16 +41,6 @@ def testMissingId(self): ) assert e.value.reason == "Missing identity.deviceId from configuration" - def testQuickstartWithAuth(self): - with pytest.raises(wiotp.sdk.ConfigurationException) as e: - wiotp.sdk.device.DeviceClient( - { - "identity": {"orgId": "quickstart", "typeId": "myType", "deviceId": "myDevice"}, - "auth": {"token": "myToken"}, - } - ) - assert e.value.reason == "Quickstart service does not support device authentication" - def testMissingAuth(self): with pytest.raises(wiotp.sdk.ConfigurationException) as e: wiotp.sdk.device.DeviceClient({"identity": {"orgId": "myOrg", "typeId": "myType", "deviceId": "myDevice"}}) @@ -67,7 +57,7 @@ def testPortNotInteger(self): with pytest.raises(wiotp.sdk.ConfigurationException) as e: wiotp.sdk.device.DeviceClient( { - "identity": {"orgId": "quickstart", "typeId": "myType", "deviceId": "myDevice"}, + "identity": {"orgId": "myOrg", "typeId": "myType", "deviceId": "myDevice"}, "options": {"mqtt": {"port": "notAnInteger"}}, } ) diff --git a/test/test_device_mgd.py b/test/test_device_mgd.py index 4cb56784..f7496888 100644 --- a/test/test_device_mgd.py +++ b/test/test_device_mgd.py @@ -1,5 +1,5 @@ # ***************************************************************************** -# Copyright (c) 2016,2018 IBM Corporation and other Contributors. +# Copyright (c) 2016, 2024 IBM Corporation and other Contributors. # # All rights reserved. This program and the accompanying materials # are made available under the terms of the Eclipse Public License v1.0 @@ -18,12 +18,6 @@ class TestDeviceMgd(testUtils.AbstractTest): - def testManagedDeviceQSException(self): - with pytest.raises(wiotp.sdk.ConfigurationException) as e: - options = {"identity": {"orgId": "quickstart", "typeId": "xxx", "deviceId": "xxx"}} - wiotp.sdk.device.ManagedDeviceClient(options) - assert "QuickStart does not support device management" == e.value.reason - def testManagedDeviceConnectException(self, device): badOptions = { "identity": {"orgId": self.ORG_ID, "typeId": device.typeId, "deviceId": device.deviceId}, diff --git a/test/test_gateway_mgd.py b/test/test_gateway_mgd.py index 8d230054..75578439 100644 --- a/test/test_gateway_mgd.py +++ b/test/test_gateway_mgd.py @@ -1,5 +1,5 @@ # ***************************************************************************** -# Copyright (c) 2016-2019 IBM Corporation and other Contributors. +# Copyright (c) 2016, 2024 IBM Corporation and other Contributors. # # All rights reserved. This program and the accompanying materials # are made available under the terms of the Eclipse Public License v1.0 @@ -20,12 +20,6 @@ class TestGatewayMgd(testUtils.AbstractTest): registeredDevice = None registeredGateway = None - def testManagedgatewayQSException(self): - with pytest.raises(wiotp.sdk.ConfigurationException) as e: - options = {"identity": {"orgId": "quickstart", "typeId": "xxx", "deviceId": "xxx"}} - wiotp.sdk.gateway.ManagedGatewayClient(options) - assert "QuickStart does not support device management" == e.value.reason - def testManagedGatewayConnectException(self, gateway): badOptions = { "identity": {"orgId": self.ORG_ID, "typeId": gateway.typeId, "deviceId": gateway.deviceId}, diff --git a/tox.ini b/tox.ini deleted file mode 100644 index 9b490abb..00000000 --- a/tox.ini +++ /dev/null @@ -1,40 +0,0 @@ -# Tox (https://tox.readthedocs.io/) is a tool for running tests -# in multiple virtualenvs. This configuration file will run the -# test suite on all supported python versions. To use it, "pip install tox" -# and then run "tox" from this directory. -# -# tox -e py37 -- test/test_api_registry_devices.py -# -# Sometimes tox fails to install the module being developed ... if hit this again -# add the following line to commands list -# python setup.py install - -[tox] -envlist = py36, py37, py38 - -[testenv] -deps = - flake8 - nose - pytest - pytest-cov - coverage - py38: black -commands = - py38: pip install black - py38: black -l 120 src samples test - flake8 --count --select=E9,F63,F72,F82 --show-source --statistics src samples test - pytest --cov=wiotp.sdk {posargs} -passenv = - ONE_JOB_ONLY_TESTS - WIOTP_API_KEY WIOTP_API_TOKEN - WIOTP_OPTIONS_DOMAIN WIOTP_OPTIONS_HTTP_VERIFY - CLOUDANT_HOST CLOUDANT_PORT CLOUDANT_USERNAME CLOUDANT_PASSWORD - EVENTSTREAMS_API_KEY EVENTSTREAMS_ADMIN_URL EVENTSTREAMS_USER EVENTSTREAMS_PASSWORD - EVENTSTREAMS_BROKER1 EVENTSTREAMS_BROKER2 EVENTSTREAMS_BROKER3 EVENTSTREAMS_BROKER4 EVENTSTREAMS_BROKER5 - DB2_PORT DB2_USERNAME DB2_PASSWORD DB2_HTTPS_URL DB2_SSL_DSN DB2_HOST DB2_URI DB2_DB DB2_SSLJDCURL DB2_JDBCURL - POSTGRES_HOSTNAME POSTGRES_PORT POSTGRES_USERNAME POSTGRES_PASSWORD POSTGRES_CERTIFICATE POSTGRES_DATABASE - -[pytest] -minversion=2.0 -python_files=test/test_*.py test/testUtils/*.py