telefonicaid / iotagent-node-lib

Module to enable IoT Agent developers to build custom agents for their devices that can easily connect to NGSI Context Brokers
https://iotagent-node-lib.rtfd.io/
GNU Affero General Public License v3.0
58 stars 84 forks source link

Config from Device API not used. #1624

Open jason-fox opened 6 days ago

jason-fox commented 6 days ago

IoT Agent Node Lib version the issue has been seen with

4.4.0

Bound or port used (API interaction)

Northbound (Provision API and NGSI Interactions)

NGSI version

NGSI-LD and NGSI-v2

Are you running a container?

Yes, I am using a contaner (Docker, Kubernetes...)

Image type

distroless

Expected behaviour you didn't see

When provisioning attributes for a device using the API, the attribute is ignored.

Unexpected behaviour you saw

When a measure is sent, the device takes static data from the group, but the device provisioning is ignored, and the measure is sent to the context broker using auto provisioned attribute names and static attributes are ignored. This also means that linked data functionality as described in the documentation no longer works.

Steps to reproduce the problem

curl -L 'http://localhost:4041/iot/about'
{
    "libVersion": "4.4.0",
    "port": "4041",
    "baseRoot": "/",
    "version": "3.4.0"
}

Using 4.4.0 library.

Provision Group with apikey

curl -L 'http://localhost:4041/iot/services' \
-H 'fiware-service: openiot' \
-H 'fiware-servicepath: /' \
-H 'Content-Type: application/json' \
-d '{
    "services": [
        {
            "apikey": "4jggokgpepnvsb2uv4s40d59ov",
            "cbroker": "http://orion:1026",
            "entity_type": "Device",
            "resource": "/iot/d",
            "static_attributes": [
                {
                    "name": "category",
                    "type": "Property",
                    "value": "sensor"
                },
                {
                    "name": "supportedProtocol",
                    "type": "Property",
                    "value": "ul20"
                }
            ]
        }
    ]
}'

Provision Device temperature001

curl -L 'http://localhost:4041/iot/devices' \
-H 'fiware-service: openiot' \
-H 'fiware-servicepath: /' \
-H 'Content-Type: application/json' \
-d '{
  "devices": [
    {
      "device_id": "temperature001",
      "entity_name": "urn:ngsi-ld:Device:temperature001",
      "entity_type": "Device",
      "timezone": "Europe/Berlin",
      "attributes": [
        {
          "object_id": "t",
          "name": "temperature",
          "type": "Property",
          "metadata": {
            "unitCode": {
              "type": "Text",
              "value": "CEL"
            }
          }
        }
      ],
      "static_attributes": [
        {
          "name": "controlledAsset",
          "type": "Relationship",
          "value": "urn:ngsi-ld:Building:barn001"
        }
      ]
    }
  ]
}'

Send measure - this is using the Ultralight IoT agent

curl -L 'http://localhost:7896/iot/d?k=4jggokgpepnvsb2uv4s40d59ov&i=temperature001' \
-H 'Content-Type: text/plain' \
-d 't|3'

Result

curl -L 'http://localhost:1026/ngsi-ld/v1/entities/urn:ngsi-ld:Device:temperature001' \
-H 'fiware-service: openiot' \
-H 'fiware-servicepath: /' \
-H 'Link: <http://context/ngsi-context.jsonld>; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json"'

Attribute called t present, controlledAsset is missing. ❌

{
    "id": "urn:ngsi-ld:Device:temperature001",
    "type": "Device",
    "t": {
        "type": "Property",
        "value": 3,
        "observedAt": "2024-06-27T06:13:10.308Z"
    },
    "category": {
        "type": "Property",
        "value": "sensor",
        "observedAt": "2024-06-27T06:13:10.308Z"
    },
    "supportedProtocol": {
        "type": "Property",
        "value": "ul20",
        "observedAt": "2024-06-27T06:13:10.308Z"
    }
}

Repeat with previous library:

curl -L 'http://localhost:4041/iot/about'
{
    "libVersion": "4.3.0",
    "port": "4041",
    "baseRoot": "/",
    "version": "3.3.0"
}

Using 4.3.0 library the behaviour was as expected.

Provision Group with apikey

curl -L 'http://localhost:4041/iot/services' \
-H 'fiware-service: openiot' \
-H 'fiware-servicepath: /' \
-H 'Content-Type: application/json' \
-d '{
    "services": [
        {
            "apikey": "4jggokgpepnvsb2uv4s40d59ov",
            "cbroker": "http://orion:1026",
            "entity_type": "Device",
            "resource": "/iot/d",
            "static_attributes": [
                {
                    "name": "category",
                    "type": "Property",
                    "value": "sensor"
                },
                {
                    "name": "supportedProtocol",
                    "type": "Property",
                    "value": "ul20"
                }
            ]
        }
    ]
}'

Provision Device temperature001

curl -L 'http://localhost:4041/iot/devices' \
-H 'fiware-service: openiot' \
-H 'fiware-servicepath: /' \
-H 'Content-Type: application/json' \
-d '{
  "devices": [
    {
      "device_id": "temperature001",
      "entity_name": "urn:ngsi-ld:Device:temperature001",
      "entity_type": "Device",
      "timezone": "Europe/Berlin",
      "attributes": [
        {
          "object_id": "t",
          "name": "temperature",
          "type": "Property",
          "metadata": {
            "unitCode": {
              "type": "Text",
              "value": "CEL"
            }
          }
        }
      ],
      "static_attributes": [
        {
          "name": "controlledAsset",
          "type": "Relationship",
          "value": "urn:ngsi-ld:Building:barn001"
        }
      ]
    }
  ]
}'

Send measure - this is using the Ultralight IoT agent

curl -L 'http://localhost:7896/iot/d?k=4jggokgpepnvsb2uv4s40d59ov&i=temperature001' \
-H 'Content-Type: text/plain' \
-d 't|3'

Result

curl -L 'http://localhost:1026/ngsi-ld/v1/entities/urn:ngsi-ld:Device:temperature001' \
-H 'fiware-service: openiot' \
-H 'fiware-servicepath: /' \
-H 'Link: <http://context/ngsi-context.jsonld>; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json"'

Attribute called temperature is present, controlledAsset is present. ✅

{
    "id": "urn:ngsi-ld:Device:temperature001",
    "type": "Device",
    "controlledAsset": {
        "type": "Relationship",
        "object": "urn:ngsi-ld:Building:barn001",
        "observedAt": "2024-06-27T06:28:42.949Z"
    },
    "category": {
        "type": "Property",
        "value": "sensor",
        "observedAt": "2024-06-27T06:28:42.949Z"
    },
    "supportedProtocol": {
        "type": "Property",
        "value": "ul20",
        "observedAt": "2024-06-27T06:28:42.949Z"
    },
    "temperature": {
        "type": "Property",
        "value": 3,
        "unitCode": "CEL",
        "observedAt": "2024-06-27T06:28:42.949Z"
    }
}

Configs

environment:
      - IOTA_NORTH_PORT=${IOTA_NORTH_PORT}
      - IOTA_REGISTRY_TYPE=mongodb #Whether to hold IoT device info in memory or in a database
      - IOTA_LOG_LEVEL=DEBUG # The log level of the IoT Agent
      - IOTA_TIMESTAMP=true # Supply timestamp information with each measurement
      - IOTA_AUTOCAST=true # Ensure Ultralight number values are read as numbers not strings
      - IOTA_MONGO_HOST=mongo-db # The host name of MongoDB
      - IOTA_MONGO_PORT=${MONGO_DB_PORT} # The port mongoDB is listening on
      - IOTA_MONGO_DB=iotagentul # The name of the database used in mongoDB
      - IOTA_HTTP_PORT=${IOTA_SOUTH_PORT} # The port used for device traffic over HTTP
      - IOTA_PROVIDER_URL=http://iot-agent:${IOTA_NORTH_PORT}
      - IOTA_CB_NGSI_VERSION=ld # use NGSI-LD when sending updates for active attributes
      - IOTA_JSON_LD_CONTEXT=http://context/ngsi-context.jsonld
      - IOTA_FALLBACK_TENANT=openiot
      - IOTA_MULTI_CORE=true

Log output

No response

jason-fox commented 6 days ago

The same issue occurs using NGSI-v2

curl -L 'http://localhost:1026/v2/entities/' \
-H 'fiware-service: openiot' \
-H 'fiware-servicepath: /' 

libversion: 4.3.0

The temperature001 device is correctly mapped to urn:ngsi-ld:Device:temperature001. Attribute called temperature is present, controlledAsset is present. ✅

{
    "id": "urn:ngsi-ld:Device:temperature001",
    "type": "Device",
    "TimeInstant": {
        "type": "DateTime",
        "value": "2024-06-27T06:40:01.074Z",
        "metadata": {}
    },
    "category": {
        "type": "Property",
        "value": "sensor",
        "metadata": {
            "TimeInstant": {
                "type": "DateTime",
                "value": "2024-06-27T06:40:01.074Z"
            }
        }
    },
    "controlledAsset": {
        "type": "Relationship",
        "value": "urn:ngsi-ld:Building:barn001",
        "metadata": {
            "TimeInstant": {
                "type": "DateTime",
                "value": "2024-06-27T06:40:01.074Z"
            }
        }
    },
    "supportedProtocol": {
        "type": "Property",
        "value": "ul20",
        "metadata": {
            "TimeInstant": {
                "type": "DateTime",
                "value": "2024-06-27T06:40:01.074Z"
            }
        }
    },
    "temperature": {
        "type": "Property",
        "value": 3,
        "metadata": {
            "TimeInstant": {
                "type": "DateTime",
                "value": "2024-06-27T06:40:01.074Z"
            },
            "unitCode": {
                "type": "Text",
                "value": "CEL"
            }
        }
    }
}

libversion: 4.3.0

The Device configuration is ignored - the id is taken as an anonymous device and the id constructed. Attribute called temperature is incorrectly called t, controlledAsset is not present. ❌

{
    "id": "Device:temperature001",
    "type": "Device",
    "TimeInstant": {
        "type": "DateTime",
        "value": "2024-06-27T06:41:53.709Z",
        "metadata": {}
    },
    "category": {
        "type": "Property",
        "value": "sensor",
        "metadata": {
            "TimeInstant": {
                "type": "DateTime",
                "value": "2024-06-27T06:41:53.709Z"
            }
        }
    },
    "supportedProtocol": {
        "type": "Property",
        "value": "ul20",
        "metadata": {
            "TimeInstant": {
                "type": "DateTime",
                "value": "2024-06-27T06:41:53.709Z"
            }
        }
    },
    "t": {
        "type": "Text",
        "value": 3,
        "metadata": {
            "TimeInstant": {
                "type": "DateTime",
                "value": "2024-06-27T06:41:53.709Z"
            }
        }
    }
}
AlvaroVega commented 6 days ago

IMHO no autoprovisioned devices should be provisioned with apikey, and then apikey should be provided in json:

curl -L 'http://localhost:4041/iot/devices' \
-H 'fiware-service: openiot' \
-H 'fiware-servicepath: /' \
-H 'Content-Type: application/json' \
-d '{
  "devices": [
    {
      "device_id": "temperature001",
      "apikey": "4jggokgpepnvsb2uv4s40d59ov",
      "entity_name": "urn:ngsi-ld:Device:temperature001",
      "entity_type": "Device",
      "timezone": "Europe/Berlin",
      "attributes": [
        {
          "object_id": "t",
          "name": "temperature",
          "type": "Property",
          "metadata": {
            "unitCode": {
              "type": "Text",
              "value": "CEL"
            }
          }
        }
      ],
      "static_attributes": [
        {
          "name": "controlledAsset",
          "type": "Relationship",
          "value": "urn:ngsi-ld:Building:barn001"
        }
      ]
    }
  ]
}'
jason-fox commented 6 days ago

I would have no problem with doing things that way as best practice (assuming it works - I haven't tried yet). Effectively you're saying use this service data for this apikey + use this device data for this apikey and then merge the two right?

The problem is that until 4.4.0 it was equally possible to provisions saying: _use this service data for this entity_type + use this device data for this entity_type_ and then merge the two. So removal of this mechanism should be noted at a breaking change, and also inhibits users from upgrading to the latest image without re-creating their provisioned devices.

Furthermore, there is nothing in the iotagent-node-lib documentation to describe what is required, and looking at the IoT Agents themselves (e.g. Getting Started for ultralight) provision the service with an apikey but do not add it to the device.

jason-fox commented 5 days ago

I can confirm that adding apiKey to the Device provisioning call does indeed work.