GraphQL Occupancy API

GraphQL API v1 is going to be deprecated in 2024Q3. If you plan to use GraphQL API, please, contact nabrosimov@nwave.io to get GraphQL API v2 documentation.

 

GraphQL Occupancy API offers similar functionality to REST Occupancy API, however, there are additional benefits to using GraphQL.

You can select the fields you want to receive from the API. By requesting only the required fields from the API you can decrease traffic and consequently increase load speed.

Additionally, GraphQL Occupancy API supports subscriptions, which allows you to receive real-time occupancy updates.

 

Authorization

In order to authorize with this API, you need to add the following header to your request:

Key

Value

Key

Value

Authorization

<your token>

Your token can be obtained from the Company Info section of the Nwave’s console.

You are able to call API not more frequent, than 300 times per 5 minutes interval with use of the same token. If amount of requests exceeds the limit, API reponds with error until the end of 5 minutes interval.

GraphQL Schema

The following GraphQL schema describes data types and operations that can be performed.

type GroupOccupancy @aws_api_key @aws_lambda { id: ID! zoneId: Int levelId: Int name: String! groupType: String! customId: String location: Location positionsOccupancy: [PositionOccupancy] summary: OccupancySummary } input GroupOccupancyInput { id: ID! zoneId: Int levelId: Int name: String! groupType: String! customId: String location: LocationInput! positionsOccupancy: [PositionOccupancyInput!]! summary: OccupancySummaryInput! } type LevelOccupancy @aws_api_key @aws_lambda { id: Int name: String zoneId: Int! floorNumber: Int summary: OccupancySummary } input LevelOccupancyInput { id: Int zoneId: Int! floorNumber: Int name: String summary: OccupancySummaryInput! } type Location @aws_api_key @aws_lambda { lat: Float! lon: Float! } input LocationInput { lat: Float! lon: Float! } enum OccupancyStatus { Occupied Free } type OccupancySummary @aws_api_key @aws_lambda { total: Int! occupied: Int! available: Int! undefined: Int! } input OccupancySummaryInput { total: Int! occupied: Int! available: Int! undefined: Int! } type PositionOccupancy @aws_api_key @aws_lambda { id: ID! customId: String groupId: Int occupancyStatus: OccupancyStatus statusChangeTime: AWSDateTime location: Location! labels: [String] } input PositionOccupancyInput { id: ID! customId: String groupId: Int! occupancyStatus: OccupancyStatus! statusChangeTime: AWSDateTime! location: LocationInput! labels: [String] } union RtaUpdateObject = ZoneOccupancy | LevelOccupancy | GroupOccupancy | PositionOccupancy interface SubscriptionArea { id: ID! location: Location radius: Int zoneId: [Int] levelId: [Int] floorNumber: [Int] groupId: [Int] labels: [String] granularity: SubscriptionGranularity! expiresOn: AWSDateTime! } type SubscriptionAreaNoUpdates implements SubscriptionArea @aws_api_key @aws_lambda { id: ID! location: Location radius: Int zoneId: [Int] levelId: [Int] floorNumber: [Int] groupId: [Int] labels: [String] granularity: SubscriptionGranularity! expiresOn: AWSDateTime! } type SubscriptionAreaWithUpdates implements SubscriptionArea @aws_api_key @aws_lambda { id: ID! location: Location radius: Int zoneId: [Int] levelId: [Int] floorNumber: [Int] groupId: [Int] labels: [String] granularity: SubscriptionGranularity! expiresOn: AWSDateTime! updates: [RtaUpdateObject] updateTime: AWSDateTime } enum SubscriptionGranularity { Position Level Group Zone } type ZoneOccupancy @aws_api_key @aws_lambda { id: ID! name: String! projectId: Int summary: OccupancySummary } input ZoneOccupancyInput { id: ID! name: String! projectId: Int summary: OccupancySummaryInput! } type Mutation { createSubscriptionArea( lat: Float, lon: Float, radius: Int, zoneId: [Int], levelId: [Int], floorNumber: [Int], groupId: [Int], labels: [String], granularity: SubscriptionGranularity ): SubscriptionAreaNoUpdates @aws_api_key @aws_lambda updateSubscriptionArea( id: ID!, lat: Float, lon: Float, radius: Int, zoneId: [Int], levelId: [Int], floorNumber: [Int], groupId: [Int], labels: [String], granularity: SubscriptionGranularity ): SubscriptionAreaNoUpdates @aws_api_key @aws_lambda updatePositionOccupancy(positionId: Int!, occupancyStatus: OccupancyStatus!, timestamp: String!): PositionOccupancy @aws_api_key pushPositionUpdates( id: ID!, location: LocationInput, radius: Int, zoneId: [Int], levelId: [Int], floorNumber: [Int], groupId: [Int], labels: [String], granularity: SubscriptionGranularity!, expiresOn: AWSDateTime!, updates: [PositionOccupancyInput!]!, updateTime: String! ): SubscriptionAreaWithUpdates @aws_api_key pushGroupUpdates( id: ID!, location: LocationInput, radius: Int, zoneId: [Int], levelId: [Int], floorNumber: [Int], groupId: [Int], labels: [String], granularity: SubscriptionGranularity!, expiresOn: AWSDateTime!, updates: [GroupOccupancyInput!]!, updateTime: String! ): SubscriptionAreaWithUpdates @aws_api_key pushLevelUpdates( id: ID!, location: LocationInput, radius: Int, zoneId: [Int], levelId: [Int], floorNumber: [Int], groupId: [Int], labels: [String], granularity: SubscriptionGranularity!, expiresOn: AWSDateTime!, updates: [LevelOccupancyInput!]!, updateTime: String! ): SubscriptionAreaWithUpdates @aws_api_key pushZoneUpdates( id: ID!, location: LocationInput, radius: Int, zoneId: [Int], levelId: [Int], floorNumber: [Int], groupId: [Int], labels: [String], granularity: SubscriptionGranularity!, expiresOn: AWSDateTime!, updates: [ZoneOccupancyInput!]!, updateTime: String! ): SubscriptionAreaWithUpdates @aws_api_key } type Query { groupOccupancy(id: ID!): GroupOccupancy @aws_api_key @aws_lambda findGroupOccupancies( ids: [Int], lat: Float, lon: Float, radius: Int, levelId: [Int!], labels: [String!], floorNumber: [Int!], zoneId: [Int!], projectId: Int, groupCustomId: String, limit: Int, offset: Int ): [GroupOccupancy] @aws_api_key @aws_lambda findPositionOccupancies( ids: [Int], lat: Float, lon: Float, radius: Int, groupId: [Int!], levelId: [Int!], labels: [String!], floorNumber: [Int!], zoneId: [Int!], projectId: Int, groupCustomId: String, limit: Int, offset: Int ): [PositionOccupancy] @aws_api_key @aws_lambda } type Subscription { onSubscriptionAreaUpdates(id: ID!): SubscriptionAreaWithUpdates @aws_api_key @aws_lambda @aws_subscribe(mutations: ["pushPositionUpdates","pushGroupUpdates","pushLevelUpdates","pushZoneUpdates"]) } # @api_key (admin) auth and @aws_lambda (user) auth are required for all types apart from: ## updatePositionOccupancy ## pushPositionUpdates ## pushGroupUpdates ## pushLevelUpdates ## pushZoneUpdates ## These are for internal subscription notification and should have exclusive admin access schema { query: Query mutation: Mutation subscription: Subscription }

Subscription Areas

A subscription area is an object that has two main functions:

  1. Filtering of the incoming position updates

  2. Defining the format of updates that is received by subscribers

There are two types defined in the GraphQL schema for Subscription Areas:

SubscriptionAreaNoUpdates - this type is returned when you create or update a Subscription Area. It does not have ‘updates’ & ‘updateTime’ fields as updates are only returned to active subscriptions.

Subscription areas expiry 5 minutes after creation. Any update of a subscription area, including an empty one, will extend the expiration time by 5 minutes from the time of update.

SubscriptionAreaWithUpdates - this type is returned to subscribers and as the name suggests it contains the ‘updates’ & ‘updateTime’ fields.

Subscription Area Filters

Subscription Areas filter the incoming updates and only notify the subscribers if the incoming position update matches all of the filters. Subscription Area filters can also be split into 2 categories:

  1. Hierarchical Filters: zoneId, groupId, levelId, floorNumber, labels

  2. Geospatial Filters: lat, lon, radius

A match is when all position attributes are a subset (⊆) of the Subscription Area filters and it implies that a given position is inside a Subscription Area.

The following example is a valid match between Subscription Area and Position Update.

Position

  1. zoneId: 1

  2. groupId: 2

  3. levelId: 3

  4. floorNumber: 4

  5. labels: ['EV', ‘Disabled’]

  6. lat: 0.0

  7. lon: 0.0

Subscription Area

  1. zoneId: [1, 2, 3]

  2. groupId: [1, 2, 3]

  3. levelId: [3]

  4. floorNumber: null

  5. lables: ['EV', ‘Disabled’, ‘VIP’]

  6. lat: 0.0

  7. lon: 0.0

  8. radius: 100

Subscription Area Updates

The updates field of a Subscription Area can contain a list of 4 different types. All types within the updates list are the same. The type you will receive in the updates field depends on the granularity you choose.

Granularity

Updates Type

Granularity

Updates Type

Zone

ZoneOccupancy

Level

LevelOccupancy

Group

GroupOccupancy

Position

PositionOccupancy

Object & Field Descriptions

SubscirptionArea

  • id - subscription area id

  • location - center coordinates of the search area

  • radius - radius from the center in meeters that creates the search area

  • zoneId - list of zone ids to match

  • levelId - list of level ids to match

  • floorNumber - list of floor numbers to match

  • groupId - list of group ids to match

  • labels - list of labels to match

  • granularity - defines the updates type

  • expiresOn - timestamp of subscription area expiration

  • udpates - occupancy updates for a subscription area

  • updateTime - timestamp of the occupancy update

ZoneOccupancy

  • id - zone id

  • name - zone name

  • projectId - project id

  • summary - summary of occupancies in the Zone

LevelOccupancy

  • id - level id

  • name - level name

  • zoneId - zone id

  • floorNumber - floor number of a level

  • summary - summary of occupancies on a Level

GroupOccupancy

  • id - group id

  • zoneId - zone id

  • levelId - level id

  • name - group name

  • groupType - group type e.g. marked_bay

  • customId - user defined group id

  • location - сenter coordinates of a group

  • positionOccupancy - list of PositionOccupancy objects for a group

  • summary - summary of occupancies in the Group

PositionOccupancy

  • id - position id

  • customId - custom id

  • groupId - group id

  • occupancyStatus - ‘occupied’, ‘free’ or null

  • statusChangeTime - timestamp of last occupancyStatus change

  • location: coordinates of a position

Operations

The following operations can be performed using either Postman or curl command except for subscription operations.

groupOccupancy

This query will return the occupancy of a single group.

Query

query MyQuery { groupOccupancy(id: 123) { id customId name location { lat lon } positionsOccupancy { customId id groupId labels location { lat lon } occupancyStatus statusChangeTime } } }

Response

{ "data": { "groupOccupancy": { "id": 3544, "customId": null, "name": "Foo", "location": { "lat": 51.493601937687224, "lon": -0.12852029611716095 }, "positionsOccupancy": [ { "customId": "", "id": 1, "groupId": 123, "labels": null, "location": { "lat": 51.49360068522545, "lon": -0.1286061268139623 }, "occupancyStatus": "Free", "statusChangeTime": "2021-01-27T19:22:47.548+00:00" }, { "customId": "", "id": 2, "groupId": 123, "labels": null, "location": { "lat": 51.493601937687224, "lon": -0.12852029611716098 }, "occupancyStatus": "Occupoed", "statusChangeTime": "2021-01-27T18:56:53.502+00:00" } ] } } }

findGroupOccupancies

This query will return a list of group occupancies.

Query

 

Response

findPositionOccupancies

This query will return a list of position occupancies.

Query

 

Response

createSubscriptionArea

This mutation will create a new subscription area.

Subscription Area Expiration

By default, subscription areas expire 5 minutes after creation. Once a subscription area is expired, it cannot be extended and subscribers will stop receiving updates.

Mutation

 

Response

onSubscriptionAreaUpdates

This subscription will subscribe to occupancy updates inside an area.

Selected updates object type must correspond to the granularity of the created search area:

  1. granularity: Position → updates { … on PositionOccupancy }

  2. granularity: Group → updates { … on GroupOccupancy }

If you are unsure of the granularity for your subscription area, you can specify both types inside the updates.

Subscription

 

Update

updateSubscriptionArea

This mutation will update an existing subscription area.

Mutation

 

 

Response

 

 

 

 

 

Use cases

Query

Use case

Query

Use case

Displaying all parking groups within 2km in a mobile app

 

Getting group occupancy summary and each position status and labels to be able to calculate occupancy statistics by labels using only one query

Displaying individual parking positions when a user approaches his desired parking location

 

Displaying occupancy statuses of positions with label “Disabled” within 2km radius

Displaying live parking statuses within 2km radius

Displaying live occupancies per level in a zone (multistory car park)

Displaying live occupancies of disabled spaces per level in a zone

 

Extend subscription area expiration

 

Postman Collection

Download Postman Collection here:

Importing Collection

  1. Click import in My Workspace section.

  1. Upload the collection file and click

Import.

 

 

Adding API key to Postman Environment

This collection uses the api_key variable to add an authorization token to the x-api-key header.

  1. To create a new environment click on the eye icon near the top right corner.

2. Click Add to add a new environment.

3. Add the gql_api_key variable name and your token in the initial value.

 

4. Select the New Environment from the list.

5. You can now test the requests in the GraphQL Occupancy Collection.

Example Python Client

There is a simple GraphQL client example in our Github repository. It is developed in Python and let you start getting data on demand and subscribe on occupancy change notifications.

Please, follow the instructions in the README.md to prepare your invironment for running example.

https://github.com/nwaveio/graphql_occupancy_example