PowerDale is a small town with around 100 residents. Most houses have a smart meter installed that can save and send information about how much power a house is drawing/using at a given point in time.
There are three major providers of energy in town that charge different amounts for the power they supply.
- Dr Evil's Dark Energy
- The Green Eco
- Power for Everyone
JOI Energy is a new start-up in the energy industry. Rather than selling energy they want to differentiate themselves from the market by recording their customers' energy usage from their smart meters and recommending the best supplier to meet their needs.
You have been placed into their development team, whose current goal it is to produce an API which their customers and smart meters will interact with.
Unfortunately, two members of the team are on annual leave, and another one has called in sick! You are left with another ThoughtWorker to progress with the current user stories on the story wall. This is your chance to make an impact on the business, improve the code base and deliver value.
To trial the new JOI software 5 people from the JOI accounts team have agreed to test the service and share their energy data.
User | Smart Meter ID | Power Supplier |
---|---|---|
Sarah | smart-meter-0 |
Dr Evil's Dark Energy |
Peter | smart-meter-1 |
The Green Eco |
Charlie | smart-meter-2 |
Dr Evil's Dark Energy |
Andrea | smart-meter-3 |
Power for Everyone |
Alex | smart-meter-4 |
The Green Eco |
These values are used in the code and in the following examples too.
The project requires Java 21 or higher. If you have multiple JVMs, you might consider a tool such as sdkman to help manage them.
The project makes use of Gradle and uses the Gradle wrapper, which means you don't need Gradle installed.
List all the tasks that Gradle can run, such as build
and test
.
$ ./gradlew tasks
Compiles the project, runs the tests and then creates an executable JAR file.
$ ./gradlew build
Run the application using Java and the executable JAR file produced by the Gradle build
task. The application will be
listening to port 8080
.
$ java -jar build/libs/developer-joyofenergy-kotlin-all.jar
The tests can be executed as follows.
$ ./gradlew test
Run the application which will be listening on port 8080
.
$ ./gradlew run
Below is a list of API endpoints with their respective input and output. Please note that the application needs to be running for the following endpoints to work. For more information about how to run the application, please refer to run the application section above.
Endpoint
POST /readings/store
Example of body
{
"smartMeterId": <smartMeterId>,
"readings": [
{
"time": <time>,
"reading": <reading>
}
]
}
Parameters
Parameter | Description |
---|---|
smartMeterId |
One of the smart meters ids listed above |
time |
The date/time (as epoch seconds) when the reading was taken |
reading |
The power consumption in kW at the time of the reading |
Example readings
Date (GMT/UTC ) |
Epoch timestamp (seconds) | Power Reading (kW ) |
---|---|---|
2020-11-29 8:00 |
1606636800 | 0.0503 |
2020-11-29 8:01 |
1606636860 | 0.0621 |
2020-11-29 8:02 |
1606636920 | 0.0222 |
2020-11-29 8:03 |
1606636980 | 0.0423 |
2020-11-29 8:04 |
1606637040 | 0.0191 |
In the above example, the smart meter sampled readings, in kW
, every minute. Note that the reading is in kW
and
not kWH
, which means that each reading represents the power consumption at the reading time. If no power is being consumed
at the time of reading, then the reading value will be 0
. Given that 0
may introduce new challenges, we can assume
that there is always some consumption, and we will never have a 0
reading value. These readings are then sent by the
smart meter to the application using the HTTP endpoint.
There is a service in the application that calculates the kWH
from these readings.
The following POST request, is an example request using curl
, sends the readings shown in the table above.
$ curl \
-X POST \
-H "Content-Type: application/json" \
"http://localhost:8080/readings/store" \
-d '{"smartMeterId":"smart-meter-0","readings":[{"time":1606636800,"reading":0.0503},{"time":1606636860,"reading":0.0621},{"time":1606636920,"reading":0.0222},{"time":1606636980,"reading":0.0423},{"time":1606637040,"reading":0.0191}]}'
The above command should return:
{
"statusCode": {
"value": 200,
"description": "OK"
}
}
Endpoint:
GET /readings/read/<smartMeterId>
Parameters:
Parameter | Description |
---|---|
smartMeterId |
One of the smart meters ids listed above. |
Retrieving readings using curl
:
$ curl "http://localhost:8080/readings/read/smart-meter-0"
Example output
{
"statusCode": {
"value": 200,
"description": "OK"
},
"body": [
{
"time": 1606636800.000000000,
"reading": 0.0503
},
{
"time": 1606636860.000000000,
"reading": 0.0621
},
{
"time": 1606636920.000000000,
"reading": 0.0222
},
{
"time": 1606636980.000000000,
"reading": 0.0423
},
{
"time": 1606637040.000000000,
"reading": 0.0191
}
]
}
Endpoint:
GET /price-plans/compare-all/<smartMeterId>
Parameters:
Parameter | Description |
---|---|
smartMeterId |
One of the smart meters ids listed above. |
Retrieving readings using curl
:
$ curl "http://localhost:8080/price-plans/compare-all/smart-meter-0"
Example output:
{
"statusCode": {
"value": 200,
"description": "OK"
},
"body": {
"pricePlanId": "price-plan-0",
"pricePlanComparisons": {
"price-plan-0": 5.8800,
"price-plan-2": 0.5880
}
}
}
Endpoint:
GET /price-plans/recommend/<smartMeterId>[?limit=<limit>]
Parameters:
Parameter | Description |
---|---|
smartMeterId |
One of the smart meters ids listed above. |
limit |
(Optional) limit the number of plans to be displayed. |
Retrieving readings using curl
:
$ curl "http://localhost:8080/price-plans/recommend/smart-meter-0?limit=2"
Example output:
{
"statusCode": {
"value": 200,
"description": "OK"
},
"body": [
{
"first": "price-plan-2",
"second": 0.5880
},
{
"first": "price-plan-0",
"second": 5.8800
}
]
}