openapi: 3.0.0 info: description: "# Introduction\nWelcome to the Gravity Connect API documentation for Device Partners (typically Device OEMs). This developer guide outlines the API endpoints and integration capabilities that Device Partners need to implement to connect their distributed energy resource (DER) devices with a virtual power plant (VPP) platform or a DERMS solution. The Device Partner endpoints work in combination with [the VPP's Gravity Connect endpoints](./vp-api.html) to support device enrollment and real-time control.\n\nAfter building the endpoints described in this guide, Device Partners can add their products to the VPP's customers' programs. Utilities will use the VPP's platform and suite of device integrations to access real-time data and control smart devices as part of VPPs, demand response programs, and/or DER management programs.\n\nThis documentation is open-source, and we encourage all DERMS and VPP platforms to use this standard. Integrations built to the Gravity Connect API standard are not limited to use with one VPP platform.\n\n# About Gravity Connect\n\nGravity Connect is an open API specification developed by Virtual Peaker to simplify and accelerate the integration of distributed energy resource (DER) devices into utility demand response (DR) and DER management programs.\n\nGravity Connect offers several key benefits to Device Partners:\n* **Streamlined device integration:** Gravity Connect provides standardized endpoints and data models to enable quick, easy integration of your devices with VPP platforms.\n* **Accelerated time-to-value:** With simplified integrations via Gravity Connect, you can get your devices connected to DR programs faster, allowing utilities to control and access data from your devices sooner. This means faster time-to-value.\n* **Open and interoperable:** Gravity Connect uses modern API best practices and is openly published to facilitate integration. It is designed to be vendor-agnostic so your devices can integrate with any VPP or DERMS platform that implements the specification.\n* **Large ecosystem access:** VPP platforms that use Gravity Connect have relationships with dozens of utilities that run DR programs. Integrating via Gravity Connect gives your devices access to this large ecosystem of potential customers and programs.\n* **Standardized device control:** As VPPs launch advanced control software to improve utility event operations, Gravity Connect will continue to standardize control commands so that each improvement does not require modifications to each integration.\n\nGravity Connect simplifies and streamlines the process of integrating DER devices into utility programs by handling the complexity behind a standardized and interoperable API. By partnering with VPPs using a Gravity Connect integration, your devices can access a large ecosystem of utility customers and programs quickly and easily.\n\n## Articles About Gravity Connect\n* [API Showdown: Gravity Connect v. OpenADR](https://virtual-peaker.com/blog/gravity-connect-v-openadr/)\n* [How the Gravity Connect API Enhances OEMS & DERMS Integrations](https://virtual-peaker.com/blog/gravity-connect-api-oem-derms-integrations/)\n* [Virtual Peaker Launches Gravity Connect API](https://virtual-peaker.com/news/virtual-peaker-launches-gravity-connect-api/)\n\n## Interested in Getting Started with Gravity Connect?\nIf you're an OEM, utility, DERMS provider, or technology provider interested in learning more about integrating with Gravity Connect, our team is here to help.\n\nTo get started and discuss your use case, please reach out to our Gravity Connect support team at [gravity-connect@virtual-peaker.com](gravity-connect@virtual-peaker.com).\n\n# Changelog\nThis list tracks noteworthy changes to the API specification.\n\n* 2.0.6 - Revised the explanatory sections of the documentation.\n* 2.0.5 - Added Device Partner Driven Enrollment.\n* 2.0.4 - Updated power and energy signals in the TSTAT device-specific definition.\n* 2.0.3 - Updated the command and signals in the TSTAT device-specific definition.\n* 2.0.2 - Add `unsubscribe` from Device publishing as VP method of unenrollment. And, updated diagrams and and minor typo fixes.\n* 2.0.1 - Major updates to documentation including the `Energy Interval` endpoint.\n* 1.5.2 - Fixed using incorrect `baseUrl` to correct `vpBaseUrl` in VP Endpoints of Postman Collection.\n* 1.5.0 - Adding specification for EVSE device type with signals, settings, and commands.\n* 1.4.1 - Adding storage HVAC device type and FAQs. Clarifying startTime may be in past. Making deviceUids array optional when creating group.\n* 1.4.0 - Clarifying CTA modes (including swapping NORMAL in for RUN_NORMAL) and cta-op-modes.\n* 1.3.3 - Requiring country when houses are published to device partner. Specifying country field follows ISO 3166-1 alpha-2 (contrary to previous documentation which reference `USA` as a value).\n* 1.3.2 - Adding an optional array of devices when publishing houses.\n* 1.3.0 - Adding endpoint for individual devices to opt out of a group command.\n* 1.2.0 - Adding Publish houses for installation endpoint under a new pairing code based onboarding flow. Also clarifying pairing code structure and international addresses.\n* 1.1.0 - Adding Pairing Code Device Discovery as a proposal. Also clarifying that 1 program = 1 utility.\n* 1.0.1 - `invalidDevices` was listed under the request body for `Update group details`, but it was meant to be under the response. Fixed this.\n\n# Getting Started\n\n## Endpoint Architecture\nThe remainder of this guide describes the Device Partner endpoints, which are half of the integration. Meanwhile, the [VPP API](./vp-api.html) guide outlines the API endpoints hosted by the VPP for receiving data from the Device Partner's devices. The endpoints support standard integration operations, including publishing device signals, command statuses, and device enrollment events. In combination with the Device Partner endpoints, they allow a two-way flow of information that enables utilities to control and manage devices.\n\nThis data enables essential capabilities like:\n* Real-time device monitoring and analytics\n* Aggregation of fleet data across a utility\n* Execution of demand response events and commands\n* Accurate measurement and verification of event performance\n\nHaving a standardized set of endpoints for data intake simplifies integration significantly compared to custom development. Device Partners simply publish data in the documented format to stream it to the VPP's platform.\n\nLearn more about the data flows in the [Integration Overview](#section/Getting-Started/Device-Partner-Integration-Overview).\n\n## Glossary\n* **Device Partner**: The party working to integrate with the VPP. This may be an OEM or a company creating digital solutions for devices.\n* **Program**: An arrangement where a utility controls homeowners' devices in exchange for incentives such as rebates or gift cards. Multiple Device Partners typically participate in each program.\n* **PROGRAM_PUBLISH_KEY**: The identifier for a program, unique to each combination of program and Device Partner.\n* **PROGRAM_PUBLISH_SECRET**: The secret used as an input to HMAC for publishing program data to the VPP.\n* **DEVICE_PUBLISH_SECRET**: The secret used as an input to HMAC for publishing device data to the VPP.\n* **Signals and Settings**: The device telemetry and configuration data that is streamed to the VPP. See the [Device Type Specifications](#section/Device-Type-Specifications) for a specific list of the signals and settings required for each device type.\n* **VP_COMMAND_OBJECT**: A portion of the payload sent to the Device Partner when the VPP sends a command. For more information about the commands required for each device type, see the [Device Type Specifications](#section/Device-Type-Specifications). For details about the command request, see the [Send Command Endpoint](#operation/sendCommand).\n* **OAuth 2.0**: An open authorization framework that enables applications to obtain limited access to user accounts on an HTTP service, such as for device onboarding. It works by delegating user authentication to the service that hosts the user account, and authorizing third-party applications to access the user account. Gravity Connect uses OAuth 2.0 for device enrollment and for all requests from the VPP to the Device Partner, such as commands.\n* **Webhook**: A method for apps to provide other applications with real-time information. When an event occurs, a HTPP POST callback is sent to the specified webhook URL endpoint. Gravity Connect uses webhooks for Device Partners to publish data updates instead of the VPP polling for data.\n\n## Building an Integration\n\nA new Device Partner typically integrates with a VPP's Gravity Connect API following this process:\n\n1. **Kickoff Meeting:** the VPP and the Device Partner discuss which device types and programs this integration will be used for initially, which affects which enrollment flows and data models will be implemented.\n2. **Onboarding:** the VPP provides the Device Partner with access to a development environment.\n4. **Implementation:** The Device Partner implements the required Gravity Connect endpoints based on the documentation and earlier discussions about the scope of the integration.\n5. **QA:** After the Device Partner has tested the integration, the VPP performs a final round of testing to validate the functionality and data flows.\n6. **Beta Launch:** The integration goes live for the first customer, and the Device Partner can begin enrolling devices into the utility program. These devices begin streaming data shortly after enrollment and are ready to participate in events. The VPP and the Device Partner monitor the first enrollments to ensure success.\n7. **Go Live:** After the integration has operated smoothly for a period of time, it becomes available to other customers and utility programs.\n\n## Postman Collection\nTo use Postman to interact with endpoints, download the [Postman Collection](./Gravity%20Connect%20API.postman_collection.json). After file opens hit Mac(command + s) or Windows(control + s) to save the file and download it to your computer.\n\n### Postman Setup\n1. Download the file linked [here](./Gravity%20Connect%20API.postman_collection.json).\n2. Open Postman and select `Import` near the top right of the left side panel menu.\n3. Select `Upload Files` and select the Postman Collection you downloaded earlier.\n4. Upon successful import it will create a collection Called `Gravity Connect API` with two main folders inside called `VP Endpoints` and `Device Partner Endpoints`.\n\n### Device Partner Endpoints\nThe collection uses Postman’s built in authentication handling to authenticate the request. Below are the steps to configure and request a new token, which will then be automatically included in subsequent requests.\n1. Select the `VP Endpoints` folder.\n2. Set the following:\n\t* Set `Type` to `OAuth2.0` and `Add auth dat to` to `Request Headers`.\n\t* Enter `Token Name` to what you want it to be called.\n\t* Set `Grant Type` to `Client Credentials`.\n\t* Use the `Client ID` and `Client Secret` you created within your application.\n\t* Set the `Access Token URL` to the auth endpoint for your application.\n\t* Set `Client Authentication` to `Send as Basic header`.\n\t* Leave any remaining fields to their default setting.\n3. Click `Get New Access Token` button.\n4. If all was successful it should pop up a confirmation the token was successfully created. Click use token and you are ready to begin accessing the endpoints.\n\n### Device Partner Endpoints (OAuth Onboarding)\nWe haven’t used the Postman collection for these endpoints yet, but we expect that using the Authorization Code flow will work something like:\n1. Select the `OAuth Onboarding` folder.\n2. Set the following:\n\t* Set `Type` to `OAuth2.0` and `Add auth dat to` to `Request Headers`.\n\t* Enter `Token Name` to what you want it to be called.\n\t* Set `Grant Type` to `Authorization Code`.\n\t* Use the `Client ID` and `Client Secret` you created within your application.\n\t* Set the scope to the scope that your application expects for our use case.\n\t* Set the `Access Token URL` and `Auth URL` to your urls.\n\t* Set the `Callback URL` to the VPP’s URL.\n\t* Set `Client Authentication` to `Send as Basic header`.\n\t* Leave any remaining fields to their default setting.\n3. Click `Get New Access Token` button.\n4. If all was successful it should pop up a confirmation the token was successfully created. Click use token and you are ready to begin accessing the endpoints.\n\n### Authentication for VPP Endpoints\nAttached to the VPP endpoints is a script that will automatically create an HMAC and set it as a variable that is then used to make the request. These scripts expect that you’ve properly set the PROGRAM_PUBLISH_KEY, PROGRAM_PUBLISH_SECRET, DEVICE_PUBLISH_SECRET.\n\nTo do this:\n1. Click on the root folder for the Gravity Connect API. Then select the tab `Variables`\n2. Find the variable and update the `CURRENT_VALUE`\n\n# Integration Flows\n\n![diagram of system connection](assets/diagrams/system-connection.png)\n[See larger image](assets/diagrams/system-connection.png)\n\n# Authentication\nGravity Connect uses the following authentication models:\n\n**Requests from the VPP to the Device Partner's endpoints**\n* These requests use OAuth 2.0 authentication. Please note that these OAuth credentials are required even if the homeowner enrollment flow is not OAuth-based. If the homeowner enrollment flow is OAuth-based, it can use separate credentials or it can re-use these.\n* The Device Partner provides:\n * `clientId`: A program-specific ID.\n * `clientSecret`: A program-specific secret.\n\n**Requests from the Device Partner to the VPP's endpoints**\n* These requests authenticate using a combination of the following credentials and HMAC authentication. The VPP provides:\n * `PROGRAM_PUBLISH_KEY`: The unique identifier for a utility program, provided when a program is set up.\n * `PROGRAM_PUBLISH_SECRET`: The secret key used to generate HMAC signatures when publishing program-level data, provided when a program is set up.\n * `DEVICE_PUBLISH_SECRET`: The secret key used to generate HMAC signatures when publishing device-level data, provided when each device is enrolled.\n* Find more detail about authenticating with the VPP's endpoints in the [VPP API Guide](./vp-api.html#section/Authentication).\n\n# Device Enrollment\nEach device will be added to a program using one of the following flows. After a device is added, the VPP will call the `/subscription` endpoint on the Device Partner's API to enable device data publishing and share the `DEVICE_PUBLISH_SECRET`.\n\nThe flows most suitable for device owners to enroll devices are:\n1. OAuth Device Discovery\n2. Pairing Code Device Discovery\n\nThe flows most suitable for installers or program managers to enroll devices are:\n1. Device Partner Driven Enrollment\n2. Pairing Code Device Discovery\n\n## Pairing Codes\n\nPairing codes are used in some enrollment flows to link device enrollments to homeowner enrollments to ensure accurate device association. The format is:\n\n- **2 alphanumeric characters** denoting the pairing code prefix representing the program (e.g., `A1`)\n- **5 random numeric characters** (e.g., `12345`)\n- **1 check digit** (Luhn algorithm, calculated from the 5 random numeric characters, e.g., `5`)\n\n**Example:** `A1123455`\n\n\n### How to Validate a Pairing Code\n1. Extract the first 2 alphanumeric characters to validate them against program pairing code prefix.\n2. Extract the 5 random numeric characters (characters 3–7).\n3. Calculate the Luhn check digit using the standard algorithm.\n4. Compare the calculated digit to the last digit (character 8).\n\nThe device partner can validate the pairing code to provide immediate feedback. The VPP's API can perform the same validation upon receipt of a pairing code.\n\n## OAuth Device Discovery (Preferred)\n[Diagram](https://d1byei05mtwnfj.cloudfront.net/apidocs/workflow/oauth-device-discovery.jpg)\n\nIn this flow, a device owner fills out an enrollment form hosted by the VPP, then logs in to their Device Partner account to grant the VPP permission to access and control their devices.\n\nThe OAuth device discovery flow works as follows:\n1. The device owner fills out an onboarding form on the VPP's site.\n2. At the end of the form, the device owner is redirected to the Device Partner's OAuth authorization page via a link.\n3. The user logs into the Device Partner's app and grants OAuth access permissions.\n4. The Device Partner app completes the OAuth authorization code flow, exchanging the code for an access token.\n5. Using the access token, the Device Partner calls their API to retrieve the user's devices.\n6. The Device Partner associates the devices with the correct VPP program in their backend and completes any other required internal onboarding logic.\n7. The Device Partner completes the OAuth callback to the VPP.\n8. The VPP discovers devices using the OAuth token provided.\n9. The VPP subscribes to devices for data publishing and provides a DEVICE_PUBLISH_SECRET.\n\nThe VPP creates a unique `client_id` for each utility program. This ID is passed in the OAuth link to associate devices with the correct program.\n\nWhen a customer onboards via the OAuth flow, we will direct them to a URL in the format below with:\n\n* Your domain substituted for example.com\n* Our domain substituted for gravity.example.com\n* The `client_id` of the program the user is onboarding to\n* A state generated by us to track the flow\n* The scopes required within your system\n* access_type=offline as per to indicate that a refresh token should be provided ([reference](https://developers.google.com/identity/protocols/oauth2/openid-connect#access-type-param))\n\n```\nhttps://example.com/oauth2/authorize?access_type=offline&response_type=code&redirect_uri=https%3A%2F%2Fgravity.example.com%2Foauth2%2Fcallback&client_id={}&state={}&scope={}\n```\n\n## Pairing Code Device Discovery - End User App\n[Diagram](https://d1byei05mtwnfj.cloudfront.net/apidocs/workflow/end-user-app.jpg)\n\nIn this flow, a device owner fills out an enrollment form hosted by the VPP, which then presents them with a pairing code. The device owner then informs the Device Partner of their pairing code, which the Device Partner sends to the VPP with information about the device owner's devices. The VPP uses the pairing code to match the devices to the correct enrollment form data.\n\nThe [pairing code](#section/Pairing-Codes) device discovery flow works as follows:\n1. The device owner fills out an onboarding form on the VPP's site.\n2. The form provides them with a pairing code and instructions to enter it in the Device Partner's app.\n3. The device owner enters the pairing code in the Device Partner's app, and the Device Partner publishes a device enrollment event to the VPP's API.\n 1. The publish event includes the pairing code, allowing the VPP to match the devices to the correct enrollment form data.\n 2. If multiple devices are enrolled for this device owner, the Device Partner sends multiple publish payloads with the same pairing code.\n4. The VPP subscribes to each device for data publishing and provides a `DEVICE_PUBLISH_SECRET`.\n\nEach utility program is assigned a different pairing code prefix that Device Partners can use to identify or confirm which program a device owner is joining.\n\n## Device Partner Driven Enrollment\n[![Device Partner Driven Enrollment Diagram](assets/diagrams/device-partner-driven-enrollment.png)](assets/diagrams/device-partner-driven-enrollment.png)\n\nDevice Partner Driven Enrollment is a flow that can be used when the Device Partner has enough information about both the devices and the site to form a complete record in the VPP platform. This includes data such as the device UID, the service address, and the device owner's contact information.\n\nThe Device Partner can use this flow to proactively enroll devices through the Device Partner's app/website, pre-enroll a device purchased directly from the Device Partner, and bulk enroll devices that are transferred to the VPP.\n\n1. The Device Partner publishes an enrollment message containing information about the site and each of the eligible devices. This enrollment message uses the program-specific credentials for the appropriate program.\n2. The VPP subscribes to the devices for data publishing and provides a `DEVICE_PUBLISH_SECRET` for each.\n3. If the enrollment application is approved, there will be no change. If the enrollment application is rejected by the utility, the VPP will unsubscribe from data publishing.\n\n## Pairing Code Device Discovery - Utility Commissioned Installation (Deprecated)\n[Diagram](https://d1byei05mtwnfj.cloudfront.net/apidocs/workflow/utility-commissioned-installation.jpg)\n\nThis flow has been deprecated. The [Device Partner Driven Enrollment](#section/Device-Onboarding/Device-Partner-Driven-Enrollment) flow is recommended for programs where the device owner is not involved in the enrollment process, such as direct install programs.\n\nThe utility commissioned [pairing code](#section/Pairing-Codes) flow works as follows:\n1. The device owner fills out an onboarding form on the VPP's site.\n2. The utility reviews the submission and approves it if eligible.\n3. The VPP informs the Device Partner of the service address and provides a pairing code.\n4. The Device Partner installs and activates the device.\n5. When the installation is complete, the Device Partner publishes a device enrollment event to the VPP.\n 1. The publish event includes the pairing code, allowing the VPP to match the devices to the correct enrollment form data.\n 2. If multiple devices are enrolled for this device owner, the Device Partner sends multiple publish payloads with the same pairing code.\n4. The VPP subscribes to each device for data publishing and provides a `DEVICE_PUBLISH_SECRET`.\n\n# Data Publishing\nAfter a device has been enrolled, the Device Partner will begin publishing data to the VPP's endpoints. The [VPP API](./vp-api.html) guide describes the endpoints that accept device telemetry and configuration updates (referred to as signals and settings), command status updates, and device enrollment updates. These endpoints are authenticated using the `DEVICE_PUBLISH_SECRET` or `PROGRAM_PUBLISH_SECRET` (depending on the endpoint) to create an HMAC.\n\nThe required data fields vary based on the device type, such as:\n* Hot Water Heaters: tank temperature, operating mode, power draw, etc.\n* Thermostats: temperature setpoints, operating mode, ambient temperature, etc.\n* Batteries: capacity, power flow, status, etc.\n\nEach device type has a set of mandatory data fields that must be implemented, plus optional data fields that can be provided to improve reporting. See [Device Type Specifications](#section/Device-Type-Specifications) for the specifications for each device type.\n\nBefore finalizing an integration, the VPP will work with each Device Partner to verify that all required data points for a given device type are implemented correctly according to the specifications.\n\nHaving accurate and complete data publishing as per the documentation is critical for properly managing each device type within utility programs and optimizing distributed energy initiatives. The required fields for each device type integration must be confirmed before final approval.\n\n# Demand Response Event Commands\n[Diagram](https://d1byei05mtwnfj.cloudfront.net/apidocs/workflow/demand-reponse-event-flow.jpg)\n\n## Scheduling and Canceling\n\nWhen a utility sends a command using the VPP's platform, the VPP [sends a command to the Device Partner](#operation/sendCommand) targeting a specific device or a group (if group management is supported). See [Device Type Specifications](#section/Device-Type-Specifications) for the commands for each device type. \n\nAs not all device models support all commands, while building the integration, the Device Partner must inform the VPP of which models support which commands. The types of commands accepted must be consistent per device type + kind. For example, if the Acme Corporation is a device partner who sets up a hot water heater (kind = `HWH`) with a model name of ACME1000 (type = `ACME1000`), all ACME devices of kind `HWH` and type `ACME1000` must support the same set of commands, but this set can be different than the command set supported by model `ACME1001`.\n\nThroughout the duration of a command, devices continue to send data to the VPP. Commands also [publish their status](./vp-api.html#operation/publishCommand). For group commands, an individual device may need to report that it opted out (separate from the status of the command status), which it reports via the [device command status endpoint](./vp-api.html#operation/publishDeviceCommand).\n\nThe VPP can cancel any command that has been sent to a Device Partner using the [cancel endpoint](#operation/cancelCommand). The VPP may cancel active commands at any point during their duration. The Device Partner should immediately return devices to normal operating modes when a cancellation command is received.\n\nCommands are sent from the VPP to the Device Partner at most 60 seconds before the start time, regardless of the VPP interface supporting program managers scheduling events hours or days in advance. The Device Partner must be able to accept both individual and group commands simultaneously for a given device.\n\n## Device Data for Events\n\nThe active operating mode of a device must align with the requested command mode if the device is participating in an event. For example, a water heater cannot report it is in 'NORMAL' mode if it is actively participating in a demand response event that calls for a ‘SHED’ (but it can if it has opted out of the event). The current mode should be updated accordingly as commands begin and end. Throughout each event, enrolled devices continue publishing telemetry data to the VPP's platform through the standard publishing endpoints.\n\nIndividual devices report opt-outs via the VPP endpoint [publish device command status endpoint](./vp-api.html#operation/publishDeviceCommand). For group commands, individual devices report opt-out status, as needed, via the Device Partner endpoint [command opt-out](./vp-api.html#operation/commandOptOut).\n\nProperly syncing modes and command statuses is crucial for accurate measurement and verification of demand response performance. The Device Partner integration must maintain consistency between the active command, device mode, and device command status.\n\n## Opting Out of Events\nFor demand response events targeting individual devices, opt-out status should be reported using the VPP endpoint [publish device command status endpoint](./vp-api.html#operation/publishDeviceCommand) endpoint.\n\nThis allows the Device Partner to share the specific UID of the device that has opted out. The payload should include the UID along with a status of \"OPT_OUT\" and relevant timestamp.\n\nFor group commands, the group-level Device Partner endpoint [command opt-out](./vp-api.html#operation/commandOptOut) should be used, and not the individual device opt-out endpoint. The group command status should be updated to \"OPT_OUT\" to indicate a specific or set of specific devices in the group has opted out.\n\nThe Device Partner API needs to implement both individual and group opt-out flows:\n* Individual devices: Report opt-out via the VPP endpoint [publish device command status endpoint](./vp-api.html#operation/publishDeviceCommand) endpoint\n* Group commands: Report that particular devices within a group have opted out via group-level Device Partner endpoint [command opt-out](./vp-api.html#operation/commandOptOut)\n\nProperly reporting opt-out status at both the individual device and group level allows the VPP to update event logs and measurement & verification accordingly.\n\n# Device Unenrollment\nIf a device is unenrolled from a VPP program via the Device Partner's app or portal, the device partner notifies the VPP via the [publish device enrollment](./vp-api.html#operation/publishDeviceEnrollment) endpoint. The payload should include the device UID and a status of \"unenrolled\". The VPP will remove the device from the program and no longer collect data or call events on the device.\n\nIf a program manager unenrolls a device via the VPP platform, the VPP will call the Device Partner endpoint to [unsubscribe](./vp-api.html#tag/Devices/operation/modifySubscription) from the device's data. The Device Partner should interpret the unsubscription as an unenrollment.\n\n# Integration Testing\nIntegrations are evaluated on the following workflows.\n\n## Enrollment & Unenrollment\n* Device enrolls in the VPP program\n* Device unenrolls from the VPP program\n\n## Telemetry\n* Device shares all required data points for the device type\n* Device shares power data in 5-minute increments or shares Energy Interval data\n\n## Events\n* Device successfully responds to group events, if applicable\n* Device successfully responds to individual device events, if applicable\n* Device successfully responds to cancelling an individual device event, if applicable\n* Device successfully responds to cancelling a group event, if applicable\n* Device signals for modes and command statuses align with test events before, during, and after the event window\n* Device signals change in the correct direction relative to test event commands\n* Device opts out of individual device event using [publish device command status endpoint](./vp-api.html#operation/publishDeviceCommand)\n* Device opts out of group event using [command opt-out](./vp-api.html#operation/commandOptOut)\n\n# Device Type Specifications\nDefinitions for `SIGNAL_KEY`, `SIGNAL_VALUE`, `SETTING_KEY`, and `VP_COMMAND_OBJECT` all depend on the type of device (`DEVICE_TYPE_ENUM`) interfacing with the Gravity Connect API. This section will outline those values per device type.\n\nNOTE: `VP_COMMAND_OBJECT` is only a portion of what is sent during an event. For the full payload, please see the [Send Command](#operation/sendCommand) endpoint.\n\n## Battery\n### Battery Command Payloads (VP_COMMAND_OBJECT)\n#### Standby\n```json\n{\n mode: 'STANDBY'\n}\n```\n\n#### Charge\n```json\n{\n mode: 'CHARGE',\n action: 'POWER',\n power: 4000\n}\n```\n\n```json\n{\n mode: 'CHARGE',\n action: 'FASTEST'\n}\n```\n\n#### Discharge\n```json\n{\n mode: 'DISCHARGE',\n action: 'POWER',\n power: 4000\n}\n```\n\n```json\n{\n mode: 'DISCHARGE',\n action: 'SITE_DEMAND',\n targetSiteDemand: 400\n}\n```\n\n```json\n{\n mode: 'DISCHARGE',\n action: 'SITE_DEMAND',\n targetSiteDemand: 400,\n maxPower: 10000\n}\n```\n\n### Battery Signals\n#### mode (String)\n* Example Values\n\t* `SELF_CONSUMPTION`\n\t* `BACKUP`\n\t* `CHARGE`\n\t* `DISCHARGE`\n\t* `STANDBY`\n\n#### status (String)\n* Internet connectivity of the device\n* Valid Values\n\t* `OFFLINE`\n\t* `ONLINE`\n\n#### grid-connection (String)\n* Connectivity to the grid. Disconnections are typically temporary events that indicate power outages.\n* Valid Values\n\t* `CONNECTED`\n\t* `DISCONNECTED`\n\n#### max-capacity (Float)\n* Maximum energy capacity for the device\n* Unit: Wh\n\n#### present-capacity (Float)\n* Remaining energy capacity for the device\n* Unit: Wh\n\n#### max-usable-capacity (Float)\n* Maximum usable energy capacity for the device\n* Unit: Wh\n\n#### present-usable-capacity (Float)\n* Usable energy capacity remaining in the device\n* Unit: Wh\n\n#### discharge-power (Float)\n* Present discharge power. Unsigned.\n* Unit: W\n\n#### charge-power (Float)\n* Present charge power. Unsigned.\n* Unit: W\n\n#### max-discharge-power (Float)\n* Maximum discharge power. Often fixed, but not always.\n* Unit: W\n\n#### max-charge-power (Float)\n* Maximum charge power. Often fixed, but not always. Often equivalent to max-discharge-power, but not always.\n* Unit: W\n\n#### house-power (Float)\n* Present load consumed by the site. Unsigned\n* Unit: W\n\n#### pv-power (Float)\n* Present power being generated by PV panels attached to the system. Unsigned\n* Unit: W\n\n### Battery Settings\n#### min-reserve-percent-mfg (Float)\n* The minimum state of charge, in %, that this device can reach during DR events. Only include a reserve value set by the homeowner if it is respected during DR events.\n* Unit: Percentage\n\n## HWH\n\n### HWH Command Payloads (VP_COMMAND_OBJECT)\n#### Shed\n```json\n{\n mode: 'SHED'\n}\n```\n\n#### Load Up\n```json\n{\n mode: 'LOAD_UP'\n}\n```\n#### Critical\n```json\n{\n mode: 'CRITICAL'\n}\n```\n\n#### Emergency\n```json\n{\n mode: 'EMERGENCY'\n}\n```\n\n#### Run Normal\n```json\n{\n mode: 'RUN_NORMAL'\n}\n```\n\n#### Advanced Load Up (Optional)\nWhere `energy` is an integer for Wh and `suggestedEfficiencyLevel` is an integer between 1 (representing low efficiency/fast heat up) and 9 (for high efficiency/slow heat up), or 10 for vacation mode.\n```json\n{\n mode: 'ADVANCED_LOAD_UP',\n energy: 1000\n suggestedEfficiencyLevel: 1\n}\n```\n\n#### Efficiency (Optional)\nWhere `suggestedEfficiencyLevel` is an integer between 1 (representing low efficiency/fast heat up) and 9 (for high efficiency/slow heat up), or 10 for vacation mode.\n```json\n{\n mode: 'EFFICIENCY',\n suggestedEfficiencyLevel: 1\n}\n```\n\n#### Duty Cycle (Optional)\nPercentage ranges from 0-100\n```json\n{\n mode: 'DUTY_CYCLE',\n percentage: 60\n}\n```\n\n### HWH Signals\n#### mode (String)\n* If additional modes are available, please let us know so we can consider standardized naming\n* Example Values\n\t* `NORMAL`\n\t* `SHED`\n\t* `CRITICAL`\n\t* `EMERGENCY`\n\t* `LOAD_UP`\n\t* `ADVANCED_LOAD_UP`\n\t* `ERROR`\n\t* `CYCLING`\n\t* `PRICE_STREAM`\n\t* `VARIABLE`\n* Non-CTA devices are TBD\n\n#### status (String)\n* Internet connectivity of the device\n* Valid Values\n\t* `OFFLINE`\n\t* `ONLINE`\n\n#### tank-temperature (Float)\n* In the case that both top and bottom temperature are reported, this would be the top temperature.\n* Unit: Degrees Fahrenheit\n* Not relevant for CTA devices\n\n#### tank-bottom-temperature (Float)\n* Optional, not all water heaters report temperature at the top and bottom of the tank\n* Unit: Degrees Fahrenheit\n* Not relevant for CTA devices\n\n#### setpoint-temperature (Float)\n* Unit: Degrees Fahrenheit\n\n#### present-capacity (Float)\n* For CTA2045, given the absence of tank-temperature / setpoint-temperature data\n* Unit: Wh\n\n#### max-capacity (Float)\n* For CTA2045, given the absence of tank-temperature / setpoint-temperature data\n* Unit: Wh\n\n#### power (Float)\n* Unit: W\n* Not relevant for CTA devices\n\n#### current (Float)\n* Optional, may not be available\n* Unit: A\n* Not relevant for CTA devices\n\n#### voltage (Float)\n* Optional, may not be available\n* Unit: V\n* Not relevant for CTA devices\n\n#### cta-op-state (String)\n* Optional setting used by CTA\n* Valid Values\n\t* `IDLE_NORMAL`\n\t* `RUNNING_NORMAL`\n\t* `RUNNING_CURTAILED`\n\t* `RUNNING_HEIGHTENED`\n\t* `IDLE_CURTAILED`\n\t* `SGD_ERROR_CONDITION`\n\t* `IDLE_HEIGHTENED`\n\t* `CYCLING_ON`\n\t* `CYCLING_OFF`\n\t* `VARIABLE_FOLLOWING`\n\t* `VARIABLE_NOT_FOLLOWING`\n\t* `IDLE_OPTED_OUT`\n\t* `IDLE_PRICE_STREAM`\n\t* `RUNNING_OPTED_OUT`\n\t* `RUNNING_PRICE_STREAM`\n\t* `IDLE_PRICE_STREAM`\n\n#### cta-advanced-present-capacity (Float)\n* Required if CTA advanced load up is supported\n* Unit: Wh\n\n#### cta-advanced-max-capacity (Float)\n* Required if CTA advanced load up is supported\n* Unit: Wh\n\n### HWH Settings\n#### max-setpoint (Float)\n* Defines the maximum setpoint that is allowed by the unit\n* Unit: Degrees Fahrenheit\n\n#### min-setpoint (Float)\n* Defines the minimum setpoint that is allowed by the unit\n* Unit: Degrees Fahrenheit\n\n#### cta-advanced-loadup-enabled (Boolean)\n* Optional setting used by CTA\n\n#### cta-user-preference-level (Integer)\n* Optional setting used by CTA. Expected values 0-10\n\n## TSTAT\n#### Temperature Units\nAs described in the following specification, all temperatures are sent and received in units of Fahrenheit for consistency. However, these temperatures can be displayed in Celsius to users in appropriate locales.\n\n### TSTAT Command Payloads (VP_COMMAND_OBJECT)\n#### Adjust Set Point\n```json\n{\n mode: 'HEAT',\n temperature: -2.0\n}\n```\n\n* This command accepts relative values only.\n* The setpoint command is always processed as relative to the homeowner’s setpoint. For example, if the homeowner’s setpoint is currently 75, and the VPP receives a command for -3 followed by a command for -2, the second command sets the setpoint to 73, not to 70.\n* Mode\n * Valid values\n * HEAT\n * COOL\n * Note that while other values can be sent as Signals, only these values will be sent in commands.\n* Temperature\n * Unit: degrees Fahrenheit\n * Sign: For all modes, positive is an increase in temperature and negative is a decrease in temperature\n\n\n### TSTAT Signals\n#### mode (String)\n* Current mode of the device. Please use the following exact values for heating, cooling, automatic, and fan modes. This signal also accepts other modes supported by the device that are not listed here, such as 'DRY'.\n* Valid Values\n\t* `HEAT`\n\t* `COOL`\n\t* `AUTO`\n\t* `FAN_ONLY`\n\n#### status (String)\n* Internet connectivity of the device\n* Valid Values\n\t* `OFFLINE`\n\t* `ONLINE`\n\n#### state (String)\n* Whether the device is on or off\n* Valid Values\n\t* `ON`\n\t* `OFF`\n\n#### humidity (Float)\n* Optional. Ambient humidity of the room, measured by the device. Range from 0 to 100\n* Unit: Percentage\n\n#### temperature (Float)\n* Ambient humidity of the room, measured by the device\n* Unit: Degrees Fahrenheit\n\n#### cool-setpoint-temperature (Float)\n* Optional; recommended if supported\n* Unit: Degrees Fahrenheit\n\n#### heat-setpoint-temperature (Float)\n* Optional; recommended if supported\n* Unit: Degrees Fahrenheit\n\n#### louver-swing (String)\n* Optional. Mode or speed of louver swing\n* Example Values\n\t* `AUTO`\n\t* `FAST`\n\t* `SLOW`\n\t* `OFF`\n\t* `ON`\n\n#### fan-mode (String)\n* Optional. Mode or speed of fan\n* Example Values\n\t* `HIGH`\n\t* `MEDIUM`\n\t* `LOW`\n\t* `OFF`\n\t* `ON`\n\n#### equipment-status (String)\n* Optional. Describes which parts of the equipment are currently running (typically more specific than 'mode'\n* Example Values\n\t* `FAN ONLY`\n\t* `HEATING`\n\t* `HEATING STAGE 2`\n\t* `NONE`\n\n#### runtime-fan (Float)\n* The number of seconds of runtime in the interval of time starting at the specified timestamp. Cadence: 5 minutes, clock-aligned\n* Unit: seconds\n\n#### runtime-heat (Float)\n* The number of seconds of runtime in the interval of time starting at the specified timestamp. Cadence: 5 minutes, clock-aligned\n* Unit: seconds\n\n#### runtime-cool (Float)\n* The number of seconds of runtime in the interval of time starting at the specified timestamp. Cadence: 5 minutes, clock-aligned\n* Unit: seconds\n\n#### power (Float)\n* Optional, the instantaneous power consumed by this device at the specified timestamp. Cadence: >= 5 minutes (not required to be clock aligned)\n* Unit: kW\n\n### TSTAT Settings\n#### max-setpoint (Float)\n* Defines the maximum setpoint that is allowed by the unit\n* Unit: Degrees Fahrenheit\n\n#### min-setpoint (Float)\n* Defines the minimum setpoint that is allowed by the unit\n* Unit: Degrees Fahrenheit\n\n## EVSE\n### EVSE Command Payloads (VP_COMMAND_OBJECT)\n#### Standby\nThis puts the EV Charger in an idle state.\n```json\n{\n mode: 'STANDBY'\n}\n```\n\n#### Dispatch - Limit Power\nLimit the charging power to the set `power` limit, in units or Watts.\n```json\n{\n mode: 'DISPATCH',\n action: 'LIMIT_POWER',\n power: 1000\n}\n```\n\n#### Dispatch - Percent Shed\n```json\n{\n mode: 'DISPATCH',\n action: 'PERCENT',\n limitPercent: 100\n}\n```\n\n### EVSE Signals\n#### status (String)\n* Internet connectivity of the device\n* Valid Values\n\t* `OFFLINE`\n\t* `ONLINE`\n\n#### mode (String)\n* The current operating state of the EVSE.\n* Valid Values\n\t* `INUSE`\n\t* `AVAILABLE`\n\t* `NEEDS_SERVICE`\n\n#### power (Float)\n* Current power consumption in Watts.\n* Unit: W\n* Example Values\n\t* `0`\n\t* `4500`\n\n#### power-limit (Float)\n* The current power limit applied at the EVSE.\n* Unit: W\n* Example Values\n\t* `0`\n\t* `4500`\n\n### EVSE Settings\n#### max-power (Float)\n* The maximum charging power that the EVSE supports.\n* Unit: W\n* Example Values\n\t* `0`\n\t* `4500`\n\n## V2G\n### V2G Command Payloads (VP_COMMAND_OBJECT)\n#### Charge\n```json\n{\n mode: 'CHARGE',\n action: 'POWER',\n power: 4000\n}\n```\n\n```json\n{\n mode: 'CHARGE',\n action: 'FASTEST'\n}\n```\n\n#### Discharge\n```json\n{\n mode: 'DISCHARGE',\n action: 'POWER',\n power: 4000\n}\n```\n\n```json\n{\n mode: 'DISCHARGE',\n action: 'SITE_DEMAND',\n targetSiteDemand: 400\n}\n```\n\n```json\n{\n mode: 'DISCHARGE',\n action: 'SITE_DEMAND',\n targetSiteDemand: 400,\n maxPower: 10000\n}\n```\n\n### V2G Signals/Settings\n### V2G Signals\n#### mode (String)\n* Example Values\n\t* `SELF_CONSUMPTION`\n\t* `BACKUP`\n\t* `CHARGE`\n\t* `DISCHARGE`\n\t* `STANDBY`\n\n#### status (String)\n* Internet connectivity of the device\n* Valid Values\n\t* `OFFLINE`\n\t* `ONLINE`\n\n#### max-capacity (Float)\n* Maximum energy capacity for the device\n* Unit: Wh\n\n#### present-capacity (Float)\n* Remaining energy capacity for the device\n* Unit: Wh\n\n#### max-usable-capacity (Float)\n* Maximum usable energy capacity for the device\n* Unit: Wh\n\n#### present-usable-capacity (Float)\n* Usable energy capacity remaining in the device\n* Unit: Wh\n\n#### discharge-power (Float)\n* Present discharge power. Unsigned.\n* Unit: W\n\n#### charge-power (Float)\n* Present charge power. Unsigned.\n* Unit: W\n\n#### max-discharge-power (Float)\n* Maximum discharge power. Often fixed, but not always.\n* Unit: W\n\n#### max-charge-power (Float)\n* Maximum charge power. Often fixed, but not always. Often equivalent to max-discharge-power, but not always.\n* Unit: W\n\n#### house-power (Float)\n* Present load consumed by the site. Unsigned\n* Unit: W\n\n#### pv-power (Float)\n* Present power being generated by PV panels attached to the system. Unsigned\n* Unit: W\n\n#### state (String)\n* State of use of the device (most commonly used for EV chargers)\n* Example Values\n\t* `UNREACHABLE`\n\t* `AVAILABLE`\n\t* `INUSE`\n\n#### power-limit (Float)\n* Max power presently allowed to charger; this is where we would expect to see a DR curtailment take effect\n* Unit: W\n\n### V2G Settings\n\n\n\n## Device Authentication\n Most of the device partner's endpoints will utilize the [device_partner_api_auth](#section/Authentication/device_partner_api_auth) authentications. The only exception is the [OAuth Device Discovery](#tag/OAuth-Device-Discovery-(Preferred)) endpoint which uses [device_partner_user_auth](#section/Authentication/device_partner_user_auth).\n\n For information on how to authenticate when publishing data to the VPP, please see the [Authentication section](./vp-api.html#section/Authentication) for those endpoints.\n\n# Energy Interval Data\n\n## Purpose\nThe Energy Interval endpoint collects energy interval data from devices, except low-voltage thermostats that do not record power data. Unlike other device telemetry, which is published to the VPP, energy intervals are polled by the VPP.\n\nThis data is more accurate than energy data that a VPP platform can compute from power data.\n\nProviders typically use this data for:\n* Performance reporting\n* Historical analysis\n* Resource planning\n* Incentive program disbursement\n\n## Endpoint Query Parameters\n* `start` and `end` use the same format as specified elsewhere for Gravity connect (as they are in the sample URL above)\n * `start` is inclusive, `end` is exclusive\n * The response should include all intervals that overlap with this window\n* `duration` = duration of each interval, in seconds\n * Support for 900 second (15 minute) intervals is required\n * Support for other typical intervals (e.g., 300, 3600) is optional\n\n## Response Properties and General Notes\n\n* `time` represents the start of the interval, such the `value` should represent the energy consumed between `time` and `time + duration`\n* `value` has units of watt-hours (Wh) and is signed:\n * For charge values, positive (>0)\n * For discharge values, negative (<0)\n* Intervals must be \"clock-aligned\" to 15-minute windows, eg 00:00:00, 00:15:00, 00:30:00, etc. See the examples below.\n* Data must be available consecutively, and it must only become available after any previous gaps have been filled in where appropriate. The VPP interprets gaps in the data as zero energy, which is appropriate for devices that are frequently \"off\", such as EV chargers and thermostats.\n * Otherwise, the VPP could not distinguish a temporary delay from a true lack of device activity.\n* For EV chargers and other devices that are \"off\", values of zero (0) should be present if they are part of a charging session, but do not need to be present for intervals outside of sessions (the VPP will infer the absence of data as non-charging time once the next explicit interval appears chronologically).\n* Zero (0) can also be used when the energy data is unavailable and not expected to become available later.\n* The VPP will typically poll this endpoint once per day per device, with a start time of the last recorded interval for the device. This picks up data that was delayed temporarily due to issues such as device connectivity.\n\n### Examples\n* The VPP requests: start time: 13:05. end time: 13:15\n * The response should include all energy consumption between 13:00 and 13:15, not just the energy consumed in the specified 10 minutes.\n* The VPP requests: start time: 12:00, end time: 15:13\n * The response should include data points for all complete 15-minute intervals, which is the intervals from 12:00 to 15:00. The data between 15:00 and 15:13 will be retrieved in the next query after the 15:00-15:15 interval is available.\n\n# FAQ\n### How far in advance can commands be scheduled? How long can commands last?\nCommands are sent in the 60 seconds before the start time and will not exceed a 24 hour duration. Commands with a start time in the past should still be considered valid so long as the current time is within the duration.\n\n### What is the difference between device registration, subscription, and enrollment?\nAn \"enrolled\" device has been approved to participate in a VPP program. Subscription refers to whether the VPP is choosing to receive data for that device. When modifying the subscription of a device, the VPP can choose to `register` or `unregister` the device. \n\n### How does the device partner know which `PROGRAM_PUBLISH_KEY` and `PROGRAM_PUBLISH_SECRET` to use when publishing data?\nThe VPP will provide a unique `PROGRAM_PUBLISH_KEY` and `PROGRAM_PUBLISH_SECRET` for new programs. The Device Partner should associate new devices with the program so that it can use the correct program credentials.\n\n### How frequently should device data be published to the VPP?\nTelemetry data in the lists of \"signals\" and \"settings\" should be published between every 5 and every 15 minutes. This data should contain 5 minute intervals for continuous signals. Categorical signals and settings that change infrequently can be updated only on change.\n\n### My device publishes data that doesn't correspond to an existing signal or setting. What should I do with it?\nThe signal and setting lists are constantly evolving. Please contact the Gravity Connect team with suggestions for updating the specification.\n\n### How should a Device Partner handle errors when publishing data to the VPP's endpoints?\nPlease see the [Error Handling section in the VPP API documentation](./vp-api.html#section/Error-Handling).\n\n### What's the difference between the command status and the device command status?\nDevice command status is for a specific device. Command status is used for groups of devices.\n \nFor individual device commands, use only the device command status endpoint.\n\nFor group commands, the command status reflects the state of the command at a scheduling engine level. For example, `OPT_OUT` would not apply and a status of `FAILED` means there was an issue scheduling the command as a whole. If a particular device's status does not reflect the greater command status (e.g., an individual device fails or opts out), report that difference via the device command status. Device command status that is the same as the current command status does not need to be reported.\n\n### Does `/subscription` control Command Status and Device Command Status publishing to the VPP?\nNo, this endpoint only controls the sending of signals and settings. In the case that an unsubscribed device is targeted by a command (which is not an expected situation), the response data should not be reported to the VPP.\n" x-logo: url: ./assets/vp_logo.png backgroundColor: '#FFFFFF' altText: Virtual Peaker Logo version: 2.0.6 title: Gravity Connect API (Device Partner) license: name: BSD servers: - url: https://example.com security: - device_partner_api_auth: - device_partner_basic_auth - device_partner_user_auth: - user_read tags: - name: Devices description: Device specific endpoints - name: Commands description: Command specific endpoints - name: Device Partner Driven Enrollment description: 'This flow is [described above](./device-partner-api.html#section/Device-Onboarding/Device-Partner-Driven-Enrollment). [The enrollment publishing endpoint is defined in the Virtual Peaker API documentation](./vp-api.html#tag/Publishing/operation/publishDevicePartnerDrivenEnrollment). ' - name: OAuth Device Discovery (Preferred) description: 'The OAuth device discovery flow works as follows: 1. The device owner fills out an onboarding form on Virtual Peaker''s site. 2. At the end of the form, we redirect them to the Device Partner''s OAuth authorization page via a link. 3. The user logs into the Device Partner''s app and grants OAuth access permissions. 4. The Device Partner app completes OAuth authorization code flow, exchanging the code for an access token. 5. Using the access token, the Device Partner calls their API to retrieve the user''s devices. 6. The Device Partner associates the devices with the correct Virtual Peaker program in their backend. 7. The Device Partner handles any additional onboarding logic in their system. 8. The Device Partner publishes enrolled device data to Virtual Peaker''s API. 9. Virtual Peaker discovers devices using the OAuth token provided. 10. We subscribe to devices for data publishing and provide a DEVICE_PUBLISH_SECRET. 11. Virtual Peaker may optionally pull initial device data from the Device Partner''s API. For each utility program, a separate client_id is created. This ID is passed in the OAuth link to associate devices with the correct program. ' - name: Pairing Code Device Discovery - End User App description: 'The [pairing code](./device-partner-api.html#section/Pairing-Codes) device discovery flow works as follows: 1. The device owner fills out an onboarding form on Virtual Peaker''s site. 2. At the end of the form, we provide them with a pairing code and instructions to enter it in the Device Partner''s app. 3. Optionally, the user selects which devices to enroll via the Device Partner''s interface. Otherwise, all devices are enrolled by default. 4. When the user enters the pairing code in the Device Partner''s app, the Device Partner publishes a device enrollment event to Virtual Peaker''s API. 5. The publish event includes the pairing code, allowing Virtual Peaker to link the device back to the onboarding form entry. 6. If multiple devices are enrolled, the same pairing code is used in each publish payload. 7. Virtual Peaker discovers the user details associated with the enrolled device(s). 8. We subscribe to the devices for data publishing and provide a `DEVICE_PUBLISH_SECRET`. The user experience is driven through the Device Partner''s web or mobile app after receiving the pairing code. This links together the Virtual Peaker and Device Partner platforms. ' - name: Pairing Code Device Discovery - Utility Commissioned Installation description: 'The utility commissioned [pairing code](./device-partner-api.html#section/Pairing-Codes) flow could work as follows: 1. The device owner fills out an onboarding form on Virtual Peaker''s site. 2. The utility reviews the submission and approves it if eligible. 3. Virtual Peaker informs the Device Partner of the install address and provides a pairing code. 4. The Device Partner takes necessary actions to install and activate the device on-site. 5. When installation is complete, the Device Partner publishes a device enrollment event to Virtual Peaker. 6. The publish includes the pairing code to link with the onboarding form. 7. If multiple devices are installed, the same code is used in each payload. 8. Virtual Peaker discovers user details for the enrolled device(s). 9. We subscribe to the device(s) for data publishing and provide a `DEVICE_PUBLISH_SECRET`. In this flow, the utility directs the installation. The pairing code links the user signup, Device Partner platform, and Virtual Peaker after install. The flow described above can have slight variations depending on the use case. ' - name: Group Management description: "Virtual Peaker supports managing groups of devices, in addition to individual device control. Grouping enables utilities to target clusters of devices together in demand response events.While device telemetry and configuration data is still reported on a per-device level, groups allow a single command to be broadcast to multiple enrolled devices simultaneously.\nTo utilize grouping, the Device Partner integration must support both individual and group commands concurrently. The same device could receive an individual command, while also belonging to a group receiving a separate directive.\nGroup commands have additional considerations:\n 1. Devices can opt out of group events individually, while the overall group command remains active.\n 2. Group command statuses reflect execution at the scheduling engine level rather than device state.\n 3. The Device Partner must reconcile group vs. individual state to ensure consistency in reporting.\n\nWhen not specified, all other workflows behave identically between individual and grouped devices - enrollment, data publishing, cancellations, etc.\n" x-tagGroups: - name: Base Implementation tags: - Devices - Commands - Energy Interval Endpoint - name: Device Onboarding tags: - OAuth Device Discovery (Preferred) - Pairing Code Device Discovery - End User App - Pairing Code Device Discovery - Utility Commissioned Installation - Device Partner Driven Enrollment - name: Group Management tags: - Group Management paths: /device/{DEVICE_UID}: get: summary: Read device details operationId: readDeviceDetails security: - device_partner_api_auth: - basic_partner_read_write tags: - Devices parameters: - $ref: '#/components/parameters/deviceUID' responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/DeviceDetails' '400': $ref: '#/components/responses/badRequest' '401': $ref: '#/components/responses/unauthorized' default: description: unexpected error content: application/json: schema: $ref: '#/components/schemas/Details' /device/{DEVICE_UID}/{SIGNAL_OR_SETTING}/{DATA_KEY}: put: summary: Update signal/setting description: There may be cases where a particular signal or setting can be modified by Virtual Peaker. If this is supported, this endpoint is to be used. operationId: updateSignalSetting tags: - Devices security: - device_partner_api_auth: - basic_partner_read_write parameters: - $ref: '#/components/parameters/deviceUID' - $ref: '#/components/parameters/signalSetting' - $ref: '#/components/parameters/dataKey' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SignalSetting' responses: '200': description: Success '400': $ref: '#/components/responses/badRequest' '401': $ref: '#/components/responses/unauthorized' default: description: unexpected error content: application/json: schema: $ref: '#/components/schemas/Details' get: summary: Read signal/setting operationId: readSignalSetting tags: - Devices security: - device_partner_api_auth: - basic_partner_read_write parameters: - $ref: '#/components/parameters/deviceUID' - $ref: '#/components/parameters/signalSetting' - $ref: '#/components/parameters/dataKey' responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/SignalSetting' '400': $ref: '#/components/responses/badRequest' '401': $ref: '#/components/responses/unauthorized' '404': $ref: '#/components/responses/notFound' default: description: unexpected error content: application/json: schema: $ref: '#/components/schemas/Details' /device/{DEVICE_UID}/user: get: summary: Describe device's user description: If a new device is added to Virtual Peaker via the Virtual Peaker's publish device enrollment endpoint. Virtual Peaker will use this endpoint to discover the user associated with the device. operationId: readDeviceUser tags: - Pairing Code Device Discovery - End User App - Pairing Code Device Discovery - Utility Commissioned Installation security: - device_partner_api_auth: - basic_partner_read_write parameters: - $ref: '#/components/parameters/deviceUID' responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/UserDetails' '400': $ref: '#/components/responses/badRequest' '401': $ref: '#/components/responses/unauthorized' default: description: unexpected error content: application/json: schema: $ref: '#/components/schemas/Details' /houses: post: summary: Publish houses for installation description: In the use case where the device partner needs a list of houses to physically install devices, Virtual Peaker will publish the list of houses and corresponding pairing codes via this endpoint. Virtual Peaker will publish to this endpoint nightly with a list of all houses/devices to be installed (the same house will be published each night until it is installed). When the device is installed, the device partner will then use the [publish device enrollment status](./vp-api.html#operation/publishDeviceEnrollment). operationId: publishHouseList tags: - Pairing Code Device Discovery - Utility Commissioned Installation security: - device_partner_api_auth: - basic_partner_read_write requestBody: required: true content: application/json: schema: type: array description: 'Array of addresses and pairing codes. NOTE: This array will include all houses with devices needing to be installed, not just new houses from that day.' items: type: object required: - serviceAddress - pairingCode properties: devices: type: array description: Included if the device partner needs more details on which device (and how many) need installed. If not included, it's assumed the device partner can infer this information based on the program. items: type: object properties: kind: $ref: '#/components/schemas/DeviceKindEnum' type: type: string description: model name/number pairingCode: type: string description: 'The current format is: * 2 alphanumeric characters to denote the pairing code prefix representing the program * 5 random numeric characters that VP uses to link the user to an existing device * 1 check digit. This check digit will be generated following the Luhn algorithm to ensure the 5 digit number is valid. This validation can be performed on the device partner side to provide immediate feedback, but will also be done within our API.' example: A1012344 serviceAddress: $ref: '#/components/schemas/ServiceAddress' responses: '200': $ref: '#/components/responses/accepted' '400': $ref: '#/components/responses/badRequest' '401': $ref: '#/components/responses/unauthorized' default: description: unexpected error content: application/json: schema: $ref: '#/components/schemas/Details' /command: post: summary: Send command operationId: sendCommand tags: - Commands security: - device_partner_api_auth: - basic_partner_read_write requestBody: required: true content: application/json: schema: type: object required: - targeting - uid - startTime - duration properties: targeting: type: string enum: - group - device uid: type: string description: Group or device uid kind: $ref: '#/components/schemas/DeviceKindEnum' startTime: type: string format: date-time description: For more see, [RFC 3339, section 5.6](https://datatracker.ietf.org/doc/html/rfc3339#section-5.6). As an example, '2017-07-21T17:32:28Z'. The timezone is always zero UTC offset. A startTime in the past should still be considered valid if startTime + duration is still in the future duration: type: integer description: Length of event in seconds. The value should be greater than 0. If a device partner imposes a maximum event duration, frequently it is either 8 or 24 hours. command: type: object description: Varies by device kind, see documentation for VP_COMMAND_OBJECT responses: '200': description: successful operation content: application/json: schema: type: object required: - refId properties: refId: type: string description: A unique ID for that command within the partners system to be used when reading/reporting command state or cancelling '400': $ref: '#/components/responses/badRequest' '401': $ref: '#/components/responses/unauthorized' default: description: unexpected error content: application/json: schema: $ref: '#/components/schemas/Details' /command/{COMMAND_REFERENCE_ID}: get: summary: Read command state operationId: readCommandState tags: - Commands security: - device_partner_api_auth: - basic_partner_read_write parameters: - name: COMMAND_REFERENCE_ID in: path required: true description: The id of the command being read schema: type: string responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/CommandState' '400': $ref: '#/components/responses/badRequest' '401': $ref: '#/components/responses/unauthorized' default: description: unexpected error content: application/json: schema: $ref: '#/components/schemas/Details' delete: summary: Cancel command operationId: cancelCommand tags: - Commands security: - device_partner_api_auth: - basic_partner_read_write parameters: - name: COMMAND_REFERENCE_ID in: path required: true description: The id of the command being cancelled schema: type: string responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/CommandState' '400': $ref: '#/components/responses/badRequest' '401': $ref: '#/components/responses/unauthorized' default: description: unexpected error content: application/json: schema: $ref: '#/components/schemas/Details' /subscription: post: summary: Manage device publishing description: This endpoint is used to enable or disable the publishing of device data (signals and settings) to Virtual Peaker. operationId: modifySubscription tags: - Devices security: - device_partner_api_auth: - basic_partner_read_write requestBody: required: true content: application/json: schema: type: object required: - state - uid properties: state: type: string enum: - register - unregister uid: type: string description: The unique identifier for the target device within the partner's platform secret: type: string description: DEVICE_PUBLISH_SECRET; Passed when a device is subscribed to data, used to create an HMAC when the Device Partner publishes device data to Virtual Peaker examples: register: summary: Registering/subscribing a device and sharing secret value: state: register uid: some-device-uid secret: some-device-secret unregister: summary: Stopping publishing for a device value: state: unregister uid: some-device-uid responses: '200': description: successful operation '400': $ref: '#/components/responses/badRequest' '401': $ref: '#/components/responses/unauthorized' default: description: unexpected error content: application/json: schema: $ref: '#/components/schemas/Details' /group: post: summary: Create group operationId: createGroup tags: - Group Management security: - device_partner_api_auth: - basic_partner_read_write requestBody: required: true content: application/json: schema: type: object properties: deviceUids: type: array description: Array of device uids for the Device Partner to assign to the newly created group. Can be an empty array items: type: string name: type: string description: Human readable name of group responses: '200': description: successful operation content: application/json: schema: type: object required: - uid properties: uid: type: string description: A unique ID for that group within the partner's system to be used when modifying or sending commands to the group invalidDevices: type: array description: Array of device uids that couldn't be located or were otherwise not added to the group items: type: string '400': $ref: '#/components/responses/badRequest' '401': $ref: '#/components/responses/unauthorized' default: description: unexpected error content: application/json: schema: $ref: '#/components/schemas/Details' /group/{GROUP_ID}: get: summary: Read group details operationId: readGroup tags: - Group Management security: - device_partner_api_auth: - basic_partner_read_write parameters: - $ref: '#/components/parameters/groupID' responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/GroupDetails' '400': $ref: '#/components/responses/badRequest' '401': $ref: '#/components/responses/unauthorized' default: description: unexpected error content: application/json: schema: $ref: '#/components/schemas/Details' put: summary: Update group details description: Update group details, if deviceUids is passed, it will overwrite the current list of devices. operationId: updateGroup tags: - Group Management security: - device_partner_api_auth: - basic_partner_read_write parameters: - $ref: '#/components/parameters/groupID' requestBody: required: true content: application/json: schema: type: object properties: deviceUids: type: array description: Updated array of deviceUids which will overwrite the existing array (as opposed to adding and removing a list of deviceUids) items: type: string name: type: string description: Human readable name of group responses: '200': description: successful operation content: application/json: schema: allOf: - $ref: '#/components/schemas/GroupDetails' - properties: invalidDevices: type: array description: Array of device uids that couldn't be located or were otherwise not added to the group items: type: string '400': $ref: '#/components/responses/badRequest' '401': $ref: '#/components/responses/unauthorized' default: description: unexpected error content: application/json: schema: $ref: '#/components/schemas/Details' delete: summary: Delete group description: Deletes a group and returns the list of devices that were within the group when deleted operationId: deleteGroup tags: - Group Management security: - device_partner_api_auth: - basic_partner_read_write parameters: - $ref: '#/components/parameters/groupID' responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/GroupDetails' '400': $ref: '#/components/responses/badRequest' '401': $ref: '#/components/responses/unauthorized' default: description: unexpected error content: application/json: schema: $ref: '#/components/schemas/Details' /group/{GROUP_ID}/devices: post: summary: Manage group devices operationId: manageGroupDevices tags: - Group Management security: - device_partner_api_auth: - basic_partner_read_write parameters: - $ref: '#/components/parameters/groupID' requestBody: required: true content: application/json: schema: type: object required: - deviceUids - action properties: deviceUids: type: array description: Array of device uids for the Device Partner to assign to or remove from the group items: type: string action: type: string enum: - add - remove responses: '200': description: successful operation content: application/json: schema: allOf: - $ref: '#/components/schemas/GroupDetails' - properties: invalidDevices: type: array description: Array of device uids that couldn't be located or were otherwise not added to the group items: type: string '400': $ref: '#/components/responses/badRequest' '401': $ref: '#/components/responses/unauthorized' default: description: unexpected error content: application/json: schema: $ref: '#/components/schemas/Details' /command/{COMMAND_REFERENCE_ID}/opt-out: post: summary: Command Opt-Out description: To communicate that particular devices within a group have opted out. For non-group command this is achieved via canceling the entire command. operationId: commandOptOut tags: - Group Management security: - device_partner_api_auth: - basic_partner_read_write parameters: - name: COMMAND_REFERENCE_ID in: path required: true description: The id of the command schema: type: string requestBody: required: true content: application/json: schema: type: object required: - deviceUids properties: deviceUids: type: array description: Array of device uids for the Device Partner to opt out of the command items: type: string responses: '200': $ref: '#/components/responses/accepted' '400': $ref: '#/components/responses/badRequest' '401': $ref: '#/components/responses/unauthorized' default: description: unexpected error content: application/json: schema: $ref: '#/components/schemas/Details' /devices: get: summary: Read current user's devices operationId: readCurrentUserDevices tags: - OAuth Device Discovery (Preferred) security: - device_partner_user_auth: - user_read responses: '200': description: successful operation content: application/json: schema: type: array items: $ref: '#/components/schemas/DeviceDetails' '400': $ref: '#/components/responses/badRequest' '401': $ref: '#/components/responses/unauthorized' default: description: unexpected error content: application/json: schema: $ref: '#/components/schemas/Details' /user: get: summary: Read current user's details operationId: readCurrentUser tags: - OAuth Device Discovery (Preferred) security: - device_partner_user_auth: - user_read responses: '200': description: successful operation content: application/json: schema: $ref: '#/components/schemas/UserDetails' '400': $ref: '#/components/responses/badRequest' '401': $ref: '#/components/responses/unauthorized' default: description: unexpected error content: application/json: schema: $ref: '#/components/schemas/Details' /device/{DEVICE_UID}/energy: get: summary: Energy interval data from non-tstat device operationId: readDeviceEnergyInterval tags: - Energy Interval Endpoint security: - device_partner_api_auth: - basic_partner_read_write parameters: - $ref: '#/components/parameters/deviceUID' - name: start in: query required: true schema: type: string format: date-time description: For more see, [RFC 3339, section 5.6](https://datatracker.ietf.org/doc/html/rfc3339#section-5.6). As an example, '2017-07-21T17:32:28Z'. The timezone is always zero UTC offset. A startTime in the past should still be considered valid if startTime + duration is still in the future - name: duration in: query required: true schema: type: integer description: Length of event in seconds. The value should be greater than 0. If a device partner imposes a maximum event duration, frequently it is either 8 or 24 hours. responses: '200': description: successful operation content: application/json: schema: type: array items: $ref: '#/components/schemas/EnergyInterval' '400': $ref: '#/components/responses/badRequest' '401': $ref: '#/components/responses/unauthorized' default: description: unexpected error content: application/json: schema: $ref: '#/components/schemas/Details' components: parameters: deviceUID: name: DEVICE_UID in: path required: true description: The unique identifier for the target device within the partner's platform schema: type: string signalSetting: name: SIGNAL_OR_SETTING in: path required: true description: Whether the data type is a signal or a setting schema: type: string enum: - signal - setting dataKey: name: DATA_KEY in: path required: true description: The signal/setting type being read, varies based on device type, see documentation schema: type: string groupID: name: GROUP_ID in: path required: true description: The id of the device being targeted schema: type: string schemas: ServiceAddress: type: object required: - streetAddress - city - state - postalCode - country properties: streetAddress: type: string streetAddress2: type: string city: type: string state: type: string description: Within the US, passed as a 2 letter abbreviation postalCode: type: string country: type: string description: A 2 letter indication of country. Following [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) UserDetails: type: object required: - userId properties: userId: type: string description: Unique identifier for the user within the device partner's platform accountNumber: type: string description: Utility customer identifier (if available) name: type: string email: type: string deviceUids: type: array items: type: string serviceAddress: $ref: '#/components/schemas/ServiceAddress' DeviceDetails: type: object required: - uid - kind - type - isSubscribed properties: uid: type: string description: DEVICE_UID, unique within the partner kind: $ref: '#/components/schemas/DeviceKindEnum' name: type: string type: type: string description: model name/number serialNumber: type: string isSubscribed: type: boolean description: If true, Virtual Peaker has enabled publishing of device data DeviceKindEnum: type: string enum: - HWH - TSTAT - BATTERY - EVSE - V2G - STORAGE-HVAC CommandState: type: object required: - state - time properties: state: type: string description: 'State of the command: * PENDING - scheduled but not started * IN_PROGRESS - when pending command hits start time * FAILED - something has prevented the command from being scheduled or started * OPT_OUT - device owner has specified that they don’t want to participate in an event * CANCELLED - utility has said they don’t want the event to happen * COMPLETED - end time of event has been reached without becoming CANCELLED or OPT_OUT' enum: - PENDING - IN_PROGRESS - FAILED - OPT_OUT - CANCELLED - COMPLETED description: type: string description: human readable status time: type: string format: date-time description: For more see, [RFC 3339, section 5.6](https://datatracker.ietf.org/doc/html/rfc3339#section-5.6). As an example, '2017-07-21T17:32:28Z'. The timezone is always zero UTC offset. refId: type: string description: Command Reference ID SignalSetting: type: object required: - key - value - time properties: key: type: string value: oneOf: - type: string - type: number time: type: string format: date-time description: For more see, [RFC 3339, section 5.6](https://datatracker.ietf.org/doc/html/rfc3339#section-5.6). As an example, '2017-07-21T17:32:28Z'. The timezone is always zero UTC offset. GroupDetails: type: object required: - uid - devices properties: uid: type: string description: A unique ID for that group within the partner's system deviceUids: type: array description: The uids of the devices within that group items: type: string name: type: string description: Human readable name of group Details: type: object required: - message properties: message: type: string description: A human readable response. Because there's no standard for what is included or how information should be formatted, this should not be parsed and utilized programmatically. EnergyInterval: type: object required: - value - time - duration properties: value: type: integer description: units of watt-hours (Wh) time: type: string format: date-time description: \`time\` should represent the start of the interval, ie the `value` should represent the energy consumed between `time` and `time + duration`. Intervals should be "clock-aligned" (meaning on even time-boundaries, eg 00:00:00, 00:15:00, 00:30:00, etc) For more see, [RFC 3339, section 5.6](https://datatracker.ietf.org/doc/html/rfc3339#section-5.6). As an example, '2017-07-21T17:32:28Z'. The timezone is always zero UTC offset. duration: type: integer description: Length of event in seconds. The value should be greater than 0. If a device partner imposes a maximum event duration, frequently it is either 8 or 24 hours. state: type: string description: Within the US, passed as a 2 letter abbreviation postalCode: type: string country: type: string description: A 2 letter indication of country. Following [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) responses: accepted: description: Message has been accepted for processing content: application/json: schema: $ref: '#/components/schemas/Details' badRequest: description: Request was not properly formatted content: application/json: schema: $ref: '#/components/schemas/Details' unauthorized: description: The request was not properly authorized content: application/json: schema: $ref: '#/components/schemas/Details' notFound: description: The data could not be found. Potentially the device hasn't reported this datapoint yet or the device is offline and the device partner does not store the most recent value content: application/json: schema: $ref: '#/components/schemas/Details' securitySchemes: device_partner_api_auth: type: oauth2 flows: clientCredentials: tokenUrl: https://example.com/oauth/token scopes: basic_partner_read_write: conducts all actions on the partners behalf device_partner_user_auth: type: oauth2 description: If using the OAuth onboarding method, this authentication method is used for the respective endpoints. Please the the FAQ for more details. flows: authorizationCode: authorizationUrl: https://example.com/oauth/authorize tokenUrl: https://example.com/oauth/token scopes: user_read: read details about new user