OSH Connect Tutorial

OSH Connect for Python is a straightforward library for interacting with OpenSensorHub using OGC API Connected Systems. This tutorial will help guide you through a few simple examples to get you started with OSH Connect.

OSH Connect Installation

OSH Connect can be installed using pip. To install the latest version of OSH Connect, run the following command:

pip install git+https://github.com/Botts-Innovative-Research/OSHConnect-Python.git

Or, if you prefer poetry:

poetry add git+https://github.com/Botts-Innovative-Research/OSHConnect-Python.git

Creating an instance of OSHConnect

The intended method of interacting with OpenSensorHub is through the OSHConnect class. To this you must first create an instance of OSHConnect:

from oshconnect.oshconnectapi import OSHConnect, TemporalModes

connect_app = OSHConnect(name='OSHConnect', playback_mode=TemporalModes.REAL_TIME)

Tip

The name parameter is optional, but can be useful for debugging purposes. The playback mode determines how the data is retrieved from OpenSensorHub.

The next step is to add a Node to the OSHConnect instance. A Node is a representation of a server that you want to connect to. The OSHConnect instance can support multiple Nodes at once.

Adding a Node to an OSHConnect instance

from oshconnect.oshconnectapi import OSHConnect, TemporalModes
from oshconnect.osh_connect_datamodels import Node

connect_app = OSHConnect(name='OSHConnect', playback_mode=TemporalModes.REAL_TIME)
node = Node(protocol='http', address="localhost", port=8585, username="test", password="test")
connect_app.add_node(node)

System Discovery

Once you have added a Node to the OSHConnect instance, you can discover the systems that are available on that Node. This is done by calling the discover_systems() method on the OSHConnect instance.

connect_app.discover_systems()

Datastream Discovery

Once you have discovered the systems that are available on a Node, you can discover the datastreams that are available to those systems. This is done by calling the discover_datastreams method on the OSHConnect instance.

connect_app.discover_datastreams()

Playing back data

Once you have discovered the datastreams that are available on a Node, you can play back the data from those datastreams. This is done by calling the playback_streams method on the OSHConnect instance.

connect_app.playback_streams()

Accessing data

To access the data retrieved from the datastreams, you need to access the messages available to the OSHConnect instance. Calling the get_messages method on the OSHConnect instance will return a list of MessageWrapper objects that contain individual observations.

messages = connect_app.get_messages()

for message in messages:
    print(message)

# or, to access the individual observations
for message in messages:
    for observation in message.observations:
        do_something_with(observation)

Resource Insertion

Other use cases of the OSH Connect library may involve inserting new resources into OpenSensorHub or another Connected Systems API server.

Adding and Inserting a New System

The first major step in a common workflow is to add a new system to the OSH Connect instance. There are a couple of ways to do this, but the recommended method is as follows:

Note

The insert_system method requires a Node object to be passed in as the second argument. Creating one is covered in an earlier section.

from oshconnect.osh_connect_datamodels import System

new_system = app.insert_system(
    System(name="Test System", description="Test System Description", label="Test System",
           urn="urn:system:test"), node)

Adding and Inserting a New Datastream

Once you have a System object, you can add a new datastream to it. This is one of the more complex operations in the library as the schema is very flexible by design. Luckily, the schemas are validated by the underlying data models, so you can be sure that your datastream is valid before inserting it.

Caution

Some implementations of the Connected Systems API may require additional fields to be filled in. OSH Connect is primarily focused on the OpenSensorHub implementation, but does not some of the fields that are required by and OpenSensorHub node.

In this example, we will add a new datastream to the new_system object that we created in the previous example. You’ll note the creation of a DataRecordSchema object, in OSH’s implementation, a DataRecord is the root of all datastream schemas.

from oshconnect.osh_connect_datamodels import Datastream

datarecord_schema = DataRecordSchema(label='Example Data Record', description='Example Data Record Description',
                                     definition='www.test.org/records/example-datarecord', fields=[])
time_schema = TimeSchema(label="Timestamp", definition="http://test.com/Time", name="timestamp",
                         uom=URI(href="http://test.com/TimeUOM"))
continuous_value_field = QuantitySchema(name='continuous-value-distance', label='Continuous Value Distance',
                                        description='Continuous Value Description',
                                        definition='www.test.org/fields/continuous-value',
                                        uom=UCUMCode(code='m', label='meters'))
example_text_field = TextSchema(name='example-text-field', label='Example Text Field', definition='www.test.org/fields/example-text-field')
# add the fields to the datarecord schema, these can also be added added to the datarecord when it is created
datarecord_schema.fields.append(time_schema)   # TimeSchema is required to be the first field in the datarecord for OSH
datarecord_schema.fields.append(continuous_value_field)
datarecord_schema.fields.append(example_text_field)
# Add the datastream to the system
datastream = new_system.add_insert_datastream(datarecord_schema)

Note

A TimeSchema is required to be the first field in the DataRecordSchema for OSH.

Inserting an Observation into and OpenSensorHub Node

Upon successfully adding a new datastream to a system, it is now possible to send observation data to the node.

datastream.insert_observation_dict({
    "resultTime": TimeInstant.now_as_time_instant().get_iso_time(),     # resultTime is required for OSH
    "phenomenonTime": TimeInstant.now_as_time_instant().get_iso_time(), # phenomenonTime is required for OSH
    "result": {
        "timestamp": TimeInstant.now_as_time_instant().epoch_time,
        "continuous-value-distance": 1.0,
        "example-text-field": "Here is some text"
    }
})

Note

The resultTime and phenomenonTime fields are required for OSH. The result field is representative of the schemas included in the DataRecordSchema’s fields. You’ll notice that they are referred to by their name field in the schema as it is the “machine” name of the output.