Search & Describe Twins
Find your own and others' Digital Twins by using the Search Twin function. Understand and inspect their data by using Describe or List All Twins.
This page covers:
- Introduction to Search Twins
- how to find your and others' Digital Twins
- Introduction to Describe Twins
- how to inspect a Digital Twins' metadata
- Introduction to List All Twins
- how to list and describe all Digital Twins in a specific IOTICSpace
Introduction to Search Twins
Use the Search Twin call to find your and others' Digital Twins in your IOTICS ecosystem. You can select and combine one or more search criteria, including:
- Property: define a list of properties, such as Type, Color, Last Update, etc.;
- Location: define Latitude, Longitude and a Radius expressed in Km;
- Text: define one or more keywords that must match text fields from Twin properties.
- Scope (ownership): distinguish between finding only your own local Digital Twins, vs all global Digital Twins
Distinguish between your own local Digital Twins and others' remote Digital Twins
- Local: returns all Digital Twins in your own IOTICSpace
- Global: returns all Digital Twins in your own and others' (remote) IOTICSpaces.
Please note that you will only find Digital Twins that you have permission to find. See Selective Data Sharing for more information on access permissions.
The following table provides an indication of the filters that can be used in the request parameters when searching for Twins.
Key | Description |
---|---|
Properties | Search by any available Digital Twin property as described here. |
Location | Defines a specific geo-location with radius. |
Text | Search one or more keywords which must match the text from the Twin's properties. Note that any (rather than all) of the keywords will produce a match. |
Scope | Distinguish between Twins in your own local Host/IOTICSpace or all Twins in your global network. |
Limit | Define the maximum number of Twins to return. |
Offset | Used to return results from a given offset. |
An important feature of the Search function is that it allows to search and describe one or more Twins with one single call through the responseType body parameter. It can be set to:
- MINIMAL: used to return minimal responses including a Twin's DID and visibility setting;
- LOCATED: includes the MINIMAL fields in addition to the Twin's location;
- FULL (default): provides a complete description of the Twin, including all metadata about the Twin, its Feeds and the Feeds' Values.
How to search for Twins with the IOTICS API
Click to see the prerequisites
import json
from datetime import datetime, timedelta, timezone
from requests import request
twins_list = [] # Initialise an empty list. It will contain the list of Twins retrieved by the search
# Add a new field in the headers.
# Client request timeout is used to stop the request processing once the timeout is reached
headers.update(
{
"Iotics-RequestTimeout": (
datetime.now(tz=timezone.utc) + timedelta(seconds=10)
).isoformat(),
}
)
with request(
method="POST",
url=f"{HOST}/qapi/searches", # Replace with URL of the Host
headers=headers, # It includes the token and the Request Timeout
stream=True,
verify=False,
params={"scope": "LOCAL"},
json={
"filter": {
"properties": [
{
"key": "http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
"uriValue": {"value": "http://data.iotics.com/app#Model"},
}
],
"text": "temperature",
"location": {
"location": {"lon": 52.2, "lat": 0.119},
"radiusKm": 3.2,
}
}
}
) as resp:
# Raises HTTPError, if one occurred
resp.raise_for_status()
# Iterates over the response data, one line at a time
for chunk in resp.iter_lines():
response = json.loads(chunk)
twins_found = []
try:
twins_found = response["result"]["payload"]["twins"]
host_id = response["result"]["payload"]["remoteHostId"]["value"]
except KeyError:
host_id = "local"
finally:
if twins_found:
# Append the twins found to the list of twins
twins_list.extend(twins_found)
print(f"Found {len(twins_list)} twin(s)")
Introduction to Describe Twins
Use Describe Twin to inspect a Digital Twin's metadata and find out more about it. It returns information about the Twin itself, its Feeds and each of the Feeds' Values in order to allow you to distinguish one Twin from another.
A prerequisite for Describe Twin is the Twin's DID
You may know it already or have to retrieve it by using Search Twin.
How to describe a Twin with the IOTICS API
Local vs Remote
The Describe Twin API is used differently depending on whether the Twin is in your own local IOTICSpace or in another remote IOTICSpace.
Click to see the prerequisites
from requests import request
response = request(
method="GET",
url=f"{HOST}/qapi/twins/{twin_id}", # Replace with URL of the Host and the Twin DID you want to describe
headers=headers, # It includes the token
)
response.raise_for_status()
print(response.json())
from requests import request
response = request(
method="GET",
# Replace the 'url' with:
# - URL of your Host
# - the ID of the Host where that Twin belongs
# - the Twin DID you want to describe
url=f"{HOST}/qapi/hosts/{host_id}/twins/{twin_id}",
headers=headers, # It includes the token
)
response.raise_for_status()
print(response.json())
Introduction to List All Twins
List All Twins allows you to list and describe all Digital Twins of a specific IOTICSpace.
Please note that you will only be able to list Digital Twins that you have permission to find. See Selective Data Sharing for more information on access permissions.
How to list all Twins with the IOTICS API
Click to see the prerequisites
from requests import request
response = request(
method="GET",
url=f"{HOST}/qapi/twins", # The host where to retrieve all the Twins
headers=headers, # It includes the token
)
response.raise_for_status()
print(response.json())
Tutorial of the Search & Describe section
Click here to see the entire code on how to search and describe a Twin
import json
from datetime import datetime, timedelta, timezone
from iotics.lib.identity.api.high_level_api import get_rest_high_level_identity_api
from requests import request
RESOLVER_URL = "resolver_url"
HOST = "host_url"
USER_KEY_NAME = "user_key_name"
AGENT_KEY_NAME = "agent_key_name"
USER_SEED = bytes.fromhex("user_seed")
AGENT_SEED = bytes.fromhex("agent_seed")
def make_api_call(method: str, url: str, json: dict = None):
response = request(method=method, url=url, headers=headers, json=json)
response.raise_for_status()
return response.json()
def describe_remote_twin(host_id: str, twin_id: str):
remote_twin = make_api_call(
method="GET", url=f"{HOST}/qapi/hosts/{host_id}/twins/{twin_id}"
)
return remote_twin
# Not used in this example
def describe_local_twin(twin_id: str):
local_twin = make_api_call(method="GET", url=f"{HOST}/qapi/twins/{twin_id}")
return local_twin
# Set up the API with authentication delegation so the AGENT can authenticate on behalf of the USER
api = get_rest_high_level_identity_api(resolver_url=RESOLVER_URL)
(
user_registered_id,
agent_registered_id,
) = api.create_user_and_agent_with_auth_delegation(
user_seed=USER_SEED,
user_key_name=USER_KEY_NAME,
agent_seed=AGENT_SEED,
agent_key_name=AGENT_KEY_NAME,
delegation_name="#AuthDeleg",
)
token = api.create_agent_auth_token(
agent_registered_identity=agent_registered_id,
user_did=user_registered_id.did,
duration=600,
)
headers = {
"accept": "application/json",
"Iotics-ClientAppId": "example_code",
"Authorization": f"Bearer {token}",
"Content-Type": "application/json",
}
# Initialise an empty list.
# It will contain the list of Twins retrieved by the search
twins_list = []
# Add a new field in the headers.
# Client request timeout is used to stop the request processing once the timeout is reached
headers.update(
{
"Iotics-RequestTimeout": (
datetime.now(tz=timezone.utc) + timedelta(seconds=10)
).isoformat(),
}
)
with request(
method="POST",
url=f"{HOST}/qapi/searches", # Replace with URL of the Host
headers=headers, # It includes the token and the Request Timeout
stream=True,
verify=False,
params={"scope": "GLOBAL"},
json={"responseType": "MINIMAL", "filter": {"text": "cambridge"}},
) as resp:
# Raises HTTPError, if one occurred
resp.raise_for_status()
# Iterates over the response data, one line at a time
for chunk in resp.iter_lines():
response = json.loads(chunk)
twins_found = []
try:
twins_found = response["result"]["payload"]["twins"]
host_id = response["result"]["payload"]["remoteHostId"]["value"]
except KeyError:
host_id = "local"
finally:
if twins_found:
# Add the host id value to each twin
for twin in twins_found:
twin["hostId"] = host_id
# Append the twins found to the list of twins
twins_list.extend(twins_found)
print(f"Found {len(twins_list)} twin(s)")
twin_of_interest = next(
iter(twins_list)
) # For demo purpose take the fist Twin in the list
twin_id = twin_of_interest["id"]["value"]
twin_host_id = twin_of_interest["hostId"]
headers.pop("Iotics-RequestTimeout") # RequestTimeout no longer needed in the headers
remote_twin = describe_remote_twin(host_id=twin_host_id, twin_id=twin_id)
print(remote_twin)
Updated 3 months ago