# The ngsildclient library
[![NGSI-LD badge](https://img.shields.io/badge/NGSI-LD-red.svg)](https://www.etsi.org/deliver/etsi_gs/CIM/001_099/009/01.02.01_60/gs_CIM009v010201p.pdf)
[![SOF support badge](https://nexus.lab.fiware.org/repository/raw/public/badges/stackoverflow/fiware.svg)](http://stackoverflow.com/questions/tagged/fiware)
<br>
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
[![Read the Docs](https://img.shields.io/readthedocs/ngsildclient)](https://ngsildclient.readthedocs.io/en/latest/index.html)
<br>
[![deploy status](https://github.com/Orange-OpenSource/python-ngsild-client/workflows/CI/badge.svg)](https://github.com/Orange-OpenSource/python-ngsild-client/actions)
[![PyPI](https://img.shields.io/pypi/v/ngsildclient.svg)](https://pypi.org/project/ngsildclient/)
[![Python version](https://img.shields.io/pypi/pyversions/ngsildclient)](https://pypi.org/project/ngsildclient/)
## Overview
**ngsildclient** is a Python library dedicated to NGSI-LD.
It combines :
- a toolbox to create and modify NGSI-LD entities effortlessly
- a NGSI-LD API client to interact with a Context Broker
## Features
### Build NGSI-LD entities
ngsildclient aims at :
- programmatically generate NGSI-LD entities
- load entities from JSON-LD payloads
Four primitives are provided `prop()`, `gprop()`, `tprop()`, `rel()` to build respectively a Property, GeoProperty, TemporalProperty and Relationship.
An Entity is backed by a Python dictionary that stores the JSON-LD payload.
The library operates the mapping between the Entity's attributes and their JSON-LD counterpart, allowing to easily manipulate NGSI-LD value and metadata directly in Python.
### Features list
- primitives to build properties and relationships (chainable)
- benefit from uri naming convention, omit scheme and entity's type, e.g. `parking = Entity("OffStreetParking", "Downtown1")`
- support dot-notation facility, e.g. `reliability = parking["availableSpotNumber.reliability"]`
- easily manipulate a property's value, e.g. `reliability.value = 0.8`
- easily manipulate a property's metadata, e.g. `reliability.datasetid = "dataset1"`
- support nesting
- support multi-attribute
- load/save to file
- load from HTTP
- load well-known sample entities, e.g. `parking = Entity.load(SmartDataModels.SmartCities.Parking.OffStreetParking)`
- provide helpers to ease building some structures, e.g. PostalAddress
- pretty-print entity and properties
### Interact with the Context Broker
Two clients are provided, `Client` and `AsyncClient` respectively for synchronous and asynchronous modes.
Prefer the synchronous one when working in interactive mode, for example to explore and visualize context data in a Jupyter notebook.
Prefer the async one if you're looking for performance, for example to develop a real-time NGSI-LD Agent with a high data-acquisition frequency rate.
### Features list
- synchronous and asynchronous clients
- support batch operations
- support pagination : transparently handle pagination (sending as many requests as needed under the hood)
- support auto-batch : transparently divide into many batch requests if needed
- support queries and alternate (POST) queries
- support temporal queries
- support pandas dataframe as a temporal query result
- support subscriptions
- find subscription conflicts
- SubscriptionBuilder to help build subscriptions
- auto-detect broker vendor and version
- support follow relationships (chainable), e.g. `camera = parking.follow("availableSpotNumber.providedBy")`
## Getting started
### Create our first parking Entity
The following code snippet builds the `OffstreetParking` sample entity from the ETSI documentation.
```python
from datetime import datetime
from ngsildclient import Entity
PARKING_CONTEXT = "https://raw.githubusercontent.com/smart-data-models/dataModel.Parking/master/context.jsonld"
e = Entity("OffStreetParking", "Downtown1")
e.ctx.append(PARKING_CONTEXT)
e.prop("name", "Downtown One")
e.prop("availableSpotNumber", 121, observedat=datetime(2022, 10, 25, 8)).anchor()
e.prop("reliability", 0.7).rel("providedBy", "Camera:C1").unanchor()
e.prop("totalSpotNumber", 200).loc(41.2, -8.5)
```
Let's print the JSON-LD payload.
```python
e.pprint()
```
The result is available [here](https://github.com/Orange-OpenSource/python-ngsild-client/blob/master/parking_sample.jsonld).<br>
### Persist our parking in the Context Broker
The following example assumes that an Orion-LD context broker is running on localhost.<br>
A docker-compose config [file](https://raw.githubusercontent.com/Orange-OpenSource/python-ngsild-client/master/brokers/orionld/docker-compose-troe.yml) file is provided for that purpose.
```python
from ngsildclient import Client
client = Client(port=8026, port_temporal=8027)
client.create(e)
```
### Increase our parking occupancy as the day goes on
Each hour ten more parkings spots are occupied, until 8 p.m.
```python
from datetime import timedelta
prop = e["availableSpotNumber"]
for _ in range(12):
prop.observedat += timedelta(hours=1)
prop.value -= 10
client.update(e)
```
### Retrieve our parking
Get back our parking from the broker and display its `availableSpotNumber` property.<br>
```python
parking = client.get("OffStreetParking:Downtown1", ctx=PARKING_CONTEXT)
parking["availableSpotNumber"].pprint()
```
Only one available parking spot remains at 8 p.m.
```json
{
"type": "Property",
"value": 1,
"observedAt": "2022-10-25T20:00:00Z",
"reliability": {
"type": "Property",
"value": 0.7
},
"providedBy": {
"type": "Relationship",
"object": "urn:ngsi-ld:Camera:C1"
}
}
```
### Request the Temporal Representation of our parking
For convenience we retrieve it as a pandas dataframe.
*If you don't have pandas installed, just omit the `as_dataframe` argument and get JSON instead.*
```python
df = client.temporal.get(e, ctx=PARKING_CONTEXT, as_dataframe=True)
```
Let's display the three last rows.
```python
df.tail(3)
```
| | OffStreetParking | observed | availableSpotNumber |
|---:|:-------------------|:--------------------------|----------------------:|
| 10 | Downtown1 | 2022-10-25 18:00:00+00:00 | 21 |
| 11 | Downtown1 | 2022-10-25 19:00:00+00:00 | 11 |
| 12 | Downtown1 | 2022-10-25 20:00:00+00:00 | 1 |
### Let's throw in a more realistic parking management system
Let us move from our first example to the more realistic parking example provided by the Smart Data Models Program.
```python
from ngsildclient import SmartDataModels
parking = Entity.load(SmartDataModels.SmartCities.Parking.OffStreetParking)
```
Once loaded we can manipulate our new parking the same way we've done until now.<br>
Let's see how it is occupied.
```python
n_total = parking["totalSpotNumber"].value
n_occupied = parking["occupiedSpotNumber"].value
n_avail= parking["availableSpotNumber"].value
print(n_total, n_occupied, n_avail)
```
This parking has 414 parking slots. 282 are occupied. 132 are available.<br>
In order to complete our parking system we would like to add 414 spots to our datamodel.<br>
Let's create a reference parking spot to be used as a template.
```python
spot = Entity("ParkingSpot", "OffStreetParking:porto-ParkingLot-23889:000")
spot.prop("status", "free")
spot.rel("refParkingSite", parking)
```
Let's clone this spot 414 times, assign a disctinct id to each one and occupy the 282 first spots.<br>
This is a simplistic strategy but enough to keep the parking system consistent.
```python
spots = spot * n_total
for i, spot in enumerate(spots):
spot.id = f"{spot.id[:-3]}{i+1:03}"
if i < n_occupied:
spot["status"].value = "occupied"
```
We now establish the relationship between the parking and its spots by adding a new attribute to the parking.<br>
Having a mutual relationship is not necessarily needed. It depends on how we want to navigate in our datamodel. <br>
Let's do it for the sake of example.
```python
from ngsildclient import MultAttrValue
mrel = MultAttrValue()
for spot in spots:
mrel.add(spot, datasetid=f"Dataset:{spot.id[-26:]}")
parking.rel("refParkingSpot", mrel)
```
To sum up we have obtained 415 entities : 1 parking and 414 spots.<br>
Make a single list of these parts and save it into a file.
```python
datamodel = sum(([parking], spots), []) # flatten lists
Entity.save_batch(datamodel, "parking_system.jsonld")
```
The result is available [here](https://github.com/Orange-OpenSource/python-ngsild-client/blob/master/parking_system.jsonld).<br>
Time now to populate our parking system in the broker.
```python
client.upsert(datamodel)
```
Check everything is fine by asking the broker for the number of occupied spots.<br>
Eventually close the client.
```python
client.count("ParkingSpot", q='refParkingSite=="urn:ngsi-ld:OffStreetParking:porto-ParkingLot-23889";status=="occupied"') # 282
client.close()
```
### Let's go further
1. Develop a NGSI-LD Agent
- Collect incoming data from parking IoT *(ground sensors, cameras)* and the parking system API
- Clean data, process data and convert to NGSI-LD entities
- Create and update entities into the NGSI-LD broker *in real-time*
2. Subscribe to events
- Create a subscription to be informed when parking occupation exceeds 90%
- The software that listens to these highly-occupied parking entities can also be a NGSI-LD Agent
<br>Example : programmatically subscribe to events
```python
from ngsildclient import SubscriptionBuilder
subscr = SubscriptionBuilder("https://parkingsystem.example.com:8000/subscription/high-occupancy")
.description("Notify me of high occupancy on parking porto-23889")
.select_type("OffStreetParking")
.watch(["occupancy"])
.query('occupancy>0.9;controlledAsset=="urn:ngsi-ld:OffStreetParking:porto-ParkingLot-23889"')
.build()
client.subscriptions.create(subscr)
```
## Where to get it
The source code is currently hosted on GitHub at :
https://github.com/Orange-OpenSource/python-ngsild-client
Binary installer for the latest released version is available at the [Python
package index](https://pypi.org/project/ngsildclient).
## Installation
**ngsildclient** requires Python 3.9+.
```sh
pip install ngsildclient
```
## Documentation
User guide is available on [Read the Docs](https://ngsildclient.readthedocs.io/en/latest/index.html).
Refer to the [Cookbook](https://ngsildclient.readthedocs.io/en/latest/cookbook.html) chapter that provides many HOWTOs to :
- develop various NGSI-LD Agents collecting data from heterogeneous datasources
- forge NGSI-LD sample entities from the Smart Data Models initiative
## License
[Apache 2.0](LICENSE)
Raw data
{
"_id": null,
"home_page": "https://github.com/Orange-OpenSource/python-ngsild-client",
"name": "ngsildclient",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.9,<4.0",
"maintainer_email": "",
"keywords": "Fiware,NGSI,NGSI-LD,NGSI Agent",
"author": "fbattello",
"author_email": "fabien.battello@orange.com",
"download_url": "https://files.pythonhosted.org/packages/26/7d/576cacd953fb21709fc850f3ad589c92ffedc342f27cf09292d2659838cc/ngsildclient-0.5.2.tar.gz",
"platform": null,
"description": "# The ngsildclient library\n\n[![NGSI-LD badge](https://img.shields.io/badge/NGSI-LD-red.svg)](https://www.etsi.org/deliver/etsi_gs/CIM/001_099/009/01.02.01_60/gs_CIM009v010201p.pdf)\n[![SOF support badge](https://nexus.lab.fiware.org/repository/raw/public/badges/stackoverflow/fiware.svg)](http://stackoverflow.com/questions/tagged/fiware)\n<br>\n[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)\n[![Read the Docs](https://img.shields.io/readthedocs/ngsildclient)](https://ngsildclient.readthedocs.io/en/latest/index.html)\n<br>\n[![deploy status](https://github.com/Orange-OpenSource/python-ngsild-client/workflows/CI/badge.svg)](https://github.com/Orange-OpenSource/python-ngsild-client/actions)\n[![PyPI](https://img.shields.io/pypi/v/ngsildclient.svg)](https://pypi.org/project/ngsildclient/)\n[![Python version](https://img.shields.io/pypi/pyversions/ngsildclient)](https://pypi.org/project/ngsildclient/)\n\n\n## Overview\n\n **ngsildclient** is a Python library dedicated to NGSI-LD.\n \n It combines :\n\n - a toolbox to create and modify NGSI-LD entities effortlessly\n - a NGSI-LD API client to interact with a Context Broker\n\n## Features\n\n### Build NGSI-LD entities\n\nngsildclient aims at :\n\n- programmatically generate NGSI-LD entities\n- load entities from JSON-LD payloads\n\nFour primitives are provided `prop()`, `gprop()`, `tprop()`, `rel()` to build respectively a Property, GeoProperty, TemporalProperty and Relationship.\n\nAn Entity is backed by a Python dictionary that stores the JSON-LD payload.\nThe library operates the mapping between the Entity's attributes and their JSON-LD counterpart, allowing to easily manipulate NGSI-LD value and metadata directly in Python.\n\n### Features list\n\n- primitives to build properties and relationships (chainable)\n- benefit from uri naming convention, omit scheme and entity's type, e.g. `parking = Entity(\"OffStreetParking\", \"Downtown1\")`\n- support dot-notation facility, e.g. `reliability = parking[\"availableSpotNumber.reliability\"]`\n- easily manipulate a property's value, e.g. `reliability.value = 0.8`\n- easily manipulate a property's metadata, e.g. `reliability.datasetid = \"dataset1\"`\n- support nesting\n- support multi-attribute\n- load/save to file\n- load from HTTP\n- load well-known sample entities, e.g. `parking = Entity.load(SmartDataModels.SmartCities.Parking.OffStreetParking)`\n- provide helpers to ease building some structures, e.g. PostalAddress\n- pretty-print entity and properties\n\n### Interact with the Context Broker\n\nTwo clients are provided, `Client` and `AsyncClient` respectively for synchronous and asynchronous modes.\n\nPrefer the synchronous one when working in interactive mode, for example to explore and visualize context data in a Jupyter notebook.\nPrefer the async one if you're looking for performance, for example to develop a real-time NGSI-LD Agent with a high data-acquisition frequency rate.\n\n### Features list\n\n - synchronous and asynchronous clients\n - support batch operations\n - support pagination : transparently handle pagination (sending as many requests as needed under the hood)\n - support auto-batch : transparently divide into many batch requests if needed\n - support queries and alternate (POST) queries\n - support temporal queries\n - support pandas dataframe as a temporal query result\n - support subscriptions\n - find subscription conflicts\n - SubscriptionBuilder to help build subscriptions\n - auto-detect broker vendor and version\n - support follow relationships (chainable), e.g. `camera = parking.follow(\"availableSpotNumber.providedBy\")`\n\n## Getting started\n\n### Create our first parking Entity\n\nThe following code snippet builds the `OffstreetParking` sample entity from the ETSI documentation.\n\n```python\nfrom datetime import datetime\nfrom ngsildclient import Entity\n\nPARKING_CONTEXT = \"https://raw.githubusercontent.com/smart-data-models/dataModel.Parking/master/context.jsonld\"\n\ne = Entity(\"OffStreetParking\", \"Downtown1\")\ne.ctx.append(PARKING_CONTEXT)\ne.prop(\"name\", \"Downtown One\")\ne.prop(\"availableSpotNumber\", 121, observedat=datetime(2022, 10, 25, 8)).anchor()\ne.prop(\"reliability\", 0.7).rel(\"providedBy\", \"Camera:C1\").unanchor()\ne.prop(\"totalSpotNumber\", 200).loc(41.2, -8.5)\n```\n\nLet's print the JSON-LD payload.\n\n```python\ne.pprint()\n```\n\nThe result is available [here](https://github.com/Orange-OpenSource/python-ngsild-client/blob/master/parking_sample.jsonld).<br>\n\n\n### Persist our parking in the Context Broker\n\nThe following example assumes that an Orion-LD context broker is running on localhost.<br>\nA docker-compose config [file](https://raw.githubusercontent.com/Orange-OpenSource/python-ngsild-client/master/brokers/orionld/docker-compose-troe.yml) file is provided for that purpose.\n\n```python\nfrom ngsildclient import Client\n\nclient = Client(port=8026, port_temporal=8027)\nclient.create(e)\n```\n\n### Increase our parking occupancy as the day goes on\n\nEach hour ten more parkings spots are occupied, until 8 p.m.\n\n```python\nfrom datetime import timedelta\n\nprop = e[\"availableSpotNumber\"]\nfor _ in range(12):\n prop.observedat += timedelta(hours=1)\n prop.value -= 10\n client.update(e)\n```\n\n### Retrieve our parking\n\nGet back our parking from the broker and display its `availableSpotNumber` property.<br>\n\n```python\nparking = client.get(\"OffStreetParking:Downtown1\", ctx=PARKING_CONTEXT)\nparking[\"availableSpotNumber\"].pprint()\n```\n\nOnly one available parking spot remains at 8 p.m.\n\n```json\n{\n \"type\": \"Property\",\n \"value\": 1,\n \"observedAt\": \"2022-10-25T20:00:00Z\",\n \"reliability\": {\n \"type\": \"Property\",\n \"value\": 0.7\n },\n \"providedBy\": {\n \"type\": \"Relationship\",\n \"object\": \"urn:ngsi-ld:Camera:C1\"\n }\n}\n```\n\n### Request the Temporal Representation of our parking\n\nFor convenience we retrieve it as a pandas dataframe.\n\n*If you don't have pandas installed, just omit the `as_dataframe` argument and get JSON instead.*\n\n```python\ndf = client.temporal.get(e, ctx=PARKING_CONTEXT, as_dataframe=True)\n```\n\nLet's display the three last rows.\n\n```python\ndf.tail(3)\n```\n\n| | OffStreetParking | observed | availableSpotNumber |\n|---:|:-------------------|:--------------------------|----------------------:|\n| 10 | Downtown1 | 2022-10-25 18:00:00+00:00 | 21 |\n| 11 | Downtown1 | 2022-10-25 19:00:00+00:00 | 11 |\n| 12 | Downtown1 | 2022-10-25 20:00:00+00:00 | 1 |\n\n### Let's throw in a more realistic parking management system\n\nLet us move from our first example to the more realistic parking example provided by the Smart Data Models Program.\n\n```python\nfrom ngsildclient import SmartDataModels\n\nparking = Entity.load(SmartDataModels.SmartCities.Parking.OffStreetParking)\n```\n\nOnce loaded we can manipulate our new parking the same way we've done until now.<br>\nLet's see how it is occupied.\n\n```python\nn_total = parking[\"totalSpotNumber\"].value\nn_occupied = parking[\"occupiedSpotNumber\"].value\nn_avail= parking[\"availableSpotNumber\"].value\nprint(n_total, n_occupied, n_avail)\n```\n\nThis parking has 414 parking slots. 282 are occupied. 132 are available.<br>\nIn order to complete our parking system we would like to add 414 spots to our datamodel.<br>\nLet's create a reference parking spot to be used as a template.\n\n```python\nspot = Entity(\"ParkingSpot\", \"OffStreetParking:porto-ParkingLot-23889:000\")\nspot.prop(\"status\", \"free\")\nspot.rel(\"refParkingSite\", parking)\n```\n\nLet's clone this spot 414 times, assign a disctinct id to each one and occupy the 282 first spots.<br>\nThis is a simplistic strategy but enough to keep the parking system consistent.\n\n```python\nspots = spot * n_total\nfor i, spot in enumerate(spots):\n spot.id = f\"{spot.id[:-3]}{i+1:03}\"\n if i < n_occupied:\n spot[\"status\"].value = \"occupied\"\n```\n\nWe now establish the relationship between the parking and its spots by adding a new attribute to the parking.<br>\nHaving a mutual relationship is not necessarily needed. It depends on how we want to navigate in our datamodel. <br>\nLet's do it for the sake of example.\n\n```python\nfrom ngsildclient import MultAttrValue\n\nmrel = MultAttrValue()\nfor spot in spots:\n mrel.add(spot, datasetid=f\"Dataset:{spot.id[-26:]}\")\nparking.rel(\"refParkingSpot\", mrel)\n```\n\nTo sum up we have obtained 415 entities : 1 parking and 414 spots.<br>\nMake a single list of these parts and save it into a file.\n\n```python\ndatamodel = sum(([parking], spots), []) # flatten lists\nEntity.save_batch(datamodel, \"parking_system.jsonld\")\n```\nThe result is available [here](https://github.com/Orange-OpenSource/python-ngsild-client/blob/master/parking_system.jsonld).<br>\nTime now to populate our parking system in the broker.\n\n```python\nclient.upsert(datamodel)\n```\n\nCheck everything is fine by asking the broker for the number of occupied spots.<br>\nEventually close the client.\n\n```python\nclient.count(\"ParkingSpot\", q='refParkingSite==\"urn:ngsi-ld:OffStreetParking:porto-ParkingLot-23889\";status==\"occupied\"') # 282\nclient.close()\n```\n\n### Let's go further\n\n1. Develop a NGSI-LD Agent\n\n - Collect incoming data from parking IoT *(ground sensors, cameras)* and the parking system API\n - Clean data, process data and convert to NGSI-LD entities\n - Create and update entities into the NGSI-LD broker *in real-time*\n\n2. Subscribe to events\n\n - Create a subscription to be informed when parking occupation exceeds 90%\n - The software that listens to these highly-occupied parking entities can also be a NGSI-LD Agent\n\n <br>Example : programmatically subscribe to events\n\n ```python\n from ngsildclient import SubscriptionBuilder\n\n subscr = SubscriptionBuilder(\"https://parkingsystem.example.com:8000/subscription/high-occupancy\")\n .description(\"Notify me of high occupancy on parking porto-23889\")\n .select_type(\"OffStreetParking\")\n .watch([\"occupancy\"])\n .query('occupancy>0.9;controlledAsset==\"urn:ngsi-ld:OffStreetParking:porto-ParkingLot-23889\"')\n .build()\n client.subscriptions.create(subscr)\n ```\n\n## Where to get it\n\nThe source code is currently hosted on GitHub at :\nhttps://github.com/Orange-OpenSource/python-ngsild-client\n\nBinary installer for the latest released version is available at the [Python\npackage index](https://pypi.org/project/ngsildclient).\n\n## Installation\n\n**ngsildclient** requires Python 3.9+.\n\n```sh\npip install ngsildclient\n```\n\n## Documentation\n\nUser guide is available on [Read the Docs](https://ngsildclient.readthedocs.io/en/latest/index.html).\n\nRefer to the [Cookbook](https://ngsildclient.readthedocs.io/en/latest/cookbook.html) chapter that provides many HOWTOs to :\n\n- develop various NGSI-LD Agents collecting data from heterogeneous datasources\n- forge NGSI-LD sample entities from the Smart Data Models initiative\n\n## License\n\n[Apache 2.0](LICENSE)\n",
"bugtrack_url": null,
"license": "Apache-2.0",
"summary": "A Python library that helps building NGSI-LD entities and interacting with a NGSI-LD Context Broker",
"version": "0.5.2",
"split_keywords": [
"fiware",
"ngsi",
"ngsi-ld",
"ngsi agent"
],
"urls": [
{
"comment_text": "",
"digests": {
"md5": "f472671cc3bc175ed204b5b98b8fdc76",
"sha256": "4b2535cef0ad5873545c8714e643ec049734e85bdb44ce2dd43be8f0a0d1b924"
},
"downloads": -1,
"filename": "ngsildclient-0.5.2-py3-none-any.whl",
"has_sig": false,
"md5_digest": "f472671cc3bc175ed204b5b98b8fdc76",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.9,<4.0",
"size": 83662,
"upload_time": "2022-12-01T16:02:41",
"upload_time_iso_8601": "2022-12-01T16:02:41.445562Z",
"url": "https://files.pythonhosted.org/packages/4f/54/b364d961bca8443a7a0c28b84a788703a8ea137454c21704f559d5d061f7/ngsildclient-0.5.2-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"md5": "924af9ac84c7941cfd52e49cede92b10",
"sha256": "ab0135b4bdb3aa4f2f654bc1cb1e772d027670d0accc07ac098abc04e79c7bac"
},
"downloads": -1,
"filename": "ngsildclient-0.5.2.tar.gz",
"has_sig": false,
"md5_digest": "924af9ac84c7941cfd52e49cede92b10",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.9,<4.0",
"size": 59938,
"upload_time": "2022-12-01T16:02:43",
"upload_time_iso_8601": "2022-12-01T16:02:43.557561Z",
"url": "https://files.pythonhosted.org/packages/26/7d/576cacd953fb21709fc850f3ad589c92ffedc342f27cf09292d2659838cc/ngsildclient-0.5.2.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2022-12-01 16:02:43",
"github": true,
"gitlab": false,
"bitbucket": false,
"github_user": "Orange-OpenSource",
"github_project": "python-ngsild-client",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "ngsildclient"
}