Resources
Now that we have a site, we can add flexible resources to it. This step is crucial for providing flexibility and enables our platform to accurately gauge activation rates, resulting in more activations for your resources. đ
Typesâ
There's a long list of resources (devices, assets) that can be flexible, the most common ones are listed below.
The Powernaut platform is indifferent to the type of resource, meaning all types can be registered. However, the type of asset is a required data point to adequately activate flexibility.
- Batteries
- Electric Vehicles (EV)
- Charging stations for EVs
- Heat pumps
- Solar panels
- Electrical boilers
- Windmill
Managingâ
Creatingâ
To create a new flexible resource, you need at least its site, type and power constraints.
- Python
- JavaScript
- Java
- Go
- C#
- cURL
import requests
import json
url = "https://api.powernaut.io/v1/connect/resources"
payload = json.dumps({
"site_id": "<uuid>",
"type": "electric_vehicle_charging_point",
"power": {
"active": {
"minimum": "0",
"maximum": "7.4"
}
}
})
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': 'Bearer <token>'
}
response = requests.request("POST", url, headers=headers, data=payload)
print(response.text)
const myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
myHeaders.append("Accept", "application/json");
myHeaders.append("Authorization", "Bearer <token>");
const raw = JSON.stringify({
"site_id": "<uuid>",
"type": "electric_vehicle_charging_point",
"power": {
"active": {
"minimum": "0",
"maximum": "7.4"
}
}
});
const requestOptions = {
method: "POST",
headers: myHeaders,
body: raw,
redirect: "follow"
};
fetch("https://api.powernaut.io/v1/connect/resources", requestOptions)
.then((response) => response.text())
.then((result) => console.log(result))
.catch((error) => console.error(error));
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{\"site_id\":\"<uuid>\",\"type\":\"electric_vehicle_charging_point\",\"power\":{\"active\":{\"minimum\":\"0\",\"maximum\":\"7.4\"}}}");
Request request = new Request.Builder()
.url("https://api.powernaut.io/v1/connect/resources")
.method("POST", body)
.addHeader("Content-Type", "application/json")
.addHeader("Accept", "application/json")
.addHeader("Authorization", "Bearer <token>")
.build();
Response response = client.newCall(request).execute();
package main
import (
"fmt"
"strings"
"net/http"
"io/ioutil"
)
func main() {
url := "https://api.powernaut.io/v1/connect/resources"
method := "POST"
payload := strings.NewReader(`{"site_id":"<uuid>","type":"electric_vehicle_charging_point","power":{"active":{"minimum":"0","maximum":"7.4"}}}`)
client := &http.Client {
}
req, err := http.NewRequest(method, url, payload)
if err != nil {
fmt.Println(err)
return
}
req.Header.Add("Content-Type", "application/json")
req.Header.Add("Accept", "application/json")
req.Header.Add("Authorization", "Bearer <token>")
res, err := client.Do(req)
if err != nil {
fmt.Println(err)
return
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(body))
}
var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://api.powernaut.io/v1/connect/resources");
request.Headers.Add("Accept", "application/json");
request.Headers.Add("Authorization", "Bearer <token>");
var content = new StringContent("{\"site_id\":\"<uuid>\",\"type\":\"electric_vehicle_charging_point\",\"power\":{\"active\":{\"minimum\":\"0\",\"maximum\":\"7.4\"}}}", null, "application/json");
request.Content = content;
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
Console.WriteLine(await response.Content.ReadAsStringAsync());
curl --location 'https://api.powernaut.io/v1/connect/resources' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer <token>' \
--data '{"site_id":"<uuid>","type":"electric_vehicle_charging_point","power":{"active":{"minimum":"0","maximum":"7.4"}}}'
You'll notice that throughout the API, numbers are communicated with a string
datatype, instead of the usual number
.
This is on purpose, and is done to avoid floating point errors.
Number types will be rejected by the API.
Updateâ
You can also update a resource, e.g. update its name.
- Python
- JavaScript
- Java
- Go
- C#
- cURL
import requests
import json
url = "https://api.powernaut.io/v1/connect/resources/<uuid>"
payload = json.dumps({
"name": "Home Battery"
})
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': 'Bearer <token>'
}
response = requests.request("PATCH", url, headers=headers, data=payload)
print(response.text)
const myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
myHeaders.append("Accept", "application/json");
myHeaders.append("Authorization", "Bearer <token>");
const raw = JSON.stringify({
"name": "Home Battery"
});
const requestOptions = {
method: "PATCH",
headers: myHeaders,
body: raw,
redirect: "follow"
};
fetch("https://api.powernaut.io/v1/connect/resources/<uuid>", requestOptions)
.then((response) => response.text())
.then((result) => console.log(result))
.catch((error) => console.error(error));
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{\"name\":\"Home Battery\"}");
Request request = new Request.Builder()
.url("https://api.powernaut.io/v1/connect/resources/<uuid>")
.method("PATCH", body)
.addHeader("Content-Type", "application/json")
.addHeader("Accept", "application/json")
.addHeader("Authorization", "Bearer <token>")
.build();
Response response = client.newCall(request).execute();
package main
import (
"fmt"
"strings"
"net/http"
"io/ioutil"
)
func main() {
url := "https://api.powernaut.io/v1/connect/resources/<uuid>"
method := "PATCH"
payload := strings.NewReader(`{"name":"Home Battery"}`)
client := &http.Client {
}
req, err := http.NewRequest(method, url, payload)
if err != nil {
fmt.Println(err)
return
}
req.Header.Add("Content-Type", "application/json")
req.Header.Add("Accept", "application/json")
req.Header.Add("Authorization", "Bearer <token>")
res, err := client.Do(req)
if err != nil {
fmt.Println(err)
return
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(body))
}
var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Patch, "https://api.powernaut.io/v1/connect/resources/<uuid>");
request.Headers.Add("Accept", "application/json");
request.Headers.Add("Authorization", "Bearer <token>");
var content = new StringContent("{\"name\":\"Home Battery\"}", null, "application/json");
request.Content = content;
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
Console.WriteLine(await response.Content.ReadAsStringAsync());
curl --location --request PATCH 'https://api.powernaut.io/v1/connect/resources/<uuid>' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer <token>' \
--data '{"name":"Home Battery"}'
Deleteâ
Deleting a resource is done similarly to sites:
- Python
- JavaScript
- Java
- Go
- C#
- cURL
import requests
url = "https://api.powernaut.io/v1/connect/resources/<uuid>"
payload = {}
headers = {
'Accept': 'application/json',
'Authorization': 'Bearer <token>'
}
response = requests.request("DELETE", url, headers=headers, data=payload)
print(response.text)
const myHeaders = new Headers();
myHeaders.append("Accept", "application/json");
myHeaders.append("Authorization", "Bearer <token>");
const requestOptions = {
method: "DELETE",
headers: myHeaders,
redirect: "follow"
};
fetch("https://api.powernaut.io/v1/connect/resources/<uuid>", requestOptions)
.then((response) => response.text())
.then((result) => console.log(result))
.catch((error) => console.error(error));
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
MediaType mediaType = MediaType.parse("text/plain");
RequestBody body = RequestBody.create(mediaType, "");
Request request = new Request.Builder()
.url("https://api.powernaut.io/v1/connect/resources/<uuid>")
.method("DELETE", body)
.addHeader("Accept", "application/json")
.addHeader("Authorization", "Bearer <token>")
.build();
Response response = client.newCall(request).execute();
package main
import (
"fmt"
"net/http"
"io/ioutil"
)
func main() {
url := "https://api.powernaut.io/v1/connect/resources/<uuid>"
method := "DELETE"
client := &http.Client {
}
req, err := http.NewRequest(method, url, nil)
if err != nil {
fmt.Println(err)
return
}
req.Header.Add("Accept", "application/json")
req.Header.Add("Authorization", "Bearer <token>")
res, err := client.Do(req)
if err != nil {
fmt.Println(err)
return
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(body))
}
var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Delete, "https://api.powernaut.io/v1/connect/resources/<uuid>");
request.Headers.Add("Accept", "application/json");
request.Headers.Add("Authorization", "Bearer <token>");
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
Console.WriteLine(await response.Content.ReadAsStringAsync());
curl --location --request DELETE 'https://api.powernaut.io/v1/connect/resources/<uuid>' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer <token>'
Deleting a resource is a permanent action and will delete all future flexibility. Ensure this is the intended action. The API will reject the creation of bids and baselines for resources that have been deleted.
In the case that a bid was selected for a procurement in the future, this bid will also be cancelled and the resource will not be activated.
Listingâ
You can fetch a resource by its ID, or list all of them.
Listing all Resourcesâ
- Python
- JavaScript
- Java
- Go
- C#
- cURL
import requests
url = "https://api.powernaut.io/v1/connect/resources"
payload = {}
headers = {
'Accept': 'application/json',
'Authorization': 'Bearer <token>'
}
response = requests.request("GET", url, headers=headers, data=payload)
print(response.text)
const myHeaders = new Headers();
myHeaders.append("Accept", "application/json");
myHeaders.append("Authorization", "Bearer <token>");
const requestOptions = {
method: "GET",
headers: myHeaders,
redirect: "follow"
};
fetch("https://api.powernaut.io/v1/connect/resources", requestOptions)
.then((response) => response.text())
.then((result) => console.log(result))
.catch((error) => console.error(error));
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
MediaType mediaType = MediaType.parse("text/plain");
RequestBody body = RequestBody.create(mediaType, "");
Request request = new Request.Builder()
.url("https://api.powernaut.io/v1/connect/resources")
.method("GET", body)
.addHeader("Accept", "application/json")
.addHeader("Authorization", "Bearer <token>")
.build();
Response response = client.newCall(request).execute();
package main
import (
"fmt"
"net/http"
"io/ioutil"
)
func main() {
url := "https://api.powernaut.io/v1/connect/resources"
method := "GET"
client := &http.Client {
}
req, err := http.NewRequest(method, url, nil)
if err != nil {
fmt.Println(err)
return
}
req.Header.Add("Accept", "application/json")
req.Header.Add("Authorization", "Bearer <token>")
res, err := client.Do(req)
if err != nil {
fmt.Println(err)
return
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(body))
}
var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://api.powernaut.io/v1/connect/resources");
request.Headers.Add("Accept", "application/json");
request.Headers.Add("Authorization", "Bearer <token>");
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
Console.WriteLine(await response.Content.ReadAsStringAsync());
curl --location 'https://api.powernaut.io/v1/connect/resources' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer <token>'
Listing a Site's Resourcesâ
- Python
- JavaScript
- Java
- Go
- C#
- cURL
import requests
url = "https://api.powernaut.io/v1/connect/resources"
payload = {}
headers = {
'Accept': 'application/json',
'Authorization': 'Bearer <token>'
}
response = requests.request("GET", url, headers=headers, data=payload)
print(response.text)
const myHeaders = new Headers();
myHeaders.append("Accept", "application/json");
myHeaders.append("Authorization", "Bearer <token>");
const requestOptions = {
method: "GET",
headers: myHeaders,
redirect: "follow"
};
fetch("https://api.powernaut.io/v1/connect/resources", requestOptions)
.then((response) => response.text())
.then((result) => console.log(result))
.catch((error) => console.error(error));
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
MediaType mediaType = MediaType.parse("text/plain");
RequestBody body = RequestBody.create(mediaType, "");
Request request = new Request.Builder()
.url("https://api.powernaut.io/v1/connect/resources")
.method("GET", body)
.addHeader("Accept", "application/json")
.addHeader("Authorization", "Bearer <token>")
.build();
Response response = client.newCall(request).execute();
package main
import (
"fmt"
"net/http"
"io/ioutil"
)
func main() {
url := "https://api.powernaut.io/v1/connect/resources"
method := "GET"
client := &http.Client {
}
req, err := http.NewRequest(method, url, nil)
if err != nil {
fmt.Println(err)
return
}
req.Header.Add("Accept", "application/json")
req.Header.Add("Authorization", "Bearer <token>")
res, err := client.Do(req)
if err != nil {
fmt.Println(err)
return
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(body))
}
var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://api.powernaut.io/v1/connect/resources");
request.Headers.Add("Accept", "application/json");
request.Headers.Add("Authorization", "Bearer <token>");
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
Console.WriteLine(await response.Content.ReadAsStringAsync());
curl --location 'https://api.powernaut.io/v1/connect/resources' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer <token>'
Statusâ
We continuously monitor all resources through automated health checks. The status of all these health checks is aggregated into a single, high-level resource status, providing a quick overview of a resource's operational readiness.
Status | Meaning |
---|---|
onboarding | The resource has been added to the platform, but no meter data has been received yet. |
operational | All health checks are successful. |
degraded | Some health checks are failing. This causes suboptimal steering. |
suspended | The resource has failing health checks. |
âšī¸ Note on a Future Update: For now, the statuses are purely informational to help you troubleshoot and resolve issues with your resources. In a future update, resources with status
suspended
will no longer be eligible to have their flexibility bids activated.
Health checksâ
The Powernaut platform performs several health checks on each resource to ensure it's functioning properly and can deliver flexibility as promised.
Availabilityâ
The availability health check monitors if the Powernaut platform is receiving meter data from the resource.
Status | Meaning |
---|---|
initial | Health check hasn't run yet. |
online | The platform is receiving meter data. |
offline | The platform hasn't received any data for the past 15 minutes. |
Activation complianceâ
đ§ We are working to add this health check
The activation compliance health check monitors if resources are accurately delivering as much flex as was requested.
Status | Meaning |
---|---|
initial | Health check hasn't run yet. |
compliant | The resource is delivering between 60-140% of the energy requested. |
not_compliant | Delivered energy deviates more than 40% from the energy requested for 4 consecutive quarter hours. |
Baselines too inaccurateâ
đ§ We are working to add this health check
This health check monitors the accuracy of the baselines for your site and resources. Without accurate baselines we cannot verify the delivered flexibility accurately.
Keep the following in mind:
- For connection points, baselines need to be accurate even when their individual resources are not bidding, as they might be part of a larger pool that is being balanced.
- You can update baselines for any resource on a connection point up until the moment the first resource on that connection point gets activated. We encourage you to take full advantage of this window to finalise your baselines for maximum accuracy and fair settlement.
Status | Meaning |
---|---|
initial | Health check hasn't run yet. |
accurate | Baseline is accurate. |
inaccurate | Normalised MAE* of corresponding site's baseline is larger than 0.75 for over 1 day. |
* MAE stands for Mean Absolute Error. This is the average of the difference between the baseline and actual meter data. We normalise MAE by dividing by the average absolute consumption.
No Bidsâ
đ§ We are working to add this health check
This health check monitors the bids made by a resource. If a resource has not made any bids for a long time, something is probably wrong. To see how we expect resources to bid have a look at our examples.
Status | Meaning |
---|---|
initial | Health check hasn't run yet. |
active | Resource is actively making bids. |
no_bids | Resource has not made any bids for a longer period. |
When we flag resources for not bidding depends on the resource type:
- Solar Panels: after 1 day
- Batteries: after 1 day
- Electric Vehicles: after 5 days
Meter data anomaliesâ
đ§ We are working to add this health check
This health check monitors if resources submit weird meter data.
Status | Meaning |
---|---|
initial | Health check hasn't run yet. |
ok | Resource has logged meter data correctly. |
rare_anomaly | Less than 1% of meter data is outside of bounds in the last 1 day. |
frequent_anomaly | More than 1% of meter data is outside of bounds in the last 1 day. |