Using rocktopus

Using the rocktopus package

rocktopus is an R package designed to interact with the Octopus Energy API, allowing users to retrieve and process energy consumption data. This vignette provides a brief overview of how to set up and use the package.

To use the rocktopus package, you need to install it along with its dependencies. The following code snippet demonstrates how to install the package and its required libraries:

# Install the rocktopus package from GitHub
if(!require("needs")) install.packages(c("needs"), repos = "https://cloud.r-project.org/")
#> Loading required package: needs

library(needs)
needs("tidyverse", "data.table", "janitor", "lubridate", "httr", "glue", "purrr", "rlist")

devtools::install_github("julianflowers12/rocktopus", force = TRUE)
#> Using GitHub PAT from the git credential store.
#> Downloading GitHub repo julianflowers12/rocktopus@HEAD
#> 
#> ── R CMD build ─────────────────────────────────────────────────────────────────
#>      checking for file ‘/private/var/folders/bk/jrqs03tx5mq9s28mhml5xzhm0000gn/T/RtmpSZwcyJ/remotes2d1d5b8a88f1/julianflowers12-rocktopus-7eecdf9/DESCRIPTION’ ...  ✔  checking for file ‘/private/var/folders/bk/jrqs03tx5mq9s28mhml5xzhm0000gn/T/RtmpSZwcyJ/remotes2d1d5b8a88f1/julianflowers12-rocktopus-7eecdf9/DESCRIPTION’
#>   ─  preparing ‘rocktopus’:
#>        checking DESCRIPTION meta-information ...  ✔  checking DESCRIPTION meta-information
#>      Warning: /private/var/folders/bk/jrqs03tx5mq9s28mhml5xzhm0000gn/T/RtmpEj4V5j/Rbuild2d324c1dd2c2/rocktopus/man/txt_to_parquet_dataset.Rd:20: unknown macro '\item'
#>    Warning: /private/var/folders/bk/jrqs03tx5mq9s28mhml5xzhm0000gn/T/RtmpEj4V5j/Rbuild2d324c1dd2c2/rocktopus/man/txt_to_parquet_dataset.Rd:21: unknown macro '\item'
#>    Warning: /private/var/folders/bk/jrqs03tx5mq9s28mhml5xzhm0000gn/T/RtmpEj4V5j/Rbuild2d324c1dd2c2/rocktopus/man/txt_to_parquet_dataset.Rd:22: unknown macro '\item'
#>      Warning: /private/var/folders/bk/jrqs03tx5mq9s28mhml5xzhm0000gn/T/RtmpEj4V5j/Rbuild2d324c1dd2c2/rocktopus/man/txt_to_parquet_dataset.Rd:99: unknown macro '\item'
#>    Warning: /private/var/folders/bk/jrqs03tx5mq9s28mhml5xzhm0000gn/T/RtmpEj4V5j/Rbuild2d324c1dd2c2/rocktopus/man/txt_to_parquet_dataset.Rd:100: unknown macro '\item'
#>    Warning: /private/var/folders/bk/jrqs03tx5mq9s28mhml5xzhm0000gn/T/RtmpEj4V5j/Rbuild2d324c1dd2c2/rocktopus/man/txt_to_parquet_dataset.Rd:101: unknown macro '\item'
#>   ─  checking for LF line-endings in source and make files and shell scripts
#>   ─  checking for empty or unneeded directories
#>      Omitted ‘LazyData’ from DESCRIPTION
#>      Warning: /private/var/folders/bk/jrqs03tx5mq9s28mhml5xzhm0000gn/T/RtmpEj4V5j/Rbuild2d324c1dd2c2/rocktopus/man/txt_to_parquet_dataset.Rd:20: unknown macro '\item'
#>    Warning: /private/var/folders/bk/jrqs03tx5mq9s28mhml5xzhm0000gn/T/RtmpEj4V5j/Rbuild2d324c1dd2c2/rocktopus/man/txt_to_parquet_dataset.Rd:21: unknown macro '\item'
#>    Warning: /private/var/folders/bk/jrqs03tx5mq9s28mhml5xzhm0000gn/T/RtmpEj4V5j/Rbuild2d324c1dd2c2/rocktopus/man/txt_to_parquet_dataset.Rd:22: unknown macro '\item'
#>      Warning: /private/var/folders/bk/jrqs03tx5mq9s28mhml5xzhm0000gn/T/RtmpEj4V5j/Rbuild2d324c1dd2c2/rocktopus/man/txt_to_parquet_dataset.Rd:99: unknown macro '\item'
#>    Warning: /private/var/folders/bk/jrqs03tx5mq9s28mhml5xzhm0000gn/T/RtmpEj4V5j/Rbuild2d324c1dd2c2/rocktopus/man/txt_to_parquet_dataset.Rd:100: unknown macro '\item'
#>    Warning: /private/var/folders/bk/jrqs03tx5mq9s28mhml5xzhm0000gn/T/RtmpEj4V5j/Rbuild2d324c1dd2c2/rocktopus/man/txt_to_parquet_dataset.Rd:101: unknown macro '\item'
#>        NB: this package now depends on R (>= 4.1.0)
#>        WARNING: Added dependency on R >= 4.1.0 because package code uses the
#>      pipe |> or function shorthand \(...) syntax added in R 4.1.0.
#>      File(s) using such syntax:
#>        ‘get_consumption_details.R’ ‘get_meter_details.R’
#>        ‘get_mpan_details.R’ ‘get_tariff_details.R’ ‘get_tariffs.R’
#>        ‘http_helper.R’ ‘process_consumption_data.R’ ‘process_ev.R’
#>   ─  building ‘rocktopus_0.1.0.tar.gz’
#>      
#> 
#> Warning in i.p(...): installation of package
#> '/var/folders/bk/jrqs03tx5mq9s28mhml5xzhm0000gn/T//RtmpSZwcyJ/file2d1d6424b52a/rocktopus_0.1.0.tar.gz'
#> had non-zero exit status
library(rocktopus)

theme_set(ggthemes::theme_economist())

Setting Up Your Environment

To use the rocktopus package, you need to set up your Octopus Energy account and API key as environment variables. This allows the package to authenticate and access your energy consumption data securely.

You can obtain an API key from your Octopus Energy account settings. An API key can be generated by creating an Octopus developer account - https://octopus.energy/dashboard/new/accounts/personal-details/api-access. Once you have your account number and API key, you can set them as environment variables in your R session or in your .Renviron file.

# Get your Octopus Energy account and API key from your environment variables

acct <- Sys.getenv("OCTOPUS_ACC")
api_key <- Sys.getenv("OCTOPUS_API_KEY")

Retrieving Meter Details

To retrieve meter details associated with your Octopus Energy account, you can use the get_meter_details function from the rocktopus package. This function requires your account number and API key as inputs.

get_meter_details retrieves a list of details about your electricity and gas meters, including their unique identifiers (MPAN for electricity and MPRN for gas), meter serial numbers, and other relevant details.

# Retrieve meter details using the get_meter_details function

meter_details <- rocktopus::get_meter_info(acct, api_key)
#> Loading required package: httr2

We can now extract the MPAN and meter serial number for each property associated with your account. The get_electric_mpan function can be used to retrieve the MPAN and serial number for a specific property index.

# Extract MPAN and meter serial number for each property

mpan <- get_mpan_details(meter_details, property_index = params$property_index) 

Getting tariff information

We can also extract a table of tariff information.


tariffs <- get_tariff_info(api_key, acct, property_index = params$property_index)
#> Using `unnest_wider(value)`; elements have 3 names in common
#> Using `unnest_wider(value)`; elements have 5 names in common
#> Using `unnest_wider(value)`; elements have 5 names in common
#> Using `unnest_wider(value)`; elements have 3 names in common
#> Using `unnest_wider(value)`; elements have 5 names in common
tariffs |>
  head(10)
#> $standing_charges
#> # A tibble: 3 × 6
#>   name    value_exc_vat value_inc_vat valid_from         valid_to payment_method
#>   <chr>           <dbl>         <dbl> <chr>              <chr>    <lgl>         
#> 1 results          43.3          45.4 2025-06-30T23:00:… <NA>     NA            
#> 2 results          45.3          47.6 2025-03-31T23:00:… 2025-06… NA            
#> 3 results          46.5          48.8 2024-10-29T00:00:… 2025-03… NA            
#> 
#> $unit_rates
#> # A tibble: 100 × 6
#>    name    value_exc_vat value_inc_vat valid_from        valid_to payment_method
#>    <chr>           <dbl>         <dbl> <chr>             <chr>    <lgl>         
#>  1 results          6.67          7.00 2025-09-15T22:30… 2025-09… NA            
#>  2 results         27.6          29.0  2025-09-15T04:30… 2025-09… NA            
#>  3 results          6.67          7.00 2025-09-14T22:30… 2025-09… NA            
#>  4 results         27.6          29.0  2025-09-14T04:30… 2025-09… NA            
#>  5 results          6.67          7.00 2025-09-13T22:30… 2025-09… NA            
#>  6 results         27.6          29.0  2025-09-13T04:30… 2025-09… NA            
#>  7 results          6.67          7.00 2025-09-12T22:30… 2025-09… NA            
#>  8 results         27.6          29.0  2025-09-12T04:30… 2025-09… NA            
#>  9 results          6.67          7.00 2025-09-11T22:30… 2025-09… NA            
#> 10 results         27.6          29.0  2025-09-11T04:30… 2025-09… NA            
#> # ℹ 90 more rows
#> 
#> $export_rates
#> # A tibble: 3 × 6
#>   name    value_exc_vat value_inc_vat valid_from         valid_to payment_method
#>   <chr>           <dbl>         <dbl> <chr>              <chr>    <lgl>         
#> 1 results          15            15   2022-09-19T23:00:… <NA>     NA            
#> 2 results           7.5           7.5 2022-02-01T00:00:… 2022-09… NA            
#> 3 results           5.5           5.5 2019-05-15T23:00:… 2022-02… NA            
#> 
#> $gas_standing_charges
#> # A tibble: 0 × 0
#> 
#> $gas_unit_rates
#> # A tibble: 0 × 0

Creating Consumption Endpoints

The next step is to create endpoints for retrieving consumption data. The create_endpoint function constructs a URL for accessing consumption data based on the MPAN and meter serial number.

# Create consumption endpoints for each property

cons_url <- create_endpoint(mpan = mpan[1,3], serial = mpan[1,4])

Retrieving Consumption Data

To retrieve consumption data, you can use the get_cons function, which takes the consumption endpoint URL as input. This function returns a tibble containing the consumption details.


consumption_data <- get_cons(cons_url, api_key = api_key)
#> Using `unnest_wider(value)`; elements have 3 names in common

head(consumption_data)
#> # A tibble: 6 × 4
#>   name    consumption interval_start            interval_end             
#>   <chr>         <dbl> <chr>                     <chr>                    
#> 1 results       3.74  2025-09-13T00:30:00+01:00 2025-09-13T01:00:00+01:00
#> 2 results       4.60  2025-09-13T00:00:00+01:00 2025-09-13T00:30:00+01:00
#> 3 results       0.009 2025-09-12T23:30:00+01:00 2025-09-13T00:00:00+01:00
#> 4 results       0     2025-09-12T23:00:00+01:00 2025-09-12T23:30:00+01:00
#> 5 results       0     2025-09-12T22:30:00+01:00 2025-09-12T23:00:00+01:00
#> 6 results       0     2025-09-12T22:00:00+01:00 2025-09-12T22:30:00+01:00

Processing Consumption Data

To process the retrieved consumption data, you can use the process_consumption_data function. This function cleans and formats the data, making it easier to analyze and visualize.


processed_data <- process_consumption_data(consumption_data) |>
  arrange(desc(date))
#> Warning: There were 2 warnings in `mutate()`.
#> The first warning was:
#> ℹ In argument: `time = hm(str_sub(interval_start, 12, 16))`.
#> Caused by warning in `.parse_hms()`:
#> ! Some strings failed to parse
#> ℹ Run `dplyr::last_dplyr_warnings()` to see the 1 remaining warning.

head(processed_data)
#> # A tibble: 6 × 14
#>      wk name    consumption interval_start      interval_end        interval
#>   <dbl> <chr>         <dbl> <dttm>              <dttm>              <drtn>  
#> 1    37 results       3.74  2025-09-12 23:30:00 2025-09-13 00:00:00 30 mins 
#> 2    37 results       4.60  2025-09-12 23:00:00 2025-09-12 23:30:00 30 mins 
#> 3    37 results       0.009 2025-09-12 22:30:00 2025-09-12 23:00:00 30 mins 
#> 4    37 results       0     2025-09-12 22:00:00 2025-09-12 22:30:00 30 mins 
#> 5    37 results       0     2025-09-12 21:30:00 2025-09-12 22:00:00 30 mins 
#> 6    37 results       0     2025-09-12 21:00:00 2025-09-12 21:30:00 30 mins 
#> # ℹ 8 more variables: date <date>, time <Period>, start <dttm>, hour_st <int>,
#> #   hour_end <int>, month <ord>, year <dbl>, peak <chr>

Trend in off peak consumption

processed_data |>
  mutate(day = lubridate::wday(date, label = TRUE),
         month = lubridate::month(date, label = TRUE),
         year = year(date), 
         sun = case_when(day == "Sun" ~ 'sun', TRUE ~ 'other')) |>
  filter(peak == "off_peak", date >= "2025-04-01") |>
  select(date, day, sun, consumption) |>
  group_by(date, day) |>
  reframe(sumconsmption = mean(consumption, na.rm = TRUE)) |>
  #left_join(ev_data, by = "date") |>
  ggplot(aes(x = date, y = sumconsmption, colour = day)) +
  #geom_point() +
  geom_smooth(method = "loess", se = FALSE, aes(lty = day), span = 0.5) +
  theme_classic()
#> `geom_smooth()` using formula = 'y ~ x'

Visualizing Consumption Data

You can visualize the processed consumption data using ggplot2. The following code snippet demonstrates how to create a bar plot showing electricity consumption over time, categorized by peak and off-peak periods.


processed_data |>
  group_by(date, peak) |>
  summarise(total = sum(consumption, na.rm = TRUE)) |>
  filter(date > "2024-08-31") |>
  ggplot(aes(x = date, y = total, colour = peak)) +
  geom_point() +
  geom_smooth(method = "gam", se = FALSE, aes(fill = peak), alpha = 0.2) +
  labs(
    title = "Electricity Consumption",
    x = "Date",
    y = "Consumption (kWh)",
    fill = "Peak"
  ) +
  scale_x_date(date_labels = "%Y-%m-%d", date_breaks = "1 month") +
  theme(axis.text.x= element_text(angle = 90, hjust = 1, size = 10),
        title = element_text(size = 14, face = "bold"),
        legend.position = "bottom", 
        plot.title.position = "plot") +
  geom_vline(xintercept = as.Date(c("2025-05-18", "2025-04-29")), linetype = "dashed", color = "black") +
  facet_wrap(~ peak, scales = "free")
#> `summarise()` has grouped output by 'date'. You can override using the
#> `.groups` argument.
#> `geom_smooth()` using formula = 'y ~ s(x, bs = "cs")'


off_peak <- processed_data |>
  filter(date > "2024-08-31") |>
  group_by(year = year(date), month = month(date), peak) |>
  reframe(mon_cons = sum(consumption))  |>
  pivot_wider(names_from = peak, values_from = mon_cons) |>
  mutate(summer = mean(peak[month %in% c(4:9)]), 
         winter = mean(peak[month %in% c(10:12, 1:3)]), 
         summer1 = mean(off_peak[month %in% c(4:9)]), 
         winter1 = mean(off_peak[month %in% c(10:12, 1:3)]),
         period = lubridate::my(paste0(month,"-", year))) |>
  ggplot(aes(x = period, y = off_peak)) +
  geom_point() +
  geom_smooth(method = "gam", se = FALSE, alpha = 0.2) +
  geom_line(aes(y = summer1), colour = "red") +
  geom_line(aes(y = winter1), colour = "blue") +
  labs(
    title = "Electricity Consumption",
    x = "Date",
    y = "Consumption (kWh)",
    fill = "Peak"
  ) +
  scale_x_date(date_labels = "%Y-%m-%d", date_breaks = "1 month") +
  theme(axis.text.x= element_text(angle = 90, hjust = 1, size = 10),
        title = element_text(size = 14, face = "bold"),
        legend.position = "bottom", 
        plot.title.position = "plot") +
  geom_vline(xintercept = as.Date(c("2025-05-18", "2025-04-29")), linetype = "dashed", color = "black")

peak <- processed_data |>
  filter(date > "2024-08-31") |>
  group_by(year = year(date), month = month(date), peak) |>
  reframe(mon_cons = sum(consumption))  |>
  pivot_wider(names_from = peak, values_from = mon_cons) |>
  mutate(summer = mean(peak[month %in% c(4:9)]), 
         winter = mean(peak[month %in% c(10:12, 1:3)]), 
         summer1 = mean(off_peak[month %in% c(4:9)]), 
         winter1 = mean(off_peak[month %in% c(10:12, 1:3)]),
         period = lubridate::my(paste0(month,"-", year))) |>
  ggplot(aes(x = period, y = peak)) +
  geom_point() +
  geom_smooth(method = "gam", se = FALSE, aes(fill = peak), alpha = 0.2) +
  geom_line(aes(y = summer), colour = "red") +
  geom_line(aes(y = winter), colour = "blue") +
  labs(
    title = "Electricity Consumption",
    x = "Date",
    y = "Consumption (kWh)",
    fill = "Peak"
  ) +
  scale_x_date(date_labels = "%Y-%m-%d", date_breaks = "1 month") +
  theme(axis.text.x= element_text(angle = 90, hjust = 1, size = 10),
        title = element_text(size = 14, face = "bold"),
        legend.position = "bottom", 
        plot.title.position = "plot") +
  geom_vline(xintercept = as.Date(c("2025-05-18", "2025-04-29")), linetype = "dashed", color = "black")

library(patchwork)


off_peak + peak + plot_layout(ncol = 1)
#> `geom_smooth()` using formula = 'y ~ s(x, bs = "cs")'
#> `geom_smooth()` using formula = 'y ~ s(x, bs = "cs")'
#> Warning: The following aesthetics were dropped during statistical transformation: fill.
#> ℹ This can happen when ggplot fails to infer the correct grouping structure in
#>   the data.
#> ℹ Did you forget to specify a `group` aesthetic or to convert a numerical
#>   variable into a factor?

Getting aggregated consumption data

Weekly exported KWh

library(rocktopus)
agg_url <- create_endpoint_agg(mpan = mpan[2,3], serial = mpan[2,4], group_by = "month")

t <- get_cons(agg_url, api_key)

octo_request <- function(url, api_key) {
  httr2::request(url) |>
    httr2::req_auth_basic(username = api_key, password = "") |>
    httr2::req_headers(
      "User-Agent" = sprintf(
        "rocktopus/%s (R httr2; https://github.com/julianflowers12/rocktopus)",
        utils::packageVersion("rocktopus")
      )
    )
}


resp <- octo_request(agg_url, api_key = Sys.getenv("OCTOPUS_API_KEY")) |>
        httr2::req_perform() |>
        httr2::resp_body_json()

response <- resp |>
        tibble::enframe() |>
        dplyr::filter(name == "results") |>
        tidyr::unnest(value) |>
        tidyr::unnest_auto(value)

export <- get_cons(agg_url, api_key)

octo_request(agg_url, api_key)

agg_consumption_data <- get_cons(agg_url, api_key = api_key) 
  mutate(date_start = ymd(str_sub(interval_start, 1, 10)))

which.max(agg_consumption_data$consumption)

agg_consumption_data |>
  select(date_start, export = consumption) |>
  filter(date_start > params$start) |>
  ggplot(aes(x = date_start, y = export)) +
  geom_col() +
  geom_smooth(method = "loess", lty= "dashed", colour = "red", se = FALSE) +
  scale_x_date(date_labels = "%Y-%m", date_breaks = "1 month") +
  theme(axis.text.x = element_text(angle = 90, hjust = 1, size = 10),
        title = element_text(size = 14, face = "bold"),
        legend.position = "bottom", 
        plot.title.position = "plot") +
    ggtitle(glue::glue("Solar export by ", {params$frequency}))