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 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.gitOr, if you prefer poetry:
poetry add git+https://github.com/Botts-Innovative-Research/OSHConnect-Python.gitThe 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.
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)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()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()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()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)Other use cases of the OSH Connect library may involve inserting new resources into OpenSensorHub or another Connected Systems API server.
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)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.
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.