From ef4ec5163e14b195485cfe1369d1e70c10285919 Mon Sep 17 00:00:00 2001 From: Simone Orru Date: Mon, 4 Nov 2024 15:42:53 +0100 Subject: [PATCH] Unify the MQTT examples Unify the MQTT examples into a single configurable example. Signed-off-by: Simone Orru --- .github/workflows/check-examples-mqtt.yaml | 44 +-- examples/datastreams/main.py | 217 ------------- examples/event_listener/README.md | 42 --- examples/event_listener/config.toml | 22 -- examples/event_listener/event_listener.py | 145 --------- ...-platform.genericsensors.SamplingRate.json | 25 -- examples/example_device/README.md | 42 --- examples/example_device/config.toml | 22 -- examples/example_device/example_device.py | 142 -------- ...tform.genericsensors.AvailableSensors.json | 23 -- ...e-platform.genericsensors.Geolocation.json | 54 ---- ...starte-platform.genericsensors.Values.json | 18 -- examples/{datastreams => mqtt}/README.md | 0 examples/{datastreams => mqtt}/config.toml | 7 + ...tform.python.examples.DeviceAggregate.json | 82 +++++ ...form.python.examples.DeviceDatastream.json | 0 ...atform.python.examples.DeviceProperty.json | 81 +++++ ...tform.python.examples.ServerAggregate.json | 82 +++++ ...form.python.examples.ServerDatastream.json | 0 ...atform.python.examples.ServerProperty.json | 81 +++++ examples/mqtt/main.py | 156 +++++++++ examples/mqtt/transmit_data.py | 302 ++++++++++++++++++ 22 files changed, 797 insertions(+), 790 deletions(-) delete mode 100644 examples/datastreams/main.py delete mode 100644 examples/event_listener/README.md delete mode 100644 examples/event_listener/config.toml delete mode 100644 examples/event_listener/event_listener.py delete mode 100644 examples/event_listener/interfaces/org.astarte-platform.genericsensors.SamplingRate.json delete mode 100644 examples/example_device/README.md delete mode 100644 examples/example_device/config.toml delete mode 100644 examples/example_device/example_device.py delete mode 100644 examples/example_device/interfaces/org.astarte-platform.genericsensors.AvailableSensors.json delete mode 100644 examples/example_device/interfaces/org.astarte-platform.genericsensors.Geolocation.json delete mode 100644 examples/example_device/interfaces/org.astarte-platform.genericsensors.Values.json rename examples/{datastreams => mqtt}/README.md (100%) rename examples/{datastreams => mqtt}/config.toml (76%) create mode 100644 examples/mqtt/interfaces/org.astarte-platform.python.examples.DeviceAggregate.json rename examples/{datastreams => mqtt}/interfaces/org.astarte-platform.python.examples.DeviceDatastream.json (100%) create mode 100644 examples/mqtt/interfaces/org.astarte-platform.python.examples.DeviceProperty.json create mode 100644 examples/mqtt/interfaces/org.astarte-platform.python.examples.ServerAggregate.json rename examples/{datastreams => mqtt}/interfaces/org.astarte-platform.python.examples.ServerDatastream.json (100%) create mode 100644 examples/mqtt/interfaces/org.astarte-platform.python.examples.ServerProperty.json create mode 100644 examples/mqtt/main.py create mode 100644 examples/mqtt/transmit_data.py diff --git a/.github/workflows/check-examples-mqtt.yaml b/.github/workflows/check-examples-mqtt.yaml index 074a6583..b4bb0b12 100644 --- a/.github/workflows/check-examples-mqtt.yaml +++ b/.github/workflows/check-examples-mqtt.yaml @@ -45,31 +45,9 @@ jobs: working-directory: ./.github/workflows timeout-minutes: 3 run: | - ./install_interfaces.sh $GITHUB_WORKSPACE/examples/example_device/interfaces - ./install_interfaces.sh $GITHUB_WORKSPACE/examples/datastreams/interfaces - ./install_interfaces.sh $GITHUB_WORKSPACE/examples/event_listener/interfaces - - name: Setup the configuration file for the datastream example - working-directory: ./examples/datastreams - run: | - echo "REALM = \"test\"" > config.toml - echo "PAIRING_URL = \"https://api.autotest.astarte-platform.org/pairing\"" >> config.toml - DEVICE_ID=$(astartectl utils device-id generate-random) - echo "DEVICE_ID = \"$DEVICE_ID\"" >> config.toml - CREDENTIALS_SECRET=$(astartectl pairing agent register --compact-output -- "$DEVICE_ID") - echo "CREDENTIALS_SECRET = \"$CREDENTIALS_SECRET\"" >> config.toml - cat config.toml - - name: Setup the configuration file for the event_listener example - working-directory: ./examples/event_listener - run: | - echo "REALM = \"test\"" > config.toml - echo "PAIRING_URL = \"https://api.autotest.astarte-platform.org/pairing\"" >> config.toml - DEVICE_ID=$(astartectl utils device-id generate-random) - echo "DEVICE_ID = \"$DEVICE_ID\"" >> config.toml - CREDENTIALS_SECRET=$(astartectl pairing agent register --compact-output -- "$DEVICE_ID") - echo "CREDENTIALS_SECRET = \"$CREDENTIALS_SECRET\"" >> config.toml - cat config.toml - - name: Setup the configuration file for the example device example - working-directory: ./examples/example_device + ./install_interfaces.sh $GITHUB_WORKSPACE/examples/mqtt/interfaces + - name: Setup the configuration file for the example + working-directory: ./examples/mqtt run: | echo "REALM = \"test\"" > config.toml echo "PAIRING_URL = \"https://api.autotest.astarte-platform.org/pairing\"" >> config.toml @@ -82,18 +60,8 @@ jobs: run: | python3 -m pip install --upgrade pip python3 -m pip install -e . - - name: Execute the datastream example - working-directory: ./examples/datastreams - run: | - export REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt - python3 ./main.py -d 10 - - name: Execute the event_listener example - working-directory: ./examples/event_listener - run: | - export REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt - python3 ./event_listener.py -d 10 - - name: Execute the example_device example - working-directory: ./examples/example_device + - name: Execute the example + working-directory: ./examples/mqtt run: | export REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt - python3 ./example_device.py -d 10 + python3 ./main.py diff --git a/examples/datastreams/main.py b/examples/datastreams/main.py deleted file mode 100644 index cb2489bf..00000000 --- a/examples/datastreams/main.py +++ /dev/null @@ -1,217 +0,0 @@ -# This file is part of Astarte. -# -# Copyright 2023 SECO Mind Srl -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# SPDX-License-Identifier: Apache-2.0 - -""" Individual datastream example - -Example showing how to send/receive individual datastreams. - -""" - -import argparse -import tempfile -import time -import tomllib -from datetime import datetime, timezone -from pathlib import Path - -from astarte.device import DeviceMqtt - -_INTERFACES_DIR = Path(__file__).parent.joinpath("interfaces").absolute() -_CONFIGURATION_FILE = Path(__file__).parent.joinpath("config.toml").absolute() - - -def on_connected_cbk(_): - """ - Callback for a connection event. - """ - print("Device connected.") - - -def on_data_received_cbk(_: DeviceMqtt, interface_name: str, path: str, payload: dict): - """ - Callback for a data reception event. - """ - print(f"Received message for interface: {interface_name} and path: {path}.") - print(f" Payload: {payload}") - - -def on_disconnected_cbk(_, reason: int): - """ - Callback for a disconnection event. - """ - print("Device disconnected" + (f" because: {reason}." if reason else ".")) - - -def stream_data(device: DeviceMqtt): - """ - Stream some hardcoded tata data from a device to Astarte. - """ - - # Send the binary blob endpoints - device.send( - "org.astarte-platform.python.examples.DeviceDatastream", - "/binaryblob_endpoint", - b"binblob", - datetime.now(tz=timezone.utc), - ) - device.send( - "org.astarte-platform.python.examples.DeviceDatastream", - "/binaryblobarray_endpoint", - [b"bin", b"blob"], - datetime.now(tz=timezone.utc), - ) - - # Send the boolean endpoints - device.send( - "org.astarte-platform.python.examples.DeviceDatastream", - "/boolean_endpoint", - False, - datetime.now(tz=timezone.utc), - ) - device.send( - "org.astarte-platform.python.examples.DeviceDatastream", - "/booleanarray_endpoint", - [False, True], - datetime.now(tz=timezone.utc), - ) - - # Send the datetime endpoints - device.send( - "org.astarte-platform.python.examples.DeviceDatastream", - "/datetime_endpoint", - datetime.now(tz=timezone.utc), - datetime.now(tz=timezone.utc), - ) - device.send( - "org.astarte-platform.python.examples.DeviceDatastream", - "/datetimearray_endpoint", - [datetime.now(tz=timezone.utc)], - datetime.now(tz=timezone.utc), - ) - - # Send the double endpoints - device.send( - "org.astarte-platform.python.examples.DeviceDatastream", - "/double_endpoint", - 21.3, - datetime.now(tz=timezone.utc), - ) - device.send( - "org.astarte-platform.python.examples.DeviceDatastream", - "/doublearray_endpoint", - [1123.0, 12.232], - datetime.now(tz=timezone.utc), - ) - - # Send the integer endpoints - device.send( - "org.astarte-platform.python.examples.DeviceDatastream", - "/integer_endpoint", - 11, - datetime.now(tz=timezone.utc), - ) - device.send( - "org.astarte-platform.python.examples.DeviceDatastream", - "/integerarray_endpoint", - [452, 0], - datetime.now(tz=timezone.utc), - ) - - # Send the long integer endpoints - device.send( - "org.astarte-platform.python.examples.DeviceDatastream", - "/longinteger_endpoint", - 2**34, - datetime.now(tz=timezone.utc), - ) - device.send( - "org.astarte-platform.python.examples.DeviceDatastream", - "/longintegerarray_endpoint", - [2**34, 2**35 + 11], - datetime.now(tz=timezone.utc), - ) - - # Send the string endpoints - device.send( - "org.astarte-platform.python.examples.DeviceDatastream", - "/string_endpoint", - "Hello world!", - datetime.now(tz=timezone.utc), - ) - device.send( - "org.astarte-platform.python.examples.DeviceDatastream", - "/stringarray_endpoint", - ["Hello,", " world!"], - datetime.now(tz=timezone.utc), - ) - - -# If called as a script -if __name__ == "__main__": - - # Accept an argument to specify a set time duration for the example - parser = argparse.ArgumentParser( - description="Datastream sample for the Astarte device SDK Python" - ) - parser.add_argument( - "-d", - "--duration", - type=int, - default=30, - help="Approximated duration in seconds for the example (default: 30)", - ) - args = parser.parse_args() - - with open(_CONFIGURATION_FILE, "rb") as config_fp: - config = tomllib.load(config_fp) - _DEVICE_ID = config["DEVICE_ID"] - _REALM = config["REALM"] - _CREDENTIALS_SECRET = config["CREDENTIALS_SECRET"] - _PAIRING_URL = config["PAIRING_URL"] - - # Creating a temporary directory - with tempfile.TemporaryDirectory(prefix="python_sdk_examples_") as temp_dir: - - # Instantiate the device - device = DeviceMqtt( - device_id=_DEVICE_ID, - realm=_REALM, - credentials_secret=_CREDENTIALS_SECRET, - pairing_base_url=_PAIRING_URL, - persistency_dir=temp_dir, - ) - # Load all the interfaces - device.add_interfaces_from_dir(_INTERFACES_DIR) - # Set all the callback functions - device.set_events_callbacks( - on_connected=on_connected_cbk, - on_data_received=on_data_received_cbk, - on_disconnected=on_disconnected_cbk, - ) - # Connect the device - device.connect() - while not device.is_connected(): - pass - - # Stream some data from device to Astarte - stream_data(device) - - # Sleep for the example duration - time.sleep(args.duration) - - device.disconnect() diff --git a/examples/event_listener/README.md b/examples/event_listener/README.md deleted file mode 100644 index 46f9cb64..00000000 --- a/examples/event_listener/README.md +++ /dev/null @@ -1,42 +0,0 @@ - - -# Astarte device SDK Python event listener example -This is an example of how to use the device SDK to connect a device to Astarte -and handle incoming messages sent to the device by third parties. - -## Usage -### 1. Device registration and credentials secret emission -If the device is already registered, skip this section. - -The device must be registered beforehand to obtain its credentials-secret. - -1. Using the astartectl command [astartectl](https://github.com/astarte-platform/astartectl). -2. Using the [Astarte Dashboard](https://docs.astarte-platform.org/snapshot/015-astarte_dashboard.html), -which is located at `https://dashboard..` - -### 2. Configuration file -Before running the example the configuration file `config.toml` should be updated to contain user -specific configuration. - -```toml -DEVICE_ID = 'DEVICE ID HERE' -REALM = 'REALM HERE' -CREDENTIALS_SECRET = 'CREDENTIAL SECRET HERE' -PAIRING_URL = 'PAIRING URL HERE' -``` - -### 3. Running the example - -To run the example the Astarte device SDK should be installed. Installing the latest release can be -done through pip with: -```shell -pip install astarte-device-sdk -``` -Then to start the example run in the example directory the following command: -```shell -python event_listener.py -``` diff --git a/examples/event_listener/config.toml b/examples/event_listener/config.toml deleted file mode 100644 index b304c9d4..00000000 --- a/examples/event_listener/config.toml +++ /dev/null @@ -1,22 +0,0 @@ -# This file is part of Astarte. -# -# Copyright 2024 SECO Mind Srl -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# SPDX-License-Identifier: Apache-2.0 - -REALM = "" -PAIRING_URL = "" -DEVICE_ID = "" -CREDENTIALS_SECRET = "" diff --git a/examples/event_listener/event_listener.py b/examples/event_listener/event_listener.py deleted file mode 100644 index f7953018..00000000 --- a/examples/event_listener/event_listener.py +++ /dev/null @@ -1,145 +0,0 @@ -# This file is part of Astarte. -# -# Copyright 2023 SECO Mind Srl -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# SPDX-License-Identifier: Apache-2.0 - -""" Device event listener example - -This module shows an example usage of the Astarte device SDK. -Here we show how to simply connect your device to Astarte and start listening on events on a -server-owned interface. - -""" - -import argparse -import asyncio -import tempfile -import time -import tomllib -from pathlib import Path -from threading import Thread -from typing import Optional, Tuple - -from astarte.device import DeviceMqtt - -_INTERFACES_DIR = Path(__file__).parent.joinpath("interfaces").absolute() -_INTERFACE_FILE = _INTERFACES_DIR.joinpath("org.astarte-platform.genericsensors.SamplingRate.json") -_CONFIGURATION_FILE = Path(__file__).parent.joinpath("config.toml").absolute() - - -def _start_background_loop(_loop: asyncio.AbstractEventLoop) -> None: - asyncio.set_event_loop(_loop) - _loop.run_forever() - - -def _generate_async_loop() -> Tuple[asyncio.AbstractEventLoop, Thread]: - _loop = asyncio.new_event_loop() - other_thread = Thread(target=_start_background_loop, args=(_loop,), daemon=True) - other_thread.start() - return _loop, other_thread - - -def on_data_received_cbk( - device: DeviceMqtt, interface_name: str, path: str, payload: object -) -> None: - """ - A function where we are going to handle the Astarte events triggered by server-owned interface - updates. - N.B. Depending if how the loop variable has been instanced this function could be run on another - thread than the main. - - Parameters - ---------- - device: Device - The Astarte device whose event is registered to - interface_name: str - The name of the server-owned interface where the event was triggered - path: Str - Path to the property/datastream that triggered the event - payload: - New Value of the property/datastream - - """ - print( - f"[device_id: {device.get_device_id()}] Received message for {interface_name}{path}:" - f" {payload}" - ) - - -def main(duration: int, persistency_dir: str, cb_loop: Optional[asyncio.AbstractEventLoop] = None): - """ - Main function - """ - - with open(_CONFIGURATION_FILE, "rb") as config_fp: - config = tomllib.load(config_fp) - _DEVICE_ID = config["DEVICE_ID"] - _REALM = config["REALM"] - _CREDENTIALS_SECRET = config["CREDENTIALS_SECRET"] - _PAIRING_URL = config["PAIRING_URL"] - - # Instance the device - device = DeviceMqtt( - device_id=_DEVICE_ID, - realm=_REALM, - credentials_secret=_CREDENTIALS_SECRET, - pairing_base_url=_PAIRING_URL, - persistency_dir=persistency_dir, - ) - # Load all the interfaces - device.add_interface_from_file(_INTERFACE_FILE) - - # Attach the callback - device.set_events_callbacks(on_data_received=on_data_received_cbk, loop=cb_loop) - - # Connect the device - device.connect() - while not device.is_connected(): - pass - - print("Initialization completed, waiting for messages") - # Keep alive this thread - time.sleep(duration) - - device.disconnect() - - -if __name__ == "__main__": - - # Accept an argument to specify a set time duration for the example - parser = argparse.ArgumentParser( - description="Listener sample for the Astarte device SDK Python" - ) - parser.add_argument( - "-d", - "--duration", - type=int, - default=30, - help="Approximated duration in seconds for the example (default: 30)", - ) - args = parser.parse_args() - - # Creating a temporary directory - with tempfile.TemporaryDirectory(prefix="python_sdk_examples_") as temp_dir: - - # [Optional] Preparing a different asyncio loop for the callbacks to prevent deadlocks - # Replace with loop = None to run the Astarte event callback in the main thread - (loop, thread) = _generate_async_loop() - main(args.duration, temp_dir, loop) - loop.call_soon_threadsafe(loop.stop) - print("Requested async loop stop") - thread.join() - print("Async loop stopped") diff --git a/examples/event_listener/interfaces/org.astarte-platform.genericsensors.SamplingRate.json b/examples/event_listener/interfaces/org.astarte-platform.genericsensors.SamplingRate.json deleted file mode 100644 index 461483f1..00000000 --- a/examples/event_listener/interfaces/org.astarte-platform.genericsensors.SamplingRate.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "interface_name": "org.astarte-platform.genericsensors.SamplingRate", - "version_major": 0, - "version_minor": 1, - "type": "properties", - "ownership": "server", - "description": "Configure sensors sampling rate and enable/disable.", - "doc": "This interface allows to set generic sensors sampling rate and enable/disable policies for each sensor. Sensors are identified by their sensor_id. See also org.astarte-platform.genericsensors.AvailableSensors.", - "mappings": [ - { - "endpoint": "/%{sensor_id}/enable", - "type": "boolean", - "allow_unset": true, - "description": "Enable/disable sensor data transmission.", - "doc": "When true sampled data transmission is always on, otherwise when false is off. When unset data transmission policy is up to the sensor." - }, - { - "endpoint": "/%{sensor_id}/samplingPeriod", - "type": "integer", - "allow_unset": true, - "description": "Sensor sample transmission period.", - "doc": "Send a sampled value every samplingPeriod seconds. When unset sampling period is up to the sensor." - } - ] -} diff --git a/examples/example_device/README.md b/examples/example_device/README.md deleted file mode 100644 index e207a255..00000000 --- a/examples/example_device/README.md +++ /dev/null @@ -1,42 +0,0 @@ - - -# Astarte device SDK Python example device -This is an example of how to use the device SDK to connect a device to Astarte and send -data on `datastream` or`properties` interfaces. - -## Usage -### 1. Device registration and credentials secret emission -If the device is already registered, skip this section. - -The device must be registered beforehand to obtain its credentials-secret. - -1. Using the astartectl command [astartectl](https://github.com/astarte-platform/astartectl). -2. Using the [Astarte Dashboard](https://docs.astarte-platform.org/snapshot/015-astarte_dashboard.html), -which is located at `https://dashboard..` - -### 2. Configuration file -Before running the example the configuration file `config.toml` should be updated to contain user -specific configuration. - -```toml -DEVICE_ID = 'DEVICE ID HERE' -REALM = 'REALM HERE' -CREDENTIALS_SECRET = 'CREDENTIAL SECRET HERE' -PAIRING_URL = 'PAIRING URL HERE' -``` - -### 3. Running the example - -To run the example the Astarte device SDK should be installed. Installing the latest release can be -done through pip with: -```shell -pip install astarte-device-sdk -``` -Then to start the example run in the example directory the following command: -```shell -python example_device.py -``` diff --git a/examples/example_device/config.toml b/examples/example_device/config.toml deleted file mode 100644 index b304c9d4..00000000 --- a/examples/example_device/config.toml +++ /dev/null @@ -1,22 +0,0 @@ -# This file is part of Astarte. -# -# Copyright 2024 SECO Mind Srl -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# SPDX-License-Identifier: Apache-2.0 - -REALM = "" -PAIRING_URL = "" -DEVICE_ID = "" -CREDENTIALS_SECRET = "" diff --git a/examples/example_device/example_device.py b/examples/example_device/example_device.py deleted file mode 100644 index 2a7272d6..00000000 --- a/examples/example_device/example_device.py +++ /dev/null @@ -1,142 +0,0 @@ -# This file is part of Astarte. -# -# Copyright 2023 SECO Mind Srl -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# SPDX-License-Identifier: Apache-2.0 - -""" Device publication example - -This module shows an example usage of the Astarte device SDK. -Here we show how to simply connect your device to Astarte to start publishing on various -interfaces. -All the interfaces we are going to use are located in the `interface` directory and are used as -follows: -1. AvailableSensors: to publish single properties -2. Values: to publish single datastreams -3. Geolocation: to publish an object aggregated datastream - -""" -import argparse -import tempfile -import time -import tomllib -from datetime import datetime, timezone -from pathlib import Path -from random import random - -from astarte.device import DeviceMqtt - -_INTERFACES_DIR = Path(__file__).parent.joinpath("interfaces").absolute() -_CONFIGURATION_FILE = Path(__file__).parent.joinpath("config.toml").absolute() - - -def main(duration: int, persistency_dir: str): - """ - Main function - """ - - with open(_CONFIGURATION_FILE, "rb") as config_fp: - config = tomllib.load(config_fp) - _DEVICE_ID = config["DEVICE_ID"] - _REALM = config["REALM"] - _CREDENTIALS_SECRET = config["CREDENTIALS_SECRET"] - _PAIRING_URL = config["PAIRING_URL"] - - # Instance the device - device = DeviceMqtt( - device_id=_DEVICE_ID, - realm=_REALM, - credentials_secret=_CREDENTIALS_SECRET, - pairing_base_url=_PAIRING_URL, - persistency_dir=persistency_dir, - ) - # Load all the interfaces - device.add_interfaces_from_dir(_INTERFACES_DIR) - # Connect the device - device.connect() - while not device.is_connected(): - pass - - # Set properties - sensor_id = "b2c5a6ed_ebe4_4c5c_9d8a_6d2f114fc6e5" - device.send( - "org.astarte-platform.genericsensors.AvailableSensors", - f"/{sensor_id}/name", - "randomThermometer", - ) - device.send( - "org.astarte-platform.genericsensors.AvailableSensors", - f"/{sensor_id}/unit", - "°C", - ) - - # Sleep for one second - time.sleep(1) - - # Unset property - device.send( - "org.astarte-platform.genericsensors.AvailableSensors", - "/wrongId/name", - "randomThermometer", - ) - device.unset_property("org.astarte-platform.genericsensors.AvailableSensors", "/wrongId/name") - - max_temp = 30 - - end_time = time.time() + duration - while time.time() < end_time: - now = datetime.now(tz=timezone.utc) - - # Send single datastream - temp = round(random() * max_temp, 2) - device.send( - "org.astarte-platform.genericsensors.Values", - f"/{sensor_id}/value", - temp, - now, - ) - - # Send object aggregated datastream - geo_data = { - "accuracy": 1.0, - "altitude": 331.81, - "altitudeAccuracy": 1.0, - "heading": 0.0, - "latitude": 43.32215, - "longitude": 11.3259, - "speed": 0.0, - } - device.send_aggregate( - "org.astarte-platform.genericsensors.Geolocation", "/gps", geo_data, now - ) - - time.sleep(5) - - -if __name__ == "__main__": - # Accept an argument to specify a set time duration for the example - parser = argparse.ArgumentParser(description="Sample for the Astarte device SDK Python") - parser.add_argument( - "-d", - "--duration", - type=int, - default=30, - help="Approximated duration in seconds for the example (default: 30)", - ) - args = parser.parse_args() - - # Creating a temporary directory - with tempfile.TemporaryDirectory(prefix="python_sdk_examples_") as temp_dir: - main(args.duration, temp_dir) diff --git a/examples/example_device/interfaces/org.astarte-platform.genericsensors.AvailableSensors.json b/examples/example_device/interfaces/org.astarte-platform.genericsensors.AvailableSensors.json deleted file mode 100644 index 5e70d57d..00000000 --- a/examples/example_device/interfaces/org.astarte-platform.genericsensors.AvailableSensors.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "interface_name": "org.astarte-platform.genericsensors.AvailableSensors", - "version_major": 1, - "version_minor": 0, - "type": "properties", - "ownership": "device", - "description": "Describes available generic sensors.", - "doc": "This interface allows to describe available sensors and their attributes such as name and sampled data measurement unit. Sensors are identified by their sensor_id. See also org.astarte-platform.genericsensors.AvailableSensors.", - "mappings": [ - { - "endpoint": "/%{sensor_id}/name", - "type": "string", - "description": "Sensor name.", - "doc": "An arbitrary sensor name." - }, - { - "endpoint": "/%{sensor_id}/unit", - "type": "string", - "description": "Sample data measurement unit.", - "doc": "SI unit such as m, kg, K, etc..." - } - ] -} diff --git a/examples/example_device/interfaces/org.astarte-platform.genericsensors.Geolocation.json b/examples/example_device/interfaces/org.astarte-platform.genericsensors.Geolocation.json deleted file mode 100644 index 01dea420..00000000 --- a/examples/example_device/interfaces/org.astarte-platform.genericsensors.Geolocation.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "interface_name": "org.astarte-platform.genericsensors.Geolocation", - "version_major": 1, - "version_minor": 0, - "type": "datastream", - "ownership": "device", - "aggregation": "object", - "description": "Generic Geolocation sampled data.", - "doc": "Geolocation allows geolocation sensors to stream location data, such as GPS data. It is usually used in combination with AvailableSensors, which makes API client aware of what sensors are present on devices and what measurement systems are used. sensor_id represents a unique identifier for an individual sensor, and should match sensor_id in AvailableSensors when used in combination.", - "mappings": [ - { - "endpoint": "/%{sensor_id}/accuracy", - "type": "double", - "explicit_timestamp": true, - "description": "Sampled accuracy of the latitude and longitude properties." - }, - { - "endpoint": "/%{sensor_id}/altitude", - "type": "double", - "explicit_timestamp": true, - "description": "Sampled altitude value." - }, - { - "endpoint": "/%{sensor_id}/altitudeAccuracy", - "type": "double", - "explicit_timestamp": true, - "description": "Sampled accuracy of the altitude property." - }, - { - "endpoint": "/%{sensor_id}/heading", - "type": "double", - "explicit_timestamp": true, - "description": "Sampled value representing the direction towards which the device is facing." - }, - { - "endpoint": "/%{sensor_id}/latitude", - "type": "double", - "explicit_timestamp": true, - "description": "Sampled latitude value." - }, - { - "endpoint": "/%{sensor_id}/longitude", - "type": "double", - "explicit_timestamp": true, - "description": "Sampled longitude value." - }, - { - "endpoint": "/%{sensor_id}/speed", - "type": "double", - "explicit_timestamp": true, - "description": "Sampled value representing the velocity of the device." - } - ] -} diff --git a/examples/example_device/interfaces/org.astarte-platform.genericsensors.Values.json b/examples/example_device/interfaces/org.astarte-platform.genericsensors.Values.json deleted file mode 100644 index 20bb1fa7..00000000 --- a/examples/example_device/interfaces/org.astarte-platform.genericsensors.Values.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "interface_name": "org.astarte-platform.genericsensors.Values", - "version_major": 1, - "version_minor": 0, - "type": "datastream", - "ownership": "device", - "description": "Generic sensors sampled data.", - "doc": "Values allows generic sensors to stream samples. It is usually used in combination with AvailableSensors, which makes API client aware of what sensors and what unit of measure they are reporting. sensor_id represents an unique identifier for an individual sensor, and should match sensor_id in AvailableSensors when used in combination.", - "mappings": [ - { - "endpoint": "/%{sensor_id}/value", - "type": "double", - "explicit_timestamp": true, - "description": "Sampled real value.", - "doc": "Datastream of sampled real values." - } - ] -} diff --git a/examples/datastreams/README.md b/examples/mqtt/README.md similarity index 100% rename from examples/datastreams/README.md rename to examples/mqtt/README.md diff --git a/examples/datastreams/config.toml b/examples/mqtt/config.toml similarity index 76% rename from examples/datastreams/config.toml rename to examples/mqtt/config.toml index b304c9d4..f447181d 100644 --- a/examples/datastreams/config.toml +++ b/examples/mqtt/config.toml @@ -20,3 +20,10 @@ REALM = "" PAIRING_URL = "" DEVICE_ID = "" CREDENTIALS_SECRET = "" +STREAM_INDIVIDUAL_DATA = true +STREAM_AGGREGATED_DATA = true +SET_PROPERTIES = true +UNSET_PROPERTIES = true +# This wait time is the time the device will wait for data from Astarte at the end of all +# transmissions. +WAIT_FOR_INCOMING_S = 30 diff --git a/examples/mqtt/interfaces/org.astarte-platform.python.examples.DeviceAggregate.json b/examples/mqtt/interfaces/org.astarte-platform.python.examples.DeviceAggregate.json new file mode 100644 index 00000000..7b985048 --- /dev/null +++ b/examples/mqtt/interfaces/org.astarte-platform.python.examples.DeviceAggregate.json @@ -0,0 +1,82 @@ +{ + "interface_name": "org.astarte-platform.python.examples.DeviceAggregate", + "version_major": 0, + "version_minor": 1, + "type": "datastream", + "aggregation": "object", + "ownership": "device", + "description": "Test aggregate interface.", + "doc": "Test interface used to test aggregates.", + "mappings": [ + { + "endpoint": "/%{sensor_id}/double_endpoint", + "type": "double", + "explicit_timestamp": false + }, + { + "endpoint": "/%{sensor_id}/integer_endpoint", + "type": "integer", + "explicit_timestamp": false + }, + { + "endpoint": "/%{sensor_id}/boolean_endpoint", + "type": "boolean", + "explicit_timestamp": false + }, + { + "endpoint": "/%{sensor_id}/longinteger_endpoint", + "type": "longinteger", + "explicit_timestamp": false + }, + { + "endpoint": "/%{sensor_id}/string_endpoint", + "type": "string", + "explicit_timestamp": false + }, + { + "endpoint": "/%{sensor_id}/binaryblob_endpoint", + "type": "binaryblob", + "explicit_timestamp": false + }, + { + "endpoint": "/%{sensor_id}/datetime_endpoint", + "type": "datetime", + "explicit_timestamp": false + }, + { + "endpoint": "/%{sensor_id}/doublearray_endpoint", + "type": "doublearray", + "explicit_timestamp": false + }, + { + "endpoint": "/%{sensor_id}/integerarray_endpoint", + "type": "integerarray", + "explicit_timestamp": false + }, + { + "endpoint": "/%{sensor_id}/booleanarray_endpoint", + "type": "booleanarray", + "explicit_timestamp": false + }, + { + "endpoint": "/%{sensor_id}/longintegerarray_endpoint", + "type": "longintegerarray", + "explicit_timestamp": false + }, + { + "endpoint": "/%{sensor_id}/stringarray_endpoint", + "type": "stringarray", + "explicit_timestamp": false + }, + { + "endpoint": "/%{sensor_id}/binaryblobarray_endpoint", + "type": "binaryblobarray", + "explicit_timestamp": false + }, + { + "endpoint": "/%{sensor_id}/datetimearray_endpoint", + "type": "datetimearray", + "explicit_timestamp": false + } + ] +} diff --git a/examples/datastreams/interfaces/org.astarte-platform.python.examples.DeviceDatastream.json b/examples/mqtt/interfaces/org.astarte-platform.python.examples.DeviceDatastream.json similarity index 100% rename from examples/datastreams/interfaces/org.astarte-platform.python.examples.DeviceDatastream.json rename to examples/mqtt/interfaces/org.astarte-platform.python.examples.DeviceDatastream.json diff --git a/examples/mqtt/interfaces/org.astarte-platform.python.examples.DeviceProperty.json b/examples/mqtt/interfaces/org.astarte-platform.python.examples.DeviceProperty.json new file mode 100644 index 00000000..3f037a9f --- /dev/null +++ b/examples/mqtt/interfaces/org.astarte-platform.python.examples.DeviceProperty.json @@ -0,0 +1,81 @@ +{ + "interface_name": "org.astarte-platform.python.examples.DeviceProperty", + "version_major": 0, + "version_minor": 1, + "type": "properties", + "ownership": "device", + "description": "Test properties interface.", + "doc": "Test interface used to test properties.", + "mappings": [ + { + "endpoint": "/%{sensor_id}/double_endpoint", + "type": "double", + "allow_unset": true + }, + { + "endpoint": "/%{sensor_id}/integer_endpoint", + "type": "integer", + "allow_unset": true + }, + { + "endpoint": "/%{sensor_id}/boolean_endpoint", + "type": "boolean", + "allow_unset": true + }, + { + "endpoint": "/%{sensor_id}/longinteger_endpoint", + "type": "longinteger", + "allow_unset": true + }, + { + "endpoint": "/%{sensor_id}/string_endpoint", + "type": "string", + "allow_unset": true + }, + { + "endpoint": "/%{sensor_id}/binaryblob_endpoint", + "type": "binaryblob", + "allow_unset": true + }, + { + "endpoint": "/%{sensor_id}/datetime_endpoint", + "type": "datetime", + "allow_unset": true + }, + { + "endpoint": "/%{sensor_id}/doublearray_endpoint", + "type": "doublearray", + "allow_unset": true + }, + { + "endpoint": "/%{sensor_id}/integerarray_endpoint", + "type": "integerarray", + "allow_unset": true + }, + { + "endpoint": "/%{sensor_id}/booleanarray_endpoint", + "type": "booleanarray", + "allow_unset": true + }, + { + "endpoint": "/%{sensor_id}/longintegerarray_endpoint", + "type": "longintegerarray", + "allow_unset": true + }, + { + "endpoint": "/%{sensor_id}/stringarray_endpoint", + "type": "stringarray", + "allow_unset": true + }, + { + "endpoint": "/%{sensor_id}/binaryblobarray_endpoint", + "type": "binaryblobarray", + "allow_unset": true + }, + { + "endpoint": "/%{sensor_id}/datetimearray_endpoint", + "type": "datetimearray", + "allow_unset": true + } + ] +} diff --git a/examples/mqtt/interfaces/org.astarte-platform.python.examples.ServerAggregate.json b/examples/mqtt/interfaces/org.astarte-platform.python.examples.ServerAggregate.json new file mode 100644 index 00000000..1dafa34d --- /dev/null +++ b/examples/mqtt/interfaces/org.astarte-platform.python.examples.ServerAggregate.json @@ -0,0 +1,82 @@ +{ + "interface_name": "org.astarte-platform.python.examples.ServerAggregate", + "version_major": 0, + "version_minor": 1, + "type": "datastream", + "aggregation": "object", + "ownership": "server", + "description": "Test aggregate interface.", + "doc": "Test interface used to test aggregates.", + "mappings": [ + { + "endpoint": "/%{sensor_id}/double_endpoint", + "type": "double", + "explicit_timestamp": true + }, + { + "endpoint": "/%{sensor_id}/integer_endpoint", + "type": "integer", + "explicit_timestamp": true + }, + { + "endpoint": "/%{sensor_id}/boolean_endpoint", + "type": "boolean", + "explicit_timestamp": true + }, + { + "endpoint": "/%{sensor_id}/longinteger_endpoint", + "type": "longinteger", + "explicit_timestamp": true + }, + { + "endpoint": "/%{sensor_id}/string_endpoint", + "type": "string", + "explicit_timestamp": true + }, + { + "endpoint": "/%{sensor_id}/binaryblob_endpoint", + "type": "binaryblob", + "explicit_timestamp": true + }, + { + "endpoint": "/%{sensor_id}/datetime_endpoint", + "type": "datetime", + "explicit_timestamp": true + }, + { + "endpoint": "/%{sensor_id}/doublearray_endpoint", + "type": "doublearray", + "explicit_timestamp": true + }, + { + "endpoint": "/%{sensor_id}/integerarray_endpoint", + "type": "integerarray", + "explicit_timestamp": true + }, + { + "endpoint": "/%{sensor_id}/booleanarray_endpoint", + "type": "booleanarray", + "explicit_timestamp": true + }, + { + "endpoint": "/%{sensor_id}/longintegerarray_endpoint", + "type": "longintegerarray", + "explicit_timestamp": true + }, + { + "endpoint": "/%{sensor_id}/stringarray_endpoint", + "type": "stringarray", + "explicit_timestamp": true + }, + { + "endpoint": "/%{sensor_id}/binaryblobarray_endpoint", + "type": "binaryblobarray", + "explicit_timestamp": true + }, + { + "endpoint": "/%{sensor_id}/datetimearray_endpoint", + "type": "datetimearray", + "explicit_timestamp": true + } + ] +} diff --git a/examples/datastreams/interfaces/org.astarte-platform.python.examples.ServerDatastream.json b/examples/mqtt/interfaces/org.astarte-platform.python.examples.ServerDatastream.json similarity index 100% rename from examples/datastreams/interfaces/org.astarte-platform.python.examples.ServerDatastream.json rename to examples/mqtt/interfaces/org.astarte-platform.python.examples.ServerDatastream.json diff --git a/examples/mqtt/interfaces/org.astarte-platform.python.examples.ServerProperty.json b/examples/mqtt/interfaces/org.astarte-platform.python.examples.ServerProperty.json new file mode 100644 index 00000000..16d6f204 --- /dev/null +++ b/examples/mqtt/interfaces/org.astarte-platform.python.examples.ServerProperty.json @@ -0,0 +1,81 @@ +{ + "interface_name": "org.astarte-platform.python.examples.ServerProperty", + "version_major": 0, + "version_minor": 1, + "type": "properties", + "ownership": "server", + "description": "Test properties interface.", + "doc": "Test interface used to test properties.", + "mappings": [ + { + "endpoint": "/%{sensor_id}/double_endpoint", + "type": "double", + "allow_unset": true + }, + { + "endpoint": "/%{sensor_id}/integer_endpoint", + "type": "integer", + "allow_unset": true + }, + { + "endpoint": "/%{sensor_id}/boolean_endpoint", + "type": "boolean", + "allow_unset": true + }, + { + "endpoint": "/%{sensor_id}/longinteger_endpoint", + "type": "longinteger", + "allow_unset": true + }, + { + "endpoint": "/%{sensor_id}/string_endpoint", + "type": "string", + "allow_unset": true + }, + { + "endpoint": "/%{sensor_id}/binaryblob_endpoint", + "type": "binaryblob", + "allow_unset": true + }, + { + "endpoint": "/%{sensor_id}/datetime_endpoint", + "type": "datetime", + "allow_unset": true + }, + { + "endpoint": "/%{sensor_id}/doublearray_endpoint", + "type": "doublearray", + "allow_unset": true + }, + { + "endpoint": "/%{sensor_id}/integerarray_endpoint", + "type": "integerarray", + "allow_unset": true + }, + { + "endpoint": "/%{sensor_id}/booleanarray_endpoint", + "type": "booleanarray", + "allow_unset": true + }, + { + "endpoint": "/%{sensor_id}/longintegerarray_endpoint", + "type": "longintegerarray", + "allow_unset": true + }, + { + "endpoint": "/%{sensor_id}/stringarray_endpoint", + "type": "stringarray", + "allow_unset": true + }, + { + "endpoint": "/%{sensor_id}/binaryblobarray_endpoint", + "type": "binaryblobarray", + "allow_unset": true + }, + { + "endpoint": "/%{sensor_id}/datetimearray_endpoint", + "type": "datetimearray", + "allow_unset": true + } + ] +} diff --git a/examples/mqtt/main.py b/examples/mqtt/main.py new file mode 100644 index 00000000..d8717f74 --- /dev/null +++ b/examples/mqtt/main.py @@ -0,0 +1,156 @@ +# This file is part of Astarte. +# +# Copyright 2024 SECO Mind Srl +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +""" Astarte device example using the MQTT protocol + +Example showing how to send/receive individual/aggregated datastreams and set/unset properties. + +""" + +import asyncio +import tempfile +import time +import tomllib +from pathlib import Path +from threading import Thread +from typing import Optional, Tuple + +from transmit_data import ( + set_properties, + stream_aggregates, + stream_individuals, + unset_properties, +) + +from astarte.device import DeviceMqtt + +_INTERFACES_DIR = Path(__file__).parent.joinpath("interfaces").absolute() +_CONFIGURATION_FILE = Path(__file__).parent.joinpath("config.toml").absolute() + + +def on_connected_cbk(_): + """ + Callback for a connection event. + """ + print("Device connected.") + + +def on_data_received_cbk(_: DeviceMqtt, interface_name: str, path: str, payload: dict): + """ + Callback for a data reception event. + """ + print(f"Received message for interface: {interface_name} and path: {path}.") + print(f" Payload: {payload}") + + +def on_disconnected_cbk(_, reason: int): + """ + Callback for a disconnection event. + """ + print("Device disconnected" + (f" because: {reason}." if reason else ".")) + + +def _start_background_loop(_loop: asyncio.AbstractEventLoop) -> None: + asyncio.set_event_loop(_loop) + _loop.run_forever() + + +def _generate_async_loop() -> Tuple[asyncio.AbstractEventLoop, Thread]: + _loop = asyncio.new_event_loop() + other_thread = Thread(target=_start_background_loop, args=(_loop,), daemon=True) + other_thread.start() + return _loop, other_thread + + +def main(cb_loop: Optional[asyncio.AbstractEventLoop] = None): + + with open(_CONFIGURATION_FILE, "rb") as config_fp: + config = tomllib.load(config_fp) + _DEVICE_ID = config["DEVICE_ID"] + _REALM = config["REALM"] + _CREDENTIALS_SECRET = config["CREDENTIALS_SECRET"] + _PAIRING_URL = config["PAIRING_URL"] + _STREAM_INDIVIDUAL_DATA = config.get("STREAM_INDIVIDUAL_DATA", True) + _STREAM_AGGREGATED_DATA = config.get("STREAM_AGGREGATED_DATA", True) + _SET_PROPERTIES = config.get("SET_PROPERTIES", True) + _UNSET_PROPERTIES = config.get("UNSET_PROPERTIES", True) + _WAIT_FOR_INCOMING_S = config.get("WAIT_FOR_INCOMING_S", 0) + + # Creating a temporary directory + with tempfile.TemporaryDirectory(prefix="python_sdk_examples_") as temp_dir: + + print("Creating and connecting the device.") + # Instantiate the device + device = DeviceMqtt( + device_id=_DEVICE_ID, + realm=_REALM, + credentials_secret=_CREDENTIALS_SECRET, + pairing_base_url=_PAIRING_URL, + persistency_dir=temp_dir, + ) + # Load all the interfaces + device.add_interfaces_from_dir(_INTERFACES_DIR) + # Set all the callback functions + device.set_events_callbacks( + on_connected=on_connected_cbk, + on_data_received=on_data_received_cbk, + on_disconnected=on_disconnected_cbk, + loop=cb_loop, + ) + # Connect the device + device.connect() + while not device.is_connected(): + pass + + time.sleep(1) + + if _STREAM_INDIVIDUAL_DATA: + print("Streaming individual data.") + stream_individuals(device) + + if _STREAM_AGGREGATED_DATA: + print("Streaming aggregated data.") + stream_aggregates(device) + + if _SET_PROPERTIES: + print("Setting properties data.") + set_properties(device) + + if _UNSET_PROPERTIES: + print("Unsetting properties data.") + unset_properties(device) + + print(f"Waiting {_WAIT_FOR_INCOMING_S} seconds for server data.") + time.sleep(_WAIT_FOR_INCOMING_S) + + print("Disconnecting the device.") + device.disconnect() + + +# If called as a script +if __name__ == "__main__": + + # [Optional] Preparing a different asyncio loop for the callbacks to prevent deadlocks + # Replace with loop = None to run the Astarte event callback in the main thread + print("Generating async loop.") + (loop, thread) = _generate_async_loop() + main(loop) + loop.call_soon_threadsafe(loop.stop) + print("Requested async loop stop.") + thread.join() + print("Async loop stopped.") diff --git a/examples/mqtt/transmit_data.py b/examples/mqtt/transmit_data.py new file mode 100644 index 00000000..e84da2bd --- /dev/null +++ b/examples/mqtt/transmit_data.py @@ -0,0 +1,302 @@ +# This file is part of Astarte. +# +# Copyright 2024 SECO Mind Srl +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +from datetime import datetime, timezone + +from astarte.device import DeviceMqtt + + +def stream_individuals(device: DeviceMqtt): + """ + Stream some hardcoded individual datastreams from a device to Astarte. + """ + + # Send the binary blob endpoints + device.send( + "org.astarte-platform.python.examples.DeviceDatastream", + "/binaryblob_endpoint", + b"binblob", + datetime.now(tz=timezone.utc), + ) + device.send( + "org.astarte-platform.python.examples.DeviceDatastream", + "/binaryblobarray_endpoint", + [b"bin", b"blob"], + datetime.now(tz=timezone.utc), + ) + + # Send the boolean endpoints + device.send( + "org.astarte-platform.python.examples.DeviceDatastream", + "/boolean_endpoint", + False, + datetime.now(tz=timezone.utc), + ) + device.send( + "org.astarte-platform.python.examples.DeviceDatastream", + "/booleanarray_endpoint", + [False, True], + datetime.now(tz=timezone.utc), + ) + + # Send the datetime endpoints + device.send( + "org.astarte-platform.python.examples.DeviceDatastream", + "/datetime_endpoint", + datetime.now(tz=timezone.utc), + datetime.now(tz=timezone.utc), + ) + device.send( + "org.astarte-platform.python.examples.DeviceDatastream", + "/datetimearray_endpoint", + [datetime.now(tz=timezone.utc)], + datetime.now(tz=timezone.utc), + ) + + # Send the double endpoints + device.send( + "org.astarte-platform.python.examples.DeviceDatastream", + "/double_endpoint", + 21.3, + datetime.now(tz=timezone.utc), + ) + device.send( + "org.astarte-platform.python.examples.DeviceDatastream", + "/doublearray_endpoint", + [1123.0, 12.232], + datetime.now(tz=timezone.utc), + ) + + # Send the integer endpoints + device.send( + "org.astarte-platform.python.examples.DeviceDatastream", + "/integer_endpoint", + 11, + datetime.now(tz=timezone.utc), + ) + device.send( + "org.astarte-platform.python.examples.DeviceDatastream", + "/integerarray_endpoint", + [452, 0], + datetime.now(tz=timezone.utc), + ) + + # Send the long integer endpoints + device.send( + "org.astarte-platform.python.examples.DeviceDatastream", + "/longinteger_endpoint", + 2**34, + datetime.now(tz=timezone.utc), + ) + device.send( + "org.astarte-platform.python.examples.DeviceDatastream", + "/longintegerarray_endpoint", + [2**34, 2**35 + 11], + datetime.now(tz=timezone.utc), + ) + + # Send the string endpoints + device.send( + "org.astarte-platform.python.examples.DeviceDatastream", + "/string_endpoint", + "Hello world!", + datetime.now(tz=timezone.utc), + ) + device.send( + "org.astarte-platform.python.examples.DeviceDatastream", + "/stringarray_endpoint", + ["Hello,", " world!"], + datetime.now(tz=timezone.utc), + ) + + +def stream_aggregates(device: DeviceMqtt): + """ + Stream some hardcoded aggregated datastreams from a device to Astarte. + """ + + aggregated_data = { + "binaryblob_endpoint": bytes([0x53, 0x47, 0x56, 0x73, 0x62, 0x47, 0x38, 0x3D]), + "binaryblobarray_endpoint": [ + bytes([0x53, 0x47, 0x56, 0x73, 0x62, 0x47, 0x38, 0x3D]), + bytes([0x64, 0x32, 0x39, 0x79, 0x62]), + ], + "boolean_endpoint": True, + "booleanarray_endpoint": [False, True, False], + "datetime_endpoint": datetime.now(tz=timezone.utc), + "datetimearray_endpoint": [ + datetime.now(tz=timezone.utc), + datetime.now(tz=timezone.utc), + ], + "double_endpoint": 11.3259, + "doublearray_endpoint": [11.3259, 43.453, 33.0], + "integer_endpoint": 11, + "integerarray_endpoint": [1, 0, 4444], + "longinteger_endpoint": 564684845165, + "longintegerarray_endpoint": [12, 2222222, 2], + "string_endpoint": "Hello world", + "stringarray_endpoint": ["Hello", "world", "!"], + } + device.send_aggregate( + "org.astarte-platform.python.examples.DeviceAggregate", + "/sensor11", + aggregated_data, + ) + + +def set_properties(device: DeviceMqtt): + """ + Set some hardcoded properties from a device to Astarte. + """ + + device.send( + "org.astarte-platform.python.examples.DeviceProperty", + "/s33/binaryblob_endpoint", + bytes([0x53, 0x47, 0x56, 0x73, 0x62, 0x47, 0x38, 0x3D]), + ) + device.send( + "org.astarte-platform.python.examples.DeviceProperty", + "/s33/binaryblobarray_endpoint", + [ + bytes([0x53, 0x47, 0x56, 0x73, 0x62, 0x47, 0x38, 0x3D]), + bytes([0x64, 0x32, 0x39, 0x79, 0x62]), + ], + ) + device.send( + "org.astarte-platform.python.examples.DeviceProperty", + "/s33/boolean_endpoint", + True, + ) + device.send( + "org.astarte-platform.python.examples.DeviceProperty", + "/s33/booleanarray_endpoint", + [False, True, False], + ) + device.send( + "org.astarte-platform.python.examples.DeviceProperty", + "/s33/datetime_endpoint", + datetime.now(tz=timezone.utc), + ) + device.send( + "org.astarte-platform.python.examples.DeviceProperty", + "/s33/datetimearray_endpoint", + [datetime.now(tz=timezone.utc), datetime.now(tz=timezone.utc)], + ) + device.send( + "org.astarte-platform.python.examples.DeviceProperty", + "/s33/double_endpoint", + 21.4, + ) + device.send( + "org.astarte-platform.python.examples.DeviceProperty", + "/s33/doublearray_endpoint", + [11.3259, 43.453, 33.0], + ) + device.send( + "org.astarte-platform.python.examples.DeviceProperty", + "/s33/integer_endpoint", + 21, + ) + device.send( + "org.astarte-platform.python.examples.DeviceProperty", + "/s33/integerarray_endpoint", + [64, 0], + ) + device.send( + "org.astarte-platform.python.examples.DeviceProperty", + "/s33/longinteger_endpoint", + 564684845165, + ) + device.send( + "org.astarte-platform.python.examples.DeviceProperty", + "/s33/longintegerarray_endpoint", + [12, 2222222, 2], + ) + device.send( + "org.astarte-platform.python.examples.DeviceProperty", + "/s33/string_endpoint", + "Hello world", + ) + device.send( + "org.astarte-platform.python.examples.DeviceProperty", + "/s33/stringarray_endpoint", + ["Hello", "world", "!"], + ) + + +def unset_properties(device: DeviceMqtt): + """ + Set some hardcoded properties from a device to Astarte. + """ + + device.unset_property( + "org.astarte-platform.python.examples.DeviceProperty", + "/s33/binaryblob_endpoint", + ) + device.unset_property( + "org.astarte-platform.python.examples.DeviceProperty", + "/s33/binaryblobarray_endpoint", + ) + device.unset_property( + "org.astarte-platform.python.examples.DeviceProperty", + "/s33/boolean_endpoint", + ) + device.unset_property( + "org.astarte-platform.python.examples.DeviceProperty", + "/s33/booleanarray_endpoint", + ) + device.unset_property( + "org.astarte-platform.python.examples.DeviceProperty", + "/s33/datetime_endpoint", + ) + device.unset_property( + "org.astarte-platform.python.examples.DeviceProperty", + "/s33/datetimearray_endpoint", + ) + device.unset_property( + "org.astarte-platform.python.examples.DeviceProperty", + "/s33/double_endpoint", + ) + device.unset_property( + "org.astarte-platform.python.examples.DeviceProperty", + "/s33/doublearray_endpoint", + ) + device.unset_property( + "org.astarte-platform.python.examples.DeviceProperty", + "/s33/integer_endpoint", + ) + device.unset_property( + "org.astarte-platform.python.examples.DeviceProperty", + "/s33/integerarray_endpoint", + ) + device.unset_property( + "org.astarte-platform.python.examples.DeviceProperty", + "/s33/longinteger_endpoint", + ) + device.unset_property( + "org.astarte-platform.python.examples.DeviceProperty", + "/s33/longintegerarray_endpoint", + ) + device.unset_property( + "org.astarte-platform.python.examples.DeviceProperty", + "/s33/string_endpoint", + ) + device.unset_property( + "org.astarte-platform.python.examples.DeviceProperty", + "/s33/stringarray_endpoint", + )