I have an IOT device that has some configurable parameters in runtime. This parameters can be configured by the device user and also by the device (it can reconfigure itself in some cases to optimize power consumption). The device communicates via HTTP and doesn't have a configuration endpoint, so it must receive the configuration updates when it sends its measurements.
I am using Orion LD and IOT agent JSON. To solve the scenario I decided to use bidirectional attributes because this parameters can be modified by the device and by the final user and are not real actuators. Nevertheless, I am having some issues configuring this.
I am deploying an Orion LD, IOT agent JSON and a context server using the following docker compose file:
version: "3.8"
services:
ngsi-ld-context-server:
image: nginx:latest
hostname: ngsi-ld-context-server
container_name: ngsi-ld-context-server
networks:
- fiware
ports:
- "9090:80"
volumes:
- ./ngsild-context.jsonld:/usr/share/nginx/html/ngsild-context.jsonld
fiware-mongo-db:
image: mongo:4.4
hostname: fiware-mongo-db
container_name: fiware-mongo-db
networks:
- fiware
ports:
- "27017:27017"
orion-ld:
platform: linux/amd64
image: quay.io/fiware/orion-ld:1.4.0
hostname: orion-ld
container_name: orion-ld
networks:
- fiware
depends_on:
- fiware-mongo-db
ports:
- "1026:1026"
command: -dbhost fiware-mongo-db -logLevel DEBUG -forwarding -experimental
iot-agent-json:
image: quay.io/fiware/iotagent-json:2.2.0
hostname: iot-agent-json
container_name: iot-agent-json
networks:
- fiware
depends_on:
- fiware-mongo-db
- orion-ld
ports:
- "4041:4041"
- "7896:7896"
environment:
- IOTA_CB_HOST=orion-ld
- IOTA_CB_PORT=1026
- IOTA_NORTH_PORT=4041
- IOTA_REGISTRY_TYPE=mongodb
- IOTA_LOG_LEVEL=DEBUG
- IOTA_TIMESTAMP=true
- IOTA_AUTOCAST=true
- IOTA_MONGO_HOST=fiware-mongo-db
- IOTA_MONGO_PORT=27017
- IOTA_MONGO_DB=iotagenjson
- IOTA_HTTP_PORT=7896
- IOTA_PROVIDER_URL=http://iot-agent-json:4041
- IOTA_DEFAULT_RESOURCE=/iot/json
- IOTA_CB_NGSI_VERSION=ld
- IOTA_JSON_LD_CONTEXT=http://ngsi-ld-context-server/ngsild-context.jsonld
- IOTA_FALLBACK_TENANT=openiot
networks:
fiware:
driver: bridge
Firs, I create a service for the device:
{
"services": [
{
"apikey": "4jggokgpepnvsb2uv4s40d59ov",
"cbroker": "http://orion-ld:1026",
"entity_type": "Device",
"resource": "/iot/json"
}
]
}
Then I am trying to configure the bidirectional attribute following the IOT agent NGSILD tutorial (no endpoint is provided because I want to polling):
{
"devices": [
{
"device_id": "device001",
"entity_name": "urn:ngsi-ld:Device:device001",
"entity_type": "Device",
"attributes": [
{
"name": "sampleTime",
"type": "Property",
"reverse": [
{
"object_id": "sampleTime",
"type": "Property"
}
]
}
]
}
]
}
But I get the following response:
{
"name": "ENTITY_GENERIC_ERROR",
"message": "Error accesing entity data for device: urn:ngsi-ld:Device:device001 of type: Device"
}
Looking through the IOT agent logs, I found that it is trying to create a subscription to the Orion with empty attributes, which is not allowed:
time=2023-08-31T06:36:43.764Z | lvl=DEBUG | corr=1cfae922-438d-473a-9511-1f4fe7ad9f3e | trans=1cfae922-438d-473a-9511-1f4fe7ad9f3e | op=IoTAgentNGSI.BidirectionalPlugin | from=n/a | srv=n/a | subsrv=n/a | msg=Extracted variables: [] | comp=IoTAgent
time=2023-08-31T06:36:43.765Z | lvl=DEBUG | corr=1cfae922-438d-473a-9511-1f4fe7ad9f3e | trans=1cfae922-438d-473a-9511-1f4fe7ad9f3e | op=IoTAgentNGSI.RestUtils | from=n/a | srv=n/a | subsrv=n/a | msg=executeWithSecurity | comp=IoTAgent
time=2023-08-31T06:36:43.765Z | lvl=DEBUG | corr=1cfae922-438d-473a-9511-1f4fe7ad9f3e | trans=1cfae922-438d-473a-9511-1f4fe7ad9f3e | op=IoTAgentNGSI.MongoDBGroupRegister | from=n/a | srv=n/a | subsrv=n/a | msg=Looking for group params ["type"] with queryObj {"type":"Device"} | comp=IoTAgent
time=2023-08-31T06:36:43.768Z | lvl=DEBUG | corr=1cfae922-438d-473a-9511-1f4fe7ad9f3e | trans=1cfae922-438d-473a-9511-1f4fe7ad9f3e | op=IoTAgentNGSI.MongoDBGroupRegister | from=n/a | srv=openiot | subsrv=/ | msg=Device group data found: {"_id":"64f033436e41de0d07b70359","resource":"/iot/json","apikey":"4jggokgpepnvsb2uv4s40d59ov","type":"Device","service":"openiot","subservice":"/"} | comp=IoTAgent
time=2023-08-31T06:36:43.769Z | lvl=DEBUG | corr=1cfae922-438d-473a-9511-1f4fe7ad9f3e | trans=1cfae922-438d-473a-9511-1f4fe7ad9f3e | op=IoTAgentNGSI.Request | from=n/a | srv=openiot | subsrv=/ | msg=Options: {
"method": "POST",
"headers": {
"fiware-service": "openiot",
"fiware-servicepath": "/",
"NGSILD-Tenant": "openiot",
"NGSILD-Path": "/",
"Content-Type": "application/ld+json"
},
"json": {
"@context": "http://ngsi-ld-context-server/ngsild-context.jsonld",
"type": "Subscription",
"entities": [
{
"id": "urn:ngsi-ld:Device:device001",
"type": "Device"
}
],
"watchedAttributes": [
"sampleTime"
],
"notification": {
"endpoint": {
"uri": "http://iot-agent-json:4041/notify",
"accept": "application/json"
},
"attributes": [],
"format": "normalized"
}
},
"uri": "http://orion-ld:1026/ngsi-ld/v1/subscriptions/"
} | comp=IoTAgent
time=2023-08-31T06:36:43.786Z | lvl=DEBUG | corr=1cfae922-438d-473a-9511-1f4fe7ad9f3e | trans=1cfae922-438d-473a-9511-1f4fe7ad9f3e | op=IoTAgentNGSI.Request | from=n/a | srv=openiot | subsrv=/ | msg=Response {
"type": "https://uri.etsi.org/ngsi-ld/errors/BadRequestData",
"title": "Empty Array",
"detail": "Subscription::notification::attributes"
} | comp=IoTAgent
time=2023-08-31T06:36:43.786Z | lvl=DEBUG | corr=1cfae922-438d-473a-9511-1f4fe7ad9f3e | trans=1cfae922-438d-473a-9511-1f4fe7ad9f3e | op=IoTAgentNGSI.Subscription-LD | from=n/a | srv=openiot | subsrv=/ | msg=Unknown error subscribing device with id [400] to entity [%s]: $s | comp=IoTAgent
Am I missing something in the provisioning?
I continued looking for a solution, and found that if i add an expression to the provisioning it works:
{
"devices": [
{
"device_id": "device001",
"entity_name": "urn:ngsi-ld:Device:device001",
"entity_type": "Device",
"attributes": [
{
"name": "sampleTime",
"type": "Property",
"reverse": [
{
"object_id": "sampleTime",
"type": "Property",
"expression": "sampleTime | tostring()"
}
]
}
]
}
]
}
But when I try to get the commands using the getCmd parameter, it returns the command twice and does not return the updated command when I update it again before it is retrieved to the device. It looks like it is not storing the commands properly on the mongodb.
As this solution does not seem to work, is there any 'standard' way to deal with attributes that can be modified by both the device and the user?
From the side of IoTAgents we discourage the use of Bidirectional Plugin. The use case you are talking about may be solved using a Polling Command (?getCmd=1), so the command with the new config gets returned as the response of a measures sending.
Bidirectional plugin, and all the plugin complexity, has been deprecated in IoTAgents library version 3.3.0 and it will be removed soon (in the next IoTAgent library version, i.e. 3.4.0).
We know there are some incompatible brokers with NGSI registrations, needed for issuing commands from CB to IoTAgent, and that motivates the usage of bidirectional plugin as a workaround. In future versions of the IoTAgents commands will be supported by subscriptions instead of registrations, so the need of this workaround gets diluted.