|
| 1 | +# OCI IoT Platform Device Workflow Demo Python script |
| 2 | + |
| 3 | +Sample Python application to create, query, and delete devices to illustrate the use of the |
| 4 | +[OCI Python SDK](https://docs.oracle.com/en-us/iaas/tools/python/latest/), in particular |
| 5 | +the bindings for the [OCI IoT Platform](https://docs.oracle.com/en-us/iaas/tools/python/latest/api/iot.html) |
| 6 | +as well as the [ORDS API](https://docs.oracle.com/iaas/tools/internet-of-things/data-api/index.html) |
| 7 | +for data access. |
| 8 | + |
| 9 | +The `manage-dt` package consists of several source files and can be installed with `pip` |
| 10 | +(see below). If you are only interested in looking at the sample code: |
| 11 | + |
| 12 | +- [mdt_iot_oci.py](./manage_dt/mdt_iot_oci.py) contains the calls to the OCI Python SDK |
| 13 | +- [mdt_iot_data.py](./manage_dt/mdt_iot_data.py) focuses on the ORDS data API |
| 14 | + |
| 15 | +## Prerequisites |
| 16 | + |
| 17 | +The application assumes that you have already set up an OCI IoT Platform environment in |
| 18 | +your OCI tenancy: |
| 19 | + |
| 20 | +- an OCI IoT Domain Group and Domain |
| 21 | +- OCI Vault secrets and/or OCI Certificates for your device credentials |
| 22 | +- a Confidential Application to issue tokens for querying telemetry via ORDS. This step is |
| 23 | + only required for the `query` command of `manage-dt`. |
| 24 | + |
| 25 | +## Installation |
| 26 | + |
| 27 | +To install and run the `manage-dt` application, you need **Python 3.12 or higher**. |
| 28 | + |
| 29 | +We recommend installing in an isolated virtual environment: |
| 30 | + |
| 31 | +```sh |
| 32 | +# Ensure Python 3.12+ is available |
| 33 | +python3 --version |
| 34 | + |
| 35 | +# Create and activate a new virtual environment |
| 36 | +python3 -m venv venv |
| 37 | +source venv/bin/activate |
| 38 | + |
| 39 | +# Upgrade pip (optional but recommended) |
| 40 | +python -m pip install --upgrade pip |
| 41 | + |
| 42 | +# Install the application and dependencies |
| 43 | +pip install . |
| 44 | +``` |
| 45 | + |
| 46 | +You can now use the console command: |
| 47 | + |
| 48 | +```sh |
| 49 | +manage-dt --help |
| 50 | +``` |
| 51 | + |
| 52 | +## Configuration file |
| 53 | + |
| 54 | +The application gets its configuration from the `iot_config.yaml` file in the `data` |
| 55 | +directory. Copy the `iot_config.distr.yaml` template to `iot_config.yaml` and enter the |
| 56 | +required data. |
| 57 | + |
| 58 | +### `iot` section |
| 59 | + |
| 60 | +This section is for the IoT Platform parameters: |
| 61 | + |
| 62 | +- `domain_id`: the OCID of your IoT Domain |
| 63 | + |
| 64 | +### `identity` section |
| 65 | + |
| 66 | +This section contains the OCI Identity parameters and relates to the OAuth configuration |
| 67 | +needed to access the ORDS API. |
| 68 | + |
| 69 | +This section is needed for the `query` command of the application. |
| 70 | + |
| 71 | +- `app_client_id` and `app_client_secret`: the Confidential Application Client ID and |
| 72 | + Secret. You can find these in the OCI Console: `Identity > Domain > Your Domain > |
| 73 | + Integrated App > OAuth` |
| 74 | +- `user` and `password`: the OCI User and Password in the Identity Domain where the |
| 75 | + Confidential Application is created. Typically, this will be your OCI username and |
| 76 | + password, unless the application is created in a separate Identity Domain. |
| 77 | + |
| 78 | +### `environ` section |
| 79 | + |
| 80 | +Define here environment variables which can be referenced in the `digital_twins` |
| 81 | +section. The sample data defines a `device_key` variable, used to name your digital |
| 82 | +twins and avoid duplicate names if different users run the same sample application. |
| 83 | +It also defines `auth_id`, the OCID of a Vault Secret containing the password for the device. |
| 84 | + |
| 85 | +### `digital_twins` section |
| 86 | + |
| 87 | +Describes the Digital Twins that can be managed by the application. The sample data |
| 88 | +provides 3 different digital twins: |
| 89 | + |
| 90 | +- `unstructured`: a Digital Twin with unstructured telemetry |
| 91 | +- `default-adapter`: a Digital Twin with structured telemetry using the default Adapter |
| 92 | +- `custom-adapter`: a Digital Twin with structured telemetry with a custom Adapter |
| 93 | + |
| 94 | +You can use these definitions as-is, customize them, or create your own Digital Twin |
| 95 | +definitions. |
| 96 | + |
| 97 | +For each Digital Twin, you must have: |
| 98 | + |
| 99 | +- `device_name`: the name of the device |
| 100 | +- `external_key`: the username the device will use to authenticate |
| 101 | +- `auth_id`: the OCID of a Vault Secret containing the password for the device |
| 102 | + (Basic authentication) or the OCID of a Certificate for the device (Certificate |
| 103 | + authentication) |
| 104 | + |
| 105 | +These parameters are sufficient for a Digital Twin with unstructured telemetry. |
| 106 | + |
| 107 | +Additionally, for a Digital Twin with structured telemetry using the default Adapter, you |
| 108 | +also need to provide a model: |
| 109 | + |
| 110 | +- `model_name`: the Digital Twin Model Identifier (DTMI) for your model |
| 111 | +- `model_description`: a plain text description of the model |
| 112 | +- `model_dtdl`: the name of a file containing the model definition (relative to the data |
| 113 | + directory) |
| 114 | +- `adapter_name`: name for the adapter that will be generated |
| 115 | + |
| 116 | +Finally, for a Digital Twin with structured telemetry with a custom Adapter you will need |
| 117 | +to provide the Adapter definition: |
| 118 | + |
| 119 | +- `adapter_name`: the name of the Adapter |
| 120 | +- `adapter_envelope`: the name of a file containing the inbound envelope definition |
| 121 | + (relative to the data directory) |
| 122 | +- `adapter_routes`: the name of a file containing the inbound routes definition (relative |
| 123 | + to the data directory) |
| 124 | + |
| 125 | +The sample definitions in this repository are for an Environmental Sensor reporting two |
| 126 | +temperatures in degrees Celsius, pressure in hectoPascals, and relative humidity in |
| 127 | +percent. |
| 128 | + |
| 129 | +Note that while the OCI CLI uses hyphen-separated words for the Adapter definitions, the |
| 130 | +API uses camelCase notation. For example, `envelope-mapping` for the OCI CLI becomes |
| 131 | +`envelopeMapping` when using the Python SDK or the API. |
| 132 | + |
| 133 | +**Note:** The structured telemetry defined in this script is based on the payload sent by |
| 134 | +an [M5Stack CoreS3](https://docs.m5stack.com/en/core/CoreS3) device with an |
| 135 | +[Env-III](https://docs.m5stack.com/en/unit/envIII) environmental sensor. |
| 136 | +The expected payload is: |
| 137 | + |
| 138 | +```json |
| 139 | +{ |
| 140 | + "sht_temperature": 23.8, |
| 141 | + "qmp_temperature": 24.4, |
| 142 | + "humidity": 56.1, |
| 143 | + "pressure": 1012.2, |
| 144 | + "count": 5479 |
| 145 | +} |
| 146 | +``` |
| 147 | + |
| 148 | +This payload can be emulated by the various sample scripts in this repository. |
| 149 | + |
| 150 | +## Usage |
| 151 | + |
| 152 | +```text |
| 153 | +$ Usage: manage-dt [OPTIONS] COMMAND [ARGS]... |
| 154 | +
|
| 155 | + Manage Digital Twins. |
| 156 | +
|
| 157 | + Sample application to create/query/delete Digital Twins for the OCI Iot |
| 158 | + Platform. |
| 159 | +
|
| 160 | +Options: |
| 161 | + -v, --verbose Verbose mode |
| 162 | + -d, --debug Debug mode |
| 163 | + --profile TEXT The profile in the config file to load. |
| 164 | + [default: wim-iot-fra] |
| 165 | + --auth [api_key|instance_principal|resource_principal] |
| 166 | + The type of auth to use for the API request. |
| 167 | + [default: api_key] |
| 168 | + --data-dir DIRECTORY Data directory [default: ./data] |
| 169 | + --iot-config-file FILE The path to the IoT config file. [default: |
| 170 | + iot_config.yaml] |
| 171 | + --version Show the version and exit. |
| 172 | + --help Show this message and exit. |
| 173 | +
|
| 174 | +Commands: |
| 175 | + create Create a new Digital Twin. |
| 176 | + delete Delete a Digital Twin. |
| 177 | + query Query recent data for a Digital Twin. |
| 178 | +``` |
| 179 | + |
| 180 | +The `verbose`/`debug` options control the verbosity of the tool. |
| 181 | + |
| 182 | +The `profile` and `auth` options are similar to those used with the OCI CLI. Similarly, |
| 183 | +they can be set with environment variables: `OCI_CLI_PROFILE` and `OCI_CLI_AUTH` |
| 184 | +respectively. |
| 185 | + |
| 186 | +`data-dir` allows you to specify the location of the data directory, and `iot-config-file` |
| 187 | +can be used to specify an alternative configuration file. |
| 188 | + |
| 189 | +The script supports three basic commands: |
| 190 | + |
| 191 | +- `create`: creates the Digital Twin (with the Model and Adapter if applicable) |
| 192 | +- `query`: queries the content of the Digital Twin (structured telemetry) as well as the |
| 193 | + Raw Data, Historized Data, and Rejected Data using the ORDS Data API. By default, data |
| 194 | + for the last 5 minutes is retrieved. You can use the `last-minutes` parameter to |
| 195 | + specify a different duration. |
| 196 | +- `delete`: deletes the Digital Twin (with the Model and Adapter if applicable) |
| 197 | + |
| 198 | +## Sample session |
| 199 | + |
| 200 | +```text |
| 201 | +$ manage-dt --debug create custom-adapter |
| 202 | +2025-09-18 20:30:59,503 - DEBUG - cli.py - Loading configuration |
| 203 | +2025-09-18 20:30:59,505 - DEBUG - mdt_oci.py - OCI authentication: Config file |
| 204 | +2025-09-18 20:30:59,506 - DEBUG - config.py - Config file found at ~/.oci/config |
| 205 | +2025-09-18 20:30:59,575 - INFO - mdt_iot_oci.py - Create Digital Twin Model 'dtmi:com:oracle:pvhd:m5:env:cstm;1' |
| 206 | +2025-09-18 20:30:59,795 - INFO - mdt_iot_oci.py - Digital Twin Model created |
| 207 | +2025-09-18 20:30:59,798 - INFO - mdt_iot_oci.py - Create custom Digital Twin Adapter 'pvhd-m5-env-cstm-adapter' |
| 208 | +2025-09-18 20:31:01,116 - INFO - mdt_iot_oci.py - Digital Twin Adapter created |
| 209 | +2025-09-18 20:31:01,116 - INFO - mdt_iot_oci.py - Create Digital Twin Instance pvhd-m5-env-cstm-01 - Structured Telemetry with custom Adapter |
| 210 | +2025-09-18 20:31:02,947 - INFO - mdt_iot_oci.py - Digital Twin Instance created |
| 211 | +$ # Send some data and query: |
| 212 | +$ manage-dt --debug query custom-adapter |
| 213 | +2025-09-18 20:32:39,566 - DEBUG - cli.py - Loading configuration |
| 214 | +2025-09-18 20:32:39,569 - DEBUG - mdt_oci.py - OCI authentication: Config file |
| 215 | +2025-09-18 20:32:39,569 - DEBUG - config.py - Config file found at ~/.oci/config |
| 216 | +2025-09-18 20:32:40,356 - INFO - mdt_iot_oci.py - Digital Twin Instance retrieved |
| 217 | +2025-09-18 20:32:40,357 - INFO - mdt_iot_oci.py - Query Digital Twin Instance 'custom-adapter' |
| 218 | +2025-09-18 20:32:40,556 - INFO - mdt_iot_oci.py - Digital Twin Instance content retrieved |
| 219 | +╭────────────────────────────────────────── Digital Twin Instance content ────────────────────────────╮ |
| 220 | +│ {'humidity': 73, 'count': 2, 'pressure': 1023, 'sht_temperature': 22.3, 'qmp_temperature': 22.1} │ |
| 221 | +╰─────────────────────────────────────────────────────────────────────────────────────────────────────╯ |
| 222 | +2025-09-18 20:32:40,573 - DEBUG - mdt_iot_data.py - Using cached data access configuration and token |
| 223 | + Recent raw data - 2 record(s) |
| 224 | +┏━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┓ |
| 225 | +┃ Id ┃ Time received (UTC) ┃ Endpoint ┃ |
| 226 | +┡━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━┩ |
| 227 | +│ 535 │ 2025-09-18T18:32:28.847244Z │ iot/pvhd │ |
| 228 | +│ 536 │ 2025-09-18T18:32:34.621067Z │ iot/pvhd │ |
| 229 | +└─────┴─────────────────────────────┴──────────┘ |
| 230 | + Recent historized data - 4 record(s) |
| 231 | +┏━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━┓ |
| 232 | +┃ Id ┃ Time observed (UTC) ┃ Content path ┃ Value ┃ |
| 233 | +┡━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━┩ |
| 234 | +│ 1852 │ 2025-09-18T18:32:28.847244Z │ sht_temperature │ 22.3 │ |
| 235 | +│ 1853 │ 2025-09-18T18:32:28.847244Z │ humidity │ 73 │ |
| 236 | +│ 1854 │ 2025-09-18T18:32:28.847244Z │ pressure │ 1023 │ |
| 237 | +│ 1855 │ 2025-09-18T18:32:28.847244Z │ qmp_temperature │ 22.1 │ |
| 238 | +└──────┴─────────────────────────────┴─────────────────┴───────┘ |
| 239 | + Recent rejected data - 1 record(s) |
| 240 | +┏━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ |
| 241 | +┃ Id ┃ Time received (UTC) ┃ Endpoint ┃ Reason code ┃ Reason message ┃ |
| 242 | +┡━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩ |
| 243 | +│ 48 │ 2025-09-18T18:32:34.621067Z │ iot/pvhd │ 500 │ Normalization output is empty │ |
| 244 | +└────┴─────────────────────────────┴──────────┴─────────────┴───────────────────────────────┘ |
| 245 | +$ manage-dt --debug delete custom-adapter |
| 246 | +2025-09-18 20:33:33,603 - DEBUG - cli.py - Loading configuration |
| 247 | +2025-09-18 20:33:33,605 - DEBUG - mdt_oci.py - OCI authentication: Config file |
| 248 | +2025-09-18 20:33:33,605 - DEBUG - config.py - Config file found at ~/.oci/config |
| 249 | +2025-09-18 20:33:33,673 - INFO - mdt_iot_oci.py - Delete Digital Twin Instance 'custom-adapter' |
| 250 | +2025-09-18 20:33:34,514 - INFO - mdt_iot_oci.py - Digital Twin Instance deleted |
| 251 | +2025-09-18 20:33:34,514 - INFO - mdt_iot_oci.py - Delete Digital Twin Adapter 'pvhd-m5-env-cstm-adapter' |
| 252 | +2025-09-18 20:33:35,434 - INFO - mdt_iot_oci.py - Digital Twin Adapter deleted |
| 253 | +2025-09-18 20:33:35,434 - INFO - mdt_iot_oci.py - Delete Digital Twin Model 'dtmi:com:oracle:pvhd:m5:env:cstm;1' |
| 254 | +2025-09-18 20:33:36,204 - INFO - mdt_iot_oci.py - Digital Twin Model deleted |
| 255 | +$ |
| 256 | +``` |
| 257 | + |
| 258 | +## Caveat |
| 259 | + |
| 260 | +As we do not store OCIDs of the IoT objects created, identification is done based on |
| 261 | +_Display Name_. However, as with most OCI resources, the _Display Name_ is not a unique |
| 262 | +identifier! While it is not a recommended practice, it is possible to have two Digital |
| 263 | +Twins with the same name. This sample application might not handle such cases properly. |
0 commit comments